browser-init.js (42089B)
1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 const { CustomKeys } = ChromeUtils.importESModule( 7 "resource:///modules/CustomKeys.sys.mjs" 8 ); 9 10 let _resolveDelayedStartup; 11 var delayedStartupPromise = new Promise(resolve => { 12 _resolveDelayedStartup = resolve; 13 }); 14 15 var gBrowserInit = { 16 delayedStartupFinished: false, 17 domContentLoaded: false, 18 19 _tabToAdopt: undefined, 20 _firstContentWindowPaintDeferred: Promise.withResolvers(), 21 idleTasksFinished: Promise.withResolvers(), 22 23 _setupFirstContentWindowPaintPromise() { 24 let lastTransactionId = window.windowUtils.lastTransactionId; 25 let layerTreeListener = () => { 26 if (this.getTabToAdopt()) { 27 // Need to wait until we finish adopting the tab, or we might end 28 // up focusing the initial browser and then losing focus when it 29 // gets swapped out for the tab to adopt. 30 return; 31 } 32 removeEventListener("MozLayerTreeReady", layerTreeListener); 33 let listener = e => { 34 if (e.transactionId > lastTransactionId) { 35 window.removeEventListener("MozAfterPaint", listener); 36 this._firstContentWindowPaintDeferred.resolve(); 37 } 38 }; 39 addEventListener("MozAfterPaint", listener); 40 }; 41 addEventListener("MozLayerTreeReady", layerTreeListener); 42 }, 43 44 getTabToAdopt() { 45 if (this._tabToAdopt !== undefined) { 46 return this._tabToAdopt; 47 } 48 49 if (window.arguments && window.XULElement.isInstance(window.arguments[0])) { 50 this._tabToAdopt = window.arguments[0]; 51 52 // Clear the reference of the tab being adopted from the arguments. 53 window.arguments[0] = null; 54 } else { 55 // There was no tab to adopt in the arguments, set _tabToAdopt to null 56 // to avoid checking it again. 57 this._tabToAdopt = null; 58 } 59 60 return this._tabToAdopt; 61 }, 62 63 _clearTabToAdopt() { 64 this._tabToAdopt = null; 65 }, 66 67 // Used to check if the new window is still adopting an existing tab as its first tab 68 // (e.g. from the WebExtensions internals). 69 isAdoptingTab() { 70 return !!this.getTabToAdopt(); 71 }, 72 73 onBeforeInitialXULLayout() { 74 this._setupFirstContentWindowPaintPromise(); 75 76 updateBookmarkToolbarVisibility(); 77 78 // Set a sane starting width/height for all resolutions on new profiles. 79 if (ChromeUtils.shouldResistFingerprinting("RoundWindowSize", null)) { 80 // When the fingerprinting resistance is enabled, making sure that we don't 81 // have a maximum window to interfere with generating rounded window dimensions. 82 document.documentElement.setAttribute("sizemode", "normal"); 83 } else if (!document.documentElement.hasAttribute("width")) { 84 const TARGET_WIDTH = 1280; 85 const TARGET_HEIGHT = 1040; 86 let width = Math.min(screen.availWidth * 0.9, TARGET_WIDTH); 87 let height = Math.min(screen.availHeight * 0.9, TARGET_HEIGHT); 88 89 document.documentElement.setAttribute("width", width); 90 document.documentElement.setAttribute("height", height); 91 92 if (width < TARGET_WIDTH && height < TARGET_HEIGHT) { 93 document.documentElement.setAttribute("sizemode", "maximized"); 94 } 95 } 96 { 97 const toolbarMenubar = document.getElementById("toolbar-menubar"); 98 const nativeMenubar = Services.appinfo.nativeMenubar; 99 toolbarMenubar.collapsed = nativeMenubar; 100 if (nativeMenubar) { 101 toolbarMenubar.removeAttribute("autohide"); 102 } else { 103 document.l10n.setAttributes( 104 toolbarMenubar, 105 "toolbar-context-menu-menu-bar-cmd" 106 ); 107 toolbarMenubar.setAttribute("data-l10n-attrs", "toolbarname"); 108 } 109 } 110 // If opening a Taskbar Tab or AI window, add an attribute to the top-level element 111 // to inform window styling. 112 if (window.arguments?.[1] instanceof Ci.nsIPropertyBag2) { 113 let extraOptions = window.arguments[1]; 114 if (extraOptions.hasKey("taskbartab")) { 115 window.document.documentElement.setAttribute( 116 "taskbartab", 117 extraOptions.getPropertyAsAString("taskbartab") 118 ); 119 } 120 121 if (extraOptions.hasKey("ai-window")) { 122 document.documentElement.setAttribute("ai-window", true); 123 } 124 } 125 126 // Run menubar initialization first, to avoid CustomTitlebar code picking 127 // up mutations from it and causing a reflow. 128 AutoHideMenubar.init(); 129 // Update the customtitlebar attribute so the window can be sized 130 // correctly. 131 window.TabBarVisibility.update(); 132 CustomTitlebar.init(); 133 134 new LightweightThemeConsumer(document); 135 136 if ( 137 Services.prefs.getBoolPref( 138 "toolkit.legacyUserProfileCustomizations.windowIcon", 139 false 140 ) 141 ) { 142 document.documentElement.setAttribute("icon", "main-window"); 143 } 144 145 // Call this after we set attributes that might change toolbars' computed 146 // text color. 147 ToolbarIconColor.init(window); 148 }, 149 150 onDOMContentLoaded() { 151 // All of this needs setting up before we create the first remote browser. 152 window.docShell.treeOwner 153 .QueryInterface(Ci.nsIInterfaceRequestor) 154 .getInterface(Ci.nsIAppWindow).XULBrowserWindow = window.XULBrowserWindow; 155 BrowserUtils.callModulesFromCategory( 156 { categoryName: "browser-window-domcontentloaded-before-tabbrowser" }, 157 window 158 ); 159 160 gBrowser = new window.Tabbrowser(); 161 gBrowser.init(); 162 gURLBar.addGBrowserListeners(); 163 if (Services.prefs.getBoolPref("browser.search.widget.new", false)) { 164 document.getElementById("searchbar-new")?.addGBrowserListeners(); 165 } 166 167 BrowserUtils.callModulesFromCategory( 168 { categoryName: "browser-window-domcontentloaded" }, 169 window 170 ); 171 172 FirefoxViewHandler.init(); 173 174 gURLBar.initPlaceHolder(); 175 176 // Hack to ensure that the various initial pages favicon is loaded 177 // instantaneously, to avoid flickering and improve perceived performance. 178 this._callWithURIToLoad(uriToLoad => { 179 let url = URL.parse(uriToLoad); 180 if (!url) { 181 return; 182 } 183 let nonQuery = url.URI.prePath + url.pathname; 184 if (nonQuery in gPageIcons) { 185 gBrowser.setIcon(gBrowser.selectedTab, gPageIcons[nonQuery]); 186 } 187 }); 188 189 updateFxaToolbarMenu(gFxaToolbarEnabled, true); 190 191 updatePrintCommands(gPrintEnabled); 192 193 gUnifiedExtensions.init(); 194 195 // Setting the focus will cause a style flush, it's preferable to call anything 196 // that will modify the DOM from within this function before this call. 197 this._setInitialFocus(); 198 199 this.domContentLoaded = true; 200 }, 201 202 onLoad() { 203 gBrowser.addEventListener("DOMUpdateBlockedPopups", e => 204 PopupAndRedirectBlockerObserver.handleEvent(e) 205 ); 206 gBrowser.addEventListener("DOMUpdateBlockedRedirect", e => 207 PopupAndRedirectBlockerObserver.handleEvent(e) 208 ); 209 gBrowser.addEventListener( 210 "TranslationsParent:LanguageState", 211 FullPageTranslationsPanel 212 ); 213 gBrowser.addEventListener( 214 "TranslationsParent:OfferTranslation", 215 FullPageTranslationsPanel 216 ); 217 gBrowser.addTabsProgressListener(FullPageTranslationsPanel); 218 219 window.addEventListener("AppCommand", HandleAppCommandEvent, true); 220 221 // These routines add message listeners. They must run before 222 // loading the frame script to ensure that we don't miss any 223 // message sent between when the frame script is loaded and when 224 // the listener is registered. 225 CaptivePortalWatcher.init(); 226 ZoomUI.init(window); 227 228 if (!gMultiProcessBrowser) { 229 // There is a Content:Click message manually sent from content. 230 gBrowser.tabpanels.addEventListener("click", contentAreaClick, { 231 capture: true, 232 mozSystemGroup: true, 233 }); 234 } 235 236 // hook up UI through progress listener 237 gBrowser.addProgressListener(window.XULBrowserWindow); 238 gBrowser.addTabsProgressListener(window.TabsProgressListener); 239 240 SidebarController.init(); 241 242 // We do this in onload because we want to ensure the button's state 243 // doesn't flicker as the window is being shown. 244 DownloadsButton.init(); 245 246 // Init the SecurityLevelButton 247 SecurityLevelButton.init(); 248 249 gTorConnectUrlbarButton.init(); 250 gTorConnectTitlebarStatus.init(); 251 252 // Init the OnionAuthPrompt 253 OnionAuthPrompt.init(); 254 255 // Init the Onion Location pill 256 OnionLocationParent.init(document); 257 258 gTorCircuitPanel.init(); 259 260 // Certain kinds of automigration rely on this notification to complete 261 // their tasks BEFORE the browser window is shown. SessionStore uses it to 262 // restore tabs into windows AFTER important parts like gMultiProcessBrowser 263 // have been initialized. 264 Services.obs.notifyObservers(window, "browser-window-before-show"); 265 266 if ( 267 !window.toolbar.visible || 268 window.document.documentElement.hasAttribute("taskbartab") 269 ) { 270 // adjust browser UI for popups 271 gURLBar.readOnly = true; 272 } 273 274 // Misc. inits. 275 gUIDensity.init(); 276 Win10TabletModeUpdater.init(); 277 CombinedStopReload.ensureInitialized(); 278 // Initialize private browsing UI only if window is private 279 if (PrivateBrowsingUtils.isWindowPrivate(window)) { 280 PrivateBrowsingUI.init(window); 281 } 282 TaskbarTabsChrome.init(window); 283 BrowserPageActions.init(); 284 if (gToolbarKeyNavEnabled) { 285 ToolbarKeyboardNavigator.init(); 286 } 287 CustomKeys.initWindow(window); 288 289 // Update UI if browser is under remote control. 290 gRemoteControl.updateVisualCue(); 291 292 // If we are given a tab to swap in, take care of it before first paint to 293 // avoid an about:blank flash. 294 let tabToAdopt = this.getTabToAdopt(); 295 if (tabToAdopt) { 296 let evt = new CustomEvent("before-initial-tab-adopted", { 297 bubbles: true, 298 }); 299 gBrowser.tabpanels.dispatchEvent(evt); 300 301 // Stop the about:blank load 302 gBrowser.stop(); 303 304 // Remove the speculative focus from the urlbar to let the url be formatted. 305 gURLBar.removeAttribute("focused"); 306 307 let swapBrowsers = () => { 308 if (gBrowser.isTabGroupLabel(tabToAdopt)) { 309 // TODO bug 1967937: Merge this case with the tab group case below. 310 gBrowser.adoptTabGroup(tabToAdopt.group, { elementIndex: 0 }); 311 gBrowser.removeTab(gBrowser.selectedTab); 312 } else if (gBrowser.isTabGroup(tabToAdopt)) { 313 // Via gBrowser.replaceGroupWithWindow 314 let tempBlankTab = gBrowser.selectedTab; 315 gBrowser.adoptTabGroup(tabToAdopt, { tabIndex: 0, selectTab: true }); 316 gBrowser.removeTab(tempBlankTab); 317 Glean.tabgroup.groupInteractions.move_window.add(1); 318 } else if (gBrowser.isSplitViewWrapper(tabToAdopt)) { 319 let tempBlankTab = gBrowser.selectedTab; 320 gBrowser.adoptSplitView(tabToAdopt, { 321 elementIndex: 0, 322 selectTab: true, 323 }); 324 gBrowser.removeTab(tempBlankTab); 325 } else { 326 if (tabToAdopt.group) { 327 Glean.tabgroup.tabInteractions.remove_new_window.add(); 328 } 329 gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToAdopt); 330 } 331 332 // Clear the reference to the tab once its adoption has been completed. 333 this._clearTabToAdopt(); 334 }; 335 if ( 336 gBrowser.isTab(tabToAdopt) && 337 !tabToAdopt.linkedBrowser.isRemoteBrowser 338 ) { 339 swapBrowsers(); 340 } else { 341 // For remote browsers, wait for the paint event, otherwise the tabs 342 // are not yet ready and focus gets confused because the browser swaps 343 // out while tabs are switching. 344 addEventListener("MozAfterPaint", swapBrowsers, { once: true }); 345 } 346 } 347 348 // Wait until chrome is painted before executing code not critical to making the window visible 349 this._boundDelayedStartup = this._delayedStartup.bind(this); 350 window.addEventListener("MozAfterPaint", this._boundDelayedStartup); 351 352 if (!PrivateBrowsingUtils.enabled) { 353 document.getElementById("Tools:PrivateBrowsing").hidden = true; 354 // Setting disabled doesn't disable the shortcut, so we just remove 355 // the keybinding. 356 document.getElementById("key_privatebrowsing").remove(); 357 } 358 359 if (BrowserUIUtils.quitShortcutDisabled) { 360 document.getElementById("key_quitApplication").remove(); 361 document.getElementById("menu_FileQuitItem").removeAttribute("key"); 362 363 PanelMultiView.getViewNode( 364 document, 365 "appMenu-quit-button2" 366 )?.removeAttribute("key"); 367 } 368 369 this._loadHandled = true; 370 }, 371 372 _cancelDelayedStartup() { 373 window.removeEventListener("MozAfterPaint", this._boundDelayedStartup); 374 this._boundDelayedStartup = null; 375 }, 376 377 _delayedStartup() { 378 let { TelemetryTimestamps } = ChromeUtils.importESModule( 379 "resource://gre/modules/TelemetryTimestamps.sys.mjs" 380 ); 381 TelemetryTimestamps.add("delayedStartupStarted"); 382 Glean.browserTimings.startupTimeline.delayedStartupStarted.set( 383 Services.telemetry.msSinceProcessStart() 384 ); 385 386 this._cancelDelayedStartup(); 387 388 gBrowser.addEventListener( 389 "PermissionStateChange", 390 function () { 391 gIdentityHandler.refreshIdentityBlock(); 392 gPermissionPanel.updateSharingIndicator(); 393 }, 394 true 395 ); 396 397 this._handleURIToLoad(); 398 399 Services.obs.addObserver(gIdentityHandler, "perm-changed"); 400 Services.obs.addObserver(gRemoteControl, "devtools-socket"); 401 Services.obs.addObserver(gRemoteControl, "marionette-listening"); 402 Services.obs.addObserver(gRemoteControl, "remote-listening"); 403 Services.obs.addObserver( 404 gSessionHistoryObserver, 405 "browser:purge-session-history" 406 ); 407 Services.obs.addObserver( 408 gStoragePressureObserver, 409 "QuotaManager::StoragePressure" 410 ); 411 Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled"); 412 Services.obs.addObserver(gXPInstallObserver, "addon-install-started"); 413 Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked"); 414 Services.obs.addObserver( 415 gXPInstallObserver, 416 "addon-install-fullscreen-blocked" 417 ); 418 Services.obs.addObserver( 419 gXPInstallObserver, 420 "addon-install-origin-blocked" 421 ); 422 Services.obs.addObserver( 423 gXPInstallObserver, 424 "addon-install-policy-blocked" 425 ); 426 Services.obs.addObserver( 427 gXPInstallObserver, 428 "addon-install-webapi-blocked" 429 ); 430 Services.obs.addObserver(gXPInstallObserver, "addon-install-failed"); 431 Services.obs.addObserver(gXPInstallObserver, "addon-install-confirmation"); 432 Services.obs.addObserver(gKeywordURIFixup, "keyword-uri-fixup"); 433 Services.obs.addObserver(gLocaleChangeObserver, "intl:app-locales-changed"); 434 435 BrowserOffline.init(); 436 437 BrowserUtils.callModulesFromCategory( 438 { 439 categoryName: "browser-window-delayed-startup", 440 profilerMarker: "delayed-startup-task", 441 }, 442 window 443 ); 444 445 // Initialize the full zoom setting. 446 // We do this before the session restore service gets initialized so we can 447 // apply full zoom settings to tabs restored by the session restore service. 448 FullZoom.init(); 449 PanelUI.init(shouldSuppressPopupNotifications); 450 451 UpdateUrlbarSearchSplitterState(); 452 453 BookmarkingUI.init(); 454 gURLBar.delayedStartupInit(); 455 if (Services.prefs.getBoolPref("browser.search.widget.new", false)) { 456 document.getElementById("searchbar-new")?.delayedStartupInit(); 457 } 458 gProtectionsHandler.init(); 459 gTrustPanelHandler.init(); 460 461 let safeMode = document.getElementById("helpSafeMode"); 462 if (Services.appinfo.inSafeMode) { 463 document.l10n.setAttributes(safeMode, "menu-help-exit-troubleshoot-mode"); 464 safeMode.setAttribute( 465 "appmenu-data-l10n-id", 466 "appmenu-help-exit-troubleshoot-mode" 467 ); 468 } 469 470 // BiDi UI 471 gBidiUI = isBidiEnabled(); 472 if (gBidiUI) { 473 document.getElementById("documentDirection-separator").hidden = false; 474 document.getElementById("documentDirection-swap").hidden = false; 475 document.getElementById("textfieldDirection-separator").hidden = false; 476 document.getElementById("textfieldDirection-swap").hidden = false; 477 } 478 479 // Setup click-and-hold gestures access to the session history 480 // menus if global click-and-hold isn't turned on 481 if (!Services.prefs.getBoolPref("ui.click_hold_context_menus", false)) { 482 SetClickAndHoldHandlers(); 483 } 484 485 function initBackForwardButtonTooltip(tooltipId, l10nId, shortcutId) { 486 let shortcut = document.getElementById(shortcutId); 487 shortcut = ShortcutUtils.prettifyShortcut(shortcut); 488 489 let tooltip = document.getElementById(tooltipId); 490 document.l10n.setAttributes(tooltip, l10nId, { shortcut }); 491 } 492 493 initBackForwardButtonTooltip( 494 "back-button-tooltip-description", 495 "navbar-tooltip-back-2", 496 "goBackKb" 497 ); 498 499 initBackForwardButtonTooltip( 500 "forward-button-tooltip-description", 501 "navbar-tooltip-forward-2", 502 "goForwardKb" 503 ); 504 505 PlacesToolbarHelper.init(); 506 507 ctrlTab.readPref(); 508 Services.prefs.addObserver(ctrlTab.prefName, ctrlTab); 509 510 // The object handling the downloads indicator is initialized here in the 511 // delayed startup function, but the actual indicator element is not loaded 512 // unless there are downloads to be displayed. 513 DownloadsButton.initializeIndicator(); 514 515 if (AppConstants.platform != "macosx") { 516 updateEditUIVisibility(); 517 let placesContext = document.getElementById("placesContext"); 518 placesContext.addEventListener("popupshowing", updateEditUIVisibility); 519 placesContext.addEventListener("popuphiding", updateEditUIVisibility); 520 } 521 522 FullScreen.init(); 523 524 if (AppConstants.MOZ_DATA_REPORTING) { 525 gDataNotificationInfoBar.init(); 526 } 527 528 if (!AppConstants.MOZILLA_OFFICIAL) { 529 DevelopmentHelpers.init(); 530 } 531 532 gExtensionsNotifications.init(); 533 534 let wasMinimized = window.windowState == window.STATE_MINIMIZED; 535 window.addEventListener("sizemodechange", () => { 536 let isMinimized = window.windowState == window.STATE_MINIMIZED; 537 if (wasMinimized != isMinimized) { 538 wasMinimized = isMinimized; 539 UpdatePopupNotificationsVisibility(); 540 } 541 }); 542 543 window.addEventListener("mousemove", MousePosTracker); 544 window.addEventListener("mouseout", MousePosTracker); 545 window.addEventListener("dragover", MousePosTracker); 546 547 gNavToolbox.addEventListener("customizationstarting", CustomizationHandler); 548 gNavToolbox.addEventListener("aftercustomization", CustomizationHandler); 549 550 SessionStore.promiseInitialized.then(() => { 551 // Bail out if the window has been closed in the meantime. 552 if (window.closed) { 553 return; 554 } 555 556 // Enable the Restore Last Session command if needed 557 gRestoreLastSessionObserver.init(); 558 559 SidebarController.startDelayedLoad(); 560 561 PanicButtonNotifier.init(); 562 }); 563 564 if (BrowserHandler.kiosk) { 565 // We don't modify popup windows for kiosk mode 566 if (!gURLBar.readOnly) { 567 window.fullScreen = true; 568 } 569 } 570 571 if (Services.policies.status === Services.policies.ACTIVE) { 572 if (!Services.policies.isAllowed("hideShowMenuBar")) { 573 document 574 .getElementById("toolbar-menubar") 575 .removeAttribute("toolbarname"); 576 } 577 if (!Services.policies.isAllowed("filepickers")) { 578 let savePageCommand = document.getElementById("Browser:SavePage"); 579 let openFileCommand = document.getElementById("Browser:OpenFile"); 580 581 savePageCommand.setAttribute("disabled", "true"); 582 openFileCommand.setAttribute("disabled", "true"); 583 584 document.addEventListener("FilePickerBlocked", function (event) { 585 let browser = event.target; 586 587 let notificationBox = browser 588 .getTabBrowser() 589 ?.getNotificationBox(browser); 590 591 // Prevent duplicate notifications 592 if ( 593 notificationBox && 594 !notificationBox.getNotificationWithValue("filepicker-blocked") 595 ) { 596 notificationBox.appendNotification("filepicker-blocked", { 597 label: { 598 "l10n-id": "filepicker-blocked-infobar", 599 }, 600 priority: notificationBox.PRIORITY_INFO_LOW, 601 }); 602 } 603 }); 604 } 605 let policies = Services.policies.getActivePolicies(); 606 if ("ManagedBookmarks" in policies) { 607 let managedBookmarks = policies.ManagedBookmarks; 608 let children = managedBookmarks.filter( 609 child => !("toplevel_name" in child) 610 ); 611 if (children.length) { 612 let managedBookmarksButton = 613 document.createXULElement("toolbarbutton"); 614 managedBookmarksButton.setAttribute("id", "managed-bookmarks"); 615 managedBookmarksButton.setAttribute("class", "bookmark-item"); 616 let toplevel = managedBookmarks.find( 617 element => "toplevel_name" in element 618 ); 619 if (toplevel) { 620 managedBookmarksButton.setAttribute( 621 "label", 622 toplevel.toplevel_name 623 ); 624 } else { 625 document.l10n.setAttributes( 626 managedBookmarksButton, 627 "managed-bookmarks" 628 ); 629 } 630 managedBookmarksButton.setAttribute("context", "placesContext"); 631 managedBookmarksButton.setAttribute("container", "true"); 632 managedBookmarksButton.setAttribute("removable", "false"); 633 managedBookmarksButton.setAttribute("type", "menu"); 634 635 let managedBookmarksPopup = document.createXULElement("menupopup"); 636 managedBookmarksPopup.setAttribute("id", "managed-bookmarks-popup"); 637 managedBookmarksPopup.addEventListener("command", event => 638 PlacesToolbarHelper.openManagedBookmark(event) 639 ); 640 managedBookmarksPopup.addEventListener( 641 "dragover", 642 event => (event.dataTransfer.effectAllowed = "none") 643 ); 644 managedBookmarksPopup.addEventListener("dragstart", event => 645 PlacesToolbarHelper.onDragStartManaged(event) 646 ); 647 managedBookmarksPopup.addEventListener("popupshowing", event => 648 PlacesToolbarHelper.populateManagedBookmarks(event.currentTarget) 649 ); 650 managedBookmarksPopup.setAttribute("placespopup", "true"); 651 managedBookmarksPopup.setAttribute("is", "places-popup"); 652 managedBookmarksPopup.classList.add("toolbar-menupopup"); 653 managedBookmarksButton.appendChild(managedBookmarksPopup); 654 655 gNavToolbox.palette.appendChild(managedBookmarksButton); 656 657 CustomizableUI.ensureWidgetPlacedInWindow( 658 "managed-bookmarks", 659 window 660 ); 661 662 // Add button if it doesn't exist 663 if (!CustomizableUI.getPlacementOfWidget("managed-bookmarks")) { 664 CustomizableUI.addWidgetToArea( 665 "managed-bookmarks", 666 CustomizableUI.AREA_BOOKMARKS, 667 0 668 ); 669 } 670 } 671 } 672 } 673 674 CaptivePortalWatcher.delayedStartup(); 675 676 SessionStore.promiseAllWindowsRestored.then(() => { 677 this._schedulePerWindowIdleTasks(); 678 document.documentElement.setAttribute("sessionrestored", "true"); 679 }); 680 681 this.delayedStartupFinished = true; 682 _resolveDelayedStartup(); 683 Services.obs.notifyObservers(window, "browser-delayed-startup-finished"); 684 TelemetryTimestamps.add("delayedStartupFinished"); 685 Glean.browserTimings.startupTimeline.delayedStartupFinished.set( 686 Services.telemetry.msSinceProcessStart() 687 ); 688 // We've announced that delayed startup has finished. Do not add code past this point. 689 }, 690 691 /** 692 * Resolved on the first MozLayerTreeReady and next MozAfterPaint in the 693 * parent process. 694 */ 695 get firstContentWindowPaintPromise() { 696 return this._firstContentWindowPaintDeferred.promise; 697 }, 698 699 _setInitialFocus() { 700 let initiallyFocusedElement = document.commandDispatcher.focusedElement; 701 702 // To prevent startup flicker, the urlbar has the 'focused' attribute set 703 // by default. If we are not sure the urlbar will be focused in this 704 // window, we need to remove the attribute before first paint. 705 // TODO (bug 1629956): The urlbar having the 'focused' attribute by default 706 // isn't a useful optimization anymore since UrlbarInput needs layout 707 // information to focus the urlbar properly. 708 let shouldRemoveFocusedAttribute = true; 709 710 this._callWithURIToLoad(uriToLoad => { 711 if ( 712 isBlankPageURL(uriToLoad) || 713 uriToLoad == "about:privatebrowsing" || 714 this.getTabToAdopt()?.isEmpty 715 ) { 716 gURLBar.select(); 717 shouldRemoveFocusedAttribute = false; 718 return; 719 } 720 721 // If the initial browser is remote, in order to optimize for first paint, 722 // we'll defer switching focus to that browser until it has painted. 723 // Otherwise use a regular promise to guarantee that mutationobserver 724 // microtasks that could affect focusability have run. 725 let promise = gBrowser.selectedBrowser.isRemoteBrowser 726 ? this.firstContentWindowPaintPromise 727 : Promise.resolve(); 728 729 promise.then(() => { 730 // If focus didn't move while we were waiting, we're okay to move to 731 // the browser. 732 if ( 733 document.commandDispatcher.focusedElement == initiallyFocusedElement 734 ) { 735 gBrowser.selectedBrowser.focus(); 736 } 737 }); 738 }); 739 740 // Delay removing the attribute using requestAnimationFrame to avoid 741 // invalidating styles multiple times in a row if uriToLoadPromise 742 // resolves before first paint. 743 if (shouldRemoveFocusedAttribute) { 744 window.requestAnimationFrame(() => { 745 if (shouldRemoveFocusedAttribute) { 746 gURLBar.removeAttribute("focused"); 747 } 748 }); 749 } 750 }, 751 752 _handleURIToLoad() { 753 this._callWithURIToLoad(uriToLoad => { 754 if (!uriToLoad) { 755 // We don't check whether window.arguments[5] (userContextId) is set 756 // because tabbrowser.js takes care of that for the initial tab. 757 return; 758 } 759 760 // We don't check if uriToLoad is a XULElement because this case has 761 // already been handled before first paint, and the argument cleared. 762 if (Array.isArray(uriToLoad)) { 763 // This function throws for certain malformed URIs, so use exception handling 764 // so that we don't disrupt startup 765 try { 766 gBrowser.loadTabs(uriToLoad, { 767 inBackground: false, 768 replace: true, 769 // See below for the semantics of window.arguments. Only the minimum is supported. 770 userContextId: window.arguments[5], 771 triggeringPrincipal: 772 window.arguments[8] || 773 Services.scriptSecurityManager.getSystemPrincipal(), 774 allowInheritPrincipal: window.arguments[9], 775 policyContainer: window.arguments[10], 776 fromExternal: true, 777 }); 778 } catch (e) {} 779 } else if (window.arguments.length >= 3) { 780 // window.arguments[1]: extraOptions (nsIPropertyBag) 781 // [2]: referrerInfo (nsIReferrerInfo) 782 // [3]: postData (nsIInputStream) 783 // [4]: allowThirdPartyFixup (bool) 784 // [5]: userContextId (int) 785 // [6]: originPrincipal (nsIPrincipal) 786 // [7]: originStoragePrincipal (nsIPrincipal) 787 // [8]: triggeringPrincipal (nsIPrincipal) 788 // [9]: allowInheritPrincipal (bool) 789 // [10]: policyContainer (nsIPolicyContainer) 790 // [11]: nsOpenWindowInfo 791 let userContextId = 792 window.arguments[5] != undefined 793 ? window.arguments[5] 794 : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID; 795 796 let hasValidUserGestureActivation = undefined; 797 let textDirectiveUserActivation = undefined; 798 let fromExternal = undefined; 799 let globalHistoryOptions = undefined; 800 let triggeringRemoteType = undefined; 801 let forceAllowDataURI = false; 802 let schemelessInput = Ci.nsILoadInfo.SchemelessInputTypeUnset; 803 if (window.arguments[1]) { 804 if (!(window.arguments[1] instanceof Ci.nsIPropertyBag2)) { 805 throw new Error( 806 "window.arguments[1] must be null or Ci.nsIPropertyBag2!" 807 ); 808 } 809 810 let extraOptions = window.arguments[1]; 811 if (extraOptions.hasKey("hasValidUserGestureActivation")) { 812 hasValidUserGestureActivation = extraOptions.getPropertyAsBool( 813 "hasValidUserGestureActivation" 814 ); 815 } 816 if (extraOptions.hasKey("textDirectiveUserActivation")) { 817 textDirectiveUserActivation = extraOptions.getPropertyAsBool( 818 "textDirectiveUserActivation" 819 ); 820 } 821 if (extraOptions.hasKey("fromExternal")) { 822 fromExternal = extraOptions.getPropertyAsBool("fromExternal"); 823 } 824 if (extraOptions.hasKey("triggeringSponsoredURL")) { 825 globalHistoryOptions = { 826 triggeringSponsoredURL: extraOptions.getPropertyAsACString( 827 "triggeringSponsoredURL" 828 ), 829 }; 830 if (extraOptions.hasKey("triggeringSponsoredURLVisitTimeMS")) { 831 globalHistoryOptions.triggeringSponsoredURLVisitTimeMS = 832 extraOptions.getPropertyAsUint64( 833 "triggeringSponsoredURLVisitTimeMS" 834 ); 835 } 836 if (extraOptions.hasKey("triggeringSource")) { 837 globalHistoryOptions.triggeringSource = 838 extraOptions.getPropertyAsACString("triggeringSource"); 839 } 840 } 841 if (extraOptions.hasKey("triggeringRemoteType")) { 842 triggeringRemoteType = extraOptions.getPropertyAsACString( 843 "triggeringRemoteType" 844 ); 845 } 846 if (extraOptions.hasKey("forceAllowDataURI")) { 847 forceAllowDataURI = 848 extraOptions.getPropertyAsBool("forceAllowDataURI"); 849 } 850 if (extraOptions.hasKey("schemelessInput")) { 851 schemelessInput = 852 extraOptions.getPropertyAsUint32("schemelessInput"); 853 } 854 } 855 856 try { 857 openLinkIn(uriToLoad, "current", { 858 referrerInfo: window.arguments[2] || null, 859 postData: window.arguments[3] || null, 860 allowThirdPartyFixup: window.arguments[4] || false, 861 userContextId, 862 // pass the origin principal (if any) and force its use to create 863 // an initial about:blank viewer if present: 864 originPrincipal: window.arguments[6], 865 originStoragePrincipal: window.arguments[7], 866 triggeringPrincipal: window.arguments[8], 867 // TODO fix allowInheritPrincipal to default to false. 868 // Default to true unless explicitly set to false because of bug 1475201. 869 allowInheritPrincipal: window.arguments[9] !== false, 870 policyContainer: window.arguments[10], 871 forceAboutBlankViewerInCurrent: !!window.arguments[6], 872 forceAllowDataURI, 873 hasValidUserGestureActivation, 874 textDirectiveUserActivation, 875 fromExternal, 876 globalHistoryOptions, 877 triggeringRemoteType, 878 schemelessInput, 879 }); 880 } catch (e) { 881 console.error(e); 882 } 883 884 window.focus(); 885 } else { 886 // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3. 887 // Such callers expect that window.arguments[0] is handled as a single URI. 888 loadOneOrMoreURIs( 889 uriToLoad, 890 Services.scriptSecurityManager.getSystemPrincipal(), 891 null 892 ); 893 } 894 }); 895 }, 896 897 /** 898 * Use this function as an entry point to schedule tasks that 899 * need to run once per window after startup, and can be scheduled 900 * by using an idle callback. 901 * 902 * The functions scheduled here will fire from idle callbacks 903 * once every window has finished being restored by session 904 * restore, and after the equivalent only-once tasks 905 * have run (from _scheduleStartupIdleTasks in BrowserGlue.sys.mjs). 906 */ 907 _schedulePerWindowIdleTasks() { 908 // Bail out if the window has been closed in the meantime. 909 if (window.closed) { 910 return; 911 } 912 913 function scheduleIdleTask(func, options) { 914 requestIdleCallback(function idleTaskRunner() { 915 if (!window.closed) { 916 func(); 917 } 918 }, options); 919 } 920 921 scheduleIdleTask(() => { 922 // Initialize the Sync UI 923 gSync.init(); 924 }); 925 926 scheduleIdleTask(() => { 927 // Read prefers-reduced-motion setting 928 let reduceMotionQuery = window.matchMedia( 929 "(prefers-reduced-motion: reduce)" 930 ); 931 function readSetting() { 932 gReduceMotionSetting = reduceMotionQuery.matches; 933 } 934 reduceMotionQuery.addListener(readSetting); 935 readSetting(); 936 }); 937 938 scheduleIdleTask(() => { 939 // setup simple gestures support 940 gGestureSupport.init(true); 941 942 // setup history swipe animation 943 gHistorySwipeAnimation.init(); 944 }); 945 946 scheduleIdleTask(() => { 947 gBrowserThumbnails.init(); 948 }); 949 950 scheduleIdleTask( 951 () => { 952 // Initialize the download manager some time after the app starts so that 953 // auto-resume downloads begin (such as after crashing or quitting with 954 // active downloads) and speeds up the first-load of the download manager UI. 955 // If the user manually opens the download manager before the timeout, the 956 // downloads will start right away, and initializing again won't hurt. 957 try { 958 DownloadsCommon.initializeAllDataLinks(); 959 ChromeUtils.importESModule( 960 "moz-src:///browser/components/downloads/DownloadsTaskbar.sys.mjs" 961 ) 962 .DownloadsTaskbar.registerIndicator(window) 963 .catch(ex => { 964 console.error(ex); 965 }); 966 if (AppConstants.platform == "macosx") { 967 ChromeUtils.importESModule( 968 "moz-src:///browser/components/downloads/DownloadsMacFinderProgress.sys.mjs" 969 ).DownloadsMacFinderProgress.register(); 970 } 971 } catch (ex) { 972 console.error(ex); 973 } 974 }, 975 { timeout: 10000 } 976 ); 977 978 if (Win7Features) { 979 scheduleIdleTask(() => Win7Features.onOpenWindow()); 980 } 981 982 scheduleIdleTask(async () => { 983 NewTabPagePreloading.maybeCreatePreloadedBrowser(window); 984 }); 985 986 scheduleIdleTask(() => { 987 gGfxUtils.init(); 988 }); 989 990 scheduleIdleTask(async () => { 991 await gProfiles.init(); 992 }); 993 994 // This should always go last, since the idle tasks (except for the ones with 995 // timeouts) should execute in order. Note that this observer notification is 996 // not guaranteed to fire, since the window could close before we get here. 997 scheduleIdleTask(() => { 998 this.idleTasksFinished.resolve(); 999 Services.obs.notifyObservers( 1000 window, 1001 "browser-idle-startup-tasks-finished" 1002 ); 1003 }); 1004 }, 1005 1006 // Returns the URI(s) to load at startup if it is immediately known, or a 1007 // promise resolving to the URI to load. 1008 get uriToLoadPromise() { 1009 delete this.uriToLoadPromise; 1010 return (this.uriToLoadPromise = (function () { 1011 // window.arguments[0]: URI to load (string), or an nsIArray of 1012 // nsISupportsStrings to load, or a xul:tab of 1013 // a tabbrowser, which will be replaced by this 1014 // window (for this case, all other arguments are 1015 // ignored). 1016 let uri = window.arguments?.[0]; 1017 if (!uri || window.XULElement.isInstance(uri)) { 1018 return null; 1019 } 1020 1021 let defaultArgs = BrowserHandler.defaultArgs; 1022 1023 // figure out which URI to actually load (or a Promise to get the uri) 1024 uri = (aUri => { 1025 // If the given URI is different from the homepage, we want to load it. 1026 if (aUri != defaultArgs) { 1027 AboutNewTab.noteNonDefaultStartup(); 1028 1029 if (aUri instanceof Ci.nsIArray) { 1030 // Transform the nsIArray of nsISupportsString's into a JS Array of 1031 // JS strings. 1032 return Array.from( 1033 aUri.enumerate(Ci.nsISupportsString), 1034 supportStr => supportStr.data 1035 ); 1036 } else if (aUri instanceof Ci.nsISupportsString) { 1037 return aUri.data; 1038 } 1039 return aUri; 1040 } 1041 1042 // The URI appears to be the the homepage. We want to load it only if 1043 // session restore isn't about to override the homepage. 1044 let willOverride = SessionStartup.willOverrideHomepage; 1045 if (typeof willOverride == "boolean") { 1046 return willOverride ? null : uri; 1047 } 1048 return willOverride.then(willOverrideHomepage => 1049 willOverrideHomepage ? null : uri 1050 ); 1051 })(uri); 1052 1053 // if using TorConnect, convert these uris to redirects 1054 if (TorConnect.shouldShowTorConnect) { 1055 return Promise.resolve(uri).then(aUri => 1056 TorConnectParent.getURIsToLoad(aUri ?? []) 1057 ); 1058 } 1059 return uri; 1060 })()); 1061 }, 1062 1063 // Calls the given callback with the URI to load at startup. 1064 // Synchronously if possible, or after uriToLoadPromise resolves otherwise. 1065 _callWithURIToLoad(callback) { 1066 let uriToLoad = this.uriToLoadPromise; 1067 if (uriToLoad && uriToLoad.then) { 1068 uriToLoad.then(callback); 1069 } else { 1070 callback(uriToLoad); 1071 } 1072 }, 1073 1074 onUnload() { 1075 gUIDensity.uninit(); 1076 1077 CustomTitlebar.uninit(); 1078 1079 ToolbarIconColor.uninit(window); 1080 1081 // In certain scenarios it's possible for unload to be fired before onload, 1082 // (e.g. if the window is being closed after browser.js loads but before the 1083 // load completes). In that case, there's nothing to do here. 1084 if (!this._loadHandled) { 1085 return; 1086 } 1087 1088 // First clean up services initialized in gBrowserInit.onLoad (or those whose 1089 // uninit methods don't depend on the services having been initialized). 1090 1091 CombinedStopReload.uninit(); 1092 1093 gGestureSupport.init(false); 1094 1095 gHistorySwipeAnimation.uninit(); 1096 1097 FullScreen.uninit(); 1098 1099 gSync.uninit(); 1100 1101 gExtensionsNotifications.uninit(); 1102 gUnifiedExtensions.uninit(); 1103 1104 try { 1105 gBrowser.removeProgressListener(window.XULBrowserWindow); 1106 gBrowser.removeTabsProgressListener(window.TabsProgressListener); 1107 } catch (ex) {} 1108 1109 PlacesToolbarHelper.uninit(); 1110 1111 BookmarkingUI.uninit(); 1112 1113 Win10TabletModeUpdater.uninit(); 1114 1115 CaptivePortalWatcher.uninit(); 1116 1117 SidebarController.uninit(); 1118 1119 DownloadsButton.uninit(); 1120 1121 SecurityLevelButton.uninit(); 1122 1123 gTorConnectUrlbarButton.uninit(); 1124 gTorConnectTitlebarStatus.uninit(); 1125 1126 OnionAuthPrompt.uninit(); 1127 1128 gTorCircuitPanel.uninit(); 1129 1130 if (gToolbarKeyNavEnabled) { 1131 ToolbarKeyboardNavigator.uninit(); 1132 } 1133 CustomKeys.uninitWindow(window); 1134 1135 // LinkPreview.sys.mjs is missing. tor-browser#44045. 1136 1137 FirefoxViewHandler.uninit(); 1138 1139 // Now either cancel delayedStartup, or clean up the services initialized from 1140 // it. 1141 if (this._boundDelayedStartup) { 1142 this._cancelDelayedStartup(); 1143 } else { 1144 if (Win7Features) { 1145 Win7Features.onCloseWindow(); 1146 } 1147 Services.prefs.removeObserver(ctrlTab.prefName, ctrlTab); 1148 ctrlTab.uninit(); 1149 gBrowserThumbnails.uninit(); 1150 gProtectionsHandler.uninit(); 1151 gTrustPanelHandler.uninit(); 1152 FullZoom.destroy(); 1153 1154 Services.obs.removeObserver(gIdentityHandler, "perm-changed"); 1155 Services.obs.removeObserver(gRemoteControl, "devtools-socket"); 1156 Services.obs.removeObserver(gRemoteControl, "marionette-listening"); 1157 Services.obs.removeObserver(gRemoteControl, "remote-listening"); 1158 Services.obs.removeObserver( 1159 gSessionHistoryObserver, 1160 "browser:purge-session-history" 1161 ); 1162 Services.obs.removeObserver( 1163 gStoragePressureObserver, 1164 "QuotaManager::StoragePressure" 1165 ); 1166 Services.obs.removeObserver(gXPInstallObserver, "addon-install-disabled"); 1167 Services.obs.removeObserver(gXPInstallObserver, "addon-install-started"); 1168 Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked"); 1169 Services.obs.removeObserver( 1170 gXPInstallObserver, 1171 "addon-install-fullscreen-blocked" 1172 ); 1173 Services.obs.removeObserver( 1174 gXPInstallObserver, 1175 "addon-install-origin-blocked" 1176 ); 1177 Services.obs.removeObserver( 1178 gXPInstallObserver, 1179 "addon-install-policy-blocked" 1180 ); 1181 Services.obs.removeObserver( 1182 gXPInstallObserver, 1183 "addon-install-webapi-blocked" 1184 ); 1185 Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed"); 1186 Services.obs.removeObserver( 1187 gXPInstallObserver, 1188 "addon-install-confirmation" 1189 ); 1190 Services.obs.removeObserver(gKeywordURIFixup, "keyword-uri-fixup"); 1191 Services.obs.removeObserver( 1192 gLocaleChangeObserver, 1193 "intl:app-locales-changed" 1194 ); 1195 1196 BrowserOffline.uninit(); 1197 PanelUI.uninit(); 1198 } 1199 1200 // Final window teardown, do this last. 1201 gBrowser.destroy(); 1202 window.XULBrowserWindow = null; 1203 window.docShell.treeOwner 1204 .QueryInterface(Ci.nsIInterfaceRequestor) 1205 .getInterface(Ci.nsIAppWindow).XULBrowserWindow = null; 1206 1207 BrowserUtils.callModulesFromCategory( 1208 { categoryName: "browser-window-unload" }, 1209 window 1210 ); 1211 }, 1212 };