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