tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 });