forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1105 lines
		
	
	
	
		
			37 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1105 lines
		
	
	
	
		
			37 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 | |
|  * 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/. */
 | |
| 
 | |
| let _resolveDelayedStartup;
 | |
| var delayedStartupPromise = new Promise(resolve => {
 | |
|   _resolveDelayedStartup = resolve;
 | |
| });
 | |
| 
 | |
| var gBrowserInit = {
 | |
|   delayedStartupFinished: false,
 | |
|   domContentLoaded: false,
 | |
| 
 | |
|   _tabToAdopt: undefined,
 | |
|   _firstContentWindowPaintDeferred: Promise.withResolvers(),
 | |
|   idleTasksFinished: Promise.withResolvers(),
 | |
| 
 | |
|   _setupFirstContentWindowPaintPromise() {
 | |
|     let lastTransactionId = window.windowUtils.lastTransactionId;
 | |
|     let layerTreeListener = () => {
 | |
|       if (this.getTabToAdopt()) {
 | |
|         // Need to wait until we finish adopting the tab, or we might end
 | |
|         // up focusing the initial browser and then losing focus when it
 | |
|         // gets swapped out for the tab to adopt.
 | |
|         return;
 | |
|       }
 | |
|       removeEventListener("MozLayerTreeReady", layerTreeListener);
 | |
|       let listener = e => {
 | |
|         if (e.transactionId > lastTransactionId) {
 | |
|           window.removeEventListener("MozAfterPaint", listener);
 | |
|           this._firstContentWindowPaintDeferred.resolve();
 | |
|         }
 | |
|       };
 | |
|       addEventListener("MozAfterPaint", listener);
 | |
|     };
 | |
|     addEventListener("MozLayerTreeReady", layerTreeListener);
 | |
|   },
 | |
| 
 | |
|   getTabToAdopt() {
 | |
|     if (this._tabToAdopt !== undefined) {
 | |
|       return this._tabToAdopt;
 | |
|     }
 | |
| 
 | |
|     if (window.arguments && window.XULElement.isInstance(window.arguments[0])) {
 | |
|       this._tabToAdopt = window.arguments[0];
 | |
| 
 | |
|       // Clear the reference of the tab being adopted from the arguments.
 | |
|       window.arguments[0] = null;
 | |
|     } else {
 | |
|       // There was no tab to adopt in the arguments, set _tabToAdopt to null
 | |
|       // to avoid checking it again.
 | |
|       this._tabToAdopt = null;
 | |
|     }
 | |
| 
 | |
|     return this._tabToAdopt;
 | |
|   },
 | |
| 
 | |
|   _clearTabToAdopt() {
 | |
|     this._tabToAdopt = null;
 | |
|   },
 | |
| 
 | |
|   // Used to check if the new window is still adopting an existing tab as its first tab
 | |
|   // (e.g. from the WebExtensions internals).
 | |
|   isAdoptingTab() {
 | |
|     return !!this.getTabToAdopt();
 | |
|   },
 | |
| 
 | |
|   onBeforeInitialXULLayout() {
 | |
|     this._setupFirstContentWindowPaintPromise();
 | |
| 
 | |
|     updateBookmarkToolbarVisibility();
 | |
| 
 | |
|     // Set a sane starting width/height for all resolutions on new profiles.
 | |
|     if (ChromeUtils.shouldResistFingerprinting("RoundWindowSize", null)) {
 | |
|       // When the fingerprinting resistance is enabled, making sure that we don't
 | |
|       // have a maximum window to interfere with generating rounded window dimensions.
 | |
|       document.documentElement.setAttribute("sizemode", "normal");
 | |
|     } else if (!document.documentElement.hasAttribute("width")) {
 | |
|       const TARGET_WIDTH = 1280;
 | |
|       const TARGET_HEIGHT = 1040;
 | |
|       let width = Math.min(screen.availWidth * 0.9, TARGET_WIDTH);
 | |
|       let height = Math.min(screen.availHeight * 0.9, TARGET_HEIGHT);
 | |
| 
 | |
|       document.documentElement.setAttribute("width", width);
 | |
|       document.documentElement.setAttribute("height", height);
 | |
| 
 | |
|       if (width < TARGET_WIDTH && height < TARGET_HEIGHT) {
 | |
|         document.documentElement.setAttribute("sizemode", "maximized");
 | |
|       }
 | |
|     }
 | |
|     if (AppConstants.MENUBAR_CAN_AUTOHIDE) {
 | |
|       const toolbarMenubar = document.getElementById("toolbar-menubar");
 | |
|       // set a default value
 | |
|       if (!toolbarMenubar.hasAttribute("autohide")) {
 | |
|         toolbarMenubar.setAttribute("autohide", true);
 | |
|       }
 | |
|       document.l10n.setAttributes(
 | |
|         toolbarMenubar,
 | |
|         "toolbar-context-menu-menu-bar-cmd"
 | |
|       );
 | |
|       toolbarMenubar.setAttribute("data-l10n-attrs", "toolbarname");
 | |
|     }
 | |
| 
 | |
|     // Run menubar initialization first, to avoid TabsInTitlebar code picking
 | |
|     // up mutations from it and causing a reflow.
 | |
|     AutoHideMenubar.init();
 | |
|     // Update the chromemargin attribute so the window can be sized correctly.
 | |
|     window.TabBarVisibility.update();
 | |
|     TabsInTitlebar.init();
 | |
| 
 | |
|     new LightweightThemeConsumer(document);
 | |
| 
 | |
|     if (
 | |
|       Services.prefs.getBoolPref(
 | |
|         "toolkit.legacyUserProfileCustomizations.windowIcon",
 | |
|         false
 | |
|       )
 | |
|     ) {
 | |
|       document.documentElement.setAttribute("icon", "main-window");
 | |
|     }
 | |
| 
 | |
|     // Call this after we set attributes that might change toolbars' computed
 | |
|     // text color.
 | |
|     ToolbarIconColor.init();
 | |
|   },
 | |
| 
 | |
|   onDOMContentLoaded() {
 | |
|     // This needs setting up before we create the first remote browser.
 | |
|     window.docShell.treeOwner
 | |
|       .QueryInterface(Ci.nsIInterfaceRequestor)
 | |
|       .getInterface(Ci.nsIAppWindow).XULBrowserWindow = window.XULBrowserWindow;
 | |
|     window.browserDOMWindow = new nsBrowserAccess();
 | |
| 
 | |
|     gBrowser = window._gBrowser;
 | |
|     delete window._gBrowser;
 | |
|     gBrowser.init();
 | |
| 
 | |
|     BrowserWindowTracker.track(window);
 | |
| 
 | |
|     FirefoxViewHandler.init();
 | |
| 
 | |
|     gNavToolbox.palette = document.getElementById(
 | |
|       "BrowserToolbarPalette"
 | |
|     ).content;
 | |
|     for (let area of CustomizableUI.areas) {
 | |
|       let type = CustomizableUI.getAreaType(area);
 | |
|       if (type == CustomizableUI.TYPE_TOOLBAR) {
 | |
|         let node = document.getElementById(area);
 | |
|         CustomizableUI.registerToolbarNode(node);
 | |
|       }
 | |
|     }
 | |
|     BrowserSearch.initPlaceHolder();
 | |
| 
 | |
|     // Hack to ensure that the various initial pages favicon is loaded
 | |
|     // instantaneously, to avoid flickering and improve perceived performance.
 | |
|     this._callWithURIToLoad(uriToLoad => {
 | |
|       let url;
 | |
|       try {
 | |
|         url = Services.io.newURI(uriToLoad);
 | |
|       } catch (e) {
 | |
|         return;
 | |
|       }
 | |
|       let nonQuery = url.prePath + url.filePath;
 | |
|       if (nonQuery in gPageIcons) {
 | |
|         gBrowser.setIcon(gBrowser.selectedTab, gPageIcons[nonQuery]);
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     updateFxaToolbarMenu(gFxaToolbarEnabled, true);
 | |
| 
 | |
|     updatePrintCommands(gPrintEnabled);
 | |
| 
 | |
|     gUnifiedExtensions.init();
 | |
| 
 | |
|     // Setting the focus will cause a style flush, it's preferable to call anything
 | |
|     // that will modify the DOM from within this function before this call.
 | |
|     this._setInitialFocus();
 | |
| 
 | |
|     this.domContentLoaded = true;
 | |
|   },
 | |
| 
 | |
|   onLoad() {
 | |
|     gBrowser.addEventListener("DOMUpdateBlockedPopups", gPopupBlockerObserver);
 | |
|     gBrowser.addEventListener(
 | |
|       "TranslationsParent:LanguageState",
 | |
|       FullPageTranslationsPanel
 | |
|     );
 | |
|     gBrowser.addEventListener(
 | |
|       "TranslationsParent:OfferTranslation",
 | |
|       FullPageTranslationsPanel
 | |
|     );
 | |
|     gBrowser.addTabsProgressListener(FullPageTranslationsPanel);
 | |
| 
 | |
|     window.addEventListener("AppCommand", HandleAppCommandEvent, true);
 | |
| 
 | |
|     // These routines add message listeners. They must run before
 | |
|     // loading the frame script to ensure that we don't miss any
 | |
|     // message sent between when the frame script is loaded and when
 | |
|     // the listener is registered.
 | |
|     CaptivePortalWatcher.init();
 | |
|     ZoomUI.init(window);
 | |
| 
 | |
|     if (!gMultiProcessBrowser) {
 | |
|       // There is a Content:Click message manually sent from content.
 | |
|       gBrowser.tabpanels.addEventListener("click", contentAreaClick, {
 | |
|         capture: true,
 | |
|         mozSystemGroup: true,
 | |
|       });
 | |
|     }
 | |
| 
 | |
|     // hook up UI through progress listener
 | |
|     gBrowser.addProgressListener(window.XULBrowserWindow);
 | |
|     gBrowser.addTabsProgressListener(window.TabsProgressListener);
 | |
| 
 | |
|     SidebarController.init();
 | |
| 
 | |
|     // We do this in onload because we want to ensure the button's state
 | |
|     // doesn't flicker as the window is being shown.
 | |
|     DownloadsButton.init();
 | |
| 
 | |
|     // Certain kinds of automigration rely on this notification to complete
 | |
|     // their tasks BEFORE the browser window is shown. SessionStore uses it to
 | |
|     // restore tabs into windows AFTER important parts like gMultiProcessBrowser
 | |
|     // have been initialized.
 | |
|     Services.obs.notifyObservers(window, "browser-window-before-show");
 | |
| 
 | |
|     if (!window.toolbar.visible) {
 | |
|       // adjust browser UI for popups
 | |
|       gURLBar.readOnly = true;
 | |
|     }
 | |
| 
 | |
|     // Misc. inits.
 | |
|     gUIDensity.init();
 | |
|     TabletModeUpdater.init();
 | |
|     CombinedStopReload.ensureInitialized();
 | |
|     gPrivateBrowsingUI.init();
 | |
|     BrowserSearch.init();
 | |
|     BrowserPageActions.init();
 | |
|     if (gToolbarKeyNavEnabled) {
 | |
|       ToolbarKeyboardNavigator.init();
 | |
|     }
 | |
| 
 | |
|     // Update UI if browser is under remote control.
 | |
|     gRemoteControl.updateVisualCue();
 | |
| 
 | |
|     // If we are given a tab to swap in, take care of it before first paint to
 | |
|     // avoid an about:blank flash.
 | |
|     let tabToAdopt = this.getTabToAdopt();
 | |
|     if (tabToAdopt) {
 | |
|       let evt = new CustomEvent("before-initial-tab-adopted", {
 | |
|         bubbles: true,
 | |
|       });
 | |
|       gBrowser.tabpanels.dispatchEvent(evt);
 | |
| 
 | |
|       // Stop the about:blank load
 | |
|       gBrowser.stop();
 | |
| 
 | |
|       // Remove the speculative focus from the urlbar to let the url be formatted.
 | |
|       gURLBar.removeAttribute("focused");
 | |
| 
 | |
|       let swapBrowsers = () => {
 | |
|         try {
 | |
|           gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToAdopt);
 | |
|         } catch (e) {
 | |
|           console.error(e);
 | |
|         }
 | |
| 
 | |
|         // Clear the reference to the tab once its adoption has been completed.
 | |
|         this._clearTabToAdopt();
 | |
|       };
 | |
|       if (tabToAdopt.linkedBrowser.isRemoteBrowser) {
 | |
|         // For remote browsers, wait for the paint event, otherwise the tabs
 | |
|         // are not yet ready and focus gets confused because the browser swaps
 | |
|         // out while tabs are switching.
 | |
|         addEventListener("MozAfterPaint", swapBrowsers, { once: true });
 | |
|       } else {
 | |
|         swapBrowsers();
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Wait until chrome is painted before executing code not critical to making the window visible
 | |
|     this._boundDelayedStartup = this._delayedStartup.bind(this);
 | |
|     window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
 | |
| 
 | |
|     if (!PrivateBrowsingUtils.enabled) {
 | |
|       document.getElementById("Tools:PrivateBrowsing").hidden = true;
 | |
|       // Setting disabled doesn't disable the shortcut, so we just remove
 | |
|       // the keybinding.
 | |
|       document.getElementById("key_privatebrowsing").remove();
 | |
|     }
 | |
| 
 | |
|     if (BrowserUIUtils.quitShortcutDisabled) {
 | |
|       document.getElementById("key_quitApplication").remove();
 | |
|       document.getElementById("menu_FileQuitItem").removeAttribute("key");
 | |
| 
 | |
|       PanelMultiView.getViewNode(
 | |
|         document,
 | |
|         "appMenu-quit-button2"
 | |
|       )?.removeAttribute("key");
 | |
|     }
 | |
| 
 | |
|     this._loadHandled = true;
 | |
|   },
 | |
| 
 | |
|   _cancelDelayedStartup() {
 | |
|     window.removeEventListener("MozAfterPaint", this._boundDelayedStartup);
 | |
|     this._boundDelayedStartup = null;
 | |
|   },
 | |
| 
 | |
|   _delayedStartup() {
 | |
|     let { TelemetryTimestamps } = ChromeUtils.importESModule(
 | |
|       "resource://gre/modules/TelemetryTimestamps.sys.mjs"
 | |
|     );
 | |
|     TelemetryTimestamps.add("delayedStartupStarted");
 | |
| 
 | |
|     this._cancelDelayedStartup();
 | |
| 
 | |
|     // Bug 1531854 - The hidden window is force-created here
 | |
|     // until all of its dependencies are handled.
 | |
|     Services.appShell.hiddenDOMWindow;
 | |
| 
 | |
|     gBrowser.addEventListener(
 | |
|       "PermissionStateChange",
 | |
|       function () {
 | |
|         gIdentityHandler.refreshIdentityBlock();
 | |
|         gPermissionPanel.updateSharingIndicator();
 | |
|       },
 | |
|       true
 | |
|     );
 | |
| 
 | |
|     this._handleURIToLoad();
 | |
| 
 | |
|     Services.obs.addObserver(gIdentityHandler, "perm-changed");
 | |
|     Services.obs.addObserver(gRemoteControl, "devtools-socket");
 | |
|     Services.obs.addObserver(gRemoteControl, "marionette-listening");
 | |
|     Services.obs.addObserver(gRemoteControl, "remote-listening");
 | |
|     Services.obs.addObserver(
 | |
|       gSessionHistoryObserver,
 | |
|       "browser:purge-session-history"
 | |
|     );
 | |
|     Services.obs.addObserver(
 | |
|       gStoragePressureObserver,
 | |
|       "QuotaManager::StoragePressure"
 | |
|     );
 | |
|     Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled");
 | |
|     Services.obs.addObserver(gXPInstallObserver, "addon-install-started");
 | |
|     Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked");
 | |
|     Services.obs.addObserver(
 | |
|       gXPInstallObserver,
 | |
|       "addon-install-fullscreen-blocked"
 | |
|     );
 | |
|     Services.obs.addObserver(
 | |
|       gXPInstallObserver,
 | |
|       "addon-install-origin-blocked"
 | |
|     );
 | |
|     Services.obs.addObserver(
 | |
|       gXPInstallObserver,
 | |
|       "addon-install-policy-blocked"
 | |
|     );
 | |
|     Services.obs.addObserver(
 | |
|       gXPInstallObserver,
 | |
|       "addon-install-webapi-blocked"
 | |
|     );
 | |
|     Services.obs.addObserver(gXPInstallObserver, "addon-install-failed");
 | |
|     Services.obs.addObserver(gXPInstallObserver, "addon-install-confirmation");
 | |
|     Services.obs.addObserver(gKeywordURIFixup, "keyword-uri-fixup");
 | |
| 
 | |
|     BrowserOffline.init();
 | |
|     CanvasPermissionPromptHelper.init();
 | |
|     WebAuthnPromptHelper.init();
 | |
|     ContentAnalysis.initialize(document);
 | |
| 
 | |
|     // Initialize the full zoom setting.
 | |
|     // We do this before the session restore service gets initialized so we can
 | |
|     // apply full zoom settings to tabs restored by the session restore service.
 | |
|     FullZoom.init();
 | |
|     PanelUI.init(shouldSuppressPopupNotifications);
 | |
|     ReportBrokenSite.init(gBrowser);
 | |
| 
 | |
|     UpdateUrlbarSearchSplitterState();
 | |
| 
 | |
|     BookmarkingUI.init();
 | |
|     BrowserSearch.delayedStartupInit();
 | |
|     SearchUIUtils.init();
 | |
|     gProtectionsHandler.init();
 | |
|     HomePage.delayedStartup().catch(console.error);
 | |
| 
 | |
|     let safeMode = document.getElementById("helpSafeMode");
 | |
|     if (Services.appinfo.inSafeMode) {
 | |
|       document.l10n.setAttributes(safeMode, "menu-help-exit-troubleshoot-mode");
 | |
|       safeMode.setAttribute(
 | |
|         "appmenu-data-l10n-id",
 | |
|         "appmenu-help-exit-troubleshoot-mode"
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     // BiDi UI
 | |
|     gBidiUI = isBidiEnabled();
 | |
|     if (gBidiUI) {
 | |
|       document.getElementById("documentDirection-separator").hidden = false;
 | |
|       document.getElementById("documentDirection-swap").hidden = false;
 | |
|       document.getElementById("textfieldDirection-separator").hidden = false;
 | |
|       document.getElementById("textfieldDirection-swap").hidden = false;
 | |
|     }
 | |
| 
 | |
|     // Setup click-and-hold gestures access to the session history
 | |
|     // menus if global click-and-hold isn't turned on
 | |
|     if (!Services.prefs.getBoolPref("ui.click_hold_context_menus", false)) {
 | |
|       SetClickAndHoldHandlers();
 | |
|     }
 | |
| 
 | |
|     function initBackForwardButtonTooltip(tooltipId, l10nId, shortcutId) {
 | |
|       let shortcut = document.getElementById(shortcutId);
 | |
|       shortcut = ShortcutUtils.prettifyShortcut(shortcut);
 | |
| 
 | |
|       let tooltip = document.getElementById(tooltipId);
 | |
|       document.l10n.setAttributes(tooltip, l10nId, { shortcut });
 | |
|     }
 | |
| 
 | |
|     initBackForwardButtonTooltip(
 | |
|       "back-button-tooltip-description",
 | |
|       "navbar-tooltip-back-2",
 | |
|       "goBackKb"
 | |
|     );
 | |
| 
 | |
|     initBackForwardButtonTooltip(
 | |
|       "forward-button-tooltip-description",
 | |
|       "navbar-tooltip-forward-2",
 | |
|       "goForwardKb"
 | |
|     );
 | |
| 
 | |
|     PlacesToolbarHelper.init();
 | |
| 
 | |
|     ctrlTab.readPref();
 | |
|     Services.prefs.addObserver(ctrlTab.prefName, ctrlTab);
 | |
| 
 | |
|     // The object handling the downloads indicator is initialized here in the
 | |
|     // delayed startup function, but the actual indicator element is not loaded
 | |
|     // unless there are downloads to be displayed.
 | |
|     DownloadsButton.initializeIndicator();
 | |
| 
 | |
|     if (AppConstants.platform != "macosx") {
 | |
|       updateEditUIVisibility();
 | |
|       let placesContext = document.getElementById("placesContext");
 | |
|       placesContext.addEventListener("popupshowing", updateEditUIVisibility);
 | |
|       placesContext.addEventListener("popuphiding", updateEditUIVisibility);
 | |
|     }
 | |
| 
 | |
|     FullScreen.init();
 | |
|     MenuTouchModeObserver.init();
 | |
| 
 | |
|     if (AppConstants.MOZ_DATA_REPORTING) {
 | |
|       gDataNotificationInfoBar.init();
 | |
|     }
 | |
| 
 | |
|     if (!AppConstants.MOZILLA_OFFICIAL) {
 | |
|       DevelopmentHelpers.init();
 | |
|     }
 | |
| 
 | |
|     gExtensionsNotifications.init();
 | |
| 
 | |
|     let wasMinimized = window.windowState == window.STATE_MINIMIZED;
 | |
|     window.addEventListener("sizemodechange", () => {
 | |
|       let isMinimized = window.windowState == window.STATE_MINIMIZED;
 | |
|       if (wasMinimized != isMinimized) {
 | |
|         wasMinimized = isMinimized;
 | |
|         UpdatePopupNotificationsVisibility();
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     window.addEventListener("mousemove", MousePosTracker);
 | |
|     window.addEventListener("dragover", MousePosTracker);
 | |
| 
 | |
|     gNavToolbox.addEventListener("customizationstarting", CustomizationHandler);
 | |
|     gNavToolbox.addEventListener("aftercustomization", CustomizationHandler);
 | |
| 
 | |
|     SessionStore.promiseInitialized.then(() => {
 | |
|       // Bail out if the window has been closed in the meantime.
 | |
|       if (window.closed) {
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       // Enable the Restore Last Session command if needed
 | |
|       RestoreLastSessionObserver.init();
 | |
| 
 | |
|       SidebarController.startDelayedLoad();
 | |
| 
 | |
|       PanicButtonNotifier.init();
 | |
|     });
 | |
| 
 | |
|     if (BrowserHandler.kiosk) {
 | |
|       // We don't modify popup windows for kiosk mode
 | |
|       if (!gURLBar.readOnly) {
 | |
|         window.fullScreen = true;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (Services.policies.status === Services.policies.ACTIVE) {
 | |
|       if (!Services.policies.isAllowed("hideShowMenuBar")) {
 | |
|         document
 | |
|           .getElementById("toolbar-menubar")
 | |
|           .removeAttribute("toolbarname");
 | |
|       }
 | |
|       if (!Services.policies.isAllowed("filepickers")) {
 | |
|         let savePageCommand = document.getElementById("Browser:SavePage");
 | |
|         let openFileCommand = document.getElementById("Browser:OpenFile");
 | |
| 
 | |
|         savePageCommand.setAttribute("disabled", "true");
 | |
|         openFileCommand.setAttribute("disabled", "true");
 | |
| 
 | |
|         document.addEventListener("FilePickerBlocked", function (event) {
 | |
|           let browser = event.target;
 | |
| 
 | |
|           let notificationBox = browser
 | |
|             .getTabBrowser()
 | |
|             ?.getNotificationBox(browser);
 | |
| 
 | |
|           // Prevent duplicate notifications
 | |
|           if (
 | |
|             notificationBox &&
 | |
|             !notificationBox.getNotificationWithValue("filepicker-blocked")
 | |
|           ) {
 | |
|             notificationBox.appendNotification("filepicker-blocked", {
 | |
|               label: {
 | |
|                 "l10n-id": "filepicker-blocked-infobar",
 | |
|               },
 | |
|               priority: notificationBox.PRIORITY_INFO_LOW,
 | |
|             });
 | |
|           }
 | |
|         });
 | |
|       }
 | |
|       let policies = Services.policies.getActivePolicies();
 | |
|       if ("ManagedBookmarks" in policies) {
 | |
|         let managedBookmarks = policies.ManagedBookmarks;
 | |
|         let children = managedBookmarks.filter(
 | |
|           child => !("toplevel_name" in child)
 | |
|         );
 | |
|         if (children.length) {
 | |
|           let managedBookmarksButton =
 | |
|             document.createXULElement("toolbarbutton");
 | |
|           managedBookmarksButton.setAttribute("id", "managed-bookmarks");
 | |
|           managedBookmarksButton.setAttribute("class", "bookmark-item");
 | |
|           let toplevel = managedBookmarks.find(
 | |
|             element => "toplevel_name" in element
 | |
|           );
 | |
|           if (toplevel) {
 | |
|             managedBookmarksButton.setAttribute(
 | |
|               "label",
 | |
|               toplevel.toplevel_name
 | |
|             );
 | |
|           } else {
 | |
|             document.l10n.setAttributes(
 | |
|               managedBookmarksButton,
 | |
|               "managed-bookmarks"
 | |
|             );
 | |
|           }
 | |
|           managedBookmarksButton.setAttribute("context", "placesContext");
 | |
|           managedBookmarksButton.setAttribute("container", "true");
 | |
|           managedBookmarksButton.setAttribute("removable", "false");
 | |
|           managedBookmarksButton.setAttribute("type", "menu");
 | |
| 
 | |
|           let managedBookmarksPopup = document.createXULElement("menupopup");
 | |
|           managedBookmarksPopup.setAttribute("id", "managed-bookmarks-popup");
 | |
|           managedBookmarksPopup.setAttribute(
 | |
|             "oncommand",
 | |
|             "PlacesToolbarHelper.openManagedBookmark(event);"
 | |
|           );
 | |
|           managedBookmarksPopup.setAttribute(
 | |
|             "ondragover",
 | |
|             "event.dataTransfer.effectAllowed='none';"
 | |
|           );
 | |
|           managedBookmarksPopup.setAttribute(
 | |
|             "ondragstart",
 | |
|             "PlacesToolbarHelper.onDragStartManaged(event);"
 | |
|           );
 | |
|           managedBookmarksPopup.setAttribute(
 | |
|             "onpopupshowing",
 | |
|             "PlacesToolbarHelper.populateManagedBookmarks(this);"
 | |
|           );
 | |
|           managedBookmarksPopup.setAttribute("placespopup", "true");
 | |
|           managedBookmarksPopup.setAttribute("is", "places-popup");
 | |
|           managedBookmarksPopup.classList.add("toolbar-menupopup");
 | |
|           managedBookmarksButton.appendChild(managedBookmarksPopup);
 | |
| 
 | |
|           gNavToolbox.palette.appendChild(managedBookmarksButton);
 | |
| 
 | |
|           CustomizableUI.ensureWidgetPlacedInWindow(
 | |
|             "managed-bookmarks",
 | |
|             window
 | |
|           );
 | |
| 
 | |
|           // Add button if it doesn't exist
 | |
|           if (!CustomizableUI.getPlacementOfWidget("managed-bookmarks")) {
 | |
|             CustomizableUI.addWidgetToArea(
 | |
|               "managed-bookmarks",
 | |
|               CustomizableUI.AREA_BOOKMARKS,
 | |
|               0
 | |
|             );
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     CaptivePortalWatcher.delayedStartup();
 | |
| 
 | |
|     ShoppingSidebarManager.ensureInitialized();
 | |
| 
 | |
|     SessionStore.promiseAllWindowsRestored.then(() => {
 | |
|       this._schedulePerWindowIdleTasks();
 | |
|       document.documentElement.setAttribute("sessionrestored", "true");
 | |
|     });
 | |
| 
 | |
|     this.delayedStartupFinished = true;
 | |
|     _resolveDelayedStartup();
 | |
|     Services.obs.notifyObservers(window, "browser-delayed-startup-finished");
 | |
|     TelemetryTimestamps.add("delayedStartupFinished");
 | |
|     // We've announced that delayed startup has finished. Do not add code past this point.
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Resolved on the first MozLayerTreeReady and next MozAfterPaint in the
 | |
|    * parent process.
 | |
|    */
 | |
|   get firstContentWindowPaintPromise() {
 | |
|     return this._firstContentWindowPaintDeferred.promise;
 | |
|   },
 | |
| 
 | |
|   _setInitialFocus() {
 | |
|     let initiallyFocusedElement = document.commandDispatcher.focusedElement;
 | |
| 
 | |
|     // To prevent startup flicker, the urlbar has the 'focused' attribute set
 | |
|     // by default. If we are not sure the urlbar will be focused in this
 | |
|     // window, we need to remove the attribute before first paint.
 | |
|     // TODO (bug 1629956): The urlbar having the 'focused' attribute by default
 | |
|     // isn't a useful optimization anymore since UrlbarInput needs layout
 | |
|     // information to focus the urlbar properly.
 | |
|     let shouldRemoveFocusedAttribute = true;
 | |
| 
 | |
|     this._callWithURIToLoad(uriToLoad => {
 | |
|       if (
 | |
|         isBlankPageURL(uriToLoad) ||
 | |
|         uriToLoad == "about:privatebrowsing" ||
 | |
|         this.getTabToAdopt()?.isEmpty
 | |
|       ) {
 | |
|         gURLBar.select();
 | |
|         shouldRemoveFocusedAttribute = false;
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       // If the initial browser is remote, in order to optimize for first paint,
 | |
|       // we'll defer switching focus to that browser until it has painted.
 | |
|       // Otherwise use a regular promise to guarantee that mutationobserver
 | |
|       // microtasks that could affect focusability have run.
 | |
|       let promise = gBrowser.selectedBrowser.isRemoteBrowser
 | |
|         ? this.firstContentWindowPaintPromise
 | |
|         : Promise.resolve();
 | |
| 
 | |
|       promise.then(() => {
 | |
|         // If focus didn't move while we were waiting, we're okay to move to
 | |
|         // the browser.
 | |
|         if (
 | |
|           document.commandDispatcher.focusedElement == initiallyFocusedElement
 | |
|         ) {
 | |
|           gBrowser.selectedBrowser.focus();
 | |
|         }
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     // Delay removing the attribute using requestAnimationFrame to avoid
 | |
|     // invalidating styles multiple times in a row if uriToLoadPromise
 | |
|     // resolves before first paint.
 | |
|     if (shouldRemoveFocusedAttribute) {
 | |
|       window.requestAnimationFrame(() => {
 | |
|         if (shouldRemoveFocusedAttribute) {
 | |
|           gURLBar.removeAttribute("focused");
 | |
|         }
 | |
|       });
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _handleURIToLoad() {
 | |
|     this._callWithURIToLoad(uriToLoad => {
 | |
|       if (!uriToLoad) {
 | |
|         // We don't check whether window.arguments[5] (userContextId) is set
 | |
|         // because tabbrowser.js takes care of that for the initial tab.
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       // We don't check if uriToLoad is a XULElement because this case has
 | |
|       // already been handled before first paint, and the argument cleared.
 | |
|       if (Array.isArray(uriToLoad)) {
 | |
|         // This function throws for certain malformed URIs, so use exception handling
 | |
|         // so that we don't disrupt startup
 | |
|         try {
 | |
|           gBrowser.loadTabs(uriToLoad, {
 | |
|             inBackground: false,
 | |
|             replace: true,
 | |
|             // See below for the semantics of window.arguments. Only the minimum is supported.
 | |
|             userContextId: window.arguments[5],
 | |
|             triggeringPrincipal:
 | |
|               window.arguments[8] ||
 | |
|               Services.scriptSecurityManager.getSystemPrincipal(),
 | |
|             allowInheritPrincipal: window.arguments[9],
 | |
|             csp: window.arguments[10],
 | |
|             fromExternal: true,
 | |
|           });
 | |
|         } catch (e) {}
 | |
|       } else if (window.arguments.length >= 3) {
 | |
|         // window.arguments[1]: extraOptions (nsIPropertyBag)
 | |
|         //                 [2]: referrerInfo (nsIReferrerInfo)
 | |
|         //                 [3]: postData (nsIInputStream)
 | |
|         //                 [4]: allowThirdPartyFixup (bool)
 | |
|         //                 [5]: userContextId (int)
 | |
|         //                 [6]: originPrincipal (nsIPrincipal)
 | |
|         //                 [7]: originStoragePrincipal (nsIPrincipal)
 | |
|         //                 [8]: triggeringPrincipal (nsIPrincipal)
 | |
|         //                 [9]: allowInheritPrincipal (bool)
 | |
|         //                 [10]: csp (nsIContentSecurityPolicy)
 | |
|         //                 [11]: nsOpenWindowInfo
 | |
|         let userContextId =
 | |
|           window.arguments[5] != undefined
 | |
|             ? window.arguments[5]
 | |
|             : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID;
 | |
| 
 | |
|         let hasValidUserGestureActivation = undefined;
 | |
|         let fromExternal = undefined;
 | |
|         let globalHistoryOptions = undefined;
 | |
|         let triggeringRemoteType = undefined;
 | |
|         let forceAllowDataURI = false;
 | |
|         let wasSchemelessInput = false;
 | |
|         if (window.arguments[1]) {
 | |
|           if (!(window.arguments[1] instanceof Ci.nsIPropertyBag2)) {
 | |
|             throw new Error(
 | |
|               "window.arguments[1] must be null or Ci.nsIPropertyBag2!"
 | |
|             );
 | |
|           }
 | |
| 
 | |
|           let extraOptions = window.arguments[1];
 | |
|           if (extraOptions.hasKey("hasValidUserGestureActivation")) {
 | |
|             hasValidUserGestureActivation = extraOptions.getPropertyAsBool(
 | |
|               "hasValidUserGestureActivation"
 | |
|             );
 | |
|           }
 | |
|           if (extraOptions.hasKey("fromExternal")) {
 | |
|             fromExternal = extraOptions.getPropertyAsBool("fromExternal");
 | |
|           }
 | |
|           if (extraOptions.hasKey("triggeringSponsoredURL")) {
 | |
|             globalHistoryOptions = {
 | |
|               triggeringSponsoredURL: extraOptions.getPropertyAsACString(
 | |
|                 "triggeringSponsoredURL"
 | |
|               ),
 | |
|             };
 | |
|             if (extraOptions.hasKey("triggeringSponsoredURLVisitTimeMS")) {
 | |
|               globalHistoryOptions.triggeringSponsoredURLVisitTimeMS =
 | |
|                 extraOptions.getPropertyAsUint64(
 | |
|                   "triggeringSponsoredURLVisitTimeMS"
 | |
|                 );
 | |
|             }
 | |
|           }
 | |
|           if (extraOptions.hasKey("triggeringRemoteType")) {
 | |
|             triggeringRemoteType = extraOptions.getPropertyAsACString(
 | |
|               "triggeringRemoteType"
 | |
|             );
 | |
|           }
 | |
|           if (extraOptions.hasKey("forceAllowDataURI")) {
 | |
|             forceAllowDataURI =
 | |
|               extraOptions.getPropertyAsBool("forceAllowDataURI");
 | |
|           }
 | |
|           if (extraOptions.hasKey("wasSchemelessInput")) {
 | |
|             wasSchemelessInput =
 | |
|               extraOptions.getPropertyAsBool("wasSchemelessInput");
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         try {
 | |
|           openLinkIn(uriToLoad, "current", {
 | |
|             referrerInfo: window.arguments[2] || null,
 | |
|             postData: window.arguments[3] || null,
 | |
|             allowThirdPartyFixup: window.arguments[4] || false,
 | |
|             userContextId,
 | |
|             // pass the origin principal (if any) and force its use to create
 | |
|             // an initial about:blank viewer if present:
 | |
|             originPrincipal: window.arguments[6],
 | |
|             originStoragePrincipal: window.arguments[7],
 | |
|             triggeringPrincipal: window.arguments[8],
 | |
|             // TODO fix allowInheritPrincipal to default to false.
 | |
|             // Default to true unless explicitly set to false because of bug 1475201.
 | |
|             allowInheritPrincipal: window.arguments[9] !== false,
 | |
|             csp: window.arguments[10],
 | |
|             forceAboutBlankViewerInCurrent: !!window.arguments[6],
 | |
|             forceAllowDataURI,
 | |
|             hasValidUserGestureActivation,
 | |
|             fromExternal,
 | |
|             globalHistoryOptions,
 | |
|             triggeringRemoteType,
 | |
|             wasSchemelessInput,
 | |
|           });
 | |
|         } catch (e) {
 | |
|           console.error(e);
 | |
|         }
 | |
| 
 | |
|         window.focus();
 | |
|       } else {
 | |
|         // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
 | |
|         // Such callers expect that window.arguments[0] is handled as a single URI.
 | |
|         loadOneOrMoreURIs(
 | |
|           uriToLoad,
 | |
|           Services.scriptSecurityManager.getSystemPrincipal(),
 | |
|           null
 | |
|         );
 | |
|       }
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Use this function as an entry point to schedule tasks that
 | |
|    * need to run once per window after startup, and can be scheduled
 | |
|    * by using an idle callback.
 | |
|    *
 | |
|    * The functions scheduled here will fire from idle callbacks
 | |
|    * once every window has finished being restored by session
 | |
|    * restore, and after the equivalent only-once tasks
 | |
|    * have run (from _scheduleStartupIdleTasks in BrowserGlue.sys.mjs).
 | |
|    */
 | |
|   _schedulePerWindowIdleTasks() {
 | |
|     // Bail out if the window has been closed in the meantime.
 | |
|     if (window.closed) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     function scheduleIdleTask(func, options) {
 | |
|       requestIdleCallback(function idleTaskRunner() {
 | |
|         if (!window.closed) {
 | |
|           func();
 | |
|         }
 | |
|       }, options);
 | |
|     }
 | |
| 
 | |
|     scheduleIdleTask(() => {
 | |
|       // Initialize the Sync UI
 | |
|       gSync.init();
 | |
|     });
 | |
| 
 | |
|     scheduleIdleTask(() => {
 | |
|       // Read prefers-reduced-motion setting
 | |
|       let reduceMotionQuery = window.matchMedia(
 | |
|         "(prefers-reduced-motion: reduce)"
 | |
|       );
 | |
|       function readSetting() {
 | |
|         gReduceMotionSetting = reduceMotionQuery.matches;
 | |
|       }
 | |
|       reduceMotionQuery.addListener(readSetting);
 | |
|       readSetting();
 | |
|     });
 | |
| 
 | |
|     scheduleIdleTask(() => {
 | |
|       // setup simple gestures support
 | |
|       gGestureSupport.init(true);
 | |
| 
 | |
|       // setup history swipe animation
 | |
|       gHistorySwipeAnimation.init();
 | |
|     });
 | |
| 
 | |
|     scheduleIdleTask(() => {
 | |
|       gBrowserThumbnails.init();
 | |
|     });
 | |
| 
 | |
|     scheduleIdleTask(
 | |
|       () => {
 | |
|         // Initialize the download manager some time after the app starts so that
 | |
|         // auto-resume downloads begin (such as after crashing or quitting with
 | |
|         // active downloads) and speeds up the first-load of the download manager UI.
 | |
|         // If the user manually opens the download manager before the timeout, the
 | |
|         // downloads will start right away, and initializing again won't hurt.
 | |
|         try {
 | |
|           DownloadsCommon.initializeAllDataLinks();
 | |
|           ChromeUtils.importESModule(
 | |
|             "resource:///modules/DownloadsTaskbar.sys.mjs"
 | |
|           ).DownloadsTaskbar.registerIndicator(window);
 | |
|           if (AppConstants.platform == "macosx") {
 | |
|             ChromeUtils.importESModule(
 | |
|               "resource:///modules/DownloadsMacFinderProgress.sys.mjs"
 | |
|             ).DownloadsMacFinderProgress.register();
 | |
|           }
 | |
|           Services.telemetry.setEventRecordingEnabled("downloads", true);
 | |
|         } catch (ex) {
 | |
|           console.error(ex);
 | |
|         }
 | |
|       },
 | |
|       { timeout: 10000 }
 | |
|     );
 | |
| 
 | |
|     if (Win7Features) {
 | |
|       scheduleIdleTask(() => Win7Features.onOpenWindow());
 | |
|     }
 | |
| 
 | |
|     scheduleIdleTask(async () => {
 | |
|       NewTabPagePreloading.maybeCreatePreloadedBrowser(window);
 | |
|     });
 | |
| 
 | |
|     scheduleIdleTask(() => {
 | |
|       gGfxUtils.init();
 | |
|     });
 | |
| 
 | |
|     // This should always go last, since the idle tasks (except for the ones with
 | |
|     // timeouts) should execute in order. Note that this observer notification is
 | |
|     // not guaranteed to fire, since the window could close before we get here.
 | |
|     scheduleIdleTask(() => {
 | |
|       this.idleTasksFinished.resolve();
 | |
|       Services.obs.notifyObservers(
 | |
|         window,
 | |
|         "browser-idle-startup-tasks-finished"
 | |
|       );
 | |
|     });
 | |
| 
 | |
|     scheduleIdleTask(() => {
 | |
|       gProfiles.init();
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   // Returns the URI(s) to load at startup if it is immediately known, or a
 | |
|   // promise resolving to the URI to load.
 | |
|   get uriToLoadPromise() {
 | |
|     delete this.uriToLoadPromise;
 | |
|     return (this.uriToLoadPromise = (function () {
 | |
|       // window.arguments[0]: URI to load (string), or an nsIArray of
 | |
|       //                      nsISupportsStrings to load, or a xul:tab of
 | |
|       //                      a tabbrowser, which will be replaced by this
 | |
|       //                      window (for this case, all other arguments are
 | |
|       //                      ignored).
 | |
|       let uri = window.arguments?.[0];
 | |
|       if (!uri || window.XULElement.isInstance(uri)) {
 | |
|         return null;
 | |
|       }
 | |
| 
 | |
|       let defaultArgs = BrowserHandler.defaultArgs;
 | |
| 
 | |
|       // If the given URI is different from the homepage, we want to load it.
 | |
|       if (uri != defaultArgs) {
 | |
|         AboutNewTab.noteNonDefaultStartup();
 | |
| 
 | |
|         if (uri instanceof Ci.nsIArray) {
 | |
|           // Transform the nsIArray of nsISupportsString's into a JS Array of
 | |
|           // JS strings.
 | |
|           return Array.from(
 | |
|             uri.enumerate(Ci.nsISupportsString),
 | |
|             supportStr => supportStr.data
 | |
|           );
 | |
|         } else if (uri instanceof Ci.nsISupportsString) {
 | |
|           return uri.data;
 | |
|         }
 | |
|         return uri;
 | |
|       }
 | |
| 
 | |
|       // The URI appears to be the the homepage. We want to load it only if
 | |
|       // session restore isn't about to override the homepage.
 | |
|       let willOverride = SessionStartup.willOverrideHomepage;
 | |
|       if (typeof willOverride == "boolean") {
 | |
|         return willOverride ? null : uri;
 | |
|       }
 | |
|       return willOverride.then(willOverrideHomepage =>
 | |
|         willOverrideHomepage ? null : uri
 | |
|       );
 | |
|     })());
 | |
|   },
 | |
| 
 | |
|   // Calls the given callback with the URI to load at startup.
 | |
|   // Synchronously if possible, or after uriToLoadPromise resolves otherwise.
 | |
|   _callWithURIToLoad(callback) {
 | |
|     let uriToLoad = this.uriToLoadPromise;
 | |
|     if (uriToLoad && uriToLoad.then) {
 | |
|       uriToLoad.then(callback);
 | |
|     } else {
 | |
|       callback(uriToLoad);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   onUnload() {
 | |
|     gUIDensity.uninit();
 | |
| 
 | |
|     TabsInTitlebar.uninit();
 | |
| 
 | |
|     ToolbarIconColor.uninit();
 | |
| 
 | |
|     // In certain scenarios it's possible for unload to be fired before onload,
 | |
|     // (e.g. if the window is being closed after browser.js loads but before the
 | |
|     // load completes). In that case, there's nothing to do here.
 | |
|     if (!this._loadHandled) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // First clean up services initialized in gBrowserInit.onLoad (or those whose
 | |
|     // uninit methods don't depend on the services having been initialized).
 | |
| 
 | |
|     CombinedStopReload.uninit();
 | |
| 
 | |
|     gGestureSupport.init(false);
 | |
| 
 | |
|     gHistorySwipeAnimation.uninit();
 | |
| 
 | |
|     FullScreen.uninit();
 | |
| 
 | |
|     gSync.uninit();
 | |
| 
 | |
|     gExtensionsNotifications.uninit();
 | |
|     gUnifiedExtensions.uninit();
 | |
| 
 | |
|     try {
 | |
|       gBrowser.removeProgressListener(window.XULBrowserWindow);
 | |
|       gBrowser.removeTabsProgressListener(window.TabsProgressListener);
 | |
|     } catch (ex) {}
 | |
| 
 | |
|     PlacesToolbarHelper.uninit();
 | |
| 
 | |
|     BookmarkingUI.uninit();
 | |
| 
 | |
|     TabletModeUpdater.uninit();
 | |
| 
 | |
|     gTabletModePageCounter.finish();
 | |
| 
 | |
|     CaptivePortalWatcher.uninit();
 | |
| 
 | |
|     SidebarController.uninit();
 | |
| 
 | |
|     DownloadsButton.uninit();
 | |
| 
 | |
|     if (gToolbarKeyNavEnabled) {
 | |
|       ToolbarKeyboardNavigator.uninit();
 | |
|     }
 | |
| 
 | |
|     BrowserSearch.uninit();
 | |
| 
 | |
|     NewTabPagePreloading.removePreloadedBrowser(window);
 | |
| 
 | |
|     FirefoxViewHandler.uninit();
 | |
| 
 | |
|     // Now either cancel delayedStartup, or clean up the services initialized from
 | |
|     // it.
 | |
|     if (this._boundDelayedStartup) {
 | |
|       this._cancelDelayedStartup();
 | |
|     } else {
 | |
|       if (Win7Features) {
 | |
|         Win7Features.onCloseWindow();
 | |
|       }
 | |
|       Services.prefs.removeObserver(ctrlTab.prefName, ctrlTab);
 | |
|       ctrlTab.uninit();
 | |
|       gBrowserThumbnails.uninit();
 | |
|       gProtectionsHandler.uninit();
 | |
|       FullZoom.destroy();
 | |
| 
 | |
|       Services.obs.removeObserver(gIdentityHandler, "perm-changed");
 | |
|       Services.obs.removeObserver(gRemoteControl, "devtools-socket");
 | |
|       Services.obs.removeObserver(gRemoteControl, "marionette-listening");
 | |
|       Services.obs.removeObserver(gRemoteControl, "remote-listening");
 | |
|       Services.obs.removeObserver(
 | |
|         gSessionHistoryObserver,
 | |
|         "browser:purge-session-history"
 | |
|       );
 | |
|       Services.obs.removeObserver(
 | |
|         gStoragePressureObserver,
 | |
|         "QuotaManager::StoragePressure"
 | |
|       );
 | |
|       Services.obs.removeObserver(gXPInstallObserver, "addon-install-disabled");
 | |
|       Services.obs.removeObserver(gXPInstallObserver, "addon-install-started");
 | |
|       Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
 | |
|       Services.obs.removeObserver(
 | |
|         gXPInstallObserver,
 | |
|         "addon-install-fullscreen-blocked"
 | |
|       );
 | |
|       Services.obs.removeObserver(
 | |
|         gXPInstallObserver,
 | |
|         "addon-install-origin-blocked"
 | |
|       );
 | |
|       Services.obs.removeObserver(
 | |
|         gXPInstallObserver,
 | |
|         "addon-install-policy-blocked"
 | |
|       );
 | |
|       Services.obs.removeObserver(
 | |
|         gXPInstallObserver,
 | |
|         "addon-install-webapi-blocked"
 | |
|       );
 | |
|       Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
 | |
|       Services.obs.removeObserver(
 | |
|         gXPInstallObserver,
 | |
|         "addon-install-confirmation"
 | |
|       );
 | |
|       Services.obs.removeObserver(gKeywordURIFixup, "keyword-uri-fixup");
 | |
| 
 | |
|       MenuTouchModeObserver.uninit();
 | |
|       BrowserOffline.uninit();
 | |
|       CanvasPermissionPromptHelper.uninit();
 | |
|       WebAuthnPromptHelper.uninit();
 | |
|       PanelUI.uninit();
 | |
|     }
 | |
| 
 | |
|     // Final window teardown, do this last.
 | |
|     gBrowser.destroy();
 | |
|     window.XULBrowserWindow = null;
 | |
|     window.docShell.treeOwner
 | |
|       .QueryInterface(Ci.nsIInterfaceRequestor)
 | |
|       .getInterface(Ci.nsIAppWindow).XULBrowserWindow = null;
 | |
|     window.browserDOMWindow = null;
 | |
|   },
 | |
| };
 | 
