michael@0: // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: "use strict"; michael@0: michael@0: Cu.import("resource://gre/modules/devtools/dbg-server.jsm") michael@0: Cu.import("resource://gre/modules/WindowsPrefSync.jsm"); michael@0: michael@0: /** michael@0: * Constants michael@0: */ michael@0: michael@0: // Devtools Messages michael@0: const debugServerStateChanged = "devtools.debugger.remote-enabled"; michael@0: const debugServerPortChanged = "devtools.debugger.remote-port"; michael@0: michael@0: // delay when showing the tab bar briefly after a new foreground tab opens michael@0: const kForegroundTabAnimationDelay = 1000; michael@0: // delay when showing the tab bar after opening a new background tab opens michael@0: const kBackgroundTabAnimationDelay = 3000; michael@0: // delay before closing tab bar after closing or selecting a tab michael@0: const kChangeTabAnimationDelay = 500; michael@0: michael@0: /** michael@0: * Cache of commonly used elements. michael@0: */ michael@0: michael@0: let Elements = {}; michael@0: [ michael@0: ["contentShowing", "bcast_contentShowing"], michael@0: ["urlbarState", "bcast_urlbarState"], michael@0: ["loadingState", "bcast_loadingState"], michael@0: ["windowState", "bcast_windowState"], michael@0: ["chromeState", "bcast_chromeState"], michael@0: ["mainKeyset", "mainKeyset"], michael@0: ["stack", "stack"], michael@0: ["tabList", "tabs"], michael@0: ["tabs", "tabs-container"], michael@0: ["controls", "browser-controls"], michael@0: ["panelUI", "panel-container"], michael@0: ["tray", "tray"], michael@0: ["toolbar", "toolbar"], michael@0: ["browsers", "browsers"], michael@0: ["navbar", "navbar"], michael@0: ["autocomplete", "urlbar-autocomplete"], michael@0: ["contextappbar", "contextappbar"], michael@0: ["findbar", "findbar"], michael@0: ["contentViewport", "content-viewport"], michael@0: ["progress", "progress-control"], michael@0: ["progressContainer", "progress-container"], michael@0: ["feedbackLabel", "feedback-label"], michael@0: ].forEach(function (aElementGlobal) { michael@0: let [name, id] = aElementGlobal; michael@0: XPCOMUtils.defineLazyGetter(Elements, name, function() { michael@0: return document.getElementById(id); michael@0: }); michael@0: }); michael@0: michael@0: /** michael@0: * Cache of commonly used string bundles. michael@0: */ michael@0: michael@0: var Strings = {}; michael@0: [ michael@0: ["browser", "chrome://browser/locale/browser.properties"], michael@0: ["brand", "chrome://branding/locale/brand.properties"] michael@0: ].forEach(function (aStringBundle) { michael@0: let [name, bundle] = aStringBundle; michael@0: XPCOMUtils.defineLazyGetter(Strings, name, function() { michael@0: return Services.strings.createBundle(bundle); michael@0: }); michael@0: }); michael@0: michael@0: var BrowserUI = { michael@0: get _edit() { return document.getElementById("urlbar-edit"); }, michael@0: get _back() { return document.getElementById("cmd_back"); }, michael@0: get _forward() { return document.getElementById("cmd_forward"); }, michael@0: michael@0: lastKnownGoodURL: "", // used when the user wants to escape unfinished url entry michael@0: ready: false, // used for tests to determine when delayed initialization is done michael@0: michael@0: init: function() { michael@0: // start the debugger now so we can use it on the startup code as well michael@0: if (Services.prefs.getBoolPref(debugServerStateChanged)) { michael@0: this.runDebugServer(); michael@0: } michael@0: Services.prefs.addObserver(debugServerStateChanged, this, false); michael@0: Services.prefs.addObserver(debugServerPortChanged, this, false); michael@0: Services.prefs.addObserver("app.crashreporter.autosubmit", this, false); michael@0: Services.prefs.addObserver("metro.private_browsing.enabled", this, false); michael@0: this.updatePrivateBrowsingUI(); michael@0: michael@0: Services.obs.addObserver(this, "handle-xul-text-link", false); michael@0: michael@0: // listen content messages michael@0: messageManager.addMessageListener("DOMTitleChanged", this); michael@0: messageManager.addMessageListener("DOMWillOpenModalDialog", this); michael@0: messageManager.addMessageListener("DOMWindowClose", this); michael@0: michael@0: messageManager.addMessageListener("Browser:OpenURI", this); michael@0: messageManager.addMessageListener("Browser:SaveAs:Return", this); michael@0: messageManager.addMessageListener("Content:StateChange", this); michael@0: michael@0: // listening escape to dismiss dialog on VK_ESCAPE michael@0: window.addEventListener("keypress", this, true); michael@0: michael@0: window.addEventListener("MozPrecisePointer", this, true); michael@0: window.addEventListener("MozImprecisePointer", this, true); michael@0: michael@0: window.addEventListener("AppCommand", this, true); michael@0: michael@0: Services.prefs.addObserver("browser.cache.disk_cache_ssl", this, false); michael@0: michael@0: // Init core UI modules michael@0: ContextUI.init(); michael@0: PanelUI.init(); michael@0: FlyoutPanelsUI.init(); michael@0: PageThumbs.init(); michael@0: NewTabUtils.init(); michael@0: SettingsCharm.init(); michael@0: NavButtonSlider.init(); michael@0: SelectionHelperUI.init(); michael@0: #ifdef NIGHTLY_BUILD michael@0: ShumwayUtils.init(); michael@0: #endif michael@0: michael@0: // We can delay some initialization until after startup. We wait until michael@0: // the first page is shown, then dispatch a UIReadyDelayed event. michael@0: messageManager.addMessageListener("pageshow", function onPageShow() { michael@0: if (getBrowser().currentURI.spec == "about:blank") michael@0: return; michael@0: michael@0: messageManager.removeMessageListener("pageshow", onPageShow); michael@0: michael@0: setTimeout(function() { michael@0: let event = document.createEvent("Events"); michael@0: event.initEvent("UIReadyDelayed", true, false); michael@0: window.dispatchEvent(event); michael@0: BrowserUI.ready = true; michael@0: }, 0); michael@0: }); michael@0: michael@0: // Only load IndexedDB.js when we actually need it. A general fix will happen in bug 647079. michael@0: messageManager.addMessageListener("IndexedDB:Prompt", function(aMessage) { michael@0: return IndexedDB.receiveMessage(aMessage); michael@0: }); michael@0: michael@0: // hook up telemetry ping for UI data michael@0: try { michael@0: UITelemetry.addSimpleMeasureFunction("metro-ui", michael@0: BrowserUI._getMeasures.bind(BrowserUI)); michael@0: } catch (ex) { michael@0: // swallow exception that occurs if metro-appbar measure is already set up michael@0: dump("Failed to addSimpleMeasureFunction in browser-ui: " + ex.message + "\n"); michael@0: } michael@0: michael@0: // Delay the panel UI and Sync initialization michael@0: window.addEventListener("UIReadyDelayed", function delayedInit(aEvent) { michael@0: Util.dumpLn("* delay load started..."); michael@0: window.removeEventListener("UIReadyDelayed", delayedInit, false); michael@0: michael@0: // Login Manager and Form History initialization michael@0: Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); michael@0: messageManager.addMessageListener("Browser:MozApplicationManifest", OfflineApps); michael@0: michael@0: try { michael@0: MetroDownloadsView.init(); michael@0: DialogUI.init(); michael@0: FormHelperUI.init(); michael@0: FindHelperUI.init(); michael@0: #ifdef NIGHTLY_BUILD michael@0: PdfJs.init(); michael@0: #endif michael@0: } catch(ex) { michael@0: Util.dumpLn("Exception in delay load module:", ex.message); michael@0: } michael@0: michael@0: BrowserUI._initFirstRunContent(); michael@0: michael@0: // check for left over crash reports and submit them if found. michael@0: BrowserUI.startupCrashCheck(); michael@0: michael@0: Util.dumpLn("* delay load complete."); michael@0: }, false); michael@0: michael@0: #ifndef MOZ_OFFICIAL_BRANDING michael@0: setTimeout(function() { michael@0: let startup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup).getStartupInfo(); michael@0: for (let name in startup) { michael@0: if (name != "process") michael@0: Services.console.logStringMessage("[timing] " + name + ": " + (startup[name] - startup.process) + "ms"); michael@0: } michael@0: }, 3000); michael@0: #endif michael@0: }, michael@0: michael@0: uninit: function() { michael@0: messageManager.removeMessageListener("DOMTitleChanged", this); michael@0: messageManager.removeMessageListener("DOMWillOpenModalDialog", this); michael@0: messageManager.removeMessageListener("DOMWindowClose", this); michael@0: michael@0: messageManager.removeMessageListener("Browser:OpenURI", this); michael@0: messageManager.removeMessageListener("Browser:SaveAs:Return", this); michael@0: messageManager.removeMessageListener("Content:StateChange", this); michael@0: michael@0: messageManager.removeMessageListener("Browser:MozApplicationManifest", OfflineApps); michael@0: michael@0: Services.prefs.removeObserver(debugServerStateChanged, this); michael@0: Services.prefs.removeObserver(debugServerPortChanged, this); michael@0: Services.prefs.removeObserver("app.crashreporter.autosubmit", this); michael@0: Services.prefs.removeObserver("metro.private_browsing.enabled", this); michael@0: michael@0: Services.obs.removeObserver(this, "handle-xul-text-link"); michael@0: michael@0: PanelUI.uninit(); michael@0: FlyoutPanelsUI.uninit(); michael@0: MetroDownloadsView.uninit(); michael@0: SettingsCharm.uninit(); michael@0: PageThumbs.uninit(); michael@0: if (WindowsPrefSync) { michael@0: WindowsPrefSync.uninit(); michael@0: } michael@0: this.stopDebugServer(); michael@0: }, michael@0: michael@0: /************************************ michael@0: * Devtools Debugger michael@0: */ michael@0: runDebugServer: function runDebugServer(aPort) { michael@0: let port = aPort || Services.prefs.getIntPref(debugServerPortChanged); michael@0: if (!DebuggerServer.initialized) { michael@0: DebuggerServer.init(); michael@0: DebuggerServer.addBrowserActors(); michael@0: DebuggerServer.addActors('chrome://browser/content/dbg-metro-actors.js'); michael@0: } michael@0: DebuggerServer.openListener(port); michael@0: }, michael@0: michael@0: stopDebugServer: function stopDebugServer() { michael@0: if (DebuggerServer.initialized) { michael@0: DebuggerServer.destroy(); michael@0: } michael@0: }, michael@0: michael@0: // If the server is not on, port changes have nothing to effect. The new value michael@0: // will be picked up if the server is started. michael@0: // To be consistent with desktop fx, if the port is changed while the server michael@0: // is running, restart server. michael@0: changeDebugPort:function changeDebugPort(aPort) { michael@0: if (DebuggerServer.initialized) { michael@0: this.stopDebugServer(); michael@0: this.runDebugServer(aPort); michael@0: } michael@0: }, michael@0: michael@0: /********************************* michael@0: * Content visibility michael@0: */ michael@0: michael@0: get isContentShowing() { michael@0: return Elements.contentShowing.getAttribute("disabled") != true; michael@0: }, michael@0: michael@0: showContent: function showContent(aURI) { michael@0: ContextUI.dismissTabs(); michael@0: ContextUI.dismissContextAppbar(); michael@0: FlyoutPanelsUI.hide(); michael@0: PanelUI.hide(); michael@0: }, michael@0: michael@0: /********************************* michael@0: * Crash reporting michael@0: */ michael@0: michael@0: get CrashSubmit() { michael@0: delete this.CrashSubmit; michael@0: Cu.import("resource://gre/modules/CrashSubmit.jsm", this); michael@0: return this.CrashSubmit; michael@0: }, michael@0: michael@0: get lastCrashID() { michael@0: return Cc["@mozilla.org/xre/runtime;1"].getService(Ci.nsIXULRuntime).lastRunCrashID; michael@0: }, michael@0: michael@0: startupCrashCheck: function startupCrashCheck() { michael@0: #ifdef MOZ_CRASHREPORTER michael@0: if (!CrashReporter.enabled) { michael@0: return; michael@0: } michael@0: michael@0: // Ensure that CrashReporter state matches pref michael@0: CrashReporter.submitReports = Services.prefs.getBoolPref("app.crashreporter.autosubmit"); michael@0: michael@0: BrowserUI.submitLastCrashReportOrShowPrompt(); michael@0: #endif michael@0: }, michael@0: michael@0: michael@0: /********************************* michael@0: * Navigation michael@0: */ michael@0: michael@0: // BrowserUI update bit flags michael@0: NO_STARTUI_VISIBILITY: 1, // don't change the start ui visibility michael@0: michael@0: /* michael@0: * Updates the overall state of startui visibility and the toolbar, but not michael@0: * the URL bar. michael@0: */ michael@0: update: function(aFlags) { michael@0: let flags = aFlags || 0; michael@0: if (!(flags & this.NO_STARTUI_VISIBILITY)) { michael@0: let uri = this.getDisplayURI(Browser.selectedBrowser); michael@0: this.updateStartURIAttributes(uri); michael@0: } michael@0: this._updateButtons(); michael@0: this._updateToolbar(); michael@0: }, michael@0: michael@0: /* Updates the URL bar. */ michael@0: updateURI: function(aOptions) { michael@0: let uri = this.getDisplayURI(Browser.selectedBrowser); michael@0: let cleanURI = Util.isURLEmpty(uri) ? "" : uri; michael@0: this._edit.value = cleanURI; michael@0: }, michael@0: michael@0: get isStartTabVisible() { michael@0: return this.isStartURI(); michael@0: }, michael@0: michael@0: isStartURI: function isStartURI(aURI) { michael@0: aURI = aURI || Browser.selectedBrowser.currentURI.spec; michael@0: return aURI.startsWith(kStartURI) || aURI == "about:start" || aURI == "about:home"; michael@0: }, michael@0: michael@0: updateStartURIAttributes: function (aURI) { michael@0: let wasStart = Elements.windowState.hasAttribute("startpage"); michael@0: aURI = aURI || Browser.selectedBrowser.currentURI.spec; michael@0: if (this.isStartURI(aURI)) { michael@0: ContextUI.displayNavbar(); michael@0: Elements.windowState.setAttribute("startpage", "true"); michael@0: } else if (aURI != "about:blank") { // about:blank is loaded briefly for new tabs; ignore it michael@0: Elements.windowState.removeAttribute("startpage"); michael@0: } michael@0: michael@0: let isStart = Elements.windowState.hasAttribute("startpage"); michael@0: if (wasStart != isStart) { michael@0: let event = document.createEvent("Events"); michael@0: event.initEvent("StartUIChange", true, true); michael@0: Browser.selectedBrowser.dispatchEvent(event); michael@0: } michael@0: }, michael@0: michael@0: getDisplayURI: function(browser) { michael@0: let uri = browser.currentURI; michael@0: let spec = uri.spec; michael@0: michael@0: try { michael@0: spec = gURIFixup.createExposableURI(uri).spec; michael@0: } catch (ex) {} michael@0: michael@0: try { michael@0: let charset = browser.characterSet; michael@0: let textToSubURI = Cc["@mozilla.org/intl/texttosuburi;1"]. michael@0: getService(Ci.nsITextToSubURI); michael@0: spec = textToSubURI.unEscapeNonAsciiURI(charset, spec); michael@0: } catch (ex) {} michael@0: michael@0: return spec; michael@0: }, michael@0: michael@0: goToURI: function(aURI) { michael@0: aURI = aURI || this._edit.value; michael@0: if (!aURI) michael@0: return; michael@0: michael@0: this._edit.value = aURI; michael@0: michael@0: // Make sure we're online before attempting to load michael@0: Util.forceOnline(); michael@0: michael@0: BrowserUI.showContent(aURI); michael@0: Browser.selectedBrowser.focus(); michael@0: michael@0: Task.spawn(function() { michael@0: let postData = {}; michael@0: let webNav = Ci.nsIWebNavigation; michael@0: let flags = webNav.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP | michael@0: webNav.LOAD_FLAGS_FIXUP_SCHEME_TYPOS; michael@0: aURI = yield Browser.getShortcutOrURI(aURI, postData); michael@0: Browser.loadURI(aURI, { flags: flags, postData: postData }); michael@0: michael@0: // Delay doing the fixup so the raw URI is passed to loadURIWithFlags michael@0: // and the proper third-party fixup can be done michael@0: let fixupFlags = Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP | michael@0: Ci.nsIURIFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS; michael@0: let uri = gURIFixup.createFixupURI(aURI, fixupFlags); michael@0: gHistSvc.markPageAsTyped(uri); michael@0: michael@0: BrowserUI._titleChanged(Browser.selectedBrowser); michael@0: }); michael@0: }, michael@0: michael@0: doOpenSearch: function doOpenSearch(aName) { michael@0: // save the current value of the urlbar michael@0: let searchValue = this._edit.value; michael@0: let engine = Services.search.getEngineByName(aName); michael@0: let submission = engine.getSubmission(searchValue, null); michael@0: michael@0: this._edit.value = submission.uri.spec; michael@0: michael@0: // Make sure we're online before attempting to load michael@0: Util.forceOnline(); michael@0: michael@0: BrowserUI.showContent(); michael@0: Browser.selectedBrowser.focus(); michael@0: michael@0: Task.spawn(function () { michael@0: Browser.loadURI(submission.uri.spec, { postData: submission.postData }); michael@0: michael@0: // loadURI may open a new tab, so get the selectedBrowser afterward. michael@0: Browser.selectedBrowser.userTypedValue = submission.uri.spec; michael@0: BrowserUI._titleChanged(Browser.selectedBrowser); michael@0: }); michael@0: }, michael@0: michael@0: /********************************* michael@0: * Tab management michael@0: */ michael@0: michael@0: /** michael@0: * Open a new tab in the foreground in response to a user action. michael@0: * See Browser.addTab for more documentation. michael@0: */ michael@0: addAndShowTab: function (aURI, aOwner, aParams) { michael@0: ContextUI.peekTabs(kForegroundTabAnimationDelay); michael@0: return Browser.addTab(aURI || kStartURI, true, aOwner, aParams); michael@0: }, michael@0: michael@0: addAndShowPrivateTab: function (aURI, aOwner) { michael@0: return this.addAndShowTab(aURI, aOwner, { private: true }); michael@0: }, michael@0: michael@0: get isPrivateBrowsingEnabled() { michael@0: return Services.prefs.getBoolPref("metro.private_browsing.enabled"); michael@0: }, michael@0: michael@0: updatePrivateBrowsingUI: function () { michael@0: let command = document.getElementById("cmd_newPrivateTab"); michael@0: if (this.isPrivateBrowsingEnabled) { michael@0: command.removeAttribute("disabled"); michael@0: } else { michael@0: command.setAttribute("disabled", "true"); michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Open a new tab in response to clicking a link in an existing tab. michael@0: * See Browser.addTab for more documentation. michael@0: */ michael@0: openLinkInNewTab: function (aURI, aBringFront, aOwner) { michael@0: ContextUI.peekTabs(aBringFront ? kForegroundTabAnimationDelay michael@0: : kBackgroundTabAnimationDelay); michael@0: let params = null; michael@0: if (aOwner) { michael@0: params = { michael@0: referrerURI: aOwner.browser.documentURI, michael@0: charset: aOwner.browser.characterSet, michael@0: }; michael@0: } michael@0: let tab = Browser.addTab(aURI, aBringFront, aOwner, params); michael@0: Elements.tabList.strip.ensureElementIsVisible(tab.chromeTab); michael@0: return tab; michael@0: }, michael@0: michael@0: setOnTabAnimationEnd: function setOnTabAnimationEnd(aCallback) { michael@0: Elements.tabs.addEventListener("animationend", function onAnimationEnd() { michael@0: Elements.tabs.removeEventListener("animationend", onAnimationEnd); michael@0: aCallback(); michael@0: }); michael@0: }, michael@0: michael@0: closeTab: function closeTab(aTab) { michael@0: // If no tab is passed in, assume the current tab michael@0: let tab = aTab || Browser.selectedTab; michael@0: Browser.closeTab(tab); michael@0: }, michael@0: michael@0: animateClosingTab: function animateClosingTab(tabToClose) { michael@0: tabToClose.chromeTab.setAttribute("closing", "true"); michael@0: michael@0: let wasCollapsed = !ContextUI.tabbarVisible; michael@0: if (wasCollapsed) { michael@0: ContextUI.displayTabs(); michael@0: } michael@0: michael@0: this.setOnTabAnimationEnd(function() { michael@0: Browser.closeTab(tabToClose, { forceClose: true } ); michael@0: if (wasCollapsed) michael@0: ContextUI.dismissTabsWithDelay(kChangeTabAnimationDelay); michael@0: }); michael@0: }, michael@0: michael@0: /** michael@0: * Re-open a closed tab. michael@0: * @param aIndex michael@0: * The index of the tab (via nsSessionStore.getClosedTabData) michael@0: * @returns a reference to the reopened tab. michael@0: */ michael@0: undoCloseTab: function undoCloseTab(aIndex) { michael@0: var tab = null; michael@0: aIndex = aIndex || 0; michael@0: var ss = Cc["@mozilla.org/browser/sessionstore;1"]. michael@0: getService(Ci.nsISessionStore); michael@0: if (ss.getClosedTabCount(window) > (aIndex)) { michael@0: tab = ss.undoCloseTab(window, aIndex); michael@0: } michael@0: return tab; michael@0: }, michael@0: michael@0: // Useful for when we've received an event to close a particular DOM window. michael@0: // Since we don't have windows, we want to close the corresponding tab. michael@0: closeTabForBrowser: function closeTabForBrowser(aBrowser) { michael@0: // Find the relevant tab, and close it. michael@0: let browsers = Browser.browsers; michael@0: for (let i = 0; i < browsers.length; i++) { michael@0: if (browsers[i] == aBrowser) { michael@0: Browser.closeTab(Browser.getTabAtIndex(i)); michael@0: return { preventDefault: true }; michael@0: } michael@0: } michael@0: michael@0: return {}; michael@0: }, michael@0: michael@0: selectTab: function selectTab(aTab) { michael@0: Browser.selectedTab = aTab; michael@0: }, michael@0: michael@0: selectTabAndDismiss: function selectTabAndDismiss(aTab) { michael@0: this.selectTab(aTab); michael@0: ContextUI.dismissTabsWithDelay(kChangeTabAnimationDelay); michael@0: }, michael@0: michael@0: selectTabAtIndex: function selectTabAtIndex(aIndex) { michael@0: // count backwards for aIndex < 0 michael@0: if (aIndex < 0) michael@0: aIndex += Browser._tabs.length; michael@0: michael@0: if (aIndex >= 0 && aIndex < Browser._tabs.length) michael@0: Browser.selectedTab = Browser._tabs[aIndex]; michael@0: }, michael@0: michael@0: selectNextTab: function selectNextTab() { michael@0: if (Browser._tabs.length == 1 || !Browser.selectedTab) { michael@0: return; michael@0: } michael@0: michael@0: let tabIndex = Browser._tabs.indexOf(Browser.selectedTab) + 1; michael@0: if (tabIndex >= Browser._tabs.length) { michael@0: tabIndex = 0; michael@0: } michael@0: michael@0: Browser.selectedTab = Browser._tabs[tabIndex]; michael@0: }, michael@0: michael@0: selectPreviousTab: function selectPreviousTab() { michael@0: if (Browser._tabs.length == 1 || !Browser.selectedTab) { michael@0: return; michael@0: } michael@0: michael@0: let tabIndex = Browser._tabs.indexOf(Browser.selectedTab) - 1; michael@0: if (tabIndex < 0) { michael@0: tabIndex = Browser._tabs.length - 1; michael@0: } michael@0: michael@0: Browser.selectedTab = Browser._tabs[tabIndex]; michael@0: }, michael@0: michael@0: // Used for when we're about to open a modal dialog, michael@0: // and want to ensure the opening tab is in front. michael@0: selectTabForBrowser: function selectTabForBrowser(aBrowser) { michael@0: for (let i = 0; i < Browser.tabs.length; i++) { michael@0: if (Browser._tabs[i].browser == aBrowser) { michael@0: Browser.selectedTab = Browser.tabs[i]; michael@0: break; michael@0: } michael@0: } michael@0: }, michael@0: michael@0: updateUIFocus: function _updateUIFocus() { michael@0: if (Elements.contentShowing.getAttribute("disabled") == "true" && Browser.selectedBrowser) michael@0: Browser.selectedBrowser.messageManager.sendAsyncMessage("Browser:Blur", { }); michael@0: }, michael@0: michael@0: blurFocusedElement: function blurFocusedElement() { michael@0: let focusedElement = document.commandDispatcher.focusedElement; michael@0: if (focusedElement) michael@0: focusedElement.blur(); michael@0: }, michael@0: michael@0: blurNavBar: function blurNavBar() { michael@0: if (this._edit.focused) { michael@0: this._edit.blur(); michael@0: michael@0: // Advanced notice to CAO, so we can shuffle the nav bar in advance michael@0: // of the keyboard transition. michael@0: ContentAreaObserver.navBarWillBlur(); michael@0: michael@0: return true; michael@0: } michael@0: return false; michael@0: }, michael@0: michael@0: observe: function BrowserUI_observe(aSubject, aTopic, aData) { michael@0: switch (aTopic) { michael@0: case "handle-xul-text-link": michael@0: let handled = aSubject.QueryInterface(Ci.nsISupportsPRBool); michael@0: if (!handled.data) { michael@0: this.addAndShowTab(aData, Browser.selectedTab); michael@0: handled.data = true; michael@0: } michael@0: break; michael@0: case "nsPref:changed": michael@0: switch (aData) { michael@0: case "browser.cache.disk_cache_ssl": michael@0: this._sslDiskCacheEnabled = Services.prefs.getBoolPref(aData); michael@0: break; michael@0: case debugServerStateChanged: michael@0: if (Services.prefs.getBoolPref(aData)) { michael@0: this.runDebugServer(); michael@0: } else { michael@0: this.stopDebugServer(); michael@0: } michael@0: break; michael@0: case debugServerPortChanged: michael@0: this.changeDebugPort(Services.prefs.getIntPref(aData)); michael@0: break; michael@0: case "app.crashreporter.autosubmit": michael@0: #ifdef MOZ_CRASHREPORTER michael@0: CrashReporter.submitReports = Services.prefs.getBoolPref(aData); michael@0: michael@0: // The user explicitly set the autosubmit option, so there is no michael@0: // need to prompt them about crash reporting in the future michael@0: Services.prefs.setBoolPref("app.crashreporter.prompted", true); michael@0: michael@0: BrowserUI.submitLastCrashReportOrShowPrompt; michael@0: #endif michael@0: break; michael@0: case "metro.private_browsing.enabled": michael@0: this.updatePrivateBrowsingUI(); michael@0: break; michael@0: } michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: submitLastCrashReportOrShowPrompt: function() { michael@0: #ifdef MOZ_CRASHREPORTER michael@0: let lastCrashID = this.lastCrashID; michael@0: if (lastCrashID && lastCrashID.length) { michael@0: if (Services.prefs.getBoolPref("app.crashreporter.autosubmit")) { michael@0: Util.dumpLn("Submitting last crash id:", lastCrashID); michael@0: let params = {}; michael@0: if (!Services.prefs.getBoolPref("app.crashreporter.submitURLs")) { michael@0: params['extraExtraKeyVals'] = { URL: '' }; michael@0: } michael@0: try { michael@0: this.CrashSubmit.submit(lastCrashID, params); michael@0: } catch (ex) { michael@0: Util.dumpLn(ex); michael@0: } michael@0: } else if (!Services.prefs.getBoolPref("app.crashreporter.prompted")) { michael@0: BrowserUI.addAndShowTab("about:crashprompt", null); michael@0: } michael@0: } michael@0: #endif michael@0: }, michael@0: michael@0: michael@0: michael@0: /********************************* michael@0: * Internal utils michael@0: */ michael@0: michael@0: _titleChanged: function(aBrowser) { michael@0: let url = this.getDisplayURI(aBrowser); michael@0: michael@0: let tabCaption; michael@0: if (aBrowser.contentTitle) { michael@0: tabCaption = aBrowser.contentTitle; michael@0: } else if (!Util.isURLEmpty(aBrowser.userTypedValue)) { michael@0: tabCaption = aBrowser.userTypedValue; michael@0: } else if (!Util.isURLEmpty(url)) { michael@0: tabCaption = url; michael@0: } else { michael@0: tabCaption = Util.getEmptyURLTabTitle(); michael@0: } michael@0: michael@0: let tab = Browser.getTabForBrowser(aBrowser); michael@0: if (tab) michael@0: tab.chromeTab.updateTitle(tabCaption); michael@0: }, michael@0: michael@0: _updateButtons: function _updateButtons() { michael@0: let browser = Browser.selectedBrowser; michael@0: if (!browser) { michael@0: return; michael@0: } michael@0: if (browser.canGoBack) { michael@0: this._back.removeAttribute("disabled"); michael@0: } else { michael@0: this._back.setAttribute("disabled", true); michael@0: } michael@0: if (browser.canGoForward) { michael@0: this._forward.removeAttribute("disabled"); michael@0: } else { michael@0: this._forward.setAttribute("disabled", true); michael@0: } michael@0: }, michael@0: michael@0: _updateToolbar: function _updateToolbar() { michael@0: if (Browser.selectedTab.isLoading()) { michael@0: Elements.loadingState.setAttribute("loading", true); michael@0: } else { michael@0: Elements.loadingState.removeAttribute("loading"); michael@0: } michael@0: }, michael@0: michael@0: _closeOrQuit: function _closeOrQuit() { michael@0: // Close active dialog, if we have one. If not then close the application. michael@0: if (!BrowserUI.isContentShowing()) { michael@0: BrowserUI.showContent(); michael@0: } else { michael@0: // Check to see if we should really close the window michael@0: if (Browser.closing()) { michael@0: window.close(); michael@0: let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup); michael@0: appStartup.quit(Ci.nsIAppStartup.eForceQuit); michael@0: } michael@0: } michael@0: }, michael@0: michael@0: _onPreciseInput: function _onPreciseInput() { michael@0: document.getElementById("bcast_preciseInput").setAttribute("input", "precise"); michael@0: let uri = Util.makeURI("chrome://browser/content/cursor.css"); michael@0: if (StyleSheetSvc.sheetRegistered(uri, Ci.nsIStyleSheetService.AGENT_SHEET)) { michael@0: StyleSheetSvc.unregisterSheet(uri, michael@0: Ci.nsIStyleSheetService.AGENT_SHEET); michael@0: } michael@0: }, michael@0: michael@0: _onImpreciseInput: function _onImpreciseInput() { michael@0: document.getElementById("bcast_preciseInput").setAttribute("input", "imprecise"); michael@0: let uri = Util.makeURI("chrome://browser/content/cursor.css"); michael@0: if (!StyleSheetSvc.sheetRegistered(uri, Ci.nsIStyleSheetService.AGENT_SHEET)) { michael@0: StyleSheetSvc.loadAndRegisterSheet(uri, michael@0: Ci.nsIStyleSheetService.AGENT_SHEET); michael@0: } michael@0: }, michael@0: michael@0: _getMeasures: function() { michael@0: let dimensions = { michael@0: "window-width": ContentAreaObserver.width, michael@0: "window-height": ContentAreaObserver.height michael@0: }; michael@0: return dimensions; michael@0: }, michael@0: michael@0: /********************************* michael@0: * Event handling michael@0: */ michael@0: michael@0: handleEvent: function handleEvent(aEvent) { michael@0: var target = aEvent.target; michael@0: switch (aEvent.type) { michael@0: // Window events michael@0: case "keypress": michael@0: if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) michael@0: this.handleEscape(aEvent); michael@0: break; michael@0: case "MozPrecisePointer": michael@0: this._onPreciseInput(); michael@0: break; michael@0: case "MozImprecisePointer": michael@0: this._onImpreciseInput(); michael@0: break; michael@0: case "AppCommand": michael@0: this.handleAppCommandEvent(aEvent); michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: // Checks if various different parts of the UI is visible and closes michael@0: // them one at a time. michael@0: handleEscape: function (aEvent) { michael@0: aEvent.stopPropagation(); michael@0: aEvent.preventDefault(); michael@0: michael@0: if (this._edit.popupOpen) { michael@0: this._edit.endEditing(true); michael@0: return; michael@0: } michael@0: michael@0: // Check open popups michael@0: if (DialogUI._popup) { michael@0: DialogUI._hidePopup(); michael@0: return; michael@0: } michael@0: michael@0: // Check open panel michael@0: if (PanelUI.isVisible) { michael@0: PanelUI.hide(); michael@0: return; michael@0: } michael@0: michael@0: // Check content helper michael@0: if (FindHelperUI.isActive) { michael@0: FindHelperUI.hide(); michael@0: return; michael@0: } michael@0: michael@0: if (Browser.selectedTab.isLoading()) { michael@0: Browser.selectedBrowser.stop(); michael@0: return; michael@0: } michael@0: michael@0: if (ContextUI.dismiss()) { michael@0: return; michael@0: } michael@0: }, michael@0: michael@0: handleBackspace: function handleBackspace() { michael@0: switch (Services.prefs.getIntPref("browser.backspace_action")) { michael@0: case 0: michael@0: CommandUpdater.doCommand("cmd_back"); michael@0: break; michael@0: case 1: michael@0: CommandUpdater.doCommand("cmd_scrollPageUp"); michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: handleShiftBackspace: function handleShiftBackspace() { michael@0: switch (Services.prefs.getIntPref("browser.backspace_action")) { michael@0: case 0: michael@0: CommandUpdater.doCommand("cmd_forward"); michael@0: break; michael@0: case 1: michael@0: CommandUpdater.doCommand("cmd_scrollPageDown"); michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: openFile: function() { michael@0: try { michael@0: const nsIFilePicker = Ci.nsIFilePicker; michael@0: let fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker); michael@0: let self = this; michael@0: let fpCallback = function fpCallback_done(aResult) { michael@0: if (aResult == nsIFilePicker.returnOK) { michael@0: self.goToURI(fp.fileURL.spec); michael@0: } michael@0: }; michael@0: michael@0: let windowTitle = Strings.browser.GetStringFromName("browserForOpenLocation"); michael@0: fp.init(window, windowTitle, nsIFilePicker.modeOpen); michael@0: fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText | michael@0: nsIFilePicker.filterImages | nsIFilePicker.filterXML | michael@0: nsIFilePicker.filterHTML); michael@0: fp.open(fpCallback); michael@0: } catch (ex) { michael@0: dump ('BrowserUI openFile exception: ' + ex + '\n'); michael@0: } michael@0: }, michael@0: michael@0: savePage: function() { michael@0: Browser.savePage(); michael@0: }, michael@0: michael@0: receiveMessage: function receiveMessage(aMessage) { michael@0: let browser = aMessage.target; michael@0: let json = aMessage.json; michael@0: switch (aMessage.name) { michael@0: case "DOMTitleChanged": michael@0: this._titleChanged(browser); michael@0: break; michael@0: case "DOMWillOpenModalDialog": michael@0: this.selectTabForBrowser(browser); michael@0: break; michael@0: case "DOMWindowClose": michael@0: return this.closeTabForBrowser(browser); michael@0: break; michael@0: // XXX this and content's sender are a little warped michael@0: case "Browser:OpenURI": michael@0: let referrerURI = null; michael@0: if (json.referrer) michael@0: referrerURI = Services.io.newURI(json.referrer, null, null); michael@0: this.goToURI(json.uri); michael@0: break; michael@0: case "Content:StateChange": { michael@0: let tab = Browser.selectedTab; michael@0: if (this.shouldCaptureThumbnails(tab)) { michael@0: PageThumbs.captureAndStore(tab.browser); michael@0: let currPage = tab.browser.currentURI.spec; michael@0: Services.obs.notifyObservers(null, "Metro:RefreshTopsiteThumbnail", currPage); michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return {}; michael@0: }, michael@0: michael@0: shouldCaptureThumbnails: function shouldCaptureThumbnails(aTab) { michael@0: // Capture only if it's the currently selected tab. michael@0: if (aTab != Browser.selectedTab) { michael@0: return false; michael@0: } michael@0: // Skip private tabs michael@0: if (aTab.isPrivate) { michael@0: return false; michael@0: } michael@0: // FIXME Bug 720575 - Don't capture thumbnails for SVG or XML documents as michael@0: // that currently regresses Talos SVG tests. michael@0: let browser = aTab.browser; michael@0: let doc = browser.contentDocument; michael@0: if (doc instanceof SVGDocument || doc instanceof XMLDocument) { michael@0: return false; michael@0: } michael@0: michael@0: // Don't capture pages in snapped mode, this produces 2/3 black michael@0: // thumbs or stretched out ones michael@0: // Ci.nsIWinMetroUtils.snapped is inaccessible on michael@0: // desktop/nonwindows systems michael@0: if(Elements.windowState.getAttribute("viewstate") == "snapped") { michael@0: return false; michael@0: } michael@0: // There's no point in taking screenshot of loading pages. michael@0: if (browser.docShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE) { michael@0: return false; michael@0: } michael@0: michael@0: // Don't take screenshots of about: pages. michael@0: if (browser.currentURI.schemeIs("about")) { michael@0: return false; michael@0: } michael@0: michael@0: // No valid document channel. We shouldn't take a screenshot. michael@0: let channel = browser.docShell.currentDocumentChannel; michael@0: if (!channel) { michael@0: return false; michael@0: } michael@0: michael@0: // Don't take screenshots of internally redirecting about: pages. michael@0: // This includes error pages. michael@0: let uri = channel.originalURI; michael@0: if (uri.schemeIs("about")) { michael@0: return false; michael@0: } michael@0: michael@0: // http checks michael@0: let httpChannel; michael@0: try { michael@0: httpChannel = channel.QueryInterface(Ci.nsIHttpChannel); michael@0: } catch (e) { /* Not an HTTP channel. */ } michael@0: michael@0: if (httpChannel) { michael@0: // Continue only if we have a 2xx status code. michael@0: try { michael@0: if (Math.floor(httpChannel.responseStatus / 100) != 2) { michael@0: return false; michael@0: } michael@0: } catch (e) { michael@0: // Can't get response information from the httpChannel michael@0: // because mResponseHead is not available. michael@0: return false; michael@0: } michael@0: michael@0: // Cache-Control: no-store. michael@0: if (httpChannel.isNoStoreResponse()) { michael@0: return false; michael@0: } michael@0: michael@0: // Don't capture HTTPS pages unless the user enabled it. michael@0: if (uri.schemeIs("https") && !this.sslDiskCacheEnabled) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: }, michael@0: michael@0: _sslDiskCacheEnabled: null, michael@0: michael@0: get sslDiskCacheEnabled() { michael@0: if (this._sslDiskCacheEnabled === null) { michael@0: this._sslDiskCacheEnabled = Services.prefs.getBoolPref("browser.cache.disk_cache_ssl"); michael@0: } michael@0: return this._sslDiskCacheEnabled; michael@0: }, michael@0: michael@0: supportsCommand : function(cmd) { michael@0: var isSupported = false; michael@0: switch (cmd) { michael@0: case "cmd_back": michael@0: case "cmd_forward": michael@0: case "cmd_reload": michael@0: case "cmd_forceReload": michael@0: case "cmd_stop": michael@0: case "cmd_go": michael@0: case "cmd_home": michael@0: case "cmd_openLocation": michael@0: case "cmd_addBookmark": michael@0: case "cmd_bookmarks": michael@0: case "cmd_history": michael@0: case "cmd_remoteTabs": michael@0: case "cmd_quit": michael@0: case "cmd_close": michael@0: case "cmd_newTab": michael@0: case "cmd_newTabKey": michael@0: case "cmd_closeTab": michael@0: case "cmd_undoCloseTab": michael@0: case "cmd_actions": michael@0: case "cmd_panel": michael@0: case "cmd_reportingCrashesSubmitURLs": michael@0: case "cmd_flyout_back": michael@0: case "cmd_sanitize": michael@0: case "cmd_volumeLeft": michael@0: case "cmd_volumeRight": michael@0: case "cmd_openFile": michael@0: case "cmd_savePage": michael@0: isSupported = true; michael@0: break; michael@0: default: michael@0: isSupported = false; michael@0: break; michael@0: } michael@0: return isSupported; michael@0: }, michael@0: michael@0: isCommandEnabled : function(cmd) { michael@0: let elem = document.getElementById(cmd); michael@0: if (elem && elem.getAttribute("disabled") == "true") michael@0: return false; michael@0: return true; michael@0: }, michael@0: michael@0: doCommand : function(cmd) { michael@0: if (!this.isCommandEnabled(cmd)) michael@0: return; michael@0: let browser = getBrowser(); michael@0: switch (cmd) { michael@0: case "cmd_back": michael@0: browser.goBack(); michael@0: break; michael@0: case "cmd_forward": michael@0: browser.goForward(); michael@0: break; michael@0: case "cmd_reload": michael@0: browser.reload(); michael@0: break; michael@0: case "cmd_forceReload": michael@0: { michael@0: // Simulate a new page michael@0: browser.lastLocation = null; michael@0: michael@0: const reloadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | michael@0: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE; michael@0: browser.reloadWithFlags(reloadFlags); michael@0: break; michael@0: } michael@0: case "cmd_stop": michael@0: browser.stop(); michael@0: break; michael@0: case "cmd_go": michael@0: this.goToURI(); michael@0: break; michael@0: case "cmd_home": michael@0: this.goToURI(Browser.getHomePage()); michael@0: break; michael@0: case "cmd_openLocation": michael@0: ContextUI.displayNavbar(); michael@0: this._edit.beginEditing(true); michael@0: this._edit.select(); michael@0: break; michael@0: case "cmd_addBookmark": michael@0: ContextUI.displayNavbar(); michael@0: Appbar.onStarButton(true); michael@0: break; michael@0: case "cmd_bookmarks": michael@0: PanelUI.show("bookmarks-container"); michael@0: break; michael@0: case "cmd_history": michael@0: PanelUI.show("history-container"); michael@0: break; michael@0: case "cmd_remoteTabs": michael@0: #ifdef MOZ_SERVICES_SYNC michael@0: if (Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED) { michael@0: FlyoutPanelsUI.show('SyncFlyoutPanel'); michael@0: } else { michael@0: PanelUI.show("remotetabs-container"); michael@0: } michael@0: #endif michael@0: break; michael@0: case "cmd_quit": michael@0: // Only close one window michael@0: this._closeOrQuit(); michael@0: break; michael@0: case "cmd_close": michael@0: this._closeOrQuit(); michael@0: break; michael@0: case "cmd_newTab": michael@0: this.addAndShowTab(); michael@0: break; michael@0: case "cmd_newTabKey": michael@0: this.addAndShowTab(); michael@0: // Make sure navbar is displayed before setting focus on url bar. Bug 907244 michael@0: ContextUI.displayNavbar(); michael@0: this._edit.beginEditing(false); michael@0: break; michael@0: case "cmd_closeTab": michael@0: this.closeTab(); michael@0: break; michael@0: case "cmd_undoCloseTab": michael@0: this.undoCloseTab(); michael@0: break; michael@0: case "cmd_sanitize": michael@0: this.confirmSanitizeDialog(); michael@0: break; michael@0: case "cmd_flyout_back": michael@0: FlyoutPanelsUI.onBackButton(); michael@0: break; michael@0: case "cmd_reportingCrashesSubmitURLs": michael@0: let urlCheckbox = document.getElementById("prefs-reporting-submitURLs"); michael@0: Services.prefs.setBoolPref('app.crashreporter.submitURLs', urlCheckbox.checked); michael@0: break; michael@0: case "cmd_panel": michael@0: PanelUI.toggle(); michael@0: break; michael@0: case "cmd_openFile": michael@0: this.openFile(); michael@0: break; michael@0: case "cmd_savePage": michael@0: this.savePage(); michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: handleAppCommandEvent: function (aEvent) { michael@0: switch (aEvent.command) { michael@0: case "Back": michael@0: this.doCommand("cmd_back"); michael@0: break; michael@0: case "Forward": michael@0: this.doCommand("cmd_forward"); michael@0: break; michael@0: case "Reload": michael@0: this.doCommand("cmd_reload"); michael@0: break; michael@0: case "Stop": michael@0: this.doCommand("cmd_stop"); michael@0: break; michael@0: case "Home": michael@0: this.doCommand("cmd_home"); michael@0: break; michael@0: case "New": michael@0: this.doCommand("cmd_newTab"); michael@0: break; michael@0: case "Close": michael@0: this.doCommand("cmd_closeTab"); michael@0: break; michael@0: case "Find": michael@0: FindHelperUI.show(); michael@0: break; michael@0: case "Open": michael@0: this.doCommand("cmd_openFile"); michael@0: break; michael@0: case "Save": michael@0: this.doCommand("cmd_savePage"); michael@0: break; michael@0: case "Search": michael@0: this.doCommand("cmd_openLocation"); michael@0: break; michael@0: default: michael@0: return; michael@0: } michael@0: aEvent.stopPropagation(); michael@0: aEvent.preventDefault(); michael@0: }, michael@0: michael@0: confirmSanitizeDialog: function () { michael@0: let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); michael@0: let title = bundle.GetStringFromName("clearPrivateData.title2"); michael@0: let message = bundle.GetStringFromName("clearPrivateData.message3"); michael@0: let clearbutton = bundle.GetStringFromName("clearPrivateData.clearButton"); michael@0: michael@0: let prefsClearButton = document.getElementById("prefs-clear-data"); michael@0: prefsClearButton.disabled = true; michael@0: michael@0: let buttonPressed = Services.prompt.confirmEx( michael@0: null, michael@0: title, michael@0: message, michael@0: Ci.nsIPrompt.BUTTON_POS_0 * Ci.nsIPrompt.BUTTON_TITLE_IS_STRING + michael@0: Ci.nsIPrompt.BUTTON_POS_1 * Ci.nsIPrompt.BUTTON_TITLE_CANCEL, michael@0: clearbutton, michael@0: null, michael@0: null, michael@0: null, michael@0: { value: false }); michael@0: michael@0: // Clicking 'Clear' will call onSanitize(). michael@0: if (buttonPressed === 0) { michael@0: SanitizeUI.onSanitize(); michael@0: } michael@0: michael@0: prefsClearButton.disabled = false; michael@0: }, michael@0: michael@0: _initFirstRunContent: function () { michael@0: let dismissed = Services.prefs.getBoolPref("browser.firstrun-content.dismissed"); michael@0: let firstRunCount = Services.prefs.getIntPref("browser.firstrun.count"); michael@0: michael@0: if (!dismissed && firstRunCount > 0) { michael@0: document.loadOverlay("chrome://browser/content/FirstRunContentOverlay.xul", null); michael@0: } michael@0: }, michael@0: michael@0: firstRunContentDismiss: function() { michael@0: let firstRunElements = Elements.stack.querySelectorAll(".firstrun-content"); michael@0: for (let node of firstRunElements) { michael@0: node.parentNode.removeChild(node); michael@0: } michael@0: michael@0: Services.prefs.setBoolPref("browser.firstrun-content.dismissed", true); michael@0: }, michael@0: }; michael@0: michael@0: var PanelUI = { michael@0: get _panels() { return document.getElementById("panel-items"); }, michael@0: michael@0: get isVisible() { michael@0: return !Elements.panelUI.hidden; michael@0: }, michael@0: michael@0: views: { michael@0: "console-container": "ConsolePanelView", michael@0: }, michael@0: michael@0: init: function() { michael@0: // Perform core init soon michael@0: setTimeout(function () { michael@0: for each (let viewName in this.views) { michael@0: let view = window[viewName]; michael@0: if (view.init) michael@0: view.init(); michael@0: } michael@0: }.bind(this), 0); michael@0: michael@0: // Lazily run other initialization tasks when the views are shown michael@0: this._panels.addEventListener("ToolPanelShown", function(aEvent) { michael@0: let viewName = this.views[this._panels.selectedPanel.id]; michael@0: let view = window[viewName]; michael@0: if (view.show) michael@0: view.show(); michael@0: }.bind(this), true); michael@0: }, michael@0: michael@0: uninit: function() { michael@0: for each (let viewName in this.views) { michael@0: let view = window[viewName]; michael@0: if (view.uninit) michael@0: view.uninit(); michael@0: } michael@0: }, michael@0: michael@0: switchPane: function switchPane(aPanelId) { michael@0: BrowserUI.blurFocusedElement(); michael@0: michael@0: let panel = aPanelId ? document.getElementById(aPanelId) : this._panels.selectedPanel; michael@0: let oldPanel = this._panels.selectedPanel; michael@0: michael@0: if (oldPanel != panel) { michael@0: this._panels.selectedPanel = panel; michael@0: michael@0: this._fire("ToolPanelHidden", oldPanel); michael@0: } michael@0: michael@0: this._fire("ToolPanelShown", panel); michael@0: }, michael@0: michael@0: isPaneVisible: function isPaneVisible(aPanelId) { michael@0: return this.isVisible && this._panels.selectedPanel.id == aPanelId; michael@0: }, michael@0: michael@0: show: function show(aPanelId) { michael@0: Elements.panelUI.hidden = false; michael@0: Elements.contentShowing.setAttribute("disabled", "true"); michael@0: michael@0: this.switchPane(aPanelId); michael@0: }, michael@0: michael@0: hide: function hide() { michael@0: if (!this.isVisible) michael@0: return; michael@0: michael@0: Elements.panelUI.hidden = true; michael@0: Elements.contentShowing.removeAttribute("disabled"); michael@0: BrowserUI.blurFocusedElement(); michael@0: michael@0: this._fire("ToolPanelHidden", this._panels); michael@0: }, michael@0: michael@0: toggle: function toggle() { michael@0: if (this.isVisible) { michael@0: this.hide(); michael@0: } else { michael@0: this.show(); michael@0: } michael@0: }, michael@0: michael@0: _fire: function _fire(aName, anElement) { michael@0: let event = document.createEvent("Events"); michael@0: event.initEvent(aName, true, true); michael@0: anElement.dispatchEvent(event); michael@0: } michael@0: }; michael@0: michael@0: var DialogUI = { michael@0: _popup: null, michael@0: michael@0: init: function() { michael@0: window.addEventListener("mousedown", this, true); michael@0: }, michael@0: michael@0: /******************************************* michael@0: * Popups michael@0: */ michael@0: michael@0: pushPopup: function pushPopup(aPanel, aElements, aParent) { michael@0: this._hidePopup(); michael@0: this._popup = { "panel": aPanel, michael@0: "elements": (aElements instanceof Array) ? aElements : [aElements] }; michael@0: this._dispatchPopupChanged(true, aPanel); michael@0: }, michael@0: michael@0: popPopup: function popPopup(aPanel) { michael@0: if (!this._popup || aPanel != this._popup.panel) michael@0: return; michael@0: this._popup = null; michael@0: this._dispatchPopupChanged(false, aPanel); michael@0: }, michael@0: michael@0: _hidePopup: function _hidePopup() { michael@0: if (!this._popup) michael@0: return; michael@0: let panel = this._popup.panel; michael@0: if (panel.hide) michael@0: panel.hide(); michael@0: }, michael@0: michael@0: /******************************************* michael@0: * Events michael@0: */ michael@0: michael@0: handleEvent: function (aEvent) { michael@0: switch (aEvent.type) { michael@0: case "mousedown": michael@0: if (!this._isEventInsidePopup(aEvent)) michael@0: this._hidePopup(); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: _dispatchPopupChanged: function _dispatchPopupChanged(aVisible, aElement) { michael@0: let event = document.createEvent("UIEvents"); michael@0: event.initUIEvent("PopupChanged", true, true, window, aVisible); michael@0: aElement.dispatchEvent(event); michael@0: }, michael@0: michael@0: _isEventInsidePopup: function _isEventInsidePopup(aEvent) { michael@0: if (!this._popup) michael@0: return false; michael@0: let elements = this._popup.elements; michael@0: let targetNode = aEvent.target; michael@0: while (targetNode && elements.indexOf(targetNode) == -1) { michael@0: if (targetNode instanceof Element && targetNode.hasAttribute("for")) michael@0: targetNode = document.getElementById(targetNode.getAttribute("for")); michael@0: else michael@0: targetNode = targetNode.parentNode; michael@0: } michael@0: return targetNode ? true : false; michael@0: } michael@0: };