tor-browser

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

GeckoViewStartup.sys.mjs (17359B)


      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 import { DelayedInit } from "resource://gre/modules/DelayedInit.sys.mjs";
      6 import { GeckoViewUtils } from "resource://gre/modules/GeckoViewUtils.sys.mjs";
      7 
      8 const lazy = {};
      9 
     10 ChromeUtils.defineESModuleGetters(lazy, {
     11  ActorManagerParent: "resource://gre/modules/ActorManagerParent.sys.mjs",
     12  DoHController: "moz-src:///toolkit/components/doh/DoHController.sys.mjs",
     13  EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
     14  PdfJs: "resource://pdf.js/PdfJs.sys.mjs",
     15  RFPHelper: "resource://gre/modules/RFPHelper.sys.mjs",
     16  TorAndroidIntegration: "resource://gre/modules/TorAndroidIntegration.sys.mjs",
     17  TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.sys.mjs",
     18 });
     19 
     20 const { debug, warn } = GeckoViewUtils.initLogging("Startup");
     21 
     22 function InitLater(fn, object, name) {
     23  return DelayedInit.schedule(fn, object, name, 15000 /* 15s max wait */);
     24 }
     25 
     26 const JSPROCESSACTORS = {
     27  GeckoViewPermissionProcess: {
     28    parent: {
     29      esModuleURI:
     30        "resource:///actors/GeckoViewPermissionProcessParent.sys.mjs",
     31    },
     32    child: {
     33      esModuleURI: "resource:///actors/GeckoViewPermissionProcessChild.sys.mjs",
     34      observers: [
     35        "getUserMedia:ask-device-permission",
     36        "getUserMedia:request",
     37        "recording-device-events",
     38        "PeerConnection:request",
     39      ],
     40    },
     41  },
     42 };
     43 
     44 const JSWINDOWACTORS = {
     45  LoadURIDelegate: {
     46    parent: {
     47      esModuleURI: "resource:///actors/LoadURIDelegateParent.sys.mjs",
     48    },
     49    child: {
     50      esModuleURI: "resource:///actors/LoadURIDelegateChild.sys.mjs",
     51    },
     52    messageManagerGroups: ["browsers"],
     53  },
     54  GeckoViewPermission: {
     55    parent: {
     56      esModuleURI: "resource:///actors/GeckoViewPermissionParent.sys.mjs",
     57    },
     58    child: {
     59      esModuleURI: "resource:///actors/GeckoViewPermissionChild.sys.mjs",
     60    },
     61    allFrames: true,
     62    includeChrome: true,
     63  },
     64  GeckoViewPrompt: {
     65    child: {
     66      esModuleURI: "resource:///actors/GeckoViewPromptChild.sys.mjs",
     67      events: {
     68        click: { capture: false, mozSystemGroup: true },
     69        contextmenu: { capture: false, mozSystemGroup: true },
     70        mozshowdropdown: {},
     71        "mozshowdropdown-sourcetouch": {},
     72        MozOpenDateTimePicker: {},
     73        DOMPopupBlocked: { capture: false, mozSystemGroup: true },
     74        DOMRedirectBlocked: { capture: false, mozSystemGroup: true },
     75      },
     76    },
     77    allFrames: true,
     78    messageManagerGroups: ["browsers"],
     79  },
     80  GeckoViewFormValidation: {
     81    child: {
     82      esModuleURI: "resource:///actors/GeckoViewFormValidationChild.sys.mjs",
     83      events: {
     84        MozInvalidForm: {},
     85      },
     86    },
     87    allFrames: true,
     88    messageManagerGroups: ["browsers"],
     89  },
     90  GeckoViewPdfjs: {
     91    parent: {
     92      esModuleURI: "resource://pdf.js/GeckoViewPdfjsParent.sys.mjs",
     93    },
     94    child: {
     95      esModuleURI: "resource://pdf.js/GeckoViewPdfjsChild.sys.mjs",
     96    },
     97    allFrames: true,
     98  },
     99 };
    100 
    101 export class GeckoViewStartup {
    102  /* ----------  nsIObserver  ---------- */
    103  observe(aSubject, aTopic) {
    104    debug`observe: ${aTopic}`;
    105    switch (aTopic) {
    106      case "content-process-ready-for-script":
    107      case "app-startup": {
    108        GeckoViewUtils.addLazyGetter(this, "GeckoViewConsole", {
    109          module: "resource://gre/modules/GeckoViewConsole.sys.mjs",
    110        });
    111 
    112        GeckoViewUtils.addLazyGetter(this, "GeckoViewStorageController", {
    113          module: "resource://gre/modules/GeckoViewStorageController.sys.mjs",
    114          ged: [
    115            "GeckoView:ClearData",
    116            "GeckoView:ClearSessionContextData",
    117            "GeckoView:ClearHostData",
    118            "GeckoView:ClearBaseDomainData",
    119            "GeckoView:GetAllPermissions",
    120            "GeckoView:GetPermissionsByURI",
    121            "GeckoView:SetPermission",
    122            "GeckoView:SetPermissionByURI",
    123            "GeckoView:GetCookieBannerModeForDomain",
    124            "GeckoView:SetCookieBannerModeForDomain",
    125            "GeckoView:RemoveCookieBannerModeForDomain",
    126          ],
    127        });
    128 
    129        GeckoViewUtils.addLazyGetter(this, "GeckoViewPushController", {
    130          module: "resource://gre/modules/GeckoViewPushController.sys.mjs",
    131          ged: ["GeckoView:PushEvent", "GeckoView:PushSubscriptionChanged"],
    132        });
    133 
    134        GeckoViewUtils.addLazyPrefObserver(
    135          {
    136            name: "geckoview.console.enabled",
    137            default: false,
    138          },
    139          {
    140            handler: _ => this.GeckoViewConsole,
    141          }
    142        );
    143 
    144        // Parent process only
    145        if (
    146          Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT
    147        ) {
    148          lazy.ActorManagerParent.addJSWindowActors(JSWINDOWACTORS);
    149          lazy.ActorManagerParent.addJSProcessActors(JSPROCESSACTORS);
    150 
    151          if (Services.appinfo.sessionHistoryInParent) {
    152            GeckoViewUtils.addLazyGetter(this, "GeckoViewSessionStore", {
    153              module: "resource://gre/modules/GeckoViewSessionStore.sys.mjs",
    154              observers: [
    155                "browsing-context-did-set-embedder",
    156                "browsing-context-discarded",
    157              ],
    158            });
    159          }
    160 
    161          GeckoViewUtils.addLazyGetter(this, "GeckoViewWebExtension", {
    162            module: "resource://gre/modules/GeckoViewWebExtension.sys.mjs",
    163            ged: [
    164              "GeckoView:ActionDelegate:Attached",
    165              "GeckoView:BrowserAction:Click",
    166              "GeckoView:PageAction:Click",
    167              "GeckoView:RegisterWebExtension",
    168              "GeckoView:UnregisterWebExtension",
    169              "GeckoView:WebExtension:CancelInstall",
    170              "GeckoView:WebExtension:Disable",
    171              "GeckoView:WebExtension:Enable",
    172              "GeckoView:WebExtension:EnsureBuiltIn",
    173              "GeckoView:WebExtension:Get",
    174              "GeckoView:WebExtension:Install",
    175              "GeckoView:WebExtension:InstallBuiltIn",
    176              "GeckoView:WebExtension:List",
    177              "GeckoView:WebExtension:PortDisconnect",
    178              "GeckoView:WebExtension:PortMessageFromApp",
    179              "GeckoView:WebExtension:SetPBAllowed",
    180              "GeckoView:WebExtension:AddOptionalPermissions",
    181              "GeckoView:WebExtension:RemoveOptionalPermissions",
    182              "GeckoView:WebExtension:Uninstall",
    183              "GeckoView:WebExtension:Update",
    184              "GeckoView:WebExtension:EnableProcessSpawning",
    185              "GeckoView:WebExtension:DisableProcessSpawning",
    186            ],
    187            observers: [
    188              "devtools-installed-addon",
    189              "testing-installed-addon",
    190              "testing-uninstalled-addon",
    191            ],
    192          });
    193 
    194          GeckoViewUtils.addLazyGetter(this, "ChildCrashHandler", {
    195            module: "resource://gre/modules/ChildCrashHandler.sys.mjs",
    196            observers: [
    197              "compositor:process-aborted",
    198              "ipc:content-created",
    199              "ipc:content-shutdown",
    200              "process-type-set",
    201            ],
    202          });
    203 
    204          lazy.EventDispatcher.instance.registerListener(this, [
    205            "GeckoView:StorageDelegate:Attached",
    206            "GeckoView:CrashPullController.Delegate:Attached",
    207          ]);
    208        }
    209 
    210        GeckoViewUtils.addLazyGetter(this, "GeckoViewTranslationsSettings", {
    211          module: "resource://gre/modules/GeckoViewTranslations.sys.mjs",
    212          ged: [
    213            "GeckoView:Translations:IsTranslationEngineSupported",
    214            "GeckoView:Translations:PreferredLanguages",
    215            "GeckoView:Translations:ManageModel",
    216            "GeckoView:Translations:TranslationInformation",
    217            "GeckoView:Translations:ModelInformation",
    218            "GeckoView:Translations:GetLanguageSetting",
    219            "GeckoView:Translations:GetLanguageSettings",
    220            "GeckoView:Translations:SetLanguageSettings",
    221            "GeckoView:Translations:GetNeverTranslateSpecifiedSites",
    222            "GeckoView:Translations:SetNeverTranslateSpecifiedSite",
    223            "GeckoView:Translations:GetTranslateDownloadSize",
    224          ],
    225        });
    226 
    227        GeckoViewUtils.addLazyGetter(this, "GeckoViewAutofillRuntime", {
    228          module: "resource://gre/modules/GeckoViewAutofill.sys.mjs",
    229          ged: ["GeckoView:Autofill:GetAddressStructure"],
    230        });
    231 
    232        GeckoViewUtils.addLazyGetter(this, "GeckoViewPreferences", {
    233          module: "resource://gre/modules/GeckoViewPreferences.sys.mjs",
    234          ged: [
    235            "GeckoView:Preferences:GetPref",
    236            "GeckoView:Preferences:SetPref",
    237            "GeckoView:Preferences:ClearPref",
    238            "GeckoView:Preferences:RegisterObserver",
    239            "GeckoView:Preferences:UnregisterObserver",
    240          ],
    241        });
    242 
    243        break;
    244      }
    245 
    246      case "profile-after-change": {
    247        GeckoViewUtils.addLazyGetter(this, "GeckoViewRemoteDebugger", {
    248          module: "resource://gre/modules/GeckoViewRemoteDebugger.sys.mjs",
    249          init: gvrd => gvrd.onInit(),
    250        });
    251 
    252        GeckoViewUtils.addLazyPrefObserver(
    253          {
    254            name: "devtools.debugger.remote-enabled",
    255            default: false,
    256          },
    257          {
    258            handler: _ => this.GeckoViewRemoteDebugger,
    259          }
    260        );
    261 
    262        GeckoViewUtils.addLazyGetter(this, "DownloadTracker", {
    263          module: "resource://gre/modules/GeckoViewWebExtension.sys.mjs",
    264          ged: ["GeckoView:WebExtension:DownloadChanged"],
    265        });
    266 
    267        // Listen for global EventDispatcher messages
    268        lazy.EventDispatcher.instance.registerListener(this, [
    269          "GeckoView:ResetUserPrefs",
    270          "GeckoView:SetDefaultPrefs",
    271          "GeckoView:SetLocale",
    272          "GeckoView:InitialForeground",
    273        ]);
    274 
    275        this.#migratePreferences();
    276 
    277        lazy.TorAndroidIntegration.init();
    278        lazy.TorDomainIsolator.init();
    279 
    280        Services.obs.addObserver(this, "browser-idle-startup-tasks-finished");
    281        Services.obs.addObserver(this, "handlersvc-store-initialized");
    282 
    283        Services.obs.notifyObservers(null, "geckoview-startup-complete");
    284        break;
    285      }
    286      case "browser-idle-startup-tasks-finished": {
    287        // TODO bug 1730026: when an alternative is introduced that runs once,
    288        // replace this observer topic with that alternative.
    289        // This only needs to happen once during startup.
    290        Services.obs.removeObserver(this, aTopic);
    291        // Notify the start up crash tracker that the browser has successfully
    292        // started up so the startup cache isn't rebuilt on next startup.
    293        Services.startup.trackStartupCrashEnd();
    294        break;
    295      }
    296      case "handlersvc-store-initialized": {
    297        // Initialize PdfJs when running in-process and remote. This only
    298        // happens once since PdfJs registers global hooks. If the PdfJs
    299        // extension is installed the init method below will be overridden
    300        // leaving initialization to the extension.
    301        // parent only: configure default prefs, set up pref observers, register
    302        // pdf content handler, and initializes parent side message manager
    303        // shim for privileged api access.
    304        try {
    305          lazy.PdfJs.init(this._isNewProfile);
    306        } catch {}
    307        break;
    308      }
    309    }
    310  }
    311 
    312  onEvent(aEvent, aData) {
    313    debug`onEvent ${aEvent}`;
    314 
    315    switch (aEvent) {
    316      case "GeckoView:InitialForeground": {
    317        // ExtensionProcessCrashObserver observes this topic to determine when
    318        // the app goes into the foreground for the first time. This could be useful
    319        // when the app is initially created in the background because, in this case,
    320        // the "application-foreground" topic isn't notified when the application is
    321        // moved into the foreground later. That is because "application-foreground"
    322        // is only going to be notified when the application was first paused.
    323        Services.obs.notifyObservers(null, "geckoview-initial-foreground");
    324 
    325        // This pref is set when during GeckoEngine initialization
    326        // and holds the value of FxNimbus.doh.autoselect-enabled (see nimbus.fml.yaml)
    327        // We check it here insead of using GeckoViewUtils.addLazyPrefObserver
    328        // because GeckoView:ResetUserPrefs also clears it during startup.
    329        if (
    330          Services.prefs.getBoolPref("network.android_doh.autoselect_enabled")
    331        ) {
    332          debug`init DoH controller`;
    333          lazy.DoHController.init();
    334        } else {
    335          // When the autoselect isn't enabled, these prefs should be
    336          // cleared. Otherwise doh-rollout.mode will override
    337          // network.trr.mode forever as DoHController isn't running.
    338          debug`cleanup DoH prefs`;
    339          lazy.DoHController.cleanupPrefs();
    340        }
    341 
    342        break;
    343      }
    344      case "GeckoView:ResetUserPrefs": {
    345        for (const name of aData.names) {
    346          Services.prefs.clearUserPref(name);
    347        }
    348        break;
    349      }
    350      case "GeckoView:SetDefaultPrefs": {
    351        const prefs = Services.prefs.getDefaultBranch("");
    352        for (const [name, value] of Object.entries(aData)) {
    353          try {
    354            switch (typeof value) {
    355              case "string":
    356                prefs.setStringPref(name, value);
    357                break;
    358              case "number":
    359                prefs.setIntPref(name, value);
    360                break;
    361              case "boolean":
    362                prefs.setBoolPref(name, value);
    363                break;
    364              default:
    365                throw new Error(
    366                  `Can't set ${name} to ${value}. Type ${typeof value} is not supported.`
    367                );
    368            }
    369          } catch (e) {
    370            warn`Failed to set preference ${name}: ${e}`;
    371          }
    372        }
    373        break;
    374      }
    375      case "GeckoView:SetLocale": {
    376        if (aData.requestedLocales) {
    377          Services.locale.requestedLocales = aData.requestedLocales;
    378        }
    379        lazy.RFPHelper._handleSpoofEnglishChanged();
    380        if (Services.prefs.getIntPref("privacy.spoof_english", 0) === 2) {
    381          break;
    382        }
    383        const pls = Cc["@mozilla.org/pref-localizedstring;1"].createInstance(
    384          Ci.nsIPrefLocalizedString
    385        );
    386        pls.data = aData.acceptLanguages;
    387        Services.prefs.setComplexValue(
    388          "intl.accept_languages",
    389          Ci.nsIPrefLocalizedString,
    390          pls
    391        );
    392        break;
    393      }
    394 
    395      case "GeckoView:StorageDelegate:Attached":
    396        InitLater(() => {
    397          const loginDetection = Cc[
    398            "@mozilla.org/login-detection-service;1"
    399          ].createInstance(Ci.nsILoginDetectionService);
    400          loginDetection.init();
    401        });
    402        break;
    403 
    404      case "GeckoView:CrashPullController.Delegate:Attached":
    405        if (!this.RemoteSettingsCrashPull) {
    406          GeckoViewUtils.addLazyGetter(this, "RemoteSettingsCrashPull", {
    407            module: "resource://gre/modules/RemoteSettingsCrashPull.sys.mjs",
    408          });
    409          GeckoViewUtils.addLazyGetter(this, "crashPullCallback", {
    410            module: "resource://gre/modules/ChildCrashHandler.sys.mjs",
    411          });
    412          InitLater(() =>
    413            this.RemoteSettingsCrashPull.start(this.crashPullCallback)
    414          );
    415        }
    416        break;
    417    }
    418  }
    419 
    420  /**
    421   * This is the equivalent of BrowserGlue._migrateUITBB.
    422   */
    423  #migratePreferences() {
    424    const MIGRATION_VERSION = 1;
    425    const MIGRATION_PREF = "torbrowser.migration_android.version";
    426 
    427    // We do not have a way to check for new profiles on Android.
    428    // However, the first version is harmless for new installs, so run it
    429    // anyway.
    430    const currentVersion = Services.prefs.getIntPref(MIGRATION_PREF, 0);
    431    if (currentVersion < 1) {
    432      // First implementation of the migration on Android (tor-browser#43124,
    433      // 14.0a5, September 2024).
    434      const prefToClear = [
    435        // Old torbutton preferences not used anymore.
    436        // Some of them should have never been set on Android, as on Android we
    437        // force PBM... But who knows about very old profiles.
    438        "browser.cache.disk.enable",
    439        "places.history.enabled",
    440        "security.nocertdb",
    441        "permissions.memory_only",
    442        "extensions.torbutton.loglevel",
    443        "extensions.torbutton.logmethod",
    444        "extensions.torbutton.pref_fixup_version",
    445        "extensions.torbutton.resize_new_windows",
    446        "extensions.torbutton.startup",
    447        "extensions.torlauncher.prompt_for_locale",
    448        "extensions.torlauncher.loglevel",
    449        "extensions.torlauncher.logmethod",
    450        "extensions.torlauncher.torrc_fixup_version",
    451        // tor-browser#42149: Do not change HTTPS-Only settings in the security
    452        // level.
    453        "dom.security.https_only_mode_send_http_background_request",
    454      ];
    455      for (const pref of prefToClear) {
    456        if (Services.prefs.prefHasUserValue(pref)) {
    457          Services.prefs.clearUserPref(pref);
    458        }
    459      }
    460    }
    461    Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION);
    462  }
    463 }
    464 
    465 GeckoViewStartup.prototype.classID = Components.ID(
    466  "{8e993c34-fdd6-432c-967e-f995d888777f}"
    467 );
    468 GeckoViewStartup.prototype.QueryInterface = ChromeUtils.generateQI([
    469  "nsIObserver",
    470 ]);