mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
2060 lines
59 KiB
JavaScript
2060 lines
59 KiB
JavaScript
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
Cu.import("resource://gre/modules/PageThumbs.jsm");
|
|
|
|
/**
|
|
* Constants
|
|
*/
|
|
|
|
// BrowserUI.update(state) constants. Currently passed in
|
|
// but update doesn't pay attention to them. Can we remove?
|
|
const TOOLBARSTATE_LOADING = 1;
|
|
const TOOLBARSTATE_LOADED = 2;
|
|
|
|
// delay for ContextUI's dismissWithDelay
|
|
const kHideContextAndTrayDelayMsec = 3000;
|
|
|
|
// delay when showing the tab bar briefly as a new tab opens
|
|
const kNewTabAnimationDelayMsec = 500;
|
|
|
|
|
|
// Page for which the start UI is shown
|
|
const kStartOverlayURI = "about:start";
|
|
|
|
/**
|
|
* Cache of commonly used elements.
|
|
*/
|
|
|
|
let Elements = {};
|
|
[
|
|
["contentShowing", "bcast_contentShowing"],
|
|
["urlbarState", "bcast_urlbarState"],
|
|
["windowState", "bcast_windowState"],
|
|
["mainKeyset", "mainKeyset"],
|
|
["stack", "stack"],
|
|
["tabList", "tabs"],
|
|
["tabs", "tabs-container"],
|
|
["controls", "browser-controls"],
|
|
["panelUI", "panel-container"],
|
|
["startUI", "start-container"],
|
|
["tray", "tray"],
|
|
["toolbar", "toolbar"],
|
|
["browsers", "browsers"],
|
|
["navbar", "navbar"],
|
|
["contextappbar", "contextappbar"],
|
|
["contentViewport", "content-viewport"],
|
|
["progress", "progress-control"],
|
|
["progressContainer", "progress-container"],
|
|
["contentNavigator", "content-navigator"],
|
|
["aboutFlyout", "about-flyoutpanel"],
|
|
["prefsFlyout", "prefs-flyoutpanel"],
|
|
["syncFlyout", "sync-flyoutpanel"]
|
|
].forEach(function (aElementGlobal) {
|
|
let [name, id] = aElementGlobal;
|
|
XPCOMUtils.defineLazyGetter(Elements, name, function() {
|
|
return document.getElementById(id);
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Cache of commonly used string bundles.
|
|
*/
|
|
|
|
var Strings = {};
|
|
[
|
|
["browser", "chrome://browser/locale/browser.properties"],
|
|
["brand", "chrome://branding/locale/brand.properties"]
|
|
].forEach(function (aStringBundle) {
|
|
let [name, bundle] = aStringBundle;
|
|
XPCOMUtils.defineLazyGetter(Strings, name, function() {
|
|
return Services.strings.createBundle(bundle);
|
|
});
|
|
});
|
|
|
|
var BrowserUI = {
|
|
get _edit() { return document.getElementById("urlbar-edit"); },
|
|
get _back() { return document.getElementById("cmd_back"); },
|
|
get _forward() { return document.getElementById("cmd_forward"); },
|
|
|
|
lastKnownGoodURL: "", //used when the user wants to escape unfinished url entry
|
|
init: function() {
|
|
// listen content messages
|
|
messageManager.addMessageListener("DOMTitleChanged", this);
|
|
messageManager.addMessageListener("DOMWillOpenModalDialog", this);
|
|
messageManager.addMessageListener("DOMWindowClose", this);
|
|
|
|
messageManager.addMessageListener("Browser:OpenURI", this);
|
|
messageManager.addMessageListener("Browser:SaveAs:Return", this);
|
|
messageManager.addMessageListener("Content:StateChange", this);
|
|
|
|
// listening escape to dismiss dialog on VK_ESCAPE
|
|
window.addEventListener("keypress", this, true);
|
|
|
|
window.addEventListener("MozPrecisePointer", this, true);
|
|
window.addEventListener("MozImprecisePointer", this, true);
|
|
|
|
Services.prefs.addObserver("browser.cache.disk_cache_ssl", this, false);
|
|
Services.prefs.addObserver("browser.urlbar.formatting.enabled", this, false);
|
|
Services.prefs.addObserver("browser.urlbar.trimURLs", this, false);
|
|
Services.obs.addObserver(this, "metro_viewstate_changed", false);
|
|
|
|
this._edit.inputField.controllers.insertControllerAt(0, this._copyCutURIController);
|
|
|
|
// Init core UI modules
|
|
ContextUI.init();
|
|
StartUI.init();
|
|
PanelUI.init();
|
|
FlyoutPanelsUI.init();
|
|
PageThumbs.init();
|
|
SettingsCharm.init();
|
|
|
|
// show the right toolbars, awesomescreen, etc for the os viewstate
|
|
BrowserUI._adjustDOMforViewState();
|
|
|
|
// We can delay some initialization until after startup. We wait until
|
|
// the first page is shown, then dispatch a UIReadyDelayed event.
|
|
messageManager.addMessageListener("pageshow", function() {
|
|
if (getBrowser().currentURI.spec == "about:blank")
|
|
return;
|
|
|
|
messageManager.removeMessageListener("pageshow", arguments.callee, true);
|
|
|
|
setTimeout(function() {
|
|
let event = document.createEvent("Events");
|
|
event.initEvent("UIReadyDelayed", true, false);
|
|
window.dispatchEvent(event);
|
|
}, 0);
|
|
});
|
|
|
|
// Only load IndexedDB.js when we actually need it. A general fix will happen in bug 647079.
|
|
messageManager.addMessageListener("IndexedDB:Prompt", function(aMessage) {
|
|
return IndexedDB.receiveMessage(aMessage);
|
|
});
|
|
|
|
// Delay the panel UI and Sync initialization
|
|
window.addEventListener("UIReadyDelayed", function(aEvent) {
|
|
Util.dumpLn("* delay load started...");
|
|
window.removeEventListener(aEvent.type, arguments.callee, false);
|
|
|
|
// Login Manager and Form History initialization
|
|
Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
|
|
Cc["@mozilla.org/satchel/form-history;1"].getService(Ci.nsIFormHistory2);
|
|
|
|
messageManager.addMessageListener("Browser:MozApplicationManifest", OfflineApps);
|
|
|
|
try {
|
|
Downloads.init();
|
|
DialogUI.init();
|
|
FormHelperUI.init();
|
|
FindHelperUI.init();
|
|
PdfJs.init();
|
|
#ifdef MOZ_SERVICES_SYNC
|
|
Sync.init();
|
|
#endif
|
|
} catch(ex) {
|
|
Util.dumpLn("Exception in delay load module:", ex.message);
|
|
}
|
|
|
|
BrowserUI._pullDesktopControlledPrefs();
|
|
|
|
// check for left over crash reports and submit them if found.
|
|
if (BrowserUI.startupCrashCheck()) {
|
|
Browser.selectedTab = BrowserUI.newOrSelectTab("about:crash");
|
|
}
|
|
Util.dumpLn("* delay load complete.");
|
|
}, false);
|
|
|
|
#ifndef MOZ_OFFICIAL_BRANDING
|
|
setTimeout(function() {
|
|
let startup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup).getStartupInfo();
|
|
for (let name in startup) {
|
|
if (name != "process")
|
|
Services.console.logStringMessage("[timing] " + name + ": " + (startup[name] - startup.process) + "ms");
|
|
}
|
|
}, 3000);
|
|
#endif
|
|
},
|
|
|
|
uninit: function() {
|
|
messageManager.removeMessageListener("Browser:MozApplicationManifest", OfflineApps);
|
|
|
|
PanelUI.uninit();
|
|
StartUI.uninit();
|
|
Downloads.uninit();
|
|
SettingsCharm.uninit();
|
|
messageManager.removeMessageListener("Content:StateChange", this);
|
|
PageThumbs.uninit();
|
|
},
|
|
|
|
|
|
/*********************************
|
|
* Content visibility
|
|
*/
|
|
|
|
get isContentShowing() {
|
|
return Elements.contentShowing.getAttribute("disabled") != true;
|
|
},
|
|
|
|
showContent: function showContent(aURI) {
|
|
DialogUI.closeAllDialogs();
|
|
StartUI.update(aURI);
|
|
ContextUI.dismissTabs();
|
|
ContextUI.dismissAppbar();
|
|
FlyoutPanelsUI.hide();
|
|
PanelUI.hide();
|
|
},
|
|
|
|
/*********************************
|
|
* Crash reporting
|
|
*/
|
|
|
|
get CrashSubmit() {
|
|
delete this.CrashSubmit;
|
|
Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
|
|
return this.CrashSubmit;
|
|
},
|
|
|
|
startupCrashCheck: function startupCrashCheck() {
|
|
#ifdef MOZ_CRASHREPORTER
|
|
if (!Services.prefs.getBoolPref("app.reportCrashes"))
|
|
return false;
|
|
if (CrashReporter.enabled) {
|
|
var lastCrashID = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).lastRunCrashID;
|
|
if (lastCrashID.length) {
|
|
this.CrashSubmit.submit(lastCrashID);
|
|
return true;
|
|
}
|
|
}
|
|
#endif
|
|
return false;
|
|
},
|
|
|
|
|
|
/*********************************
|
|
* Navigation
|
|
*/
|
|
|
|
update: function(aState) {
|
|
this._updateToolbar();
|
|
},
|
|
|
|
getDisplayURI: function(browser) {
|
|
let uri = browser.currentURI;
|
|
try {
|
|
uri = gURIFixup.createExposableURI(uri);
|
|
} catch (ex) {}
|
|
|
|
return uri.spec;
|
|
},
|
|
|
|
/**
|
|
* Some prefs that have consequences in both Metro and Desktop such as
|
|
* app-update prefs, are automatically pulled from Desktop here.
|
|
*/
|
|
_pullDesktopControlledPrefs: function() {
|
|
function pullDesktopControlledPrefType(prefType, prefFunc) {
|
|
try {
|
|
registry.create(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
|
|
"Software\\Mozilla\\Firefox\\Metro\\Prefs\\" + prefType,
|
|
Ci.nsIWindowsRegKey.ACCESS_ALL);
|
|
for (let i = 0; i < registry.valueCount; i++) {
|
|
let prefName = registry.getValueName(i);
|
|
let prefValue = registry.readStringValue(prefName);
|
|
if (prefType == Ci.nsIPrefBranch.PREF_BOOL) {
|
|
prefValue = prefValue == "true";
|
|
}
|
|
Services.prefs[prefFunc](prefName, prefValue);
|
|
}
|
|
} catch (ex) {
|
|
Util.dumpLn("Could not pull for prefType " + prefType + ": " + ex);
|
|
} finally {
|
|
registry.close();
|
|
}
|
|
}
|
|
let registry = Cc["@mozilla.org/windows-registry-key;1"].
|
|
createInstance(Ci.nsIWindowsRegKey);
|
|
pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_INT, "setIntPref");
|
|
pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_BOOL, "setBoolPref");
|
|
pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_STRING, "setCharPref");
|
|
},
|
|
|
|
/* Set the location to the current content */
|
|
updateURI: function(aOptions) {
|
|
aOptions = aOptions || {};
|
|
|
|
let uri = this.getDisplayURI(Browser.selectedBrowser);
|
|
let cleanURI = Util.isURLEmpty(uri) ? "" : uri;
|
|
this._setURI(cleanURI);
|
|
|
|
if ("captionOnly" in aOptions && aOptions.captionOnly)
|
|
return;
|
|
|
|
StartUI.update(uri);
|
|
this._updateButtons();
|
|
this._updateToolbar();
|
|
},
|
|
|
|
goToURI: function(aURI) {
|
|
aURI = aURI || this._edit.value;
|
|
if (!aURI)
|
|
return;
|
|
|
|
// Make sure we're online before attempting to load
|
|
Util.forceOnline();
|
|
|
|
BrowserUI.showContent(aURI);
|
|
content.focus();
|
|
this._setURI(aURI);
|
|
|
|
Task.spawn(function() {
|
|
let postData = {};
|
|
aURI = yield Browser.getShortcutOrURI(aURI, postData);
|
|
Browser.loadURI(aURI, { flags: Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP, postData: postData });
|
|
|
|
// Delay doing the fixup so the raw URI is passed to loadURIWithFlags
|
|
// and the proper third-party fixup can be done
|
|
let fixupFlags = Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
|
|
let uri = gURIFixup.createFixupURI(aURI, fixupFlags);
|
|
gHistSvc.markPageAsTyped(uri);
|
|
|
|
BrowserUI._titleChanged(Browser.selectedBrowser);
|
|
});
|
|
},
|
|
|
|
handleUrlbarEnter: function handleUrlbarEnter(aEvent) {
|
|
let url = this._edit.value;
|
|
if (aEvent instanceof KeyEvent)
|
|
url = this._canonizeURL(url, aEvent);
|
|
this.goToURI(url);
|
|
},
|
|
|
|
_canonizeURL: function _canonizeURL(aUrl, aTriggeringEvent) {
|
|
if (!aUrl)
|
|
return "";
|
|
|
|
// Only add the suffix when the URL bar value isn't already "URL-like",
|
|
// and only if we get a keyboard event, to match user expectations.
|
|
if (/^\s*[^.:\/\s]+(?:\/.*|\s*)$/i.test(aUrl)) {
|
|
let accel = aTriggeringEvent.ctrlKey;
|
|
let shift = aTriggeringEvent.shiftKey;
|
|
let suffix = "";
|
|
|
|
switch (true) {
|
|
case (accel && shift):
|
|
suffix = ".org/";
|
|
break;
|
|
case (shift):
|
|
suffix = ".net/";
|
|
break;
|
|
case (accel):
|
|
try {
|
|
suffix = gPrefService.getCharPref("browser.fixup.alternate.suffix");
|
|
if (suffix.charAt(suffix.length - 1) != "/")
|
|
suffix += "/";
|
|
} catch(e) {
|
|
suffix = ".com/";
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (suffix) {
|
|
// trim leading/trailing spaces (bug 233205)
|
|
aUrl = aUrl.trim();
|
|
|
|
// Tack www. and suffix on. If user has appended directories, insert
|
|
// suffix before them (bug 279035). Be careful not to get two slashes.
|
|
let firstSlash = aUrl.indexOf("/");
|
|
if (firstSlash >= 0) {
|
|
aUrl = aUrl.substring(0, firstSlash) + suffix + aUrl.substring(firstSlash + 1);
|
|
} else {
|
|
aUrl = aUrl + suffix;
|
|
}
|
|
aUrl = "http://www." + aUrl;
|
|
}
|
|
}
|
|
return aUrl;
|
|
},
|
|
|
|
doOpenSearch: function doOpenSearch(aName) {
|
|
// save the current value of the urlbar
|
|
let searchValue = this._edit.value;
|
|
|
|
// Make sure we're online before attempting to load
|
|
Util.forceOnline();
|
|
BrowserUI.showContent();
|
|
|
|
let engine = Services.search.getEngineByName(aName);
|
|
let submission = engine.getSubmission(searchValue, null);
|
|
Browser.loadURI(submission.uri.spec, { postData: submission.postData });
|
|
|
|
// loadURI may open a new tab, so get the selectedBrowser afterward.
|
|
Browser.selectedBrowser.userTypedValue = submission.uri.spec;
|
|
this._titleChanged(Browser.selectedBrowser);
|
|
},
|
|
|
|
/*********************************
|
|
* Tab management
|
|
*/
|
|
|
|
newTab: function newTab(aURI, aOwner) {
|
|
aURI = aURI || kStartOverlayURI;
|
|
let tab = Browser.addTab(aURI, true, aOwner);
|
|
ContextUI.peekTabs();
|
|
return tab;
|
|
},
|
|
|
|
newOrSelectTab: function newOrSelectTab(aURI, aOwner) {
|
|
let tabs = Browser.tabs;
|
|
for (let i = 0; i < tabs.length; i++) {
|
|
if (tabs[i].browser.currentURI.spec == aURI) {
|
|
Browser.selectedTab = tabs[i];
|
|
return;
|
|
}
|
|
}
|
|
this.newTab(aURI, aOwner);
|
|
},
|
|
|
|
setOnTabAnimationEnd: function setOnTabAnimationEnd(aCallback) {
|
|
Elements.tabs.addEventListener("animationend", function onAnimationEnd() {
|
|
Elements.tabs.removeEventListener("animationend", onAnimationEnd);
|
|
aCallback();
|
|
});
|
|
},
|
|
|
|
closeTab: function closeTab(aTab) {
|
|
// If no tab is passed in, assume the current tab
|
|
let tab = aTab || Browser.selectedTab;
|
|
Browser.closeTab(tab);
|
|
},
|
|
|
|
animateClosingTab: function animateClosingTab(tabToClose) {
|
|
tabToClose.chromeTab.setAttribute("closing", "true");
|
|
|
|
let wasCollapsed = !ContextUI.isExpanded;
|
|
if (wasCollapsed) {
|
|
ContextUI.displayTabs();
|
|
}
|
|
|
|
this.setOnTabAnimationEnd(function() {
|
|
Browser.closeTab(tabToClose, { forceClose: true } );
|
|
if (wasCollapsed)
|
|
ContextUI.dismissWithDelay(kNewTabAnimationDelayMsec);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Re-open a closed tab.
|
|
* @param aIndex
|
|
* The index of the tab (via nsSessionStore.getClosedTabData)
|
|
* @returns a reference to the reopened tab.
|
|
*/
|
|
undoCloseTab: function undoCloseTab(aIndex) {
|
|
var tab = null;
|
|
aIndex = aIndex || 0;
|
|
var ss = Cc["@mozilla.org/browser/sessionstore;1"].
|
|
getService(Ci.nsISessionStore);
|
|
if (ss.getClosedTabCount(window) > (aIndex)) {
|
|
tab = ss.undoCloseTab(window, aIndex);
|
|
}
|
|
return tab;
|
|
},
|
|
|
|
// Useful for when we've received an event to close a particular DOM window.
|
|
// Since we don't have windows, we want to close the corresponding tab.
|
|
closeTabForBrowser: function closeTabForBrowser(aBrowser) {
|
|
// Find the relevant tab, and close it.
|
|
let browsers = Browser.browsers;
|
|
for (let i = 0; i < browsers.length; i++) {
|
|
if (browsers[i] == aBrowser) {
|
|
Browser.closeTab(Browser.getTabAtIndex(i));
|
|
return { preventDefault: true };
|
|
}
|
|
}
|
|
|
|
return {};
|
|
},
|
|
|
|
selectTab: function selectTab(aTab) {
|
|
Browser.selectedTab = aTab;
|
|
},
|
|
|
|
selectTabAndDismiss: function selectTabAndDismiss(aTab) {
|
|
this.selectTab(aTab);
|
|
ContextUI.dismiss();
|
|
},
|
|
|
|
selectTabAtIndex: function selectTabAtIndex(aIndex) {
|
|
// count backwards for aIndex < 0
|
|
if (aIndex < 0)
|
|
aIndex += Browser._tabs.length;
|
|
|
|
if (aIndex >= 0 && aIndex < Browser._tabs.length)
|
|
Browser.selectedTab = Browser._tabs[aIndex];
|
|
},
|
|
|
|
selectNextTab: function selectNextTab() {
|
|
if (Browser._tabs.length == 1 || !Browser.selectedTab) {
|
|
return;
|
|
}
|
|
|
|
let tabIndex = Browser._tabs.indexOf(Browser.selectedTab) + 1;
|
|
if (tabIndex >= Browser._tabs.length) {
|
|
tabIndex = 0;
|
|
}
|
|
|
|
Browser.selectedTab = Browser._tabs[tabIndex];
|
|
},
|
|
|
|
selectPreviousTab: function selectPreviousTab() {
|
|
if (Browser._tabs.length == 1 || !Browser.selectedTab) {
|
|
return;
|
|
}
|
|
|
|
let tabIndex = Browser._tabs.indexOf(Browser.selectedTab) - 1;
|
|
if (tabIndex < 0) {
|
|
tabIndex = Browser._tabs.length - 1;
|
|
}
|
|
|
|
Browser.selectedTab = Browser._tabs[tabIndex];
|
|
},
|
|
|
|
// Used for when we're about to open a modal dialog,
|
|
// and want to ensure the opening tab is in front.
|
|
selectTabForBrowser: function selectTabForBrowser(aBrowser) {
|
|
for (let i = 0; i < Browser.tabs.length; i++) {
|
|
if (Browser._tabs[i].browser == aBrowser) {
|
|
Browser.selectedTab = Browser.tabs[i];
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
|
|
updateUIFocus: function _updateUIFocus() {
|
|
if (Elements.contentShowing.getAttribute("disabled") == "true" && Browser.selectedBrowser)
|
|
Browser.selectedBrowser.messageManager.sendAsyncMessage("Browser:Blur", { });
|
|
},
|
|
|
|
blurFocusedElement: function blurFocusedElement() {
|
|
let focusedElement = document.commandDispatcher.focusedElement;
|
|
if (focusedElement)
|
|
focusedElement.blur();
|
|
},
|
|
|
|
blurNavBar: function blurNavBar() {
|
|
if (this._edit.focused) {
|
|
this._edit.blur();
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
// If the user types in the address bar, cancel pending
|
|
// navbar autohide if set.
|
|
navEditKeyPress: function navEditKeyPress() {
|
|
ContextUI.cancelDismiss();
|
|
},
|
|
|
|
observe: function BrowserUI_observe(aSubject, aTopic, aData) {
|
|
switch (aTopic) {
|
|
case "nsPref:changed":
|
|
switch (aData) {
|
|
case "browser.cache.disk_cache_ssl":
|
|
this._sslDiskCacheEnabled = Services.prefs.getBoolPref(aData);
|
|
break;
|
|
case "browser.urlbar.formatting.enabled":
|
|
this._formattingEnabled = Services.prefs.getBookPref(aData);
|
|
break;
|
|
case "browser.urlbar.trimURLs":
|
|
this._mayTrimURLs = Services.prefs.getBoolPref(aData);
|
|
break;
|
|
}
|
|
break;
|
|
case "metro_viewstate_changed":
|
|
this._adjustDOMforViewState();
|
|
let autocomplete = document.getElementById("start-autocomplete");
|
|
if (aData == "snapped") {
|
|
FlyoutPanelsUI.hide();
|
|
// Order matters (need grids to get dimensions, etc), now
|
|
// let snapped grid know to refresh/redraw
|
|
Services.obs.notifyObservers(null, "metro_viewstate_dom_snapped", null);
|
|
autocomplete.setAttribute("orient", "vertical");
|
|
}
|
|
else {
|
|
autocomplete.setAttribute("orient", "horizontal");
|
|
}
|
|
break;
|
|
}
|
|
},
|
|
|
|
/*********************************
|
|
* Internal utils
|
|
*/
|
|
|
|
_adjustDOMforViewState: function() {
|
|
if (MetroUtils.immersive) {
|
|
let currViewState = "";
|
|
switch (MetroUtils.snappedState) {
|
|
case Ci.nsIWinMetroUtils.fullScreenLandscape:
|
|
currViewState = "landscape";
|
|
break;
|
|
case Ci.nsIWinMetroUtils.fullScreenPortrait:
|
|
currViewState = "portrait";
|
|
break;
|
|
case Ci.nsIWinMetroUtils.filled:
|
|
currViewState = "filled";
|
|
break;
|
|
case Ci.nsIWinMetroUtils.snapped:
|
|
currViewState = "snapped";
|
|
break;
|
|
}
|
|
Elements.windowState.setAttribute("viewstate", currViewState);
|
|
}
|
|
},
|
|
|
|
_titleChanged: function(aBrowser) {
|
|
let url = this.getDisplayURI(aBrowser);
|
|
|
|
let tabCaption;
|
|
if (aBrowser.contentTitle) {
|
|
tabCaption = aBrowser.contentTitle;
|
|
} else if (!Util.isURLEmpty(aBrowser.userTypedValue)) {
|
|
tabCaption = aBrowser.userTypedValue;
|
|
} else if (!Util.isURLEmpty(url)) {
|
|
tabCaption = url;
|
|
} else {
|
|
tabCaption = Util.getEmptyURLTabTitle();
|
|
}
|
|
|
|
let tab = Browser.getTabForBrowser(aBrowser);
|
|
if (tab)
|
|
tab.chromeTab.updateTitle(tabCaption);
|
|
},
|
|
|
|
_updateButtons: function _updateButtons() {
|
|
let browser = Browser.selectedBrowser;
|
|
if (!browser) {
|
|
return;
|
|
}
|
|
if (browser.canGoBack) {
|
|
this._back.removeAttribute("disabled");
|
|
} else {
|
|
this._back.setAttribute("disabled", true);
|
|
}
|
|
if (browser.canGoForward) {
|
|
this._forward.removeAttribute("disabled");
|
|
} else {
|
|
this._forward.setAttribute("disabled", true);
|
|
}
|
|
},
|
|
|
|
_updateToolbar: function _updateToolbar() {
|
|
let mode = Elements.urlbarState.getAttribute("mode");
|
|
let isLoading = Browser.selectedTab.isLoading();
|
|
|
|
if (isLoading && mode != "loading")
|
|
Elements.urlbarState.setAttribute("mode", "loading");
|
|
else if (!isLoading && mode != "edit")
|
|
Elements.urlbarState.setAttribute("mode", "view");
|
|
},
|
|
|
|
_trimURL: function _trimURL(aURL) {
|
|
// This function must not modify the given URL such that calling
|
|
// nsIURIFixup::createFixupURI with the result will produce a different URI.
|
|
return aURL /* remove single trailing slash for http/https/ftp URLs */
|
|
.replace(/^((?:http|https|ftp):\/\/[^/]+)\/$/, "$1")
|
|
/* remove http:// unless the host starts with "ftp\d*\." or contains "@" */
|
|
.replace(/^http:\/\/((?!ftp\d*\.)[^\/@]+(?:\/|$))/, "$1");
|
|
},
|
|
|
|
trimURL: function trimURL(aURL) {
|
|
return this.mayTrimURLs ? this._trimURL(aURL) : aURL;
|
|
},
|
|
|
|
_setURI: function _setURI(aURL) {
|
|
this._edit.value = aURL;
|
|
this.lastKnownGoodURL = aURL;
|
|
},
|
|
|
|
_getSelectedURIForClipboard: function _getSelectedURIForClipboard() {
|
|
// Grab the actual input field's value, not our value, which could include moz-action:
|
|
let inputVal = this._edit.inputField.value;
|
|
let selectedVal = inputVal.substring(this._edit.selectionStart, this._edit.electionEnd);
|
|
|
|
// If the selection doesn't start at the beginning or doesn't span the full domain or
|
|
// the URL bar is modified, nothing else to do here.
|
|
if (this._edit.selectionStart > 0 || this._edit.valueIsTyped)
|
|
return selectedVal;
|
|
// The selection doesn't span the full domain if it doesn't contain a slash and is
|
|
// followed by some character other than a slash.
|
|
if (!selectedVal.contains("/")) {
|
|
let remainder = inputVal.replace(selectedVal, "");
|
|
if (remainder != "" && remainder[0] != "/")
|
|
return selectedVal;
|
|
}
|
|
|
|
let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
|
|
|
|
let uri;
|
|
try {
|
|
uri = uriFixup.createFixupURI(inputVal, Ci.nsIURIFixup.FIXUP_FLAG_USE_UTF8);
|
|
} catch (e) {}
|
|
if (!uri)
|
|
return selectedVal;
|
|
|
|
// Only copy exposable URIs
|
|
try {
|
|
uri = uriFixup.createExposableURI(uri);
|
|
} catch (ex) {}
|
|
|
|
// If the entire URL is selected, just use the actual loaded URI.
|
|
if (inputVal == selectedVal) {
|
|
// ... but only if isn't a javascript: or data: URI, since those
|
|
// are hard to read when encoded
|
|
if (!uri.schemeIs("javascript") && !uri.schemeIs("data")) {
|
|
// Parentheses are known to confuse third-party applications (bug 458565).
|
|
selectedVal = uri.spec.replace(/[()]/g, function (c) escape(c));
|
|
}
|
|
|
|
return selectedVal;
|
|
}
|
|
|
|
// Just the beginning of the URL is selected, check for a trimmed value
|
|
let spec = uri.spec;
|
|
let trimmedSpec = this.trimURL(spec);
|
|
if (spec != trimmedSpec) {
|
|
// Prepend the portion that trimURL removed from the beginning.
|
|
// This assumes trimURL will only truncate the URL at
|
|
// the beginning or end (or both).
|
|
let trimmedSegments = spec.split(trimmedSpec);
|
|
selectedVal = trimmedSegments[0] + selectedVal;
|
|
}
|
|
|
|
return selectedVal;
|
|
},
|
|
|
|
_copyCutURIController: {
|
|
doCommand: function(aCommand) {
|
|
let urlbar = BrowserUI._edit;
|
|
let val = BrowserUI._getSelectedURIForClipboard();
|
|
if (!val)
|
|
return;
|
|
|
|
if (aCommand == "cmd_cut" && this.isCommandEnabled(aCommand)) {
|
|
let start = urlbar.selectionStart;
|
|
let end = urlbar.selectionEnd;
|
|
urlbar.inputField.value = urlbar.inputField.value.substring(0, start) +
|
|
urlbar.inputField.value.substring(end);
|
|
urlbar.selectionStart = urlbar.selectionEnd = start;
|
|
}
|
|
|
|
Cc["@mozilla.org/widget/clipboardhelper;1"]
|
|
.getService(Ci.nsIClipboardHelper)
|
|
.copyString(val, document);
|
|
},
|
|
|
|
supportsCommand: function(aCommand) {
|
|
switch (aCommand) {
|
|
case "cmd_copy":
|
|
case "cmd_cut":
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
isCommandEnabled: function(aCommand) {
|
|
let urlbar = BrowserUI._edit;
|
|
return this.supportsCommand(aCommand) &&
|
|
(aCommand != "cmd_cut" || !urlbar.readOnly) &&
|
|
urlbar.selectionStart < urlbar.selectionEnd;
|
|
},
|
|
|
|
onEvent: function(aEventName) {}
|
|
},
|
|
|
|
_urlbarClicked: function _urlbarClicked() {
|
|
// If the urlbar is not already focused, focus it and select the contents.
|
|
if (Elements.urlbarState.getAttribute("mode") != "edit")
|
|
this._editURI(true);
|
|
},
|
|
|
|
_editURI: function _editURI(aShouldDismiss) {
|
|
this._clearURIFormatting();
|
|
this._edit.focus();
|
|
this._edit.select();
|
|
|
|
Elements.urlbarState.setAttribute("mode", "edit");
|
|
StartUI.show();
|
|
if (aShouldDismiss)
|
|
ContextUI.dismissTabs();
|
|
},
|
|
|
|
formatURI: function formatURI() {
|
|
if (!this.formattingEnabled ||
|
|
Elements.urlbarState.getAttribute("mode") == "edit")
|
|
return;
|
|
|
|
let controller = this._edit.editor.selectionController;
|
|
let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
|
|
selection.removeAllRanges();
|
|
|
|
let textNode = this._edit.editor.rootElement.firstChild;
|
|
let value = textNode.textContent;
|
|
|
|
let protocol = value.match(/^[a-z\d.+\-]+:(?=[^\d])/);
|
|
if (protocol &&
|
|
["http:", "https:", "ftp:"].indexOf(protocol[0]) == -1)
|
|
return;
|
|
let matchedURL = value.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/);
|
|
if (!matchedURL)
|
|
return;
|
|
|
|
let [, preDomain, domain] = matchedURL;
|
|
let baseDomain = domain;
|
|
let subDomain = "";
|
|
// getBaseDomainFromHost doesn't recognize IPv6 literals in brackets as IPs (bug 667159)
|
|
if (domain[0] != "[") {
|
|
try {
|
|
baseDomain = Services.eTLD.getBaseDomainFromHost(domain);
|
|
if (!domain.endsWith(baseDomain)) {
|
|
// getBaseDomainFromHost converts its resultant to ACE.
|
|
let IDNService = Cc["@mozilla.org/network/idn-service;1"]
|
|
.getService(Ci.nsIIDNService);
|
|
baseDomain = IDNService.convertACEtoUTF8(baseDomain);
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
if (baseDomain != domain) {
|
|
subDomain = domain.slice(0, -baseDomain.length);
|
|
}
|
|
|
|
let rangeLength = preDomain.length + subDomain.length;
|
|
if (rangeLength) {
|
|
let range = document.createRange();
|
|
range.setStart(textNode, 0);
|
|
range.setEnd(textNode, rangeLength);
|
|
selection.addRange(range);
|
|
}
|
|
|
|
let startRest = preDomain.length + domain.length;
|
|
if (startRest < value.length) {
|
|
let range = document.createRange();
|
|
range.setStart(textNode, startRest);
|
|
range.setEnd(textNode, value.length);
|
|
selection.addRange(range);
|
|
}
|
|
},
|
|
|
|
_clearURIFormatting: function _clearURIFormatting() {
|
|
if (!this.formattingEnabled)
|
|
return;
|
|
|
|
let controller = this._edit.editor.selectionController;
|
|
let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
|
|
selection.removeAllRanges();
|
|
},
|
|
|
|
_urlbarBlurred: function _urlbarBlurred() {
|
|
let state = Elements.urlbarState;
|
|
if (state.getAttribute("mode") == "edit")
|
|
state.removeAttribute("mode");
|
|
this._updateToolbar();
|
|
this.formatURI();
|
|
},
|
|
|
|
_closeOrQuit: function _closeOrQuit() {
|
|
// Close active dialog, if we have one. If not then close the application.
|
|
if (!BrowserUI.isContentShowing()) {
|
|
BrowserUI.showContent();
|
|
} else {
|
|
// Check to see if we should really close the window
|
|
if (Browser.closing()) {
|
|
window.close();
|
|
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
|
|
appStartup.quit(Ci.nsIAppStartup.eForceQuit);
|
|
}
|
|
}
|
|
},
|
|
|
|
_onPreciseInput: function _onPreciseInput() {
|
|
document.getElementById("bcast_preciseInput").setAttribute("input", "precise");
|
|
let uri = Util.makeURI("chrome://browser/content/cursor.css");
|
|
if (StyleSheetSvc.sheetRegistered(uri, Ci.nsIStyleSheetService.AGENT_SHEET)) {
|
|
StyleSheetSvc.unregisterSheet(uri,
|
|
Ci.nsIStyleSheetService.AGENT_SHEET);
|
|
}
|
|
},
|
|
|
|
_onImpreciseInput: function _onImpreciseInput() {
|
|
document.getElementById("bcast_preciseInput").setAttribute("input", "imprecise");
|
|
let uri = Util.makeURI("chrome://browser/content/cursor.css");
|
|
if (!StyleSheetSvc.sheetRegistered(uri, Ci.nsIStyleSheetService.AGENT_SHEET)) {
|
|
StyleSheetSvc.loadAndRegisterSheet(uri,
|
|
Ci.nsIStyleSheetService.AGENT_SHEET);
|
|
}
|
|
},
|
|
|
|
/*********************************
|
|
* Event handling
|
|
*/
|
|
|
|
handleEvent: function handleEvent(aEvent) {
|
|
var target = aEvent.target;
|
|
switch (aEvent.type) {
|
|
// Window events
|
|
case "keypress":
|
|
if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE)
|
|
this.handleEscape(aEvent);
|
|
break;
|
|
case "MozPrecisePointer":
|
|
this._onPreciseInput();
|
|
break;
|
|
case "MozImprecisePointer":
|
|
this._onImpreciseInput();
|
|
break;
|
|
}
|
|
},
|
|
|
|
// Checks if various different parts of the UI is visible and closes
|
|
// them one at a time.
|
|
handleEscape: function (aEvent) {
|
|
aEvent.stopPropagation();
|
|
aEvent.preventDefault();
|
|
|
|
if (this._edit.popupOpen) {
|
|
this._edit.value = this.lastKnownGoodURL;
|
|
this._edit.closePopup();
|
|
StartUI.hide();
|
|
ContextUI.dismiss();
|
|
return;
|
|
}
|
|
|
|
// Check open popups
|
|
if (DialogUI._popup) {
|
|
DialogUI._hidePopup();
|
|
return;
|
|
}
|
|
|
|
// Check open dialogs
|
|
let dialog = DialogUI.activeDialog;
|
|
if (dialog) {
|
|
dialog.close();
|
|
return;
|
|
}
|
|
|
|
// Check open modal elements
|
|
if (DialogUI.modals.length > 0) {
|
|
return;
|
|
}
|
|
|
|
// Check open panel
|
|
if (PanelUI.isVisible) {
|
|
PanelUI.hide();
|
|
return;
|
|
}
|
|
|
|
// Check content helper
|
|
let contentHelper = Elements.contentNavigator;
|
|
if (contentHelper.isActive) {
|
|
contentHelper.model.hide();
|
|
return;
|
|
}
|
|
|
|
if (StartUI.hide()) {
|
|
// When escaping from the start screen, hide the toolbar too.
|
|
ContextUI.dismiss();
|
|
return;
|
|
}
|
|
|
|
if (ContextUI.dismiss()) {
|
|
return;
|
|
}
|
|
|
|
if (Browser.selectedTab.isLoading()) {
|
|
Browser.selectedBrowser.stop();
|
|
return;
|
|
}
|
|
},
|
|
|
|
handleBackspace: function handleBackspace() {
|
|
switch (Services.prefs.getIntPref("browser.backspace_action")) {
|
|
case 0:
|
|
CommandUpdater.doCommand("cmd_back");
|
|
break;
|
|
case 1:
|
|
CommandUpdater.doCommand("cmd_scrollPageUp");
|
|
break;
|
|
}
|
|
},
|
|
|
|
handleShiftBackspace: function handleShiftBackspace() {
|
|
switch (Services.prefs.getIntPref("browser.backspace_action")) {
|
|
case 0:
|
|
CommandUpdater.doCommand("cmd_forward");
|
|
break;
|
|
case 1:
|
|
CommandUpdater.doCommand("cmd_scrollPageDown");
|
|
break;
|
|
}
|
|
},
|
|
|
|
openFile: function() {
|
|
try {
|
|
const nsIFilePicker = Ci.nsIFilePicker;
|
|
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
|
|
let self = this;
|
|
let fpCallback = function fpCallback_done(aResult) {
|
|
if (aResult == nsIFilePicker.returnOK) {
|
|
self.goToURI(fp.fileURL.spec);
|
|
}
|
|
};
|
|
|
|
let windowTitle = Strings.browser.GetStringFromName("browserForOpenLocation");
|
|
fp.init(window, windowTitle, nsIFilePicker.modeOpen);
|
|
fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText |
|
|
nsIFilePicker.filterImages | nsIFilePicker.filterXML |
|
|
nsIFilePicker.filterHTML);
|
|
fp.open(fpCallback);
|
|
} catch (ex) {
|
|
dump ('BrowserUI openFile exception: ' + ex + '\n');
|
|
}
|
|
},
|
|
|
|
savePage: function() {
|
|
Browser.savePage();
|
|
},
|
|
|
|
receiveMessage: function receiveMessage(aMessage) {
|
|
let browser = aMessage.target;
|
|
let json = aMessage.json;
|
|
switch (aMessage.name) {
|
|
case "DOMTitleChanged":
|
|
this._titleChanged(browser);
|
|
break;
|
|
case "DOMWillOpenModalDialog":
|
|
this.selectTabForBrowser(browser);
|
|
break;
|
|
case "DOMWindowClose":
|
|
return this.closeTabForBrowser(browser);
|
|
break;
|
|
// XXX this and content's sender are a little warped
|
|
case "Browser:OpenURI":
|
|
let referrerURI = null;
|
|
if (json.referrer)
|
|
referrerURI = Services.io.newURI(json.referrer, null, null);
|
|
//Browser.addTab(json.uri, json.bringFront, Browser.selectedTab, { referrerURI: referrerURI });
|
|
this.goToURI(json.uri);
|
|
break;
|
|
case "Content:StateChange":
|
|
let currBrowser = Browser.selectedBrowser;
|
|
if (this.shouldCaptureThumbnails(currBrowser)) {
|
|
PageThumbs.captureAndStore(currBrowser);
|
|
let currPage = currBrowser.currentURI.spec;
|
|
Services.obs.notifyObservers(null, "Metro:RefreshTopsiteThumbnail", currPage);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return {};
|
|
},
|
|
|
|
// Private Browsing is not supported on metro at this time, when it is added
|
|
// this function must be updated to skip capturing those pages
|
|
shouldCaptureThumbnails: function shouldCaptureThumbnails(aBrowser) {
|
|
// Capture only if it's the currently selected tab.
|
|
if (aBrowser != Browser.selectedBrowser) {
|
|
return false;
|
|
}
|
|
// FIXME Bug 720575 - Don't capture thumbnails for SVG or XML documents as
|
|
// that currently regresses Talos SVG tests.
|
|
let doc = aBrowser.contentDocument;
|
|
if (doc instanceof SVGDocument || doc instanceof XMLDocument) {
|
|
return false;
|
|
}
|
|
|
|
// Don't capture pages in snapped mode, this produces 2/3 black
|
|
// thumbs or stretched out ones
|
|
// Ci.nsIWinMetroUtils.snapped is inaccessible on
|
|
// desktop/nonwindows systems
|
|
if(Elements.windowState.getAttribute("viewstate") == "snapped") {
|
|
return false;
|
|
}
|
|
// There's no point in taking screenshot of loading pages.
|
|
if (aBrowser.docShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE) {
|
|
return false;
|
|
}
|
|
|
|
// Don't take screenshots of about: pages.
|
|
if (aBrowser.currentURI.schemeIs("about")) {
|
|
return false;
|
|
}
|
|
|
|
// No valid document channel. We shouldn't take a screenshot.
|
|
let channel = aBrowser.docShell.currentDocumentChannel;
|
|
if (!channel) {
|
|
return false;
|
|
}
|
|
|
|
// Don't take screenshots of internally redirecting about: pages.
|
|
// This includes error pages.
|
|
let uri = channel.originalURI;
|
|
if (uri.schemeIs("about")) {
|
|
return false;
|
|
}
|
|
|
|
// http checks
|
|
let httpChannel;
|
|
try {
|
|
httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
|
} catch (e) { /* Not an HTTP channel. */ }
|
|
|
|
if (httpChannel) {
|
|
// Continue only if we have a 2xx status code.
|
|
try {
|
|
if (Math.floor(httpChannel.responseStatus / 100) != 2) {
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
// Can't get response information from the httpChannel
|
|
// because mResponseHead is not available.
|
|
return false;
|
|
}
|
|
|
|
// Cache-Control: no-store.
|
|
if (httpChannel.isNoStoreResponse()) {
|
|
return false;
|
|
}
|
|
|
|
// Don't capture HTTPS pages unless the user enabled it.
|
|
if (uri.schemeIs("https") && !this.sslDiskCacheEnabled) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
_sslDiskCacheEnabled: null,
|
|
|
|
get sslDiskCacheEnabled() {
|
|
if (this._sslDiskCacheEnabled === null) {
|
|
this._sslDiskCacheEnabled = Services.prefs.getBoolPref("browser.cache.disk_cache_ssl");
|
|
}
|
|
return this._sslDiskCacheEnabled;
|
|
},
|
|
|
|
_formattingEnabled: null,
|
|
|
|
get formattingEnabled() {
|
|
if (this._formattingEnabled === null) {
|
|
this._formattingEnabled = Services.prefs.getBoolPref("browser.urlbar.formatting.enabled");
|
|
}
|
|
return this._formattingEnabled;
|
|
},
|
|
|
|
_mayTrimURLs: null,
|
|
|
|
get mayTrimURLs() {
|
|
if (this._mayTrimURLs === null) {
|
|
this._mayTrimURLs = Services.prefs.getBoolPref("browser.urlbar.trimURLs");
|
|
}
|
|
return this._mayTrimURLs;
|
|
},
|
|
|
|
supportsCommand : function(cmd) {
|
|
var isSupported = false;
|
|
switch (cmd) {
|
|
case "cmd_back":
|
|
case "cmd_forward":
|
|
case "cmd_reload":
|
|
case "cmd_forceReload":
|
|
case "cmd_stop":
|
|
case "cmd_go":
|
|
case "cmd_home":
|
|
case "cmd_openLocation":
|
|
case "cmd_addBookmark":
|
|
case "cmd_bookmarks":
|
|
case "cmd_history":
|
|
case "cmd_remoteTabs":
|
|
case "cmd_quit":
|
|
case "cmd_close":
|
|
case "cmd_newTab":
|
|
case "cmd_closeTab":
|
|
case "cmd_undoCloseTab":
|
|
case "cmd_actions":
|
|
case "cmd_panel":
|
|
case "cmd_flyout_back":
|
|
case "cmd_sanitize":
|
|
case "cmd_volumeLeft":
|
|
case "cmd_volumeRight":
|
|
case "cmd_openFile":
|
|
case "cmd_savePage":
|
|
isSupported = true;
|
|
break;
|
|
default:
|
|
isSupported = false;
|
|
break;
|
|
}
|
|
return isSupported;
|
|
},
|
|
|
|
isCommandEnabled : function(cmd) {
|
|
let elem = document.getElementById(cmd);
|
|
if (elem && elem.getAttribute("disabled") == "true")
|
|
return false;
|
|
return true;
|
|
},
|
|
|
|
doCommand : function(cmd) {
|
|
if (!this.isCommandEnabled(cmd))
|
|
return;
|
|
let browser = getBrowser();
|
|
switch (cmd) {
|
|
case "cmd_back":
|
|
browser.goBack();
|
|
break;
|
|
case "cmd_forward":
|
|
browser.goForward();
|
|
break;
|
|
case "cmd_reload":
|
|
browser.reload();
|
|
break;
|
|
case "cmd_forceReload":
|
|
{
|
|
// Simulate a new page
|
|
browser.lastLocation = null;
|
|
|
|
const reloadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY |
|
|
Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
|
|
browser.reloadWithFlags(reloadFlags);
|
|
break;
|
|
}
|
|
case "cmd_stop":
|
|
browser.stop();
|
|
break;
|
|
case "cmd_go":
|
|
this.goToURI();
|
|
break;
|
|
case "cmd_home":
|
|
this.goToURI(Browser.getHomePage());
|
|
break;
|
|
case "cmd_openLocation":
|
|
ContextUI.displayNavbar();
|
|
this._editURI(true);
|
|
break;
|
|
case "cmd_addBookmark":
|
|
Elements.navbar.show();
|
|
Appbar.onStarButton(true);
|
|
break;
|
|
case "cmd_bookmarks":
|
|
PanelUI.show("bookmarks-container");
|
|
break;
|
|
case "cmd_history":
|
|
PanelUI.show("history-container");
|
|
break;
|
|
case "cmd_remoteTabs":
|
|
if (Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED) {
|
|
Sync.open();
|
|
} else {
|
|
PanelUI.show("remotetabs-container");
|
|
}
|
|
break;
|
|
case "cmd_quit":
|
|
// Only close one window
|
|
this._closeOrQuit();
|
|
break;
|
|
case "cmd_close":
|
|
this._closeOrQuit();
|
|
break;
|
|
case "cmd_newTab":
|
|
this.newTab();
|
|
this._editURI(false);
|
|
break;
|
|
case "cmd_closeTab":
|
|
this.closeTab();
|
|
break;
|
|
case "cmd_undoCloseTab":
|
|
this.undoCloseTab();
|
|
break;
|
|
case "cmd_sanitize":
|
|
SanitizeUI.onSanitize();
|
|
break;
|
|
case "cmd_flyout_back":
|
|
FlyoutPanelsUI.hide();
|
|
MetroUtils.showSettingsFlyout();
|
|
break;
|
|
case "cmd_panel":
|
|
PanelUI.toggle();
|
|
break;
|
|
case "cmd_volumeLeft":
|
|
// Zoom in (portrait) or out (landscape)
|
|
Browser.zoom(Util.isPortrait() ? -1 : 1);
|
|
break;
|
|
case "cmd_volumeRight":
|
|
// Zoom out (portrait) or in (landscape)
|
|
Browser.zoom(Util.isPortrait() ? 1 : -1);
|
|
break;
|
|
case "cmd_openFile":
|
|
this.openFile();
|
|
break;
|
|
case "cmd_savePage":
|
|
this.savePage();
|
|
break;
|
|
}
|
|
},
|
|
|
|
crashReportingPrefChanged: function crashReportingPrefChanged(aState) {
|
|
CrashReporter.submitReports = aState;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Tracks whether context UI (app bar, tab bar, url bar) is shown or hidden.
|
|
* Manages events to summon and hide the context UI.
|
|
*/
|
|
var ContextUI = {
|
|
_expandable: true,
|
|
_hidingId: 0,
|
|
|
|
/*******************************************
|
|
* init
|
|
*/
|
|
|
|
init: function init() {
|
|
Elements.browsers.addEventListener("mousedown", this, true);
|
|
Elements.browsers.addEventListener("touchstart", this, true);
|
|
Elements.browsers.addEventListener("AlertActive", this, true);
|
|
window.addEventListener("MozEdgeUIStarted", this, true);
|
|
window.addEventListener("MozEdgeUICanceled", this, true);
|
|
window.addEventListener("MozEdgeUICompleted", this, true);
|
|
window.addEventListener("keypress", this, true);
|
|
window.addEventListener("KeyboardChanged", this, false);
|
|
|
|
Elements.tray.addEventListener("transitionend", this, true);
|
|
|
|
Appbar.init();
|
|
},
|
|
|
|
/*******************************************
|
|
* Context UI state getters & setters
|
|
*/
|
|
|
|
get isVisible() {
|
|
return (Elements.navbar.hasAttribute("visible") ||
|
|
Elements.navbar.hasAttribute("startpage"));
|
|
},
|
|
get isExpanded() { return Elements.tray.hasAttribute("expanded"); },
|
|
get isExpandable() { return this._expandable; },
|
|
|
|
set isExpandable(aFlag) {
|
|
this._expandable = aFlag;
|
|
if (!this._expandable)
|
|
this.dismiss();
|
|
},
|
|
|
|
/*******************************************
|
|
* Context UI state control
|
|
*/
|
|
|
|
toggle: function toggle() {
|
|
if (!this._expandable) {
|
|
// exandable setter takes care of resetting state
|
|
// so if we're not expandable, there's nothing to do here
|
|
return;
|
|
}
|
|
// if we're not showing, show
|
|
if (!this.dismiss()) {
|
|
dump("* ContextUI is hidden, show it\n");
|
|
this.show();
|
|
}
|
|
},
|
|
|
|
// show all context UI
|
|
// returns true if any non-visible UI was shown
|
|
show: function() {
|
|
let shown = false;
|
|
if (!this.isExpanded) {
|
|
// show the tab tray
|
|
this._setIsExpanded(true);
|
|
shown = true;
|
|
}
|
|
if (!Elements.navbar.isShowing) {
|
|
// show the navbar
|
|
Elements.navbar.show();
|
|
shown = true;
|
|
}
|
|
|
|
this._clearDelayedTimeout();
|
|
if (shown) {
|
|
ContentAreaObserver.update(window.innerWidth, window.innerHeight);
|
|
}
|
|
return shown;
|
|
},
|
|
|
|
// Display the nav bar
|
|
displayNavbar: function displayNavbar() {
|
|
this._clearDelayedTimeout();
|
|
Elements.navbar.show();
|
|
},
|
|
|
|
// Display the toolbar and tabs
|
|
displayTabs: function displayTabs() {
|
|
this._clearDelayedTimeout();
|
|
this._setIsExpanded(true, true);
|
|
},
|
|
|
|
/** Briefly show the tab bar and then hide it */
|
|
peekTabs: function peekTabs() {
|
|
if (this.isExpanded) {
|
|
setTimeout(function () {
|
|
ContextUI.dismissWithDelay(kNewTabAnimationDelayMsec);
|
|
}, 0);
|
|
} else {
|
|
BrowserUI.setOnTabAnimationEnd(function () {
|
|
ContextUI.dismissWithDelay(kNewTabAnimationDelayMsec);
|
|
});
|
|
|
|
this.displayTabs();
|
|
}
|
|
},
|
|
|
|
// Dismiss all context UI.
|
|
// Returns true if any visible UI was dismissed.
|
|
dismiss: function dismiss() {
|
|
let dismissed = false;
|
|
if (this.isExpanded) {
|
|
this._setIsExpanded(false);
|
|
dismissed = true;
|
|
}
|
|
if (Elements.navbar.isShowing) {
|
|
this.dismissAppbar();
|
|
dismissed = true;
|
|
}
|
|
this._clearDelayedTimeout();
|
|
if (dismissed) {
|
|
ContentAreaObserver.update(window.innerWidth, window.innerHeight);
|
|
}
|
|
return dismissed;
|
|
},
|
|
|
|
// Dismiss all context ui after a delay
|
|
dismissWithDelay: function dismissWithDelay(aDelay) {
|
|
aDelay = aDelay || kHideContextAndTrayDelayMsec;
|
|
this._clearDelayedTimeout();
|
|
this._hidingId = setTimeout(function () {
|
|
ContextUI.dismiss();
|
|
}, aDelay);
|
|
},
|
|
|
|
// Cancel any pending delayed dismiss
|
|
cancelDismiss: function cancelDismiss() {
|
|
this._clearDelayedTimeout();
|
|
},
|
|
|
|
dismissTabs: function dimissTabs() {
|
|
this._clearDelayedTimeout();
|
|
this._setIsExpanded(false, true);
|
|
},
|
|
|
|
dismissAppbar: function dismissAppbar() {
|
|
this._fire("MozAppbarDismiss");
|
|
},
|
|
|
|
/*******************************************
|
|
* Internal tray state setters
|
|
*/
|
|
|
|
// tab tray state
|
|
_setIsExpanded: function _setIsExpanded(aFlag, setSilently) {
|
|
// if the tray can't be expanded, don't expand it.
|
|
if (!this.isExpandable || this.isExpanded == aFlag)
|
|
return;
|
|
|
|
if (aFlag)
|
|
Elements.tray.setAttribute("expanded", "true");
|
|
else
|
|
Elements.tray.removeAttribute("expanded");
|
|
|
|
if (!setSilently)
|
|
this._fire("MozContextUIExpand");
|
|
},
|
|
|
|
/*******************************************
|
|
* Internal utils
|
|
*/
|
|
|
|
_clearDelayedTimeout: function _clearDelayedTimeout() {
|
|
if (this._hidingId) {
|
|
clearTimeout(this._hidingId);
|
|
this._hidingId = 0;
|
|
}
|
|
},
|
|
|
|
/*******************************************
|
|
* Events
|
|
*/
|
|
|
|
_onEdgeUIStarted: function(aEvent) {
|
|
this._hasEdgeSwipeStarted = true;
|
|
this._clearDelayedTimeout();
|
|
|
|
if (StartUI.hide()) {
|
|
this.dismiss();
|
|
return;
|
|
}
|
|
this.toggle();
|
|
},
|
|
|
|
_onEdgeUICanceled: function(aEvent) {
|
|
this._hasEdgeSwipeStarted = false;
|
|
StartUI.hide();
|
|
this.dismiss();
|
|
},
|
|
|
|
_onEdgeUICompleted: function(aEvent) {
|
|
if (this._hasEdgeSwipeStarted) {
|
|
this._hasEdgeSwipeStarted = false;
|
|
return;
|
|
}
|
|
|
|
this._clearDelayedTimeout();
|
|
if (StartUI.hide()) {
|
|
this.dismiss();
|
|
return;
|
|
}
|
|
this.toggle();
|
|
},
|
|
|
|
handleEvent: function handleEvent(aEvent) {
|
|
switch (aEvent.type) {
|
|
case "MozEdgeUIStarted":
|
|
this._onEdgeUIStarted(aEvent);
|
|
break;
|
|
case "MozEdgeUICanceled":
|
|
this._onEdgeUICanceled(aEvent);
|
|
break;
|
|
case "MozEdgeUICompleted":
|
|
this._onEdgeUICompleted(aEvent);
|
|
break;
|
|
case "mousedown":
|
|
if (aEvent.button == 0 && this.isVisible)
|
|
this.dismiss();
|
|
break;
|
|
case "touchstart":
|
|
// ContextUI can hide the notification bar. Workaround until bug 845348 is fixed.
|
|
case "AlertActive":
|
|
this.dismiss();
|
|
break;
|
|
case "keypress":
|
|
if (String.fromCharCode(aEvent.which) == "z" &&
|
|
aEvent.getModifierState("Win"))
|
|
this.toggle();
|
|
break;
|
|
case "transitionend":
|
|
setTimeout(function () {
|
|
ContentAreaObserver.updateContentArea();
|
|
}, 0);
|
|
break;
|
|
case "KeyboardChanged":
|
|
this.dismissTabs();
|
|
break;
|
|
}
|
|
},
|
|
|
|
_fire: function (name) {
|
|
let event = document.createEvent("Events");
|
|
event.initEvent(name, true, true);
|
|
window.dispatchEvent(event);
|
|
}
|
|
};
|
|
|
|
var StartUI = {
|
|
get isVisible() { return this.isStartPageVisible || this.isFiltering; },
|
|
get isStartPageVisible() { return Elements.windowState.hasAttribute("startpage"); },
|
|
get isFiltering() { return Elements.windowState.hasAttribute("filtering"); },
|
|
|
|
get maxResultsPerSection() {
|
|
return Services.prefs.getIntPref("browser.display.startUI.maxresults");
|
|
},
|
|
|
|
sections: [
|
|
"TopSitesStartView",
|
|
"TopSitesSnappedView",
|
|
"BookmarksStartView",
|
|
"HistoryStartView",
|
|
"RemoteTabsStartView"
|
|
],
|
|
|
|
init: function init() {
|
|
Elements.startUI.addEventListener("autocompletestart", this, false);
|
|
Elements.startUI.addEventListener("autocompleteend", this, false);
|
|
Elements.startUI.addEventListener("contextmenu", this, false);
|
|
Elements.startUI.addEventListener("click", this, false);
|
|
Elements.startUI.addEventListener("MozMousePixelScroll", this, false);
|
|
|
|
this.sections.forEach(function (sectionName) {
|
|
let section = window[sectionName];
|
|
if (section.init)
|
|
section.init();
|
|
});
|
|
},
|
|
|
|
uninit: function() {
|
|
this.sections.forEach(function (sectionName) {
|
|
let section = window[sectionName];
|
|
if (section.uninit)
|
|
section.uninit();
|
|
});
|
|
},
|
|
|
|
/** Show the Firefox start page / "new tab" page */
|
|
show: function show() {
|
|
if (this.isStartPageVisible)
|
|
return false;
|
|
|
|
ContextUI.displayNavbar();
|
|
|
|
Elements.contentShowing.setAttribute("disabled", "true");
|
|
Elements.windowState.setAttribute("startpage", "true");
|
|
|
|
this.sections.forEach(function (sectionName) {
|
|
let section = window[sectionName];
|
|
if (section.show)
|
|
section.show();
|
|
});
|
|
return true;
|
|
},
|
|
|
|
/** Show the autocomplete popup */
|
|
filter: function filter() {
|
|
if (this.isFiltering)
|
|
return;
|
|
|
|
BrowserUI._edit.openPopup();
|
|
Elements.windowState.setAttribute("filtering", "true");
|
|
},
|
|
|
|
/** Hide the autocomplete popup */
|
|
unfilter: function unfilter() {
|
|
if (!this.isFiltering)
|
|
return;
|
|
|
|
BrowserUI._edit.closePopup();
|
|
Elements.windowState.removeAttribute("filtering");
|
|
},
|
|
|
|
/** Hide the Firefox start page */
|
|
hide: function hide(aURI) {
|
|
aURI = aURI || Browser.selectedBrowser.currentURI.spec;
|
|
if (!this.isStartPageVisible || this.isStartURI(aURI))
|
|
return false;
|
|
|
|
Elements.contentShowing.removeAttribute("disabled");
|
|
Elements.windowState.removeAttribute("startpage");
|
|
|
|
this.unfilter();
|
|
return true;
|
|
},
|
|
|
|
/** Is the current tab supposed to show the Firefox start page? */
|
|
isStartURI: function isStartURI(aURI) {
|
|
aURI = aURI || Browser.selectedBrowser.currentURI.spec;
|
|
return aURI == kStartOverlayURI || aURI == "about:home";
|
|
},
|
|
|
|
/** Call this to show or hide the start page when switching tabs or pages */
|
|
update: function update(aURI) {
|
|
aURI = aURI || Browser.selectedBrowser.currentURI.spec;
|
|
if (this.isStartURI(aURI)) {
|
|
this.show();
|
|
} else if (aURI != "about:blank") { // about:blank is loaded briefly for new tabs; ignore it
|
|
this.hide(aURI);
|
|
}
|
|
},
|
|
|
|
onClick: function onClick(aEvent) {
|
|
// If someone clicks / taps in empty grid space, take away
|
|
// focus from the nav bar edit so the soft keyboard will hide.
|
|
if (BrowserUI.blurNavBar()) {
|
|
// Advanced notice to CAO, so we can shuffle the nav bar in advance
|
|
// of the keyboard transition.
|
|
ContentAreaObserver.navBarWillBlur();
|
|
}
|
|
},
|
|
|
|
handleEvent: function handleEvent(aEvent) {
|
|
switch (aEvent.type) {
|
|
case "autocompletestart":
|
|
this.filter();
|
|
break;
|
|
case "autocompleteend":
|
|
this.unfilter();
|
|
break;
|
|
case "contextmenu":
|
|
let event = document.createEvent("Events");
|
|
event.initEvent("MozEdgeUICompleted", true, false);
|
|
window.dispatchEvent(event);
|
|
break;
|
|
case "click":
|
|
this.onClick(aEvent);
|
|
break;
|
|
|
|
case "MozMousePixelScroll":
|
|
let startBox = document.getElementById("start-scrollbox");
|
|
let [, scrollInterface] = ScrollUtils.getScrollboxFromElement(startBox);
|
|
|
|
scrollInterface.scrollBy(aEvent.detail, 0);
|
|
|
|
aEvent.preventDefault();
|
|
aEvent.stopPropagation();
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
var SyncPanelUI = {
|
|
init: function() {
|
|
// Run some setup code the first time the panel is shown.
|
|
Elements.syncFlyout.addEventListener("PopupChanged", function onShow(aEvent) {
|
|
if (aEvent.detail && aEvent.target === Elements.syncFlyout) {
|
|
Elements.syncFlyout.removeEventListener("PopupChanged", onShow, false);
|
|
Sync.init();
|
|
}
|
|
}, false);
|
|
}
|
|
};
|
|
|
|
var FlyoutPanelsUI = {
|
|
init: function() {
|
|
AboutPanelUI.init();
|
|
PreferencesPanelView.init();
|
|
SyncPanelUI.init();
|
|
|
|
// make sure to hide all flyouts when window is deactivated
|
|
window.addEventListener("deactivate", function(window) {
|
|
FlyoutPanelsUI.hide();
|
|
});
|
|
},
|
|
|
|
hide: function() {
|
|
Elements.aboutFlyout.hide();
|
|
Elements.prefsFlyout.hide();
|
|
Elements.syncFlyout.hide();
|
|
}
|
|
};
|
|
|
|
var PanelUI = {
|
|
get _panels() { return document.getElementById("panel-items"); },
|
|
get _switcher() { return document.getElementById("panel-view-switcher"); },
|
|
|
|
get isVisible() {
|
|
return !Elements.panelUI.hidden;
|
|
},
|
|
|
|
views: {
|
|
"bookmarks-container": "BookmarksPanelView",
|
|
"downloads-container": "DownloadsPanelView",
|
|
"console-container": "ConsolePanelView",
|
|
"remotetabs-container": "RemoteTabsPanelView",
|
|
"history-container" : "HistoryPanelView"
|
|
},
|
|
|
|
init: function() {
|
|
// Perform core init soon
|
|
setTimeout(function () {
|
|
for each (let viewName in this.views) {
|
|
let view = window[viewName];
|
|
if (view.init)
|
|
view.init();
|
|
}
|
|
}.bind(this), 0);
|
|
|
|
// Lazily run other initialization tasks when the views are shown
|
|
this._panels.addEventListener("ToolPanelShown", function(aEvent) {
|
|
let viewName = this.views[this._panels.selectedPanel.id];
|
|
let view = window[viewName];
|
|
if (view.show)
|
|
view.show();
|
|
}.bind(this), true);
|
|
},
|
|
|
|
uninit: function() {
|
|
for each (let viewName in this.views) {
|
|
let view = window[viewName];
|
|
if (view.uninit)
|
|
view.uninit();
|
|
}
|
|
},
|
|
|
|
switchPane: function switchPane(aPanelId) {
|
|
BrowserUI.blurFocusedElement();
|
|
|
|
let panel = aPanelId ? document.getElementById(aPanelId) : this._panels.selectedPanel;
|
|
let oldPanel = this._panels.selectedPanel;
|
|
|
|
if (oldPanel != panel) {
|
|
this._panels.selectedPanel = panel;
|
|
this._switcher.value = panel.id;
|
|
|
|
this._fire("ToolPanelHidden", oldPanel);
|
|
}
|
|
|
|
this._fire("ToolPanelShown", panel);
|
|
},
|
|
|
|
isPaneVisible: function isPaneVisible(aPanelId) {
|
|
return this.isVisible && this._panels.selectedPanel.id == aPanelId;
|
|
},
|
|
|
|
show: function show(aPanelId) {
|
|
Elements.panelUI.hidden = false;
|
|
Elements.contentShowing.setAttribute("disabled", "true");
|
|
|
|
this.switchPane(aPanelId);
|
|
},
|
|
|
|
hide: function hide() {
|
|
if (!this.isVisible)
|
|
return;
|
|
|
|
Elements.panelUI.hidden = true;
|
|
Elements.contentShowing.removeAttribute("disabled");
|
|
BrowserUI.blurFocusedElement();
|
|
|
|
this._fire("ToolPanelHidden", this._panels);
|
|
},
|
|
|
|
toggle: function toggle() {
|
|
if (this.isVisible) {
|
|
this.hide();
|
|
} else {
|
|
this.show();
|
|
}
|
|
},
|
|
|
|
_fire: function _fire(aName, anElement) {
|
|
let event = document.createEvent("Events");
|
|
event.initEvent(aName, true, true);
|
|
anElement.dispatchEvent(event);
|
|
}
|
|
};
|
|
|
|
var DialogUI = {
|
|
_dialogs: [],
|
|
_popup: null,
|
|
|
|
init: function() {
|
|
window.addEventListener("mousedown", this, true);
|
|
},
|
|
|
|
/*******************************************
|
|
* Modal popups
|
|
*/
|
|
|
|
get modals() {
|
|
return document.getElementsByClassName("modal-block");
|
|
},
|
|
|
|
importModal: function importModal(aParent, aSrc, aArguments) {
|
|
// load the dialog with a synchronous XHR
|
|
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
|
|
xhr.open("GET", aSrc, false);
|
|
xhr.overrideMimeType("text/xml");
|
|
xhr.send(null);
|
|
if (!xhr.responseXML)
|
|
return null;
|
|
|
|
let currentNode;
|
|
let nodeIterator = xhr.responseXML.createNodeIterator(xhr.responseXML, NodeFilter.SHOW_TEXT, null, false);
|
|
while (!!(currentNode = nodeIterator.nextNode())) {
|
|
let trimmed = currentNode.nodeValue.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
|
|
if (!trimmed.length)
|
|
currentNode.parentNode.removeChild(currentNode);
|
|
}
|
|
|
|
let doc = xhr.responseXML.documentElement;
|
|
|
|
let dialog = null;
|
|
|
|
// we need to insert before context-container if we want allow pasting (using
|
|
// the context menu) into dialogs
|
|
let contentMenuContainer = document.getElementById("context-container");
|
|
let parentNode = contentMenuContainer.parentNode;
|
|
|
|
// emit DOMWillOpenModalDialog event
|
|
let event = document.createEvent("Events");
|
|
event.initEvent("DOMWillOpenModalDialog", true, false);
|
|
let dispatcher = aParent || getBrowser();
|
|
dispatcher.dispatchEvent(event);
|
|
|
|
// create a full-screen semi-opaque box as a background
|
|
let back = document.createElement("box");
|
|
back.setAttribute("class", "modal-block");
|
|
dialog = back.appendChild(document.importNode(doc, true));
|
|
parentNode.insertBefore(back, contentMenuContainer);
|
|
|
|
dialog.arguments = aArguments;
|
|
dialog.parent = aParent;
|
|
return dialog;
|
|
},
|
|
|
|
/*******************************************
|
|
* Dialogs
|
|
*/
|
|
|
|
get activeDialog() {
|
|
// Return the topmost dialog
|
|
if (this._dialogs.length)
|
|
return this._dialogs[this._dialogs.length - 1];
|
|
return null;
|
|
},
|
|
|
|
closeAllDialogs: function closeAllDialogs() {
|
|
while (this.activeDialog)
|
|
this.activeDialog.close();
|
|
},
|
|
|
|
pushDialog: function pushDialog(aDialog) {
|
|
// If we have a dialog push it on the stack and set the attr for CSS
|
|
if (aDialog) {
|
|
this._dialogs.push(aDialog);
|
|
Elements.contentShowing.setAttribute("disabled", "true");
|
|
}
|
|
},
|
|
|
|
popDialog: function popDialog() {
|
|
if (this._dialogs.length)
|
|
this._dialogs.pop();
|
|
|
|
// If no more dialogs are being displayed, remove the attr for CSS
|
|
if (!this._dialogs.length)
|
|
Elements.contentShowing.removeAttribute("disabled");
|
|
},
|
|
|
|
/*******************************************
|
|
* Popups
|
|
*/
|
|
|
|
pushPopup: function pushPopup(aPanel, aElements, aParent) {
|
|
this._hidePopup();
|
|
this._popup = { "panel": aPanel,
|
|
"elements": (aElements instanceof Array) ? aElements : [aElements] };
|
|
this._dispatchPopupChanged(true, aPanel);
|
|
},
|
|
|
|
popPopup: function popPopup(aPanel) {
|
|
if (!this._popup || aPanel != this._popup.panel)
|
|
return;
|
|
this._popup = null;
|
|
this._dispatchPopupChanged(false, aPanel);
|
|
},
|
|
|
|
_hidePopup: function _hidePopup() {
|
|
if (!this._popup)
|
|
return;
|
|
let panel = this._popup.panel;
|
|
if (panel.hide)
|
|
panel.hide();
|
|
},
|
|
|
|
/*******************************************
|
|
* Events
|
|
*/
|
|
|
|
handleEvent: function (aEvent) {
|
|
switch (aEvent.type) {
|
|
case "mousedown":
|
|
if (!this._isEventInsidePopup(aEvent))
|
|
this._hidePopup();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
},
|
|
|
|
_dispatchPopupChanged: function _dispatchPopupChanged(aVisible, aElement) {
|
|
let event = document.createEvent("UIEvents");
|
|
event.initUIEvent("PopupChanged", true, true, window, aVisible);
|
|
aElement.dispatchEvent(event);
|
|
},
|
|
|
|
_isEventInsidePopup: function _isEventInsidePopup(aEvent) {
|
|
if (!this._popup)
|
|
return false;
|
|
let elements = this._popup.elements;
|
|
let targetNode = aEvent.target;
|
|
while (targetNode && elements.indexOf(targetNode) == -1) {
|
|
if (targetNode instanceof Element && targetNode.hasAttribute("for"))
|
|
targetNode = document.getElementById(targetNode.getAttribute("for"));
|
|
else
|
|
targetNode = targetNode.parentNode;
|
|
}
|
|
return targetNode ? true : false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Manage the contents of the Windows 8 "Settings" charm.
|
|
*/
|
|
var SettingsCharm = {
|
|
_entries: new Map(),
|
|
_nextId: 0,
|
|
|
|
/**
|
|
* Add a new item to the "Settings" menu in the Windows 8 charms.
|
|
* @param aEntry Object with a "label" property (string that will appear in the UI)
|
|
* and an "onselected" property (function to be called when the user chooses this entry)
|
|
*/
|
|
addEntry: function addEntry(aEntry) {
|
|
try {
|
|
let id = MetroUtils.addSettingsPanelEntry(aEntry.label);
|
|
this._entries.set(id, aEntry);
|
|
} catch (e) {
|
|
// addSettingsPanelEntry does not work on non-Metro platforms
|
|
Cu.reportError(e);
|
|
}
|
|
},
|
|
|
|
init: function SettingsCharm_init() {
|
|
Services.obs.addObserver(this, "metro-settings-entry-selected", false);
|
|
|
|
// Options
|
|
this.addEntry({
|
|
label: Strings.browser.GetStringFromName("optionsCharm"),
|
|
onselected: function() Elements.prefsFlyout.show()
|
|
});
|
|
// Sync
|
|
this.addEntry({
|
|
label: Strings.browser.GetStringFromName("syncCharm"),
|
|
onselected: function() Elements.syncFlyout.show()
|
|
});
|
|
// About
|
|
this.addEntry({
|
|
label: Strings.browser.GetStringFromName("aboutCharm1"),
|
|
onselected: function() Elements.aboutFlyout.show()
|
|
});
|
|
// Help
|
|
this.addEntry({
|
|
label: Strings.browser.GetStringFromName("helpOnlineCharm"),
|
|
onselected: function() {
|
|
let url = Services.urlFormatter.formatURLPref("app.support.baseURL");
|
|
BrowserUI.newTab(url, Browser.selectedTab);
|
|
}
|
|
});
|
|
},
|
|
|
|
observe: function SettingsCharm_observe(aSubject, aTopic, aData) {
|
|
if (aTopic == "metro-settings-entry-selected") {
|
|
let entry = this._entries.get(parseInt(aData, 10));
|
|
if (entry)
|
|
entry.onselected();
|
|
}
|
|
},
|
|
|
|
uninit: function SettingsCharm_uninit() {
|
|
Services.obs.removeObserver(this, "metro-settings-entry-selected");
|
|
}
|
|
};
|