/* -*- 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; }, }; gBrowserInit.idleTasksFinishedPromise = gBrowserInit.idleTasksFinished.promise;