geckoview.js (27998B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 var { DelayedInit } = ChromeUtils.importESModule( 8 "resource://gre/modules/DelayedInit.sys.mjs" 9 ); 10 var { XPCOMUtils } = ChromeUtils.importESModule( 11 "resource://gre/modules/XPCOMUtils.sys.mjs" 12 ); 13 14 ChromeUtils.defineESModuleGetters(this, { 15 Blocklist: "resource://gre/modules/Blocklist.sys.mjs", 16 E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs", 17 EventDispatcher: "resource://gre/modules/Messaging.sys.mjs", 18 GeckoViewActorManager: "resource://gre/modules/GeckoViewActorManager.sys.mjs", 19 GeckoViewSettings: "resource://gre/modules/GeckoViewSettings.sys.mjs", 20 GeckoViewUtils: "resource://gre/modules/GeckoViewUtils.sys.mjs", 21 InitializationTracker: "resource://gre/modules/GeckoViewTelemetry.sys.mjs", 22 RemoteSecuritySettings: 23 "resource://gre/modules/psm/RemoteSecuritySettings.sys.mjs", 24 RemoteSettings: "resource://services-settings/remote-settings.sys.mjs", 25 SafeBrowsing: "resource://gre/modules/SafeBrowsing.sys.mjs", 26 CaptchaDetectionPingUtils: 27 "resource://gre/modules/CaptchaDetectionPingUtils.sys.mjs", 28 }); 29 30 ChromeUtils.defineLazyGetter(this, "WindowEventDispatcher", () => 31 EventDispatcher.for(window) 32 ); 33 34 XPCOMUtils.defineLazyScriptGetter( 35 this, 36 "PrintUtils", 37 "chrome://global/content/printUtils.js" 38 ); 39 40 // This file assumes `warn` and `debug` are imported into scope 41 // by the child scripts. 42 /* global debug, warn */ 43 44 /** 45 * ModuleManager creates and manages GeckoView modules. Each GeckoView module 46 * normally consists of a JSM module file with an optional content module file. 47 * The module file contains a class that extends GeckoViewModule, and the 48 * content module file contains a class that extends GeckoViewChildModule. A 49 * module usually pairs with a particular GeckoSessionHandler or delegate on the 50 * Java side, and automatically receives module lifetime events such as 51 * initialization, change in enabled state, and change in settings. 52 */ 53 var ModuleManager = { 54 get _initData() { 55 return window.arguments[0].QueryInterface(Ci.nsIGeckoViewView).initData; 56 }, 57 58 init(aBrowser, aModules) { 59 const initData = this._initData; 60 this._browser = aBrowser; 61 this._settings = initData.settings; 62 this._frozenSettings = Object.freeze(Object.assign({}, this._settings)); 63 64 const self = this; 65 this._modules = new Map( 66 (function* () { 67 for (const module of aModules) { 68 yield [ 69 module.name, 70 new ModuleInfo({ 71 enabled: !!initData.modules[module.name], 72 manager: self, 73 ...module, 74 }), 75 ]; 76 } 77 })() 78 ); 79 80 window.document.documentElement.appendChild(aBrowser); 81 82 // By default all layers are discarded when a browser is set to inactive. 83 // GeckoView by default sets browsers to inactive every time they're not 84 // visible. To avoid flickering when changing tabs, we preserve layers for 85 // all loaded tabs. 86 aBrowser.preserveLayers(true); 87 // GeckoView browsers start off as active (for now at least). 88 // See bug 1815015 for an attempt at making them start off inactive. 89 aBrowser.docShellIsActive = true; 90 91 WindowEventDispatcher.registerListener(this, [ 92 "GeckoView:UpdateModuleState", 93 "GeckoView:UpdateInitData", 94 "GeckoView:UpdateSettings", 95 ]); 96 97 this.messageManager.addMessageListener( 98 "GeckoView:ContentModuleLoaded", 99 this 100 ); 101 102 this._moduleByActorName = new Map(); 103 this.forEach(module => { 104 module.onInit(); 105 module.loadInitFrameScript(); 106 for (const actorName of module.actorNames) { 107 this._moduleByActorName[actorName] = module; 108 } 109 }); 110 111 window.addEventListener("unload", () => { 112 this.forEach(module => { 113 module.enabled = false; 114 module.onDestroy(); 115 }); 116 117 this._modules.clear(); 118 }); 119 }, 120 121 onPrintWindow(aParams) { 122 if (!aParams.openWindowInfo.isForWindowDotPrint) { 123 return PrintUtils.handleStaticCloneCreatedForPrint( 124 aParams.openWindowInfo 125 ); 126 } 127 const printActor = this.window.moduleManager.getActor( 128 "GeckoViewPrintDelegate" 129 ); 130 // Prevents continually making new static browsers 131 if (printActor.browserStaticClone != null) { 132 throw new Error("A prior window.print is still in progress."); 133 } 134 const staticBrowser = PrintUtils.createParentBrowserForStaticClone( 135 aParams.openWindowInfo.parent, 136 aParams.openWindowInfo 137 ); 138 printActor.browserStaticClone = staticBrowser; 139 printActor.printRequest(); 140 return staticBrowser; 141 }, 142 143 get window() { 144 return window; 145 }, 146 147 get browser() { 148 return this._browser; 149 }, 150 151 get messageManager() { 152 return this._browser.messageManager; 153 }, 154 155 get eventDispatcher() { 156 return WindowEventDispatcher; 157 }, 158 159 get settings() { 160 return this._frozenSettings; 161 }, 162 163 forEach(aCallback) { 164 this._modules.forEach(aCallback, this); 165 }, 166 167 getActor(aActorName) { 168 return this.browser.browsingContext.currentWindowGlobal?.getActor( 169 aActorName 170 ); 171 }, 172 173 // Ensures that session history has been flushed before changing remoteness 174 async prepareToChangeRemoteness() { 175 // Session state like history is maintained at the process level so we need 176 // to collect it and restore it in the other process when switching. 177 // TODO: This should go away when we migrate the history to the main 178 // process Bug 1507287. 179 const { history } = await this.getActor("GeckoViewContent").collectState(); 180 181 // Ignore scroll and form data since we're navigating away from this page 182 // anyway 183 this.sessionState = { history }; 184 }, 185 186 willChangeBrowserRemoteness() { 187 debug`WillChangeBrowserRemoteness`; 188 189 // Now we're switching the remoteness. 190 this.disabledModules = []; 191 this.forEach(module => { 192 if (module.enabled && module.disableOnProcessSwitch) { 193 module.enabled = false; 194 this.disabledModules.push(module); 195 } 196 }); 197 198 this.forEach(module => { 199 module.onDestroyBrowser(); 200 }); 201 }, 202 203 didChangeBrowserRemoteness() { 204 debug`DidChangeBrowserRemoteness`; 205 206 this.forEach(module => { 207 if (module.impl) { 208 module.impl.onInitBrowser(); 209 } 210 }); 211 212 this.messageManager.addMessageListener( 213 "GeckoView:ContentModuleLoaded", 214 this 215 ); 216 217 this.forEach(module => { 218 // We're attaching a new browser so we have to reload the frame scripts 219 module.loadInitFrameScript(); 220 }); 221 222 this.disabledModules.forEach(module => { 223 module.enabled = true; 224 }); 225 this.disabledModules = null; 226 }, 227 228 afterBrowserRemotenessChange(aSwitchId) { 229 const { sessionState } = this; 230 this.sessionState = null; 231 232 sessionState.switchId = aSwitchId; 233 234 this.getActor("GeckoViewContent").restoreState(sessionState); 235 this.browser.focus(); 236 237 // Load was handled 238 return true; 239 }, 240 241 _updateSettings(aSettings) { 242 Object.assign(this._settings, aSettings); 243 this._frozenSettings = Object.freeze(Object.assign({}, this._settings)); 244 245 const windowType = aSettings.isExtensionPopup 246 ? "navigator:popup" 247 : "navigator:geckoview"; 248 window.document.documentElement.setAttribute("windowtype", windowType); 249 250 this.forEach(module => { 251 if (module.impl) { 252 module.impl.onSettingsUpdate(); 253 } 254 }); 255 }, 256 257 onMessageFromActor(aActorName, aMessage) { 258 this._moduleByActorName[aActorName].receiveMessage(aMessage); 259 }, 260 261 onEvent(aEvent, aData) { 262 debug`onEvent ${aEvent} ${aData}`; 263 switch (aEvent) { 264 case "GeckoView:UpdateModuleState": { 265 const module = this._modules.get(aData.module); 266 if (module) { 267 module.enabled = aData.enabled; 268 } 269 break; 270 } 271 272 case "GeckoView:UpdateInitData": { 273 // Replace all settings during a transfer. 274 const initData = this._initData; 275 this._updateSettings(initData.settings); 276 277 // Update module enabled states. 278 for (const name in initData.modules) { 279 const module = this._modules.get(name); 280 if (module) { 281 module.enabled = initData.modules[name]; 282 } 283 } 284 285 // Notify child of the transfer. 286 this._browser.messageManager.sendAsyncMessage(aEvent); 287 break; 288 } 289 290 case "GeckoView:UpdateSettings": { 291 this._updateSettings(aData); 292 break; 293 } 294 } 295 }, 296 297 receiveMessage(aMsg) { 298 debug`receiveMessage ${aMsg.name} ${aMsg.data}`; 299 switch (aMsg.name) { 300 case "GeckoView:ContentModuleLoaded": { 301 const module = this._modules.get(aMsg.data.module); 302 if (module) { 303 module.onContentModuleLoaded(); 304 } 305 break; 306 } 307 } 308 }, 309 }; 310 311 /** 312 * ModuleInfo is the structure used by ModuleManager to represent individual 313 * modules. It is responsible for loading the module JSM file if necessary, 314 * and it acts as the intermediary between ModuleManager and the module 315 * object that extends GeckoViewModule. 316 */ 317 class ModuleInfo { 318 /** 319 * Create a ModuleInfo instance. See _loadPhase for phase object description. 320 * 321 * @param manager the ModuleManager instance. 322 * @param name Name of the module. 323 * @param enabled Enabled state of the module at startup. 324 * @param onInit Phase object for the init phase, when the window is created. 325 * @param onEnable Phase object for the enable phase, when the module is first 326 * enabled by setting a delegate in Java. 327 */ 328 constructor({ manager, name, enabled, onInit, onEnable }) { 329 this._manager = manager; 330 this._name = name; 331 332 // We don't support having more than one main process script, so let's 333 // check that we're not accidentally defining two. We could support this if 334 // needed by making _impl an array for each phase impl. 335 if (onInit?.resource !== undefined && onEnable?.resource !== undefined) { 336 throw new Error( 337 "Only one main process script is allowed for each module." 338 ); 339 } 340 341 this._impl = null; 342 this._contentModuleLoaded = false; 343 this._enabled = false; 344 // Only enable once we performed initialization. 345 this._enabledOnInit = enabled; 346 347 // For init, load resource _before_ initializing browser to support the 348 // onInitBrowser() override. However, load content module after initializing 349 // browser, because we don't have a message manager before then. 350 this._loadResource(onInit); 351 this._loadActors(onInit); 352 if (this._enabledOnInit) { 353 this._loadActors(onEnable); 354 } 355 356 this._onInitPhase = onInit; 357 this._onEnablePhase = onEnable; 358 359 const actorNames = []; 360 if (this._onInitPhase?.actors) { 361 actorNames.push(Object.keys(this._onInitPhase.actors)); 362 } 363 if (this._onEnablePhase?.actors) { 364 actorNames.push(Object.keys(this._onEnablePhase.actors)); 365 } 366 this._actorNames = Object.freeze(actorNames); 367 } 368 369 get actorNames() { 370 return this._actorNames; 371 } 372 373 onInit() { 374 if (this._impl) { 375 this._impl.onInit(); 376 this._impl.onSettingsUpdate(); 377 } 378 379 this.enabled = this._enabledOnInit; 380 } 381 382 /** 383 * Loads the onInit frame script 384 */ 385 loadInitFrameScript() { 386 this._loadFrameScript(this._onInitPhase); 387 } 388 389 onDestroy() { 390 if (this._impl) { 391 this._impl.onDestroy(); 392 } 393 } 394 395 /** 396 * Called before the browser is removed 397 */ 398 onDestroyBrowser() { 399 if (this._impl) { 400 this._impl.onDestroyBrowser(); 401 } 402 this._contentModuleLoaded = false; 403 } 404 405 _loadActors(aPhase) { 406 if (!aPhase || !aPhase.actors) { 407 return; 408 } 409 410 GeckoViewActorManager.addJSWindowActors(aPhase.actors); 411 } 412 413 /** 414 * Load resource according to a phase object that contains possible keys, 415 * 416 * "resource": specify the JSM resource to load for this module. 417 * "frameScript": specify a content JS frame script to load for this module. 418 */ 419 _loadResource(aPhase) { 420 if (!aPhase || !aPhase.resource || this._impl) { 421 return; 422 } 423 424 const exports = ChromeUtils.importESModule(aPhase.resource); 425 this._impl = new exports[this._name](this); 426 } 427 428 /** 429 * Load frameScript according to a phase object that contains possible keys, 430 * 431 * "frameScript": specify a content JS frame script to load for this module. 432 */ 433 _loadFrameScript(aPhase) { 434 if (!aPhase || !aPhase.frameScript || this._contentModuleLoaded) { 435 return; 436 } 437 438 if (this._impl) { 439 this._impl.onLoadContentModule(); 440 } 441 this._manager.messageManager.loadFrameScript(aPhase.frameScript, true); 442 this._contentModuleLoaded = true; 443 } 444 445 get manager() { 446 return this._manager; 447 } 448 449 get disableOnProcessSwitch() { 450 // Only disable while process switching if it has a frameScript 451 return ( 452 !!this._onInitPhase?.frameScript || !!this._onEnablePhase?.frameScript 453 ); 454 } 455 456 get name() { 457 return this._name; 458 } 459 460 get impl() { 461 return this._impl; 462 } 463 464 get enabled() { 465 return this._enabled; 466 } 467 468 set enabled(aEnabled) { 469 if (aEnabled === this._enabled) { 470 return; 471 } 472 473 if (!aEnabled && this._impl) { 474 this._impl.onDisable(); 475 } 476 477 this._enabled = aEnabled; 478 479 if (aEnabled) { 480 this._loadResource(this._onEnablePhase); 481 this._loadFrameScript(this._onEnablePhase); 482 this._loadActors(this._onEnablePhase); 483 if (this._impl) { 484 this._impl.onEnable(); 485 this._impl.onSettingsUpdate(); 486 } 487 } 488 489 this._updateContentModuleState(); 490 } 491 492 receiveMessage(aMessage) { 493 if (!this._impl) { 494 throw new Error(`No impl for message: ${aMessage.name}.`); 495 } 496 497 try { 498 this._impl.receiveMessage(aMessage); 499 } catch (error) { 500 warn`this._impl.receiveMessage failed ${aMessage.name}`; 501 throw error; 502 } 503 } 504 505 onContentModuleLoaded() { 506 this._updateContentModuleState(); 507 508 if (this._impl) { 509 this._impl.onContentModuleLoaded(); 510 } 511 } 512 513 _updateContentModuleState() { 514 this._manager.messageManager.sendAsyncMessage( 515 "GeckoView:UpdateModuleState", 516 { 517 module: this._name, 518 enabled: this.enabled, 519 } 520 ); 521 } 522 } 523 524 function createBrowser() { 525 const browser = (window.browser = document.createXULElement("browser")); 526 // Identify this `<browser>` element uniquely to Marionette, devtools, etc. 527 // Use the JSM global to create the permanentKey, so that if the 528 // permanentKey is held by something after this window closes, it 529 // doesn't keep the window alive. See also Bug 1501789. 530 browser.permanentKey = new (Cu.getGlobalForObject(Services).Object)(); 531 532 browser.setAttribute("nodefaultsrc", "true"); 533 browser.setAttribute("type", "content"); 534 browser.setAttribute("primary", "true"); 535 browser.setAttribute("flex", "1"); 536 browser.setAttribute("maychangeremoteness", "true"); 537 browser.setAttribute("remote", "true"); 538 browser.setAttribute("remoteType", E10SUtils.DEFAULT_REMOTE_TYPE); 539 browser.setAttribute("messagemanagergroup", "browsers"); 540 browser.setAttribute("manualactiveness", "true"); 541 542 // This is only needed for mochitests, so that they honor the 543 // prefers-color-scheme.content-override pref. GeckoView doesn't set this 544 // pref to anything other than the default value otherwise. 545 browser.style.colorScheme = "env(-moz-content-preferred-color-scheme)"; 546 547 return browser; 548 } 549 550 function InitLater(fn, object, name) { 551 return DelayedInit.schedule(fn, object, name, 15000 /* 15s max wait */); 552 } 553 554 function startup() { 555 GeckoViewUtils.initLogging("XUL", window); 556 557 const browser = createBrowser(); 558 ModuleManager.init(browser, [ 559 { 560 name: "GeckoViewContent", 561 onInit: { 562 resource: "resource://gre/modules/GeckoViewContent.sys.mjs", 563 actors: { 564 GeckoViewContent: { 565 parent: { 566 esModuleURI: "resource:///actors/GeckoViewContentParent.sys.mjs", 567 }, 568 child: { 569 esModuleURI: "resource:///actors/GeckoViewContentChild.sys.mjs", 570 events: { 571 mozcaretstatechanged: { capture: true, mozSystemGroup: true }, 572 pageshow: { mozSystemGroup: true }, 573 }, 574 }, 575 allFrames: true, 576 messageManagerGroups: ["browsers"], 577 }, 578 }, 579 }, 580 onEnable: { 581 actors: { 582 ContentDelegate: { 583 parent: { 584 esModuleURI: "resource:///actors/ContentDelegateParent.sys.mjs", 585 }, 586 child: { 587 esModuleURI: "resource:///actors/ContentDelegateChild.sys.mjs", 588 events: { 589 DOMContentLoaded: {}, 590 DOMMetaViewportFitChanged: {}, 591 "MozDOMFullscreen:Entered": {}, 592 "MozDOMFullscreen:Exit": {}, 593 "MozDOMFullscreen:Exited": {}, 594 "MozDOMFullscreen:Request": {}, 595 MozFirstContentfulPaint: {}, 596 MozPaintStatusReset: {}, 597 contextmenu: {}, 598 }, 599 }, 600 allFrames: true, 601 messageManagerGroups: ["browsers"], 602 }, 603 }, 604 }, 605 }, 606 { 607 name: "GeckoViewNavigation", 608 onInit: { 609 resource: "resource://gre/modules/GeckoViewNavigation.sys.mjs", 610 }, 611 }, 612 { 613 name: "GeckoViewProcessHangMonitor", 614 onInit: { 615 resource: "resource://gre/modules/GeckoViewProcessHangMonitor.sys.mjs", 616 }, 617 }, 618 { 619 name: "GeckoViewProgress", 620 onEnable: { 621 resource: "resource://gre/modules/GeckoViewProgress.sys.mjs", 622 actors: { 623 ProgressDelegate: { 624 parent: { 625 esModuleURI: "resource:///actors/ProgressDelegateParent.sys.mjs", 626 }, 627 child: { 628 esModuleURI: "resource:///actors/ProgressDelegateChild.sys.mjs", 629 events: { 630 MozAfterPaint: { capture: false, mozSystemGroup: true }, 631 DOMContentLoaded: { capture: false, mozSystemGroup: true }, 632 pageshow: { capture: false, mozSystemGroup: true }, 633 }, 634 }, 635 messageManagerGroups: ["browsers"], 636 }, 637 }, 638 }, 639 }, 640 { 641 name: "GeckoViewScroll", 642 onEnable: { 643 actors: { 644 ScrollDelegate: { 645 parent: { 646 esModuleURI: "resource:///actors/ScrollDelegateParent.sys.mjs", 647 }, 648 child: { 649 esModuleURI: "resource:///actors/ScrollDelegateChild.sys.mjs", 650 events: { 651 mozvisualscroll: { mozSystemGroup: true }, 652 }, 653 }, 654 messageManagerGroups: ["browsers"], 655 }, 656 }, 657 }, 658 }, 659 { 660 name: "GeckoViewSelectionAction", 661 onEnable: { 662 resource: "resource://gre/modules/GeckoViewSelectionAction.sys.mjs", 663 actors: { 664 SelectionActionDelegate: { 665 parent: { 666 esModuleURI: 667 "resource:///actors/SelectionActionDelegateParent.sys.mjs", 668 }, 669 child: { 670 esModuleURI: 671 "resource:///actors/SelectionActionDelegateChild.sys.mjs", 672 events: { 673 mozcaretstatechanged: { mozSystemGroup: true }, 674 pagehide: { capture: true, mozSystemGroup: true }, 675 deactivate: { mozSystemGroup: true }, 676 }, 677 }, 678 allFrames: true, 679 messageManagerGroups: ["browsers"], 680 }, 681 }, 682 }, 683 }, 684 { 685 name: "GeckoViewSettings", 686 onInit: { 687 resource: "resource://gre/modules/GeckoViewSettings.sys.mjs", 688 actors: { 689 GeckoViewSettings: { 690 child: { 691 esModuleURI: "resource:///actors/GeckoViewSettingsChild.sys.mjs", 692 }, 693 }, 694 }, 695 }, 696 }, 697 { 698 name: "GeckoViewTab", 699 onInit: { 700 resource: "resource://gre/modules/GeckoViewTab.sys.mjs", 701 }, 702 }, 703 { 704 name: "GeckoViewContentBlocking", 705 onInit: { 706 resource: "resource://gre/modules/GeckoViewContentBlocking.sys.mjs", 707 }, 708 }, 709 { 710 name: "SessionStateAggregator", 711 onInit: { 712 frameScript: "chrome://geckoview/content/SessionStateAggregator.js", 713 }, 714 }, 715 { 716 name: "GeckoViewAutofill", 717 onInit: { 718 actors: { 719 GeckoViewAutoFill: { 720 parent: { 721 esModuleURI: "resource:///actors/GeckoViewAutoFillParent.sys.mjs", 722 }, 723 child: { 724 esModuleURI: "resource:///actors/GeckoViewAutoFillChild.sys.mjs", 725 events: { 726 DOMFormHasPassword: { 727 mozSystemGroup: true, 728 capture: false, 729 }, 730 DOMInputPasswordAdded: { 731 mozSystemGroup: true, 732 capture: false, 733 }, 734 pagehide: { 735 mozSystemGroup: true, 736 capture: false, 737 }, 738 pageshow: { 739 mozSystemGroup: true, 740 capture: false, 741 }, 742 focusin: { 743 mozSystemGroup: true, 744 capture: false, 745 }, 746 focusout: { 747 mozSystemGroup: true, 748 capture: false, 749 }, 750 "PasswordManager:ShowDoorhanger": {}, 751 }, 752 }, 753 allFrames: true, 754 messageManagerGroups: ["browsers"], 755 }, 756 }, 757 }, 758 }, 759 { 760 name: "GeckoViewMediaControl", 761 onEnable: { 762 resource: "resource://gre/modules/GeckoViewMediaControl.sys.mjs", 763 actors: { 764 MediaControlDelegate: { 765 parent: { 766 esModuleURI: 767 "resource:///actors/MediaControlDelegateParent.sys.mjs", 768 }, 769 child: { 770 esModuleURI: 771 "resource:///actors/MediaControlDelegateChild.sys.mjs", 772 events: { 773 "MozDOMFullscreen:Entered": {}, 774 "MozDOMFullscreen:Exited": {}, 775 }, 776 }, 777 allFrames: true, 778 messageManagerGroups: ["browsers"], 779 }, 780 }, 781 }, 782 }, 783 { 784 name: "GeckoViewAutocomplete", 785 onInit: { 786 actors: { 787 FormAutofill: { 788 parent: { 789 esModuleURI: "resource://autofill/FormAutofillParent.sys.mjs", 790 }, 791 child: { 792 esModuleURI: "resource://autofill/FormAutofillChild.sys.mjs", 793 events: { 794 focusin: {}, 795 "form-changed": {}, 796 "form-submission-detected": {}, 797 }, 798 }, 799 allFrames: true, 800 messageManagerGroups: ["browsers"], 801 }, 802 }, 803 }, 804 }, 805 { 806 name: "GeckoViewPrompter", 807 onInit: { 808 actors: { 809 GeckoViewPrompter: { 810 parent: { 811 esModuleURI: "resource:///actors/GeckoViewPrompterParent.sys.mjs", 812 }, 813 child: { 814 esModuleURI: "resource:///actors/GeckoViewPrompterChild.sys.mjs", 815 }, 816 allFrames: true, 817 includeChrome: true, 818 }, 819 }, 820 }, 821 }, 822 { 823 name: "GeckoViewPrintDelegate", 824 onInit: { 825 actors: { 826 GeckoViewPrintDelegate: { 827 parent: { 828 esModuleURI: 829 "resource:///actors/GeckoViewPrintDelegateParent.sys.mjs", 830 }, 831 child: { 832 esModuleURI: 833 "resource:///actors/GeckoViewPrintDelegateChild.sys.mjs", 834 }, 835 allFrames: true, 836 }, 837 }, 838 }, 839 }, 840 { 841 name: "GeckoViewExperimentDelegate", 842 onInit: { 843 actors: { 844 GeckoViewExperimentDelegate: { 845 parent: { 846 esModuleURI: 847 "resource:///actors/GeckoViewExperimentDelegateParent.sys.mjs", 848 }, 849 allFrames: true, 850 }, 851 }, 852 }, 853 }, 854 { 855 name: "GeckoViewTranslations", 856 onInit: { 857 resource: "resource://gre/modules/GeckoViewTranslations.sys.mjs", 858 }, 859 }, 860 ]); 861 862 if (!Services.appinfo.sessionHistoryInParent) { 863 browser.prepareToChangeRemoteness = () => 864 ModuleManager.prepareToChangeRemoteness(); 865 browser.afterChangeRemoteness = switchId => 866 ModuleManager.afterBrowserRemotenessChange(switchId); 867 } 868 869 browser.addEventListener("WillChangeBrowserRemoteness", () => 870 ModuleManager.willChangeBrowserRemoteness() 871 ); 872 873 browser.addEventListener("DidChangeBrowserRemoteness", () => 874 ModuleManager.didChangeBrowserRemoteness() 875 ); 876 877 // Allows actors to access ModuleManager. 878 window.moduleManager = ModuleManager; 879 880 window.prompts = () => { 881 return window.ModuleManager.getActor("GeckoViewPrompter").getPrompts(); 882 }; 883 884 Services.tm.dispatchToMainThread(() => { 885 // This should always be the first thing we do here - any additional delayed 886 // initialisation tasks should be added between "browser-delayed-startup-finished" 887 // and "browser-idle-startup-tasks-finished". 888 889 // Bug 1496684: Various bits of platform stuff depend on this notification 890 // to learn when a browser window has finished its initial (chrome) 891 // initialisation, especially with regards to the very first window that is 892 // created. Therefore, GeckoView "windows" need to send this, too. 893 InitLater(() => 894 Services.obs.notifyObservers(window, "browser-delayed-startup-finished") 895 ); 896 897 // Let the extension code know it can start loading things that were delayed 898 // while GeckoView started up. 899 InitLater(() => { 900 Services.obs.notifyObservers(window, "extensions-late-startup"); 901 }); 902 903 InitLater(() => { 904 // TODO bug 1730026: this runs too often. It should run once. 905 RemoteSecuritySettings.init(); 906 }); 907 908 InitLater(() => { 909 // Initialize safe browsing module. This is required for content 910 // blocking features and manages blocklist downloads and updates. 911 SafeBrowsing.init(); 912 }); 913 914 InitLater(() => { 915 // It's enough to run this once to set up FOG. 916 // (See also bug 1730026.) 917 Services.fog.registerCustomPings(); 918 }); 919 920 InitLater(() => { 921 // Initialize the blocklist module. 922 // TODO bug 1730026: this runs too often. It should run once. 923 Blocklist.loadBlocklistAsync(); 924 }); 925 926 InitLater(() => { 927 // Call the init function for the CaptchaDetectionPingUtils module. 928 // This function adds pref observers that flushes the ping. It also 929 // submits the ping if it has data and has been about 24 hours since the 930 // last submission. 931 CaptchaDetectionPingUtils.init(); 932 }); 933 934 InitLater(() => { 935 RemoteSettings.pollChanges({ trigger: "timer" }); 936 }); 937 938 // This should always go last, since the idle tasks (except for the ones with 939 // timeouts) should execute in order. Note that this observer notification is 940 // not guaranteed to fire, since the window could close before we get here. 941 942 // This notification in particular signals the ScriptPreloader that we have 943 // finished startup, so it can now stop recording script usage and start 944 // updating the startup cache for faster script loading. 945 InitLater(() => 946 Services.obs.notifyObservers( 947 window, 948 "browser-idle-startup-tasks-finished" 949 ) 950 ); 951 }); 952 953 // Move focus to the content window at the end of startup, 954 // so things like text selection can work properly. 955 browser.focus(); 956 957 InitializationTracker.onInitialized(ChromeUtils.now()); 958 } 959 960 window.addEventListener("DOMContentLoaded", startup, { once: true });