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/. */
5 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
6 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
10 // Ignore unused lazy property for PluginManager.
11 // eslint-disable-next-line mozilla/valid-lazy
12 ChromeUtils.defineESModuleGetters(lazy, {
14 "resource://activity-stream/lib/ASRouterNewTabHook.sys.mjs",
15 ActorManagerParent: "resource://gre/modules/ActorManagerParent.sys.mjs",
16 AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
17 AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.sys.mjs",
18 AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
19 Blocklist: "resource://gre/modules/Blocklist.sys.mjs",
20 BookmarkHTMLUtils: "resource://gre/modules/BookmarkHTMLUtils.sys.mjs",
21 BookmarkJSONUtils: "resource://gre/modules/BookmarkJSONUtils.sys.mjs",
22 BrowserSearchTelemetry: "resource:///modules/BrowserSearchTelemetry.sys.mjs",
23 BuiltInThemes: "resource:///modules/BuiltInThemes.sys.mjs",
24 ContextualIdentityService:
25 "resource://gre/modules/ContextualIdentityService.sys.mjs",
26 Corroborate: "resource://gre/modules/Corroborate.sys.mjs",
27 DAPTelemetrySender: "resource://gre/modules/DAPTelemetrySender.sys.mjs",
28 DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
29 DoHController: "resource:///modules/DoHController.sys.mjs",
30 DownloadsViewableInternally:
31 "resource:///modules/DownloadsViewableInternally.sys.mjs",
32 E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs",
33 FeatureGate: "resource://featuregates/FeatureGate.sys.mjs",
34 FxAccounts: "resource://gre/modules/FxAccounts.sys.mjs",
35 Integration: "resource://gre/modules/Integration.sys.mjs",
36 Interactions: "resource:///modules/Interactions.sys.mjs",
37 Log: "resource://gre/modules/Log.sys.mjs",
38 LoginBreaches: "resource:///modules/LoginBreaches.sys.mjs",
39 NewTabUtils: "resource://gre/modules/NewTabUtils.sys.mjs",
40 NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
41 Normandy: "resource://normandy/Normandy.sys.mjs",
42 OsEnvironment: "resource://gre/modules/OsEnvironment.sys.mjs",
43 PageDataService: "resource:///modules/pagedata/PageDataService.sys.mjs",
44 PageThumbs: "resource://gre/modules/PageThumbs.sys.mjs",
45 PdfJs: "resource://pdf.js/PdfJs.sys.mjs",
46 PermissionUI: "resource:///modules/PermissionUI.sys.mjs",
47 PlacesBackups: "resource://gre/modules/PlacesBackups.sys.mjs",
48 PlacesDBUtils: "resource://gre/modules/PlacesDBUtils.sys.mjs",
49 PlacesUIUtils: "resource:///modules/PlacesUIUtils.sys.mjs",
50 PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
51 PluginManager: "resource:///actors/PluginParent.sys.mjs",
52 PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
53 ProvenanceData: "resource:///modules/ProvenanceData.sys.mjs",
55 "resource://gre/modules/netwerk-dns/PublicSuffixList.sys.mjs",
56 QuickSuggest: "resource:///modules/QuickSuggest.sys.mjs",
57 RFPHelper: "resource://gre/modules/RFPHelper.sys.mjs",
58 RemoteSecuritySettings:
59 "resource://gre/modules/psm/RemoteSecuritySettings.sys.mjs",
60 RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
61 SafeBrowsing: "resource://gre/modules/SafeBrowsing.sys.mjs",
62 Sanitizer: "resource:///modules/Sanitizer.sys.mjs",
63 SaveToPocket: "chrome://pocket/content/SaveToPocket.sys.mjs",
64 ScreenshotsUtils: "resource:///modules/ScreenshotsUtils.sys.mjs",
65 SearchSERPTelemetry: "resource:///modules/SearchSERPTelemetry.sys.mjs",
66 SessionStartup: "resource:///modules/sessionstore/SessionStartup.sys.mjs",
67 SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
68 ShellService: "resource:///modules/ShellService.sys.mjs",
69 ShortcutUtils: "resource://gre/modules/ShortcutUtils.sys.mjs",
70 SpecialMessageActions:
71 "resource://messaging-system/lib/SpecialMessageActions.sys.mjs",
72 TRRRacer: "resource:///modules/TRRPerformance.sys.mjs",
73 TelemetryUtils: "resource://gre/modules/TelemetryUtils.sys.mjs",
74 UIState: "resource://services-sync/UIState.sys.mjs",
75 UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
76 WebChannel: "resource://gre/modules/WebChannel.sys.mjs",
77 WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs",
78 clearTimeout: "resource://gre/modules/Timer.sys.mjs",
79 setTimeout: "resource://gre/modules/Timer.sys.mjs",
82 XPCOMUtils.defineLazyModuleGetters(lazy, {
83 AboutNewTab: "resource:///modules/AboutNewTab.jsm",
84 ASRouterDefaultConfig:
85 "resource://activity-stream/lib/ASRouterDefaultConfig.jsm",
86 ASRouter: "resource://activity-stream/lib/ASRouter.jsm",
87 BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.jsm",
88 BrowserUIUtils: "resource:///modules/BrowserUIUtils.jsm",
89 BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
90 Discovery: "resource:///modules/Discovery.jsm",
91 ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
92 HomePage: "resource:///modules/HomePage.jsm",
93 NetUtil: "resource://gre/modules/NetUtil.jsm",
94 OnboardingMessageProvider:
95 "resource://activity-stream/lib/OnboardingMessageProvider.jsm",
96 PageActions: "resource:///modules/PageActions.jsm",
97 ProcessHangMonitor: "resource:///modules/ProcessHangMonitor.jsm",
98 TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
99 TabUnloader: "resource:///modules/TabUnloader.jsm",
102 if (AppConstants.MOZ_UPDATER) {
103 ChromeUtils.defineESModuleGetters(lazy, {
104 UpdateListener: "resource://gre/modules/UpdateListener.sys.mjs",
107 if (AppConstants.MOZ_UPDATE_AGENT) {
108 ChromeUtils.defineESModuleGetters(lazy, {
109 BackgroundUpdate: "resource://gre/modules/BackgroundUpdate.sys.mjs",
113 // PluginManager is used in the listeners object below.
114 XPCOMUtils.defineLazyServiceGetters(lazy, {
115 BrowserHandler: ["@mozilla.org/browser/clh;1", "nsIBrowserHandler"],
116 PushService: ["@mozilla.org/push/Service;1", "nsIPushService"],
119 XPCOMUtils.defineLazyGetter(
124 ["browser/accounts.ftl", "toolkit/branding/accounts.ftl"],
129 if (AppConstants.ENABLE_WEBDRIVER) {
130 XPCOMUtils.defineLazyServiceGetter(
133 "@mozilla.org/remote/marionette;1",
137 XPCOMUtils.defineLazyServiceGetter(
140 "@mozilla.org/remote/agent;1",
144 lazy.Marionette = { running: false };
145 lazy.RemoteAgent = { running: false };
148 const PREF_PDFJS_ISDEFAULT_CACHE_STATE = "pdfjs.enabledCache.state";
150 const PRIVATE_BROWSING_BINARY = "private_browsing.exe";
151 // Index of Private Browsing icon in private_browsing.exe
152 // Must line up with IDI_PBICON_PB_PB_EXE in nsNativeAppSupportWin.h.
153 const PRIVATE_BROWSING_EXE_ICON_INDEX = 1;
154 const PREF_PRIVATE_BROWSING_SHORTCUT_CREATED =
155 "browser.privacySegmentation.createdShortcut";
158 * Fission-compatible JSProcess implementations.
159 * Each actor options object takes the form of a ProcessActorOptions dictionary.
160 * Detailed documentation of these options is in dom/docs/ipc/jsactors.rst,
161 * available at https://firefox-source-docs.mozilla.org/dom/ipc/jsactors.html
163 let JSPROCESSACTORS = {
164 // Miscellaneous stuff that needs to be initialized per process.
167 esModuleURI: "resource:///actors/BrowserProcessChild.sys.mjs",
169 // WebRTC related notifications. They are here to avoid loading WebRTC
170 // components when not needed.
171 "getUserMedia:request",
172 "recording-device-stopped",
173 "PeerConnection:request",
174 "recording-device-events",
175 "recording-window-ended",
180 RefreshBlockerObserver: {
182 esModuleURI: "resource:///actors/RefreshBlockerChild.sys.mjs",
184 "webnavigation-create",
185 "chrome-webnavigation-create",
186 "webnavigation-destroy",
187 "chrome-webnavigation-destroy",
191 enablePreference: "accessibility.blockautorefresh",
192 onPreferenceChanged: (prefName, prevValue, isEnabled) => {
193 lazy.BrowserWindowTracker.orderedWindows.forEach(win => {
194 for (let browser of win.gBrowser.browsers) {
196 browser.sendMessageToActor(
210 * Fission-compatible JSWindowActor implementations.
211 * Detailed documentation of these options is in dom/docs/ipc/jsactors.rst,
212 * available at https://firefox-source-docs.mozilla.org/dom/ipc/jsactors.html
214 let JSWINDOWACTORS = {
217 esModuleURI: "resource:///actors/AboutLoginsParent.sys.mjs",
220 esModuleURI: "resource:///actors/AboutLoginsChild.sys.mjs",
222 AboutLoginsCopyLoginDetail: { wantUntrusted: true },
223 AboutLoginsCreateLogin: { wantUntrusted: true },
224 AboutLoginsDeleteLogin: { wantUntrusted: true },
225 AboutLoginsDismissBreachAlert: { wantUntrusted: true },
226 AboutLoginsImportFromBrowser: { wantUntrusted: true },
227 AboutLoginsImportFromFile: { wantUntrusted: true },
228 AboutLoginsImportReportInit: { wantUntrusted: true },
229 AboutLoginsImportReportReady: { wantUntrusted: true },
230 AboutLoginsInit: { wantUntrusted: true },
231 AboutLoginsGetHelp: { wantUntrusted: true },
232 AboutLoginsOpenPreferences: { wantUntrusted: true },
233 AboutLoginsOpenSite: { wantUntrusted: true },
234 AboutLoginsRecordTelemetryEvent: { wantUntrusted: true },
235 AboutLoginsRemoveAllLogins: { wantUntrusted: true },
236 AboutLoginsSortChanged: { wantUntrusted: true },
237 AboutLoginsSyncEnable: { wantUntrusted: true },
238 AboutLoginsSyncOptions: { wantUntrusted: true },
239 AboutLoginsUpdateLogin: { wantUntrusted: true },
240 AboutLoginsExportPasswords: { wantUntrusted: true },
243 matches: ["about:logins", "about:logins?*", "about:loginsimportreport"],
245 remoteTypes: ["privilegedabout"],
248 AboutMessagePreview: {
250 esModuleURI: "resource:///actors/AboutMessagePreviewParent.sys.mjs",
253 esModuleURI: "resource:///actors/AboutMessagePreviewChild.sys.mjs",
255 DOMDocElementInserted: { capture: true },
258 matches: ["about:messagepreview", "about:messagepreview?*"],
263 esModuleURI: "resource:///actors/AboutNewTabParent.sys.mjs",
266 esModuleURI: "resource:///actors/AboutNewTabChild.sys.mjs",
268 DOMDocElementInserted: {},
269 DOMContentLoaded: {},
270 load: { capture: true },
271 unload: { capture: true },
273 visibilitychange: {},
276 // The wildcard on about:newtab is for the ?endpoint query parameter
277 // that is used for snippets debugging. The wildcard for about:home
278 // is similar, and also allows for falling back to loading the
279 // about:home document dynamically if an attempt is made to load
280 // about:home?jscache from the AboutHomeStartupCache as a top-level
282 matches: ["about:home*", "about:welcome", "about:newtab*"],
283 remoteTypes: ["privilegedabout"],
288 esModuleURI: "resource:///actors/AboutPluginsParent.sys.mjs",
291 esModuleURI: "resource:///actors/AboutPluginsChild.sys.mjs",
294 DOMDocElementInserted: { capture: true },
298 matches: ["about:plugins"],
302 esModuleURI: "resource:///actors/AboutPocketParent.sys.mjs",
305 esModuleURI: "resource:///actors/AboutPocketChild.sys.mjs",
308 DOMDocElementInserted: { capture: true },
312 remoteTypes: ["privilegedabout"],
314 "about:pocket-saved*",
315 "about:pocket-signup*",
316 "about:pocket-home*",
317 "about:pocket-style-guide*",
321 AboutPrivateBrowsing: {
323 esModuleURI: "resource:///actors/AboutPrivateBrowsingParent.sys.mjs",
326 esModuleURI: "resource:///actors/AboutPrivateBrowsingChild.sys.mjs",
329 DOMDocElementInserted: { capture: true },
333 matches: ["about:privatebrowsing*"],
338 esModuleURI: "resource:///actors/AboutProtectionsParent.sys.mjs",
341 esModuleURI: "resource:///actors/AboutProtectionsChild.sys.mjs",
344 DOMDocElementInserted: { capture: true },
348 matches: ["about:protections", "about:protections?*"],
353 esModuleURI: "resource:///actors/AboutReaderParent.sys.mjs",
356 esModuleURI: "resource:///actors/AboutReaderChild.sys.mjs",
358 DOMContentLoaded: {},
359 pageshow: { mozSystemGroup: true },
360 // Don't try to create the actor if only the pagehide event fires.
361 // This can happen with the initial about:blank documents.
362 pagehide: { mozSystemGroup: true, createActor: false },
365 messageManagerGroups: ["browsers"],
370 esModuleURI: "resource:///actors/AboutTabCrashedParent.sys.mjs",
373 esModuleURI: "resource:///actors/AboutTabCrashedChild.sys.mjs",
375 DOMDocElementInserted: { capture: true },
379 matches: ["about:tabcrashed*"],
384 moduleURI: "resource:///actors/AboutWelcomeParent.jsm",
387 moduleURI: "resource:///actors/AboutWelcomeChild.jsm",
389 // This is added so the actor instantiates immediately and makes
390 // methods available to the page js on load.
391 DOMDocElementInserted: {},
394 matches: ["about:welcome"],
395 remoteTypes: ["privilegedabout"],
398 // Remove this preference check when we turn on separate about:welcome for all users.
399 enablePreference: "browser.aboutwelcome.enabled",
404 esModuleURI: "resource:///actors/BlockedSiteParent.sys.mjs",
407 esModuleURI: "resource:///actors/BlockedSiteChild.sys.mjs",
409 AboutBlockedLoaded: { wantUntrusted: true },
413 matches: ["about:blocked?*"],
419 esModuleURI: "resource:///actors/BrowserTabChild.sys.mjs",
422 messageManagerGroups: ["browsers"],
427 esModuleURI: "resource:///actors/ClickHandlerParent.sys.mjs",
430 esModuleURI: "resource:///actors/ClickHandlerChild.sys.mjs",
432 chromelinkclick: { capture: true, mozSystemGroup: true },
439 /* Note: this uses the same JSMs as ClickHandler, but because it
440 * relies on "normal" click events anywhere on the page (not just
441 * links) and is expensive, and only does something for the
442 * small group of people who have the feature enabled, it is its
443 * own actor which is only registered if the pref is enabled.
445 MiddleMousePasteHandler: {
447 esModuleURI: "resource:///actors/ClickHandlerParent.sys.mjs",
450 esModuleURI: "resource:///actors/ClickHandlerChild.sys.mjs",
452 auxclick: { capture: true, mozSystemGroup: true },
455 enablePreference: "middlemouse.contentLoadURL",
462 esModuleURI: "resource:///actors/ContentSearchParent.sys.mjs",
465 esModuleURI: "resource:///actors/ContentSearchChild.sys.mjs",
467 ContentSearchClient: { capture: true, wantUntrusted: true },
474 "about:privatebrowsing",
475 "about:test-about-content-search-ui",
477 remoteTypes: ["privilegedabout"],
482 esModuleURI: "resource:///actors/ContextMenuParent.sys.mjs",
486 esModuleURI: "resource:///actors/ContextMenuChild.sys.mjs",
488 contextmenu: { mozSystemGroup: true },
497 esModuleURI: "resource:///actors/DecoderDoctorParent.sys.mjs",
501 esModuleURI: "resource:///actors/DecoderDoctorChild.sys.mjs",
502 observers: ["decoder-doctor-notification"],
505 messageManagerGroups: ["browsers"],
511 esModuleURI: "resource:///actors/DOMFullscreenParent.sys.mjs",
515 esModuleURI: "resource:///actors/DOMFullscreenChild.sys.mjs",
517 "MozDOMFullscreen:Request": {},
518 "MozDOMFullscreen:Entered": {},
519 "MozDOMFullscreen:NewOrigin": {},
520 "MozDOMFullscreen:Exit": {},
521 "MozDOMFullscreen:Exited": {},
525 messageManagerGroups: ["browsers"],
531 esModuleURI: "resource:///actors/EncryptedMediaParent.sys.mjs",
535 esModuleURI: "resource:///actors/EncryptedMediaChild.sys.mjs",
536 observers: ["mediakeys-request"],
539 messageManagerGroups: ["browsers"],
545 esModuleURI: "resource:///actors/FormValidationParent.sys.mjs",
549 esModuleURI: "resource:///actors/FormValidationChild.sys.mjs",
552 // Listening to ‘pageshow’ event is only relevant if an invalid form
553 // popup was open, so don't create the actor when fired.
554 pageshow: { createActor: false },
563 esModuleURI: "resource:///actors/LightweightThemeChild.sys.mjs",
565 pageshow: { mozSystemGroup: true },
566 DOMContentLoaded: {},
575 "chrome://browser/content/syncedtabs/sidebar.xhtml",
576 "chrome://browser/content/places/historySidebar.xhtml",
577 "chrome://browser/content/places/bookmarksSidebar.xhtml",
579 "about:firefoxview-next",
585 esModuleURI: "resource:///actors/LinkHandlerParent.sys.mjs",
588 esModuleURI: "resource:///actors/LinkHandlerChild.sys.mjs",
590 DOMHeadElementParsed: {},
594 // The `pagehide` event is only used to clean up state which will not be
595 // present if the actor hasn't been created.
596 pagehide: { createActor: false },
600 messageManagerGroups: ["browsers"],
605 esModuleURI: "resource:///actors/MigrationWizardParent.sys.mjs",
609 esModuleURI: "resource:///actors/MigrationWizardChild.sys.mjs",
611 "MigrationWizard:RequestState": { wantUntrusted: true },
612 "MigrationWizard:BeginMigration": { wantUntrusted: true },
613 "MigrationWizard:RequestSafariPermissions": { wantUntrusted: true },
614 "MigrationWizard:SelectSafariPasswordFile": { wantUntrusted: true },
624 "chrome://browser/content/migration/migration-dialog-window.html",
630 esModuleURI: "resource:///actors/PageInfoChild.sys.mjs",
638 esModuleURI: "resource:///actors/PageStyleParent.sys.mjs",
641 esModuleURI: "resource:///actors/PageStyleChild.sys.mjs",
643 pageshow: { createActor: false },
647 messageManagerGroups: ["browsers"],
653 esModuleURI: "resource://pdf.js/PdfjsParent.sys.mjs",
656 esModuleURI: "resource://pdf.js/PdfjsChild.sys.mjs",
661 // GMP crash reporting
664 esModuleURI: "resource:///actors/PluginParent.sys.mjs",
667 esModuleURI: "resource:///actors/PluginChild.sys.mjs",
669 PluginCrashed: { capture: true },
678 esModuleURI: "resource:///actors/PointerLockParent.sys.mjs",
681 esModuleURI: "resource:///actors/PointerLockChild.sys.mjs",
683 "MozDOMPointerLock:Entered": {},
684 "MozDOMPointerLock:Exited": {},
688 messageManagerGroups: ["browsers"],
694 esModuleURI: "resource:///actors/PromptParent.sys.mjs",
702 esModuleURI: "resource:///actors/RefreshBlockerParent.sys.mjs",
705 esModuleURI: "resource:///actors/RefreshBlockerChild.sys.mjs",
708 messageManagerGroups: ["browsers"],
709 enablePreference: "accessibility.blockautorefresh",
712 ScreenshotsComponent: {
714 esModuleURI: "resource:///modules/ScreenshotsUtils.sys.mjs",
717 esModuleURI: "resource:///actors/ScreenshotsComponentChild.sys.mjs",
719 enablePreference: "screenshots.browser.component.enabled",
722 SearchSERPTelemetry: {
724 esModuleURI: "resource:///actors/SearchSERPTelemetryParent.sys.mjs",
727 esModuleURI: "resource:///actors/SearchSERPTelemetryChild.sys.mjs",
729 DOMContentLoaded: {},
730 pageshow: { mozSystemGroup: true },
731 // The 'pagehide' event is only used to clean up state, and should not
732 // force actor creation.
733 pagehide: { createActor: false },
734 load: { mozSystemGroup: true, capture: true },
737 matches: ["https://*/*"],
742 esModuleURI: "resource://normandy-content/ShieldFrameParent.sys.mjs",
745 esModuleURI: "resource://normandy-content/ShieldFrameChild.sys.mjs",
749 ShieldPageEvent: { wantUntrusted: true },
752 matches: ["about:studies*"],
757 esModuleURI: "resource:///actors/SpeechDispatcherParent.sys.mjs",
761 esModuleURI: "resource:///actors/SpeechDispatcherChild.sys.mjs",
762 observers: ["chrome-synth-voices-error"],
765 messageManagerGroups: ["browsers"],
771 esModuleURI: "resource:///actors/ASRouterParent.sys.mjs",
774 esModuleURI: "resource:///actors/ASRouterChild.sys.mjs",
776 // This is added so the actor instantiates immediately and makes
777 // methods available to the page js on load.
778 DOMDocElementInserted: {},
785 "about:privatebrowsing*",
787 remoteTypes: ["privilegedabout"],
790 SwitchDocumentDirection: {
792 esModuleURI: "resource:///actors/SwitchDocumentDirectionChild.sys.mjs",
798 // The older translations feature backed by external services.
799 // This is being replaced by a newer ML-backed translation service. See Bug 971044.
802 moduleURI: "resource:///modules/translation/TranslationParent.jsm",
805 moduleURI: "resource:///modules/translation/TranslationChild.jsm",
808 load: { mozSystemGroup: true, capture: true },
811 enablePreference: "browser.translation.detectLanguage",
816 esModuleURI: "resource:///modules/UITourParent.sys.mjs",
819 esModuleURI: "resource:///modules/UITourChild.sys.mjs",
821 mozUITour: { wantUntrusted: true },
825 messageManagerGroups: ["browsers"],
830 esModuleURI: "resource:///actors/WebRTCParent.sys.mjs",
833 esModuleURI: "resource:///actors/WebRTCChild.sys.mjs",
840 XPCOMUtils.defineLazyGetter(
843 () => Cc["@mozilla.org/weave/service;1"].getService().wrappedJSObject
846 if (AppConstants.MOZ_CRASHREPORTER) {
847 XPCOMUtils.defineLazyModuleGetters(lazy, {
848 UnsubmittedCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
852 XPCOMUtils.defineLazyGetter(lazy, "gBrandBundle", function () {
853 return Services.strings.createBundle(
854 "chrome://branding/locale/brand.properties"
858 XPCOMUtils.defineLazyGetter(lazy, "gBrowserBundle", function () {
859 return Services.strings.createBundle(
860 "chrome://browser/locale/browser.properties"
866 "gmp-plugin-crash": ["PluginManager"],
867 "plugin-crashed": ["PluginManager"],
870 observe(subject, topic, data) {
871 for (let module of this.observers[topic]) {
873 lazy[module].observe(subject, topic, data);
881 for (let observer of Object.keys(this.observers)) {
882 Services.obs.addObserver(this, observer);
886 if (AppConstants.MOZ_UPDATER) {
887 listeners.observers["update-downloading"] = ["UpdateListener"];
888 listeners.observers["update-staged"] = ["UpdateListener"];
889 listeners.observers["update-downloaded"] = ["UpdateListener"];
890 listeners.observers["update-available"] = ["UpdateListener"];
891 listeners.observers["update-error"] = ["UpdateListener"];
892 listeners.observers["update-swap"] = ["UpdateListener"];
895 // Seconds of idle before trying to create a bookmarks backup.
896 const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 8 * 60;
897 // Minimum interval between backups. We try to not create more than one backup
899 const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1;
900 // Seconds of idle time before the late idle tasks will be scheduled.
901 const LATE_TASKS_IDLE_TIME_SEC = 20;
902 // Time after we stop tracking startup crashes.
903 const STARTUP_CRASHES_END_DELAY_MS = 30 * 1000;
906 * OS X has the concept of zero-window sessions and therefore ignores the
907 * browser-lastwindow-close-* topics.
909 const OBSERVE_LASTWINDOW_CLOSE_TOPICS = AppConstants.platform != "macosx";
911 export let BrowserInitState = {};
912 BrowserInitState.startupIdleTaskPromise = new Promise(resolve => {
913 BrowserInitState._resolveStartupIdleTask = resolve;
916 export function BrowserGlue() {
917 XPCOMUtils.defineLazyServiceGetter(
920 "@mozilla.org/widget/useridleservice;1",
924 XPCOMUtils.defineLazyGetter(this, "_distributionCustomizer", function () {
925 const { DistributionCustomizer } = ChromeUtils.import(
926 "resource:///modules/distribution.js"
928 return new DistributionCustomizer();
931 XPCOMUtils.defineLazyServiceGetter(
934 "@mozilla.org/alerts-service;1",
941 BrowserGlue.prototype = {
943 _migrationImportsDefaultBookmarks: false,
944 _placesBrowserInitComplete: false,
945 _isNewProfile: undefined,
946 _defaultCookieBehaviorAtStartup: null,
948 _setPrefToSaveSession: function BG__setPrefToSaveSession(aForce) {
949 if (!this._saveSession && !aForce) {
953 if (!lazy.PrivateBrowsingUtils.permanentPrivateBrowsing) {
954 Services.prefs.setBoolPref(
955 "browser.sessionstore.resume_session_once",
960 // This method can be called via [NSApplication terminate:] on Mac, which
961 // ends up causing prefs not to be flushed to disk, so we need to do that
962 // explicitly here. See bug 497652.
963 Services.prefs.savePrefFile(null);
966 // nsIObserver implementation
967 observe: async function BG_observe(subject, topic, data) {
969 case "notifications-open-settings":
970 this._openPreferences("privacy-permissions");
972 case "final-ui-startup":
973 this._beforeUIStartup();
975 case "browser-delayed-startup-finished":
976 this._onFirstWindowLoaded(subject);
977 Services.obs.removeObserver(this, "browser-delayed-startup-finished");
979 case "sessionstore-windows-restored":
980 this._onWindowsRestored();
982 case "browser:purge-session-history":
983 // reset the console service's error buffer
984 Services.console.logStringMessage(null); // clear the console (in case it's open)
985 Services.console.reset();
987 case "restart-in-safe-mode":
988 this._onSafeModeRestart(subject);
990 case "quit-application-requested":
991 this._onQuitRequest(subject, data);
993 case "quit-application-granted":
994 this._onQuitApplicationGranted();
996 case "browser-lastwindow-close-requested":
997 if (OBSERVE_LASTWINDOW_CLOSE_TOPICS) {
998 // The application is not actually quitting, but the last full browser
999 // window is about to be closed.
1000 this._onQuitRequest(subject, "lastwindow");
1003 case "browser-lastwindow-close-granted":
1004 if (OBSERVE_LASTWINDOW_CLOSE_TOPICS) {
1005 this._setPrefToSaveSession();
1008 case "fxaccounts:onverified":
1009 this._onThisDeviceConnected();
1011 case "fxaccounts:device_connected":
1012 this._onDeviceConnected(data);
1014 case "fxaccounts:verify_login":
1015 this._onVerifyLoginNotification(JSON.parse(data));
1017 case "fxaccounts:device_disconnected":
1018 data = JSON.parse(data);
1019 if (data.isLocalDevice) {
1020 this._onDeviceDisconnected();
1023 case "fxaccounts:commands:open-uri":
1024 this._onDisplaySyncURIs(subject);
1026 case "session-save":
1027 this._setPrefToSaveSession(true);
1028 subject.QueryInterface(Ci.nsISupportsPRBool);
1029 subject.data = true;
1031 case "places-init-complete":
1032 Services.obs.removeObserver(this, "places-init-complete");
1033 if (!this._migrationImportsDefaultBookmarks) {
1034 this._initPlaces(false);
1038 this._backupBookmarks();
1040 case "distribution-customization-complete":
1041 Services.obs.removeObserver(
1043 "distribution-customization-complete"
1045 // Customization has finished, we don't need the customizer anymore.
1046 delete this._distributionCustomizer;
1048 case "browser-glue-test": // used by tests
1049 if (data == "force-ui-migration") {
1051 } else if (data == "force-distribution-customization") {
1052 this._distributionCustomizer.applyCustomizations();
1053 // To apply distribution bookmarks use "places-init-complete".
1054 } else if (data == "test-force-places-init") {
1055 this._placesInitialized = false;
1056 this._initPlaces(false);
1057 } else if (data == "mock-alerts-service") {
1058 Object.defineProperty(this, "AlertsService", {
1059 value: subject.wrappedJSObject,
1061 } else if (data == "places-browser-init-complete") {
1062 if (this._placesBrowserInitComplete) {
1063 Services.obs.notifyObservers(null, "places-browser-init-complete");
1065 } else if (data == "add-breaches-sync-handler") {
1066 this._addBreachesSyncHandler();
1069 case "initial-migration-will-import-default-bookmarks":
1070 this._migrationImportsDefaultBookmarks = true;
1072 case "initial-migration-did-import-default-bookmarks":
1073 this._initPlaces(true);
1075 case "handle-xul-text-link":
1076 let linkHandled = subject.QueryInterface(Ci.nsISupportsPRBool);
1077 if (!linkHandled.data) {
1078 let win = lazy.BrowserWindowTracker.getTopWindow();
1080 data = JSON.parse(data);
1081 let where = win.whereToOpenLink(data);
1082 // Preserve legacy behavior of non-modifier left-clicks
1083 // opening in a new selected tab.
1084 if (where == "current") {
1087 win.openTrustedLinkIn(data.href, where);
1088 linkHandled.data = true;
1092 case "profile-before-change":
1093 // Any component depending on Places should be finalized in
1094 // _onPlacesShutdown. Any component that doesn't need to act after
1095 // the UI has gone should be finalized in _onQuitApplicationGranted.
1098 case "keyword-search":
1099 // This notification is broadcast by the docshell when it "fixes up" a
1100 // URI that it's been asked to load into a keyword search.
1103 engine = Services.search.getEngineByName(
1104 subject.QueryInterface(Ci.nsISupportsString).data
1109 let win = lazy.BrowserWindowTracker.getTopWindow();
1110 lazy.BrowserSearchTelemetry.recordSearch(
1111 win.gBrowser.selectedBrowser,
1116 case "browser-search-engine-modified":
1117 // Ensure we cleanup the hiddenOneOffs pref when removing
1118 // an engine, and that newly added engines are visible.
1119 if (data == "engine-added" || data == "engine-removed") {
1120 let engineName = subject.QueryInterface(Ci.nsISearchEngine).name;
1121 let pref = Services.prefs.getStringPref(
1122 "browser.search.hiddenOneOffs"
1124 let hiddenList = pref ? pref.split(",") : [];
1125 hiddenList = hiddenList.filter(x => x !== engineName);
1126 Services.prefs.setStringPref(
1127 "browser.search.hiddenOneOffs",
1128 hiddenList.join(",")
1132 case "xpi-signature-changed":
1133 let disabledAddons = JSON.parse(data).disabled;
1134 let addons = await lazy.AddonManager.getAddonsByIDs(disabledAddons);
1135 if (addons.some(addon => addon)) {
1136 this._notifyUnsignedAddonsDisabled();
1139 case "sync-ui-state:update":
1140 this._updateFxaBadges(lazy.BrowserWindowTracker.getTopWindow());
1142 case "handlersvc-store-initialized":
1143 // Initialize PdfJs when running in-process and remote. This only
1144 // happens once since PdfJs registers global hooks. If the PdfJs
1145 // extension is installed the init method below will be overridden
1146 // leaving initialization to the extension.
1147 // parent only: configure default prefs, set up pref observers, register
1148 // pdf content handler, and initializes parent side message manager
1149 // shim for privileged api access.
1150 lazy.PdfJs.init(this._isNewProfile);
1152 // Allow certain viewable internally types to be opened from downloads.
1153 lazy.DownloadsViewableInternally.register();
1157 this._earlyBlankFirstPaint(subject);
1162 // initialization (called on application startup)
1163 _init: function BG__init() {
1164 let os = Services.obs;
1166 "notifications-open-settings",
1168 "browser-delayed-startup-finished",
1169 "sessionstore-windows-restored",
1170 "browser:purge-session-history",
1171 "quit-application-requested",
1172 "quit-application-granted",
1173 "fxaccounts:onverified",
1174 "fxaccounts:device_connected",
1175 "fxaccounts:verify_login",
1176 "fxaccounts:device_disconnected",
1177 "fxaccounts:commands:open-uri",
1179 "places-init-complete",
1180 "distribution-customization-complete",
1181 "handle-xul-text-link",
1182 "profile-before-change",
1184 "browser-search-engine-modified",
1185 "restart-in-safe-mode",
1186 "xpi-signature-changed",
1187 "sync-ui-state:update",
1188 "handlersvc-store-initialized",
1189 ].forEach(topic => os.addObserver(this, topic, true));
1190 if (OBSERVE_LASTWINDOW_CLOSE_TOPICS) {
1191 os.addObserver(this, "browser-lastwindow-close-requested", true);
1192 os.addObserver(this, "browser-lastwindow-close-granted", true);
1195 lazy.ActorManagerParent.addJSProcessActors(JSPROCESSACTORS);
1196 lazy.ActorManagerParent.addJSWindowActors(JSWINDOWACTORS);
1198 this._firstWindowReady = new Promise(
1199 resolve => (this._firstWindowLoaded = resolve)
1201 if (AppConstants.platform == "win") {
1202 JawsScreenReaderVersionCheck.init();
1206 // cleanup (called on application shutdown)
1207 _dispose: function BG__dispose() {
1208 // AboutHomeStartupCache might write to the cache during
1209 // quit-application-granted, so we defer uninitialization
1211 AboutHomeStartupCache.uninit();
1213 if (this._bookmarksBackupIdleTime) {
1214 this._userIdleService.removeIdleObserver(
1216 this._bookmarksBackupIdleTime
1218 this._bookmarksBackupIdleTime = null;
1220 if (this._lateTasksIdleObserver) {
1221 this._userIdleService.removeIdleObserver(
1222 this._lateTasksIdleObserver,
1223 LATE_TASKS_IDLE_TIME_SEC
1225 delete this._lateTasksIdleObserver;
1227 if (this._gmpInstallManager) {
1228 this._gmpInstallManager.uninit();
1229 delete this._gmpInstallManager;
1232 Services.prefs.removeObserver(
1233 "privacy.trackingprotection",
1234 this._matchCBCategory
1236 Services.prefs.removeObserver(
1237 "network.cookie.cookieBehavior",
1238 this._matchCBCategory
1240 Services.prefs.removeObserver(
1241 "network.cookie.cookieBehavior.pbmode",
1242 this._matchCBCategory
1244 Services.prefs.removeObserver(
1245 "network.http.referer.disallowCrossSiteRelaxingDefault",
1246 this._matchCBCategory
1248 Services.prefs.removeObserver(
1249 "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation",
1250 this._matchCBCategory
1252 Services.prefs.removeObserver(
1253 "privacy.partition.network_state.ocsp_cache",
1254 this._matchCBCategory
1256 Services.prefs.removeObserver(
1257 "privacy.query_stripping.enabled",
1258 this._matchCBCategory
1260 Services.prefs.removeObserver(
1261 "privacy.query_stripping.enabled.pbmode",
1262 this._matchCBCategory
1264 Services.prefs.removeObserver(
1265 ContentBlockingCategoriesPrefs.PREF_CB_CATEGORY,
1266 this._updateCBCategory
1268 Services.prefs.removeObserver(
1269 "privacy.trackingprotection",
1270 this._setPrefExpectations
1272 Services.prefs.removeObserver(
1273 "browser.contentblocking.features.strict",
1274 this._setPrefExpectationsAndUpdate
1278 // runs on startup, before the first command line handler is invoked
1279 // (i.e. before the first window is opened)
1280 _beforeUIStartup: function BG__beforeUIStartup() {
1281 lazy.SessionStartup.init();
1283 // check if we're in safe mode
1284 if (Services.appinfo.inSafeMode) {
1285 Services.ww.openWindow(
1287 "chrome://browser/content/safeMode.xhtml",
1289 "chrome,centerscreen,modal,resizable=no",
1294 // apply distribution customizations
1295 this._distributionCustomizer.applyCustomizations();
1297 // handle any UI migration
1300 if (!Services.prefs.prefHasUserValue(PREF_PDFJS_ISDEFAULT_CACHE_STATE)) {
1301 lazy.PdfJs.checkIsDefault(this._isNewProfile);
1306 lazy.SessionStore.init();
1308 lazy.BuiltInThemes.maybeInstallActiveBuiltInTheme();
1310 if (AppConstants.MOZ_NORMANDY) {
1311 lazy.Normandy.init();
1314 lazy.SaveToPocket.init();
1316 AboutHomeStartupCache.init();
1318 Services.obs.notifyObservers(null, "browser-ui-startup-complete");
1321 _checkForOldBuildUpdates() {
1322 // check for update if our build is old
1324 AppConstants.MOZ_UPDATER &&
1325 Services.prefs.getBoolPref("app.update.checkInstallTime")
1327 let buildID = Services.appinfo.appBuildID;
1328 let today = new Date().getTime();
1329 /* eslint-disable no-multi-spaces */
1330 let buildDate = new Date(
1331 buildID.slice(0, 4), // year
1332 buildID.slice(4, 6) - 1, // months are zero-based.
1333 buildID.slice(6, 8), // day
1334 buildID.slice(8, 10), // hour
1335 buildID.slice(10, 12), // min
1336 buildID.slice(12, 14)
1339 /* eslint-enable no-multi-spaces */
1341 const millisecondsIn24Hours = 86400000;
1343 Services.prefs.getIntPref("app.update.checkInstallTime.days") *
1344 millisecondsIn24Hours;
1346 if (buildDate + acceptableAge < today) {
1347 Cc["@mozilla.org/updates/update-service;1"]
1348 .getService(Ci.nsIApplicationUpdateService)
1349 .checkForBackgroundUpdates();
1354 async _onSafeModeRestart(window) {
1355 // prompt the user to confirm
1356 let productName = lazy.gBrandBundle.GetStringFromName("brandShortName");
1357 let strings = lazy.gBrowserBundle;
1358 let promptTitle = strings.formatStringFromName(
1359 "troubleshootModeRestartPromptTitle",
1362 let promptMessage = strings.GetStringFromName(
1363 "troubleshootModeRestartPromptMessage"
1365 let restartText = strings.GetStringFromName(
1366 "troubleshootModeRestartButton"
1369 Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING +
1370 Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_CANCEL +
1371 Services.prompt.BUTTON_POS_0_DEFAULT;
1373 let rv = await Services.prompt.asyncConfirmEx(
1374 window.browsingContext,
1375 Ci.nsIPrompt.MODAL_TYPE_INTERNAL_WINDOW,
1385 if (rv.get("buttonNumClicked") != 0) {
1389 let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(
1390 Ci.nsISupportsPRBool
1392 Services.obs.notifyObservers(
1394 "quit-application-requested",
1398 if (!cancelQuit.data) {
1399 Services.startup.restartInSafeMode(Ci.nsIAppStartup.eAttemptQuit);
1404 * Show a notification bar offering a reset.
1407 * String of either "unused" or "uninstall", specifying the reason
1408 * why a profile reset is offered.
1410 _resetProfileNotification(reason) {
1411 let win = lazy.BrowserWindowTracker.getTopWindow();
1416 const { ResetProfile } = ChromeUtils.importESModule(
1417 "resource://gre/modules/ResetProfile.sys.mjs"
1419 if (!ResetProfile.resetSupported()) {
1423 let productName = lazy.gBrandBundle.GetStringFromName("brandShortName");
1424 let resetBundle = Services.strings.createBundle(
1425 "chrome://global/locale/resetProfile.properties"
1429 if (reason == "unused") {
1430 message = resetBundle.formatStringFromName("resetUnusedProfile.message", [
1433 } else if (reason == "uninstall") {
1434 message = resetBundle.formatStringFromName("resetUninstalled.message", [
1439 `Unknown reason (${reason}) given to _resetProfileNotification.`
1444 label: resetBundle.formatStringFromName(
1445 "refreshProfile.resetButton.label",
1448 accessKey: resetBundle.GetStringFromName(
1449 "refreshProfile.resetButton.accesskey"
1452 ResetProfile.openConfirmationDialog(win);
1457 win.gNotificationBox.appendNotification(
1458 "reset-profile-notification",
1461 image: "chrome://global/skin/icons/question-64.png",
1462 priority: win.gNotificationBox.PRIORITY_INFO_LOW,
1468 _notifyUnsignedAddonsDisabled() {
1469 let win = lazy.BrowserWindowTracker.getTopWindow();
1474 let message = win.gNavigatorBundle.getString(
1475 "unsignedAddonsDisabled.message"
1479 label: win.gNavigatorBundle.getString(
1480 "unsignedAddonsDisabled.learnMore.label"
1482 accessKey: win.gNavigatorBundle.getString(
1483 "unsignedAddonsDisabled.learnMore.accesskey"
1486 win.BrowserOpenAddonsMgr("addons://list/extension?unsigned=true");
1491 win.gNotificationBox.appendNotification(
1492 "unsigned-addons-disabled",
1495 priority: win.gNotificationBox.PRIORITY_WARNING_MEDIUM,
1501 _earlyBlankFirstPaint(cmdLine) {
1502 let startTime = Cu.now();
1504 AppConstants.platform == "macosx" ||
1505 Services.startup.wasSilentlyStarted ||
1506 !Services.prefs.getBoolPref("browser.startup.blankWindow", false)
1511 // Until bug 1450626 and bug 1488384 are fixed, skip the blank window when
1512 // using a non-default theme.
1514 !Services.startup.showedPreXULSkeletonUI &&
1515 Services.prefs.getCharPref(
1516 "extensions.activeThemeID",
1517 "default-theme@mozilla.org"
1518 ) != "default-theme@mozilla.org"
1523 let store = Services.xulStore;
1524 let getValue = attr =>
1525 store.getValue(AppConstants.BROWSER_CHROME_URL, "main-window", attr);
1526 let width = getValue("width");
1527 let height = getValue("height");
1529 // The clean profile case isn't handled yet. Return early for now.
1530 if (!width || !height) {
1534 let browserWindowFeatures =
1535 "chrome,all,dialog=no,extrachrome,menubar,resizable,scrollbars,status," +
1536 "location,toolbar,personalbar";
1537 // This needs to be set when opening the window to ensure that the AppUserModelID
1538 // is set correctly on Windows. Without it, initial launches with `-private-window`
1539 // will show up under the regular Firefox taskbar icon first, and then switch
1540 // to the Private Browsing icon shortly thereafter.
1541 if (cmdLine.findFlag("private-window", false) != -1) {
1542 browserWindowFeatures += ",private";
1544 let win = Services.ww.openWindow(
1548 browserWindowFeatures,
1552 // Hide the titlebar if the actual browser window will draw in it.
1553 let hiddenTitlebar = Services.appinfo.drawInTitlebar;
1554 if (hiddenTitlebar) {
1555 win.windowUtils.setChromeMargin(0, 2, 2, 2);
1558 let docElt = win.document.documentElement;
1559 docElt.setAttribute("screenX", getValue("screenX"));
1560 docElt.setAttribute("screenY", getValue("screenY"));
1562 // The sizemode="maximized" attribute needs to be set before first paint.
1563 let sizemode = getValue("sizemode");
1564 if (sizemode == "maximized") {
1565 docElt.setAttribute("sizemode", sizemode);
1567 // Set the size to use when the user leaves the maximized mode.
1568 // The persisted size is the outer size, but the height/width
1569 // attributes set the inner size.
1570 let appWin = win.docShell.treeOwner
1571 .QueryInterface(Ci.nsIInterfaceRequestor)
1572 .getInterface(Ci.nsIAppWindow);
1573 height -= appWin.outerToInnerHeightDifferenceInCSSPixels;
1574 width -= appWin.outerToInnerWidthDifferenceInCSSPixels;
1575 docElt.setAttribute("height", height);
1576 docElt.setAttribute("width", width);
1578 // Setting the size of the window in the features string instead of here
1579 // causes the window to grow by the size of the titlebar.
1580 win.resizeTo(width, height);
1583 // Set this before showing the window so that graphics code can use it to
1584 // decide to skip some expensive code paths (eg. starting the GPU process).
1585 docElt.setAttribute("windowtype", "navigator:blank");
1587 // The window becomes visible after OnStopRequest, so make this happen now.
1590 ChromeUtils.addProfilerMarker("earlyBlankFirstPaint", startTime);
1591 win.openTime = Cu.now();
1593 let { TelemetryTimestamps } = ChromeUtils.importESModule(
1594 "resource://gre/modules/TelemetryTimestamps.sys.mjs"
1596 TelemetryTimestamps.add("blankWindowShown");
1599 _firstWindowTelemetry(aWindow) {
1600 let scaling = aWindow.devicePixelRatio * 100;
1602 Services.telemetry.getHistogramById("DISPLAY_SCALING").add(scaling);
1606 _collectStartupConditionsTelemetry() {
1607 let nowSeconds = Math.round(Date.now() / 1000);
1608 // Don't include cases where we don't have the pref. This rules out the first install
1609 // as well as the first run of a build since this was introduced. These could by some
1610 // definitions be referred to as "cold" startups, but probably not since we likely
1611 // just wrote many of the files we use to disk. This way we should approximate a lower
1612 // bound to the number of cold startups rather than an upper bound.
1613 let lastCheckSeconds = Services.prefs.getIntPref(
1614 "browser.startup.lastColdStartupCheck",
1617 Services.prefs.setIntPref(
1618 "browser.startup.lastColdStartupCheck",
1622 let secondsSinceLastOSRestart =
1623 Services.startup.secondsSinceLastOSRestart;
1625 nowSeconds - secondsSinceLastOSRestart > lastCheckSeconds;
1626 Services.telemetry.scalarSet("startup.is_cold", isColdStartup);
1627 Services.telemetry.scalarSet(
1628 "startup.seconds_since_last_os_restart",
1629 secondsSinceLastOSRestart
1636 // the first browser window has finished initializing
1637 _onFirstWindowLoaded: function BG__onFirstWindowLoaded(aWindow) {
1638 lazy.AboutNewTab.init();
1640 lazy.TabCrashHandler.init();
1642 lazy.ProcessHangMonitor.init();
1644 lazy.UrlbarPrefs.updateFirefoxSuggestScenario();
1646 // A channel for "remote troubleshooting" code...
1647 let channel = new lazy.WebChannel(
1648 "remote-troubleshooting",
1649 "remote-troubleshooting"
1651 channel.listen((id, data, target) => {
1652 if (data.command == "request") {
1653 let { Troubleshoot } = ChromeUtils.importESModule(
1654 "resource://gre/modules/Troubleshoot.sys.mjs"
1656 Troubleshoot.snapshot().then(snapshotData => {
1657 // for privacy we remove crash IDs and all preferences (but bug 1091944
1658 // exists to expose prefs once we are confident of privacy implications)
1659 delete snapshotData.crashes;
1660 delete snapshotData.modifiedPreferences;
1661 delete snapshotData.printingPreferences;
1662 channel.send(snapshotData, target);
1667 // Offer to reset a user's profile if it hasn't been used for 60 days.
1668 const OFFER_PROFILE_RESET_INTERVAL_MS = 60 * 24 * 60 * 60 * 1000;
1669 let lastUse = Services.appinfo.replacedLockTime;
1670 let disableResetPrompt = Services.prefs.getBoolPref(
1671 "browser.disableResetPrompt",
1676 !disableResetPrompt &&
1678 Date.now() - lastUse >= OFFER_PROFILE_RESET_INTERVAL_MS
1680 this._resetProfileNotification("unused");
1681 } else if (AppConstants.platform == "win" && !disableResetPrompt) {
1682 // Check if we were just re-installed and offer Firefox Reset
1685 updateChannel = ChromeUtils.importESModule(
1686 "resource://gre/modules/UpdateUtils.sys.mjs"
1687 ).UpdateUtils.UpdateChannel;
1689 if (updateChannel) {
1690 let uninstalledValue = lazy.WindowsRegistry.readRegKey(
1691 Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
1692 "Software\\Mozilla\\Firefox",
1693 `Uninstalled-${updateChannel}`
1695 let removalSuccessful = lazy.WindowsRegistry.removeRegKey(
1696 Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
1697 "Software\\Mozilla\\Firefox",
1698 `Uninstalled-${updateChannel}`
1700 if (removalSuccessful && uninstalledValue == "True") {
1701 this._resetProfileNotification("uninstall");
1706 this._checkForOldBuildUpdates();
1708 // Check if Sync is configured
1709 if (Services.prefs.prefHasUserValue("services.sync.username")) {
1710 lazy.WeaveService.init();
1713 lazy.PageThumbs.init();
1715 lazy.NewTabUtils.init();
1717 Services.telemetry.setEventRecordingEnabled(
1718 "security.ui.protections",
1722 Services.telemetry.setEventRecordingEnabled("security.doh.neterror", true);
1724 lazy.PageActions.init();
1726 lazy.DoHController.init();
1728 this._firstWindowTelemetry(aWindow);
1729 this._firstWindowLoaded();
1731 this._collectStartupConditionsTelemetry();
1733 // Set the default favicon size for UI views that use the page-icon protocol.
1734 lazy.PlacesUtils.favicons.setDefaultIconURIPreferredSize(
1735 16 * aWindow.devicePixelRatio
1738 this._setPrefExpectationsAndUpdate();
1739 this._matchCBCategory();
1741 // This observes the entire privacy.trackingprotection.* pref tree.
1742 Services.prefs.addObserver(
1743 "privacy.trackingprotection",
1744 this._matchCBCategory
1746 Services.prefs.addObserver(
1747 "network.cookie.cookieBehavior",
1748 this._matchCBCategory
1750 Services.prefs.addObserver(
1751 "network.cookie.cookieBehavior.pbmode",
1752 this._matchCBCategory
1754 Services.prefs.addObserver(
1755 "network.http.referer.disallowCrossSiteRelaxingDefault",
1756 this._matchCBCategory
1758 Services.prefs.addObserver(
1759 "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation",
1760 this._matchCBCategory
1762 Services.prefs.addObserver(
1763 "privacy.partition.network_state.ocsp_cache",
1764 this._matchCBCategory
1766 Services.prefs.addObserver(
1767 "privacy.query_stripping.enabled",
1768 this._matchCBCategory
1770 Services.prefs.addObserver(
1771 "privacy.query_stripping.enabled.pbmode",
1772 this._matchCBCategory
1774 Services.prefs.addObserver(
1775 ContentBlockingCategoriesPrefs.PREF_CB_CATEGORY,
1776 this._updateCBCategory
1778 Services.prefs.addObserver(
1779 "media.autoplay.default",
1780 this._updateAutoplayPref
1782 Services.prefs.addObserver(
1783 "privacy.trackingprotection",
1784 this._setPrefExpectations
1786 Services.prefs.addObserver(
1787 "browser.contentblocking.features.strict",
1788 this._setPrefExpectationsAndUpdate
1792 _updateAutoplayPref() {
1793 const blocked = Services.prefs.getIntPref("media.autoplay.default", 1);
1794 const telemetry = Services.telemetry.getHistogramById(
1795 "AUTOPLAY_DEFAULT_SETTING_CHANGE"
1797 const labels = { 0: "allow", 1: "blockAudible", 5: "blockAll" };
1798 if (blocked in labels) {
1799 telemetry.add(labels[blocked]);
1803 _setPrefExpectations() {
1804 ContentBlockingCategoriesPrefs.setPrefExpectations();
1807 _setPrefExpectationsAndUpdate() {
1808 ContentBlockingCategoriesPrefs.setPrefExpectations();
1809 ContentBlockingCategoriesPrefs.updateCBCategory();
1812 _matchCBCategory() {
1813 ContentBlockingCategoriesPrefs.matchCBCategory();
1816 _updateCBCategory() {
1817 ContentBlockingCategoriesPrefs.updateCBCategory();
1820 _recordContentBlockingTelemetry() {
1821 Services.telemetry.setEventRecordingEnabled(
1822 "security.ui.protectionspopup",
1823 Services.prefs.getBoolPref(
1824 "security.protectionspopup.recordEventTelemetry"
1827 Services.telemetry.setEventRecordingEnabled(
1828 "security.ui.app_menu",
1829 Services.prefs.getBoolPref("security.app_menu.recordEventTelemetry")
1832 let tpEnabled = Services.prefs.getBoolPref(
1833 "privacy.trackingprotection.enabled"
1836 .getHistogramById("TRACKING_PROTECTION_ENABLED")
1839 let tpPBDisabled = Services.prefs.getBoolPref(
1840 "privacy.trackingprotection.pbmode.enabled"
1843 .getHistogramById("TRACKING_PROTECTION_PBM_DISABLED")
1844 .add(!tpPBDisabled);
1846 let cookieBehavior = Services.prefs.getIntPref(
1847 "network.cookie.cookieBehavior"
1849 Services.telemetry.getHistogramById("COOKIE_BEHAVIOR").add(cookieBehavior);
1851 let fpEnabled = Services.prefs.getBoolPref(
1852 "privacy.trackingprotection.fingerprinting.enabled"
1854 let cmEnabled = Services.prefs.getBoolPref(
1855 "privacy.trackingprotection.cryptomining.enabled"
1859 Services.prefs.getStringPref("browser.contentblocking.category", null)
1871 // Any other value is unsupported.
1876 Services.telemetry.scalarSet(
1877 "contentblocking.fingerprinting_blocking_enabled",
1880 Services.telemetry.scalarSet(
1881 "contentblocking.cryptomining_blocking_enabled",
1884 Services.telemetry.scalarSet("contentblocking.category", categoryPref);
1887 _recordDataSanitizationPrefs() {
1888 Services.telemetry.scalarSet(
1889 "datasanitization.privacy_sanitize_sanitizeOnShutdown",
1890 Services.prefs.getBoolPref("privacy.sanitize.sanitizeOnShutdown")
1892 Services.telemetry.scalarSet(
1893 "datasanitization.privacy_clearOnShutdown_cookies",
1894 Services.prefs.getBoolPref("privacy.clearOnShutdown.cookies")
1896 Services.telemetry.scalarSet(
1897 "datasanitization.privacy_clearOnShutdown_history",
1898 Services.prefs.getBoolPref("privacy.clearOnShutdown.history")
1900 Services.telemetry.scalarSet(
1901 "datasanitization.privacy_clearOnShutdown_formdata",
1902 Services.prefs.getBoolPref("privacy.clearOnShutdown.formdata")
1904 Services.telemetry.scalarSet(
1905 "datasanitization.privacy_clearOnShutdown_downloads",
1906 Services.prefs.getBoolPref("privacy.clearOnShutdown.downloads")
1908 Services.telemetry.scalarSet(
1909 "datasanitization.privacy_clearOnShutdown_cache",
1910 Services.prefs.getBoolPref("privacy.clearOnShutdown.cache")
1912 Services.telemetry.scalarSet(
1913 "datasanitization.privacy_clearOnShutdown_sessions",
1914 Services.prefs.getBoolPref("privacy.clearOnShutdown.sessions")
1916 Services.telemetry.scalarSet(
1917 "datasanitization.privacy_clearOnShutdown_offlineApps",
1918 Services.prefs.getBoolPref("privacy.clearOnShutdown.offlineApps")
1920 Services.telemetry.scalarSet(
1921 "datasanitization.privacy_clearOnShutdown_siteSettings",
1922 Services.prefs.getBoolPref("privacy.clearOnShutdown.siteSettings")
1924 Services.telemetry.scalarSet(
1925 "datasanitization.privacy_clearOnShutdown_openWindows",
1926 Services.prefs.getBoolPref("privacy.clearOnShutdown.openWindows")
1930 for (let permission of Services.perms.all) {
1931 // We consider just permissions set for http, https and file URLs.
1933 permission.type == "cookie" &&
1934 permission.capability == Ci.nsICookiePermission.ACCESS_SESSION &&
1935 ["http", "https", "file"].some(scheme =>
1936 permission.principal.schemeIs(scheme)
1942 Services.telemetry.scalarSet(
1943 "datasanitization.session_permission_exceptions",
1949 * Application shutdown handler.
1951 _onQuitApplicationGranted() {
1953 // This pref must be set here because SessionStore will use its value
1954 // on quit-application.
1955 () => this._setPrefToSaveSession(),
1957 // Call trackStartupCrashEnd here in case the delayed call on startup hasn't
1958 // yet occurred (see trackStartupCrashEnd caller in browser.js).
1959 () => Services.startup.trackStartupCrashEnd(),
1962 if (this._bookmarksBackupIdleTime) {
1963 this._userIdleService.removeIdleObserver(
1965 this._bookmarksBackupIdleTime
1967 this._bookmarksBackupIdleTime = null;
1971 () => lazy.BrowserUsageTelemetry.uninit(),
1972 () => lazy.SearchSERPTelemetry.uninit(),
1973 () => lazy.Interactions.uninit(),
1974 () => lazy.PageDataService.uninit(),
1975 () => lazy.PageThumbs.uninit(),
1976 () => lazy.NewTabUtils.uninit(),
1977 () => lazy.Normandy.uninit(),
1978 () => lazy.RFPHelper.uninit(),
1979 () => lazy.ASRouterNewTabHook.destroy(),
1981 if (AppConstants.MOZ_UPDATER) {
1982 lazy.UpdateListener.reset();
1987 for (let task of tasks) {
1991 console.error(`Error during quit-application-granted: ${ex}`);
1992 if (Cu.isInAutomation) {
1993 // This usually happens after the test harness is done collecting
1994 // test errors, thus we can't easily add a failure to it. The only
1995 // noticeable solution we have is crashing.
1996 Cc["@mozilla.org/xpcom/debug;1"]
1997 .getService(Ci.nsIDebug2)
1998 .abort(ex.filename, ex.lineNumber);
2004 // Set up a listener to enable/disable the screenshots extension
2005 // based on its preference.
2006 _monitorScreenshotsPref() {
2007 const SCREENSHOTS_PREF = "extensions.screenshots.disabled";
2008 const COMPONENT_PREF = "screenshots.browser.component.enabled";
2009 const ID = "screenshots@mozilla.org";
2010 const _checkScreenshotsPref = async () => {
2011 let addon = await lazy.AddonManager.getAddonByID(ID);
2015 let screenshotsDisabled = Services.prefs.getBoolPref(
2019 let componentEnabled = Services.prefs.getBoolPref(COMPONENT_PREF, false);
2020 if (screenshotsDisabled) {
2021 if (componentEnabled) {
2022 lazy.ScreenshotsUtils.uninitialize();
2024 await addon.disable({ allowSystemAddons: true });
2026 } else if (componentEnabled) {
2027 lazy.ScreenshotsUtils.initialize();
2028 await addon.disable({ allowSystemAddons: true });
2030 await addon.enable({ allowSystemAddons: true });
2031 lazy.ScreenshotsUtils.uninitialize();
2034 Services.prefs.addObserver(SCREENSHOTS_PREF, _checkScreenshotsPref);
2035 Services.prefs.addObserver(COMPONENT_PREF, _checkScreenshotsPref);
2036 _checkScreenshotsPref();
2039 _monitorWebcompatReporterPref() {
2040 const PREF = "extensions.webcompat-reporter.enabled";
2041 const ID = "webcompat-reporter@mozilla.org";
2042 Services.prefs.addObserver(PREF, async () => {
2043 let addon = await lazy.AddonManager.getAddonByID(ID);
2047 let enabled = Services.prefs.getBoolPref(PREF, false);
2048 if (enabled && !addon.isActive) {
2049 await addon.enable({ allowSystemAddons: true });
2050 } else if (!enabled && addon.isActive) {
2051 await addon.disable({ allowSystemAddons: true });
2056 async _setupSearchDetection() {
2057 // There is no pref for this add-on because it shouldn't be disabled.
2058 const ID = "addons-search-detection@mozilla.com";
2060 let addon = await lazy.AddonManager.getAddonByID(ID);
2062 // first time install of addon and install on firefox update
2064 (await lazy.AddonManager.maybeInstallBuiltinAddon(
2067 "resource://builtin-addons/search-detection/"
2070 if (!addon.isActive) {
2075 _monitorHTTPSOnlyPref() {
2076 const PREF_ENABLED = "dom.security.https_only_mode";
2077 const PREF_WAS_ENABLED = "dom.security.https_only_mode_ever_enabled";
2078 const _checkHTTPSOnlyPref = async () => {
2079 const enabled = Services.prefs.getBoolPref(PREF_ENABLED, false);
2080 const was_enabled = Services.prefs.getBoolPref(PREF_WAS_ENABLED, false);
2084 Services.prefs.setBoolPref(PREF_WAS_ENABLED, true);
2085 } else if (was_enabled) {
2088 Services.telemetry.scalarSet("security.https_only_mode_enabled", value);
2091 Services.prefs.addObserver(PREF_ENABLED, _checkHTTPSOnlyPref);
2092 _checkHTTPSOnlyPref();
2094 const PREF_PBM_WAS_ENABLED =
2095 "dom.security.https_only_mode_ever_enabled_pbm";
2096 const PREF_PBM_ENABLED = "dom.security.https_only_mode_pbm";
2098 const _checkHTTPSOnlyPBMPref = async () => {
2099 const enabledPBM = Services.prefs.getBoolPref(PREF_PBM_ENABLED, false);
2100 const was_enabledPBM = Services.prefs.getBoolPref(
2101 PREF_PBM_WAS_ENABLED,
2107 Services.prefs.setBoolPref(PREF_PBM_WAS_ENABLED, true);
2108 } else if (was_enabledPBM) {
2111 Services.telemetry.scalarSet(
2112 "security.https_only_mode_enabled_pbm",
2117 Services.prefs.addObserver(PREF_PBM_ENABLED, _checkHTTPSOnlyPBMPref);
2118 _checkHTTPSOnlyPBMPref();
2122 const PREF_ION_ID = "toolkit.telemetry.pioneerId";
2124 const _checkIonPref = async () => {
2125 for (let win of Services.wm.getEnumerator("navigator:browser")) {
2126 win.document.getElementById("ion-button").hidden =
2127 !Services.prefs.getStringPref(PREF_ION_ID, null);
2131 const windowListener = {
2132 onOpenWindow(xulWindow) {
2133 const win = xulWindow.docShell.domWindow;
2134 win.addEventListener("load", () => {
2135 const ionButton = win.document.getElementById("ion-button");
2137 ionButton.hidden = !Services.prefs.getStringPref(PREF_ION_ID, null);
2144 Services.prefs.addObserver(PREF_ION_ID, _checkIonPref);
2145 Services.wm.addListener(windowListener);
2149 _monitorIonStudies() {
2150 const STUDY_ADDON_COLLECTION_KEY = "pioneer-study-addons-v1";
2151 const PREF_ION_NEW_STUDIES_AVAILABLE =
2152 "toolkit.telemetry.pioneer-new-studies-available";
2154 const _badgeIcon = async () => {
2155 for (let win of Services.wm.getEnumerator("navigator:browser")) {
2157 .getElementById("ion-button")
2158 .querySelector(".toolbarbutton-badge")
2159 .classList.add("feature-callout");
2163 const windowListener = {
2164 onOpenWindow(xulWindow) {
2165 const win = xulWindow.docShell.domWindow;
2166 win.addEventListener("load", () => {
2167 const ionButton = win.document.getElementById("ion-button");
2169 const badge = ionButton.querySelector(".toolbarbutton-badge");
2171 Services.prefs.getBoolPref(PREF_ION_NEW_STUDIES_AVAILABLE, false)
2173 badge.classList.add("feature-callout");
2175 badge.classList.remove("feature-callout");
2183 // Update all open windows if the pref changes.
2184 Services.prefs.addObserver(PREF_ION_NEW_STUDIES_AVAILABLE, _badgeIcon);
2186 // Badge any currently-open windows.
2187 if (Services.prefs.getBoolPref(PREF_ION_NEW_STUDIES_AVAILABLE, false)) {
2191 lazy.RemoteSettings(STUDY_ADDON_COLLECTION_KEY).on("sync", async event => {
2192 Services.prefs.setBoolPref(PREF_ION_NEW_STUDIES_AVAILABLE, true);
2195 // When a new window opens, check if we need to badge the icon.
2196 Services.wm.addListener(windowListener);
2200 const FEATURE_PREF_ENABLED = "privacy.globalprivacycontrol.enabled";
2201 const FUNCTIONALITY_PREF_ENABLED =
2202 "privacy.globalprivacycontrol.functionality.enabled";
2203 const PREF_WAS_ENABLED = "privacy.globalprivacycontrol.was_ever_enabled";
2204 const _checkGPCPref = async () => {
2205 const feature_enabled = Services.prefs.getBoolPref(
2206 FEATURE_PREF_ENABLED,
2209 const functionality_enabled = Services.prefs.getBoolPref(
2210 FUNCTIONALITY_PREF_ENABLED,
2213 const was_enabled = Services.prefs.getBoolPref(PREF_WAS_ENABLED, false);
2215 if (feature_enabled && functionality_enabled) {
2217 Services.prefs.setBoolPref(PREF_WAS_ENABLED, true);
2218 } else if (was_enabled) {
2221 Services.telemetry.scalarSet(
2222 "security.global_privacy_control_enabled",
2227 Services.prefs.addObserver(FEATURE_PREF_ENABLED, _checkGPCPref);
2228 Services.prefs.addObserver(FUNCTIONALITY_PREF_ENABLED, _checkGPCPref);
2232 // All initial windows have opened.
2233 _onWindowsRestored: function BG__onWindowsRestored() {
2234 if (this._windowsWereRestored) {
2237 this._windowsWereRestored = true;
2239 lazy.BrowserUsageTelemetry.init();
2240 lazy.SearchSERPTelemetry.init();
2242 lazy.Interactions.init();
2243 lazy.PageDataService.init();
2244 lazy.ExtensionsUI.init();
2246 let signingRequired;
2247 if (AppConstants.MOZ_REQUIRE_SIGNING) {
2248 signingRequired = true;
2250 signingRequired = Services.prefs.getBoolPref(
2251 "xpinstall.signatures.required"
2255 if (signingRequired) {
2256 let disabledAddons = lazy.AddonManager.getStartupChanges(
2257 lazy.AddonManager.STARTUP_CHANGE_DISABLED
2259 lazy.AddonManager.getAddonsByIDs(disabledAddons).then(addons => {
2260 for (let addon of addons) {
2261 if (addon.signedState <= lazy.AddonManager.SIGNEDSTATE_MISSING) {
2262 this._notifyUnsignedAddonsDisabled();
2269 if (AppConstants.MOZ_CRASHREPORTER) {
2270 lazy.UnsubmittedCrashHandler.init();
2271 lazy.UnsubmittedCrashHandler.scheduleCheckForUnsubmittedCrashReports();
2272 lazy.FeatureGate.annotateCrashReporter();
2273 lazy.FeatureGate.observePrefChangesForCrashReportAnnotation();
2276 if (AppConstants.ASAN_REPORTER) {
2277 var { AsanReporter } = ChromeUtils.importESModule(
2278 "resource://gre/modules/AsanReporter.sys.mjs"
2280 AsanReporter.init();
2283 lazy.Sanitizer.onStartup();
2284 this._maybeShowRestoreSessionInfoBar();
2285 this._scheduleStartupIdleTasks();
2286 this._lateTasksIdleObserver = (idleService, topic, data) => {
2287 if (topic == "idle") {
2288 idleService.removeIdleObserver(
2289 this._lateTasksIdleObserver,
2290 LATE_TASKS_IDLE_TIME_SEC
2292 delete this._lateTasksIdleObserver;
2293 this._scheduleBestEffortUserIdleTasks();
2296 this._userIdleService.addIdleObserver(
2297 this._lateTasksIdleObserver,
2298 LATE_TASKS_IDLE_TIME_SEC
2301 this._monitorScreenshotsPref();
2302 this._monitorWebcompatReporterPref();
2303 this._monitorHTTPSOnlyPref();
2304 this._monitorIonPref();
2305 this._monitorIonStudies();
2306 this._setupSearchDetection();
2308 this._monitorGPCPref();
2312 * Use this function as an entry point to schedule tasks that
2313 * need to run only once after startup, and can be scheduled
2314 * by using an idle callback.
2316 * The functions scheduled here will fire from idle callbacks
2317 * once every window has finished being restored by session
2318 * restore, and it's guaranteed that they will run before
2319 * the equivalent per-window idle tasks
2320 * (from _schedulePerWindowIdleTasks in browser.js).
2322 * If you have something that can wait even further than the
2323 * per-window initialization, and is okay with not being run in some
2324 * sessions, please schedule them using
2325 * _scheduleBestEffortUserIdleTasks.
2326 * Don't be fooled by thinking that the use of the timeout parameter
2327 * will delay your function: it will just ensure that it potentially
2328 * happens _earlier_ than expected (when the timeout limit has been reached),
2329 * but it will not make it happen later (and out of order) compared
2330 * to the other ones scheduled together.
2332 _scheduleStartupIdleTasks() {
2334 // It's important that SafeBrowsing is initialized reasonably
2335 // early, so we use a maximum timeout for it.
2337 name: "SafeBrowsing.init",
2339 lazy.SafeBrowsing.init();
2345 name: "ContextualIdentityService.load",
2347 await lazy.ContextualIdentityService.load();
2348 lazy.Discovery.update();
2353 name: "PlacesUIUtils.unblockToolbars",
2355 // We postponed loading bookmarks toolbar content until startup
2356 // has finished, so we can start loading it now:
2357 lazy.PlacesUIUtils.unblockToolbars();
2362 name: "PlacesDBUtils.telemetry",
2363 condition: lazy.TelemetryUtils.isTelemetryEnabled,
2365 lazy.PlacesDBUtils.telemetry().catch(console.error);
2369 // Begin listening for incoming push messages.
2371 name: "PushService.ensureReady",
2374 lazy.PushService.wrappedJSObject.ensureReady();
2376 // NS_ERROR_NOT_AVAILABLE will get thrown for the PushService
2377 // getter if the PushService is disabled.
2378 if (ex.result != Cr.NS_ERROR_NOT_AVAILABLE) {
2386 name: "BrowserGlue._recordContentBlockingTelemetry",
2388 this._recordContentBlockingTelemetry();
2393 name: "BrowserGlue._recordDataSanitizationPrefs",
2395 this._recordDataSanitizationPrefs();
2400 name: "enableCertErrorUITelemetry",
2402 let enableCertErrorUITelemetry = Services.prefs.getBoolPref(
2403 "security.certerrors.recordEventTelemetry",
2406 Services.telemetry.setEventRecordingEnabled(
2407 "security.ui.certerror",
2408 enableCertErrorUITelemetry
2413 // Load the Login Manager data from disk off the main thread, some time
2414 // after startup. If the data is required before this runs, for example
2415 // because a restored page contains a password field, it will be loaded on
2416 // the main thread, and this initialization request will be ignored.
2418 name: "Services.logins",
2429 // Add breach alerts pref observer reasonably early so the pref flip works
2431 name: "_addBreachAlertsPrefObserver",
2433 this._addBreachAlertsPrefObserver();
2437 // Report pinning status and the type of shortcut used to launch
2439 name: "pinningStatusTelemetry",
2440 condition: AppConstants.platform == "win",
2442 let shellService = Cc[
2443 "@mozilla.org/browser/shell-service;1"
2444 ].getService(Ci.nsIWindowsShellService);
2445 let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"].getService(
2450 Services.telemetry.scalarSet(
2451 "os.environment.is_taskbar_pinned",
2452 await shellService.isCurrentAppPinnedToTaskbarAsync(
2453 winTaskbar.defaultGroupId
2456 Services.telemetry.scalarSet(
2457 "os.environment.is_taskbar_pinned_private",
2458 await shellService.isCurrentAppPinnedToTaskbarAsync(
2459 winTaskbar.defaultPrivateGroupId
2469 shortcut = Services.appinfo.processStartupShortcut;
2470 classification = shellService.classifyShortcut(shortcut);
2475 if (!classification) {
2477 classification = "OtherShortcut";
2479 classification = "Other";
2483 Services.telemetry.scalarSet(
2484 "os.environment.launch_method",
2490 // Ensure a Private Browsing Shortcut exists. This is needed in case
2491 // a user tries to use Windows functionality to pin our Private Browsing
2492 // mode icon to the Taskbar (eg: the "Pin to Taskbar" context menu item).
2493 // This is also created by the installer, but it's possible that a user
2494 // has removed it, or is running out of a zip build. The consequences of not
2495 // having a Shortcut for this are that regular Firefox will be pinned instead
2496 // of the Private Browsing version -- so it's quite important we do our best
2497 // to make sure one is available.
2498 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1762994 for additional
2501 name: "ensurePrivateBrowsingShortcutExists",
2503 AppConstants.platform == "win" &&
2504 // Pref'ed off until Private Browsing window separation is enabled by default
2505 // to avoid a situation where a user pins the Private Browsing shortcut to
2506 // the Taskbar, which will end up launching into a different Taskbar icon.
2507 lazy.NimbusFeatures.majorRelease2022.getVariable(
2508 "feltPrivacyWindowSeparation"
2510 // We don't want a shortcut if it's been disabled, eg: by enterprise policy.
2511 lazy.PrivateBrowsingUtils.enabled &&
2512 // Private Browsing shortcuts for packaged builds come with the package,
2513 // if they exist at all. We shouldn't try to create our own.
2514 !Services.sysinfo.getProperty("hasWinPackageId") &&
2515 // If we've ever done this successfully before, don't try again. The
2516 // user may have deleted the shortcut, and we don't want to force it
2518 !Services.prefs.getBoolPref(
2519 PREF_PRIVATE_BROWSING_SHORTCUT_CREATED,
2523 let shellService = Cc[
2524 "@mozilla.org/browser/shell-service;1"
2525 ].getService(Ci.nsIWindowsShellService);
2526 let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"].getService(
2531 !(await shellService.hasMatchingShortcut(
2532 winTaskbar.defaultPrivateGroupId,
2536 let appdir = Services.dirsvc.get("GreD", Ci.nsIFile);
2537 let exe = appdir.clone();
2538 exe.append(PRIVATE_BROWSING_BINARY);
2539 let strings = new Localization(
2540 ["branding/brand.ftl", "browser/browser.ftl"],
2543 let [desc] = await strings.formatValues([
2544 "private-browsing-shortcut-text-2",
2546 await shellService.createShortcut(
2551 // The code we're calling indexes from 0 instead of 1
2552 PRIVATE_BROWSING_EXE_ICON_INDEX - 1,
2553 winTaskbar.defaultPrivateGroupId,
2559 // We always set this as long as no exception has been thrown. This
2560 // ensure that it is `true` both if we created one because it didn't
2561 // exist, or if it already existed (most likely because it was created
2562 // by the installer). This avoids the need to call `hasMatchingShortcut`
2563 // again, which necessarily does pointless I/O.
2564 Services.prefs.setBoolPref(
2565 PREF_PRIVATE_BROWSING_SHORTCUT_CREATED,
2571 // Report whether Firefox is the default handler for various files types,
2572 // in particular, ".pdf".
2574 name: "IsDefaultHandlerForPDF",
2575 condition: AppConstants.platform == "win",
2577 Services.telemetry.keyedScalarSet(
2578 "os.environment.is_default_handler",
2580 lazy.ShellService.isDefaultHandlerFor(".pdf")
2585 // Install built-in themes. We already installed the active built-in
2586 // theme, if any, before UI startup.
2588 name: "BuiltInThemes.ensureBuiltInThemes",
2590 await lazy.BuiltInThemes.ensureBuiltInThemes();
2595 name: "WinTaskbarJumpList.startup",
2596 condition: AppConstants.platform == "win",
2598 // For Windows 7, initialize the jump list module.
2599 const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
2601 WINTASKBAR_CONTRACTID in Cc &&
2602 Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available
2604 const { WinTaskbarJumpList } = ChromeUtils.import(
2605 "resource:///modules/WindowsJumpLists.jsm"
2607 WinTaskbarJumpList.startup();
2612 // Report macOS Dock status
2614 name: "MacDockSupport.isAppInDock",
2615 condition: AppConstants.platform == "macosx",
2618 Services.telemetry.scalarSet(
2619 "os.environment.is_kept_in_dock",
2620 Cc["@mozilla.org/widget/macdocksupport;1"].getService(
2621 Ci.nsIMacDockSupport
2631 name: "BrowserGlue._maybeShowDefaultBrowserPrompt",
2633 this._maybeShowDefaultBrowserPrompt();
2638 name: "ScreenshotsUtils.initialize",
2641 Services.prefs.getBoolPref("screenshots.browser.component.enabled")
2643 lazy.ScreenshotsUtils.initialize();
2649 name: "trackStartupCrashEndSetTimeout",
2651 lazy.setTimeout(function () {
2652 Services.tm.idleDispatchToMainThread(
2653 Services.startup.trackStartupCrashEnd
2655 }, STARTUP_CRASHES_END_DELAY_MS);
2660 name: "handlerService.asyncInit",
2662 let handlerService = Cc[
2663 "@mozilla.org/uriloader/handler-service;1"
2664 ].getService(Ci.nsIHandlerService);
2665 handlerService.asyncInit();
2670 name: "JawsScreenReaderVersionCheck.onWindowsRestored",
2671 condition: AppConstants.platform == "win",
2673 JawsScreenReaderVersionCheck.onWindowsRestored();
2678 name: "RFPHelper.init",
2680 lazy.RFPHelper.init();
2685 name: "Blocklist.loadBlocklistAsync",
2687 lazy.Blocklist.loadBlocklistAsync();
2692 name: "TabUnloader.init",
2694 lazy.TabUnloader.init();
2698 // Run TRR performance measurements for DoH.
2700 name: "doh-rollout.trrRacer.run",
2702 let enabledPref = "doh-rollout.trrRace.enabled";
2703 let completePref = "doh-rollout.trrRace.complete";
2705 if (Services.prefs.getBoolPref(enabledPref, false)) {
2706 if (!Services.prefs.getBoolPref(completePref, false)) {
2707 new lazy.TRRRacer().run(() => {
2708 Services.prefs.setBoolPref(completePref, true);
2712 Services.prefs.addObserver(enabledPref, function observer() {
2713 if (Services.prefs.getBoolPref(enabledPref, false)) {
2714 Services.prefs.removeObserver(enabledPref, observer);
2716 if (!Services.prefs.getBoolPref(completePref, false)) {
2717 new lazy.TRRRacer().run(() => {
2718 Services.prefs.setBoolPref(completePref, true);
2727 // FOG doesn't need to be initialized _too_ early because it has a
2730 name: "initializeFOG",
2732 Services.fog.initializeFOG();
2734 // Register Glean to listen for experiment updates releated to the
2735 // "glean" feature defined in the t/c/nimbus/FeatureManifest.yaml
2736 lazy.NimbusFeatures.glean.onUpdate(() => {
2737 let cfg = lazy.NimbusFeatures.glean.getVariable(
2738 "gleanMetricConfiguration"
2740 Services.fog.setMetricsFeatureConfig(JSON.stringify(cfg));
2745 // Add the import button if this is the first startup.
2747 name: "PlacesUIUtils.ImportButton",
2749 // First check if we've already added the import button, in which
2750 // case we should check for events indicating we can remove it.
2752 Services.prefs.getBoolPref(
2753 "browser.bookmarks.addedImportButton",
2757 lazy.PlacesUIUtils.removeImportButtonWhenImportSucceeds();
2761 // Otherwise, check if this is a new profile where we need to add it.
2762 // `maybeAddImportButton` will call
2763 // `removeImportButtonWhenImportSucceeds`itself if/when it adds the
2764 // button. Doing things in this order avoids listening for removal
2767 this._isNewProfile &&
2768 // Not in automation: the button changes CUI state, breaking tests
2771 await lazy.PlacesUIUtils.maybeAddImportButton();
2777 name: "ASRouterNewTabHook.createInstance",
2779 lazy.ASRouterNewTabHook.createInstance(lazy.ASRouterDefaultConfig());
2784 name: "BackgroundUpdate",
2785 condition: AppConstants.MOZ_UPDATE_AGENT,
2787 // Never in automation! This is close to
2788 // `UpdateService.disabledForTesting`, but without creating the
2789 // service, which can perform a good deal of I/O in order to log its
2790 // state. Since this is in the startup path, we avoid all of that.
2791 let disabledForTesting =
2792 (Cu.isInAutomation ||
2793 lazy.Marionette.running ||
2794 lazy.RemoteAgent.running) &&
2795 Services.prefs.getBoolPref("app.update.disabledForTesting", false);
2796 if (!disabledForTesting) {
2798 await lazy.BackgroundUpdate.scheduleFirefoxMessagingSystemTargetingSnapshotting();
2801 "There was an error scheduling Firefox Messaging System targeting snapshotting: ",
2805 await lazy.BackgroundUpdate.maybeScheduleBackgroundUpdateTask();
2810 // Login detection service is used in fission to identify high value sites.
2812 name: "LoginDetection.init",
2814 let loginDetection = Cc[
2815 "@mozilla.org/login-detection-service;1"
2816 ].createInstance(Ci.nsILoginDetectionService);
2817 loginDetection.init();
2822 name: "BrowserGlue._collectTelemetryPiPEnabled",
2824 this._collectTelemetryPiPEnabled();
2827 // Schedule a sync (if enabled) after we've loaded
2829 name: "WeaveService",
2831 if (lazy.WeaveService.enabled) {
2832 await lazy.WeaveService.whenLoaded();
2833 lazy.WeaveService.Weave.Service.scheduler.autoConnect();
2839 name: "unblock-untrusted-modules-thread",
2840 condition: AppConstants.platform == "win",
2842 Services.obs.notifyObservers(
2844 "unblock-untrusted-modules-thread"
2850 name: "UpdateListener.maybeShowUnsupportedNotification",
2851 condition: AppConstants.MOZ_UPDATER,
2853 lazy.UpdateListener.maybeShowUnsupportedNotification();
2858 name: "QuickSuggest.init",
2860 lazy.QuickSuggest.init();
2865 name: "DAPTelemetrySender.startup",
2867 lazy.TelemetryUtils.isTelemetryEnabled &&
2868 lazy.NimbusFeatures.dapTelemetry.getVariable("enabled"),
2870 lazy.DAPTelemetrySender.startup();
2875 // Starts the JSOracle process for ORB JavaScript validation, if it hasn't started already.
2876 name: "start-orb-javascript-oracle",
2878 ChromeUtils.ensureJSOracleStarted();
2883 name: "report-attribution-provenance-telemetry",
2884 condition: lazy.TelemetryUtils.isTelemetryEnabled,
2886 await lazy.ProvenanceData.submitProvenanceTelemetry();
2891 name: "browser-startup-idle-tasks-finished",
2893 // Use idleDispatch a second time to run this after the per-window
2895 ChromeUtils.idleDispatch(() => {
2896 Services.obs.notifyObservers(
2898 "browser-startup-idle-tasks-finished"
2900 BrowserInitState._resolveStartupIdleTask();
2904 // Do NOT add anything after idle tasks finished.
2907 for (let task of idleTasks) {
2908 if ("condition" in task && !task.condition) {
2912 ChromeUtils.idleDispatch(
2914 if (!Services.startup.shuttingDown) {
2915 let startTime = Cu.now();
2921 ChromeUtils.addProfilerMarker(
2929 task.timeout ? { timeout: task.timeout } : undefined
2935 * Use this function as an entry point to schedule tasks that we hope
2936 * to run once per session, at any arbitrary point in time, and which we
2937 * are okay with sometimes not running at all.
2939 * This function will be called from an idle observer. Check the value of
2940 * LATE_TASKS_IDLE_TIME_SEC to see the current value for this idle
2943 * Note: this function may never be called if the user is never idle for the
2944 * requisite time (LATE_TASKS_IDLE_TIME_SEC). Be certain before adding
2945 * something here that it's okay that it never be run.
2947 _scheduleBestEffortUserIdleTasks() {
2949 function primaryPasswordTelemetry() {
2950 // Telemetry for primary-password - we do this after a delay as it
2951 // can cause IO if NSS/PSM has not already initialized.
2952 let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"].getService(
2955 let token = tokenDB.getInternalKeyToken();
2956 let mpEnabled = token.hasPassword;
2959 .getHistogramById("MASTER_PASSWORD_ENABLED")
2964 function GMPInstallManagerSimpleCheckAndInstall() {
2965 let { GMPInstallManager } = ChromeUtils.importESModule(
2966 "resource://gre/modules/GMPInstallManager.sys.mjs"
2968 this._gmpInstallManager = new GMPInstallManager();
2969 // We don't really care about the results, if someone is interested they
2970 // can check the log.
2971 this._gmpInstallManager.simpleCheckAndInstall().catch(() => {});
2974 function RemoteSettingsInit() {
2975 lazy.RemoteSettings.init();
2976 this._addBreachesSyncHandler();
2979 function PublicSuffixListInit() {
2980 lazy.PublicSuffixList.init();
2983 function RemoteSecuritySettingsInit() {
2984 lazy.RemoteSecuritySettings.init();
2987 function CorroborateInit() {
2988 if (Services.prefs.getBoolPref("corroborator.enabled", false)) {
2989 lazy.Corroborate.init().catch(console.error);
2993 function BrowserUsageTelemetryReportProfileCount() {
2994 lazy.BrowserUsageTelemetry.reportProfileCount();
2997 function reportAllowedAppSources() {
2998 lazy.OsEnvironment.reportAllowedAppSources();
3001 function searchBackgroundChecks() {
3002 Services.search.runBackgroundChecks();
3005 function reportInstallationTelemetry() {
3006 lazy.BrowserUsageTelemetry.reportInstallationTelemetry();
3010 for (let task of idleTasks) {
3011 ChromeUtils.idleDispatch(async () => {
3012 if (!Services.startup.shuttingDown) {
3013 let startTime = Cu.now();
3019 ChromeUtils.addProfilerMarker(
3020 "startupLateIdleTask",
3030 _addBreachesSyncHandler() {
3032 Services.prefs.getBoolPref(
3033 "signon.management.page.breach-alerts.enabled",
3038 .RemoteSettings(lazy.LoginBreaches.REMOTE_SETTINGS_COLLECTION)
3039 .on("sync", async event => {
3040 await lazy.LoginBreaches.update(event.data.current);
3045 _addBreachAlertsPrefObserver() {
3046 const BREACH_ALERTS_PREF = "signon.management.page.breach-alerts.enabled";
3047 const clearVulnerablePasswordsIfBreachAlertsDisabled = async function () {
3048 if (!Services.prefs.getBoolPref(BREACH_ALERTS_PREF)) {
3049 await lazy.LoginBreaches.clearAllPotentiallyVulnerablePasswords();
3052 clearVulnerablePasswordsIfBreachAlertsDisabled();
3053 Services.prefs.addObserver(
3055 clearVulnerablePasswordsIfBreachAlertsDisabled
3059 _quitSource: "unknown",
3060 _registerQuitSource(source) {
3061 this._quitSource = source;
3064 _onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) {
3065 // If user has already dismissed quit request, then do nothing
3066 if (aCancelQuit instanceof Ci.nsISupportsPRBool && aCancelQuit.data) {
3070 // There are several cases where we won't show a dialog here:
3071 // 1. There is only 1 tab open in 1 window
3072 // 2. browser.warnOnQuit == false
3073 // 3. The browser is currently in Private Browsing mode
3074 // 4. The browser will be restarted.
3075 // 5. The user has automatic session restore enabled and
3076 // browser.sessionstore.warnOnQuit is not set to true.
3077 // 6. The user doesn't have automatic session restore enabled
3078 // and browser.tabs.warnOnClose is not set to true.
3080 // Otherwise, we will show the "closing multiple tabs" dialog.
3082 // aQuitType == "lastwindow" is overloaded. "lastwindow" is used to indicate
3083 // "the last window is closing but we're not quitting (a non-browser window is open)"
3084 // and also "we're quitting by closing the last window".
3086 if (aQuitType == "restart" || aQuitType == "os-restart") {
3090 // browser.warnOnQuit is a hidden global boolean to override all quit prompts.
3091 if (!Services.prefs.getBoolPref("browser.warnOnQuit")) {
3095 let windowcount = 0;
3097 let pinnedcount = 0;
3098 for (let win of lazy.BrowserWindowTracker.orderedWindows) {
3103 let tabbrowser = win.gBrowser;
3105 pinnedcount += tabbrowser._numPinnedTabs;
3106 pagecount += tabbrowser.visibleTabs.length - tabbrowser._numPinnedTabs;
3110 // No windows open so no need for a warning.
3115 // browser.warnOnQuitShortcut is checked when quitting using the shortcut key.
3116 // The warning will appear even when only one window/tab is open. For other
3117 // methods of quitting, the warning only appears when there is more than one
3118 // window or tab open.
3119 let shouldWarnForShortcut =
3120 this._quitSource == "shortcut" &&
3121 Services.prefs.getBoolPref("browser.warnOnQuitShortcut");
3122 let shouldWarnForTabs =
3123 pagecount >= 2 && Services.prefs.getBoolPref("browser.tabs.warnOnClose");
3124 if (!shouldWarnForTabs && !shouldWarnForShortcut) {
3132 let win = lazy.BrowserWindowTracker.getTopWindow();
3134 // Our prompt for quitting is most important, so replace others.
3135 win.gDialogBox.replaceDialogIfOpen();
3137 let titleId, buttonLabelId;
3138 if (windowcount > 1) {
3139 // More than 1 window. Compose our own message.
3141 id: "tabbrowser-confirm-close-windows-title",
3142 args: { windowCount: windowcount },
3144 buttonLabelId = "tabbrowser-confirm-close-windows-button";
3145 } else if (shouldWarnForShortcut) {
3146 titleId = "tabbrowser-confirm-close-tabs-with-key-title";
3147 buttonLabelId = "tabbrowser-confirm-close-tabs-with-key-button";
3150 id: "tabbrowser-confirm-close-tabs-title",
3151 args: { tabCount: pagecount },
3153 buttonLabelId = "tabbrowser-confirm-close-tabs-button";
3156 // The checkbox label is different depending on whether the shortcut
3157 // was used to quit or not.
3158 let checkboxLabelId;
3159 if (shouldWarnForShortcut) {
3160 const quitKeyElement = win.document.getElementById("key_quitApplication");
3161 const quitKey = lazy.ShortcutUtils.prettifyShortcut(quitKeyElement);
3163 id: "tabbrowser-confirm-close-tabs-with-key-checkbox",
3167 checkboxLabelId = "tabbrowser-confirm-close-tabs-checkbox";
3170 const [title, buttonLabel, checkboxLabel] =
3171 win.gBrowser.tabLocalization.formatMessagesSync([
3177 let warnOnClose = { value: true };
3179 Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0 +
3180 Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1;
3181 // buttonPressed will be 0 for closing, 1 for cancel (don't close/quit)
3182 let buttonPressed = Services.prompt.confirmEx(
3190 checkboxLabel.value,
3193 Services.telemetry.setEventRecordingEnabled("close_tab_warning", true);
3194 let warnCheckbox = warnOnClose.value ? "checked" : "unchecked";
3196 let sessionWillBeRestored =
3197 Services.prefs.getIntPref("browser.startup.page") == 3 ||
3198 Services.prefs.getBoolPref("browser.sessionstore.resume_session_once");
3199 Services.telemetry.recordEvent(
3200 "close_tab_warning",
3205 source: this._quitSource,
3206 button: buttonPressed == 0 ? "close" : "cancel",
3207 warn_checkbox: warnCheckbox,
3208 closing_wins: "" + windowcount,
3209 closing_tabs: "" + (pagecount + pinnedcount),
3210 will_restore: sessionWillBeRestored ? "yes" : "no",
3214 // If the user has unticked the box, and has confirmed closing, stop showing
3216 if (buttonPressed == 0 && !warnOnClose.value) {
3217 if (shouldWarnForShortcut) {
3218 Services.prefs.setBoolPref("browser.warnOnQuitShortcut", false);
3220 Services.prefs.setBoolPref("browser.tabs.warnOnClose", false);
3224 this._quitSource = "unknown";
3226 aCancelQuit.data = buttonPressed != 0;
3231 * - imports the bookmarks html file if bookmarks database is empty, try to
3232 * restore bookmarks from a JSON backup if the backend indicates that the
3233 * database was corrupt.
3235 * These prefs can be set up by the frontend:
3237 * WARNING: setting these preferences to true will overwite existing bookmarks
3239 * - browser.places.importBookmarksHTML
3240 * Set to true will import the bookmarks.html file from the profile folder.
3241 * - browser.bookmarks.restore_default_bookmarks
3242 * Set to true by safe-mode dialog to indicate we must restore default
3245 _initPlaces: function BG__initPlaces(aInitialMigrationPerformed) {
3246 if (this._placesInitialized) {
3247 throw new Error("Cannot initialize Places more than once");
3249 this._placesInitialized = true;
3251 // We must instantiate the history service since it will tell us if we
3252 // need to import or restore bookmarks due to first-run, corruption or
3253 // forced migration (due to a major schema change).
3254 // If the database is corrupt or has been newly created we should
3255 // import bookmarks.
3256 let dbStatus = lazy.PlacesUtils.history.databaseStatus;
3258 // Show a notification with a "more info" link for a locked places.sqlite.
3259 if (dbStatus == lazy.PlacesUtils.history.DATABASE_STATUS_LOCKED) {
3260 // Note: initPlaces should always happen when the first window is ready,
3261 // in any case, better safe than sorry.
3262 this._firstWindowReady.then(() => {
3263 this._showPlacesLockedNotificationBox();
3264 this._placesBrowserInitComplete = true;
3265 Services.obs.notifyObservers(null, "places-browser-init-complete");
3270 let importBookmarks =
3271 !aInitialMigrationPerformed &&
3272 (dbStatus == lazy.PlacesUtils.history.DATABASE_STATUS_CREATE ||
3273 dbStatus == lazy.PlacesUtils.history.DATABASE_STATUS_CORRUPT);
3275 // Check if user or an extension has required to import bookmarks.html
3276 let importBookmarksHTML = false;
3278 importBookmarksHTML = Services.prefs.getBoolPref(
3279 "browser.places.importBookmarksHTML"
3281 if (importBookmarksHTML) {
3282 importBookmarks = true;
3286 // Support legacy bookmarks.html format for apps that depend on that format.
3287 let autoExportHTML = Services.prefs.getBoolPref(
3288 "browser.bookmarks.autoExportHTML",
3290 ); // Do not export.
3291 if (autoExportHTML) {
3292 // Sqlite.sys.mjs and Places shutdown happen at profile-before-change, thus,
3293 // to be on the safe side, this should run earlier.
3294 lazy.AsyncShutdown.profileChangeTeardown.addBlocker(
3295 "Places: export bookmarks.html",
3297 lazy.BookmarkHTMLUtils.exportToFile(
3298 lazy.BookmarkHTMLUtils.defaultPath
3304 // Check if Safe Mode or the user has required to restore bookmarks from
3305 // default profile's bookmarks.html
3306 let restoreDefaultBookmarks = false;
3308 restoreDefaultBookmarks = Services.prefs.getBoolPref(
3309 "browser.bookmarks.restore_default_bookmarks"
3311 if (restoreDefaultBookmarks) {
3312 // Ensure that we already have a bookmarks backup for today.
3313 await this._backupBookmarks();
3314 importBookmarks = true;
3318 // If the user did not require to restore default bookmarks, or import
3319 // from bookmarks.html, we will try to restore from JSON
3320 if (importBookmarks && !restoreDefaultBookmarks && !importBookmarksHTML) {
3321 // get latest JSON backup
3322 let lastBackupFile = await lazy.PlacesBackups.getMostRecentBackup();
3323 if (lastBackupFile) {
3324 // restore from JSON backup
3325 await lazy.BookmarkJSONUtils.importFromFile(lastBackupFile, {
3327 source: lazy.PlacesUtils.bookmarks.SOURCES.RESTORE_ON_STARTUP,
3329 importBookmarks = false;
3331 // We have created a new database but we don't have any backup available
3332 importBookmarks = true;
3333 if (await IOUtils.exists(lazy.BookmarkHTMLUtils.defaultPath)) {
3334 // If bookmarks.html is available in current profile import it...
3335 importBookmarksHTML = true;
3337 // ...otherwise we will restore defaults
3338 restoreDefaultBookmarks = true;
3343 // Import default bookmarks when necessary.
3344 // Otherwise, if any kind of import runs, default bookmarks creation should be
3345 // delayed till the import operations has finished. Not doing so would
3346 // cause them to be overwritten by the newly imported bookmarks.
3347 if (!importBookmarks) {
3348 // Now apply distribution customized bookmarks.
3349 // This should always run after Places initialization.
3351 await this._distributionCustomizer.applyBookmarks();
3356 // An import operation is about to run.
3357 let bookmarksUrl = null;
3358 if (restoreDefaultBookmarks) {
3359 // User wants to restore the default set of bookmarks shipped with the
3360 // browser, those that new profiles start with.
3361 bookmarksUrl = "chrome://browser/content/default-bookmarks.html";
3362 } else if (await IOUtils.exists(lazy.BookmarkHTMLUtils.defaultPath)) {
3363 bookmarksUrl = PathUtils.toFileURI(
3364 lazy.BookmarkHTMLUtils.defaultPath
3369 // Import from bookmarks.html file.
3371 if (Services.policies.isAllowed("defaultBookmarks")) {
3372 await lazy.BookmarkHTMLUtils.importFromURL(bookmarksUrl, {
3374 source: lazy.PlacesUtils.bookmarks.SOURCES.RESTORE_ON_STARTUP,
3378 console.error("Bookmarks.html file could be corrupt. ", e);
3381 // Now apply distribution customized bookmarks.
3382 // This should always run after Places initialization.
3383 await this._distributionCustomizer.applyBookmarks();
3388 console.error(new Error("Unable to find bookmarks.html file."));
3391 // Reset preferences, so we won't try to import again at next run
3392 if (importBookmarksHTML) {
3393 Services.prefs.setBoolPref(
3394 "browser.places.importBookmarksHTML",
3398 if (restoreDefaultBookmarks) {
3399 Services.prefs.setBoolPref(
3400 "browser.bookmarks.restore_default_bookmarks",
3406 // Initialize bookmark archiving on idle.
3407 // If the last backup has been created before the last browser session,
3408 // and is days old, be more aggressive with the idle timer.
3409 let idleTime = BOOKMARKS_BACKUP_IDLE_TIME_SEC;
3410 if (!(await lazy.PlacesBackups.hasRecentBackup())) {
3413 this._userIdleService.addIdleObserver(this, idleTime);
3414 this._bookmarksBackupIdleTime = idleTime;
3416 if (this._isNewProfile) {
3417 // New profiles may have existing bookmarks (imported from another browser or
3418 // copied into the profile) and we want to show the bookmark toolbar for them
3420 await lazy.PlacesUIUtils.maybeToggleBookmarkToolbarVisibility();
3427 // NB: deliberately after the catch so that we always do this, even if
3428 // we threw halfway through initializing in the Task above.
3429 this._placesBrowserInitComplete = true;
3430 Services.obs.notifyObservers(null, "places-browser-init-complete");
3435 * If a backup for today doesn't exist, this creates one.
3437 _backupBookmarks: function BG__backupBookmarks() {
3438 return (async function () {
3439 let lastBackupFile = await lazy.PlacesBackups.getMostRecentBackup();
3440 // Should backup bookmarks if there are no backups or the maximum
3441 // interval between backups elapsed.
3444 new Date() - lazy.PlacesBackups.getDateForFile(lastBackupFile) >
3445 BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS * 86400000
3447 let maxBackups = Services.prefs.getIntPref(
3448 "browser.bookmarks.max_backups"
3450 await lazy.PlacesBackups.create(maxBackups);
3456 * Show the notificationBox for a locked places database.
3458 _showPlacesLockedNotificationBox:
3459 function BG__showPlacesLockedNotificationBox() {
3460 var win = lazy.BrowserWindowTracker.getTopWindow();
3461 var buttons = [{ supportPage: "places-locked" }];
3463 var notifyBox = win.gBrowser.getNotificationBox();
3464 var notification = notifyBox.appendNotification(
3467 label: { "l10n-id": "places-locked-prompt" },
3468 priority: win.gNotificationBox.PRIORITY_CRITICAL_MEDIUM,
3472 notification.persistence = -1; // Until user closes it
3475 _onThisDeviceConnected() {
3476 const [title, body] = lazy.accountsL10n.formatValuesSync([
3477 "account-connection-title",
3478 "account-connection-connected",
3481 let clickCallback = (subject, topic, data) => {
3482 if (topic != "alertclickcallback") {
3485 this._openPreferences("sync");
3487 this.AlertsService.showAlertNotification(
3497 _migrateXULStoreForDocument(fromURL, toURL) {
3498 Array.from(Services.xulStore.getIDsEnumerator(fromURL)).forEach(id => {
3499 Array.from(Services.xulStore.getAttributeEnumerator(fromURL, id)).forEach(
3501 let value = Services.xulStore.getValue(fromURL, id, attr);
3502 Services.xulStore.setValue(toURL, id, attr, value);
3508 _migrateHashedKeysForXULStoreForDocument(docUrl) {
3509 Array.from(Services.xulStore.getIDsEnumerator(docUrl))
3510 .filter(id => id.startsWith("place:"))
3512 Services.xulStore.removeValue(docUrl, id, "open");
3513 let hashedId = lazy.PlacesUIUtils.obfuscateUrlForXulStore(id);
3514 Services.xulStore.setValue(docUrl, hashedId, "open", "true");
3518 // eslint-disable-next-line complexity
3519 _migrateUI: function BG__migrateUI() {
3520 // Use an increasing number to keep track of the current migration state.
3521 // Completely unrelated to the current Firefox release number.
3522 const UI_VERSION = 137;
3523 const BROWSER_DOCURL = AppConstants.BROWSER_CHROME_URL;
3525 const PROFILE_DIR = Services.dirsvc.get("ProfD", Ci.nsIFile).path;
3527 if (!Services.prefs.prefHasUserValue("browser.migration.version")) {
3528 // This is a new profile, nothing to migrate.
3529 Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
3530 this._isNewProfile = true;
3534 this._isNewProfile = false;
3535 let currentUIVersion = Services.prefs.getIntPref(
3536 "browser.migration.version"
3538 if (currentUIVersion >= UI_VERSION) {
3542 let xulStore = Services.xulStore;
3544 if (currentUIVersion < 64) {
3545 IOUtils.remove(PathUtils.join(PROFILE_DIR, "directoryLinks.json"), {
3551 currentUIVersion < 65 &&
3552 Services.prefs.getCharPref("general.config.filename", "") ==
3555 let searchInitializedPromise = new Promise(resolve => {
3556 if (Services.search.isInitialized) {
3559 const SEARCH_SERVICE_TOPIC = "browser-search-service";
3560 Services.obs.addObserver(function observer(subject, topic, data) {
3561 if (data != "init-complete") {
3564 Services.obs.removeObserver(observer, SEARCH_SERVICE_TOPIC);
3566 }, SEARCH_SERVICE_TOPIC);
3568 searchInitializedPromise.then(() => {
3570 "Bing Search Engine",
3571 "Yahoo! Search Engine",
3572 "Yandex Search Engine",
3574 for (let engineName of engineNames) {
3575 let engine = Services.search.getEngineByName(engineName);
3577 Services.search.removeEngine(engine);
3583 if (currentUIVersion < 67) {
3584 // Migrate devtools firebug theme users to light theme (bug 1378108):
3585 if (Services.prefs.getCharPref("devtools.theme") == "firebug") {
3586 Services.prefs.setCharPref("devtools.theme", "light");
3590 if (currentUIVersion < 68) {
3591 // Remove blocklists legacy storage, now relying on IndexedDB.
3592 IOUtils.remove(PathUtils.join(PROFILE_DIR, "kinto.sqlite"), {
3597 if (currentUIVersion < 69) {
3598 // Clear old social prefs from profile (bug 1460675)
3599 let socialPrefs = Services.prefs.getBranch("social.");
3601 let socialPrefsArray = socialPrefs.getChildList("");
3602 for (let item of socialPrefsArray) {
3603 Services.prefs.clearUserPref("social." + item);
3608 if (currentUIVersion < 70) {
3609 // Migrate old ctrl-tab pref to new one in existing profiles. (This code
3610 // doesn't run at all in new profiles.)
3611 Services.prefs.setBoolPref(
3612 "browser.ctrlTab.recentlyUsedOrder",
3613 Services.prefs.getBoolPref("browser.ctrlTab.previews", false)
3615 Services.prefs.clearUserPref("browser.ctrlTab.previews");
3616 // Remember that we migrated the pref in case we decide to flip it for
3618 Services.prefs.setBoolPref("browser.ctrlTab.migrated", true);
3621 if (currentUIVersion < 71) {
3622 // Clear legacy saved prefs for content handlers.
3623 let savedContentHandlers = Services.prefs.getChildList(
3624 "browser.contentHandlers.types"
3626 for (let savedHandlerPref of savedContentHandlers) {
3627 Services.prefs.clearUserPref(savedHandlerPref);
3631 if (currentUIVersion < 72) {
3632 // Migrate performance tool's recording interval value from msec to usec.
3633 let pref = "devtools.performance.recording.interval";
3634 Services.prefs.setIntPref(
3636 Services.prefs.getIntPref(pref, 1) * 1000
3640 if (currentUIVersion < 73) {
3641 // Remove blocklist JSON local dumps in profile.
3642 IOUtils.remove(PathUtils.join(PROFILE_DIR, "blocklists"), {
3646 IOUtils.remove(PathUtils.join(PROFILE_DIR, "blocklists-preview"), {
3650 for (const filename of ["addons.json", "plugins.json", "gfx.json"]) {
3651 // Some old versions used to dump without subfolders. Clean them while we are at it.
3652 const path = PathUtils.join(PROFILE_DIR, `blocklists-${filename}`);
3653 IOUtils.remove(path, { ignoreAbsent: true });
3657 if (currentUIVersion < 76) {
3658 // Clear old onboarding prefs from profile (bug 1462415)
3659 let onboardingPrefs = Services.prefs.getBranch("browser.onboarding.");
3660 if (onboardingPrefs) {
3661 let onboardingPrefsArray = onboardingPrefs.getChildList("");
3662 for (let item of onboardingPrefsArray) {
3663 Services.prefs.clearUserPref("browser.onboarding." + item);
3668 if (currentUIVersion < 77) {
3669 // Remove currentset from all the toolbars
3676 for (let toolbarId of toolbars) {
3677 xulStore.removeValue(BROWSER_DOCURL, toolbarId, "currentset");
3681 if (currentUIVersion < 78) {
3682 Services.prefs.clearUserPref("browser.search.region");
3685 if (currentUIVersion < 79) {
3686 // The handler app service will read this. We need to wait with migrating
3687 // until the handler service has started up, so just set a pref here.
3688 Services.prefs.setCharPref("browser.handlers.migrations", "30boxes");
3691 if (currentUIVersion < 80) {
3692 let hosts = Services.prefs.getCharPref("network.proxy.no_proxies_on");
3693 // remove "localhost" and "127.0.0.1" from the no_proxies_on list
3694 const kLocalHosts = new Set(["localhost", "127.0.0.1"]);
3697 .filter(host => !kLocalHosts.has(host))
3699 Services.prefs.setCharPref("network.proxy.no_proxies_on", hosts);
3702 if (currentUIVersion < 81) {
3703 // Reset homepage pref for users who have it set to a default from before Firefox 4:
3704 // <locale>.(start|start2|start3).mozilla.(com|org)
3705 if (lazy.HomePage.overridden) {
3706 const DEFAULT = lazy.HomePage.getDefault();
3707 let value = lazy.HomePage.get();
3708 let updated = value.replace(
3709 /https?:\/\/([\w\-]+\.)?start\d*\.mozilla\.(org|com)[^|]*/gi,
3712 if (updated != value) {
3713 if (updated == DEFAULT) {
3714 lazy.HomePage.reset();
3717 lazy.HomePage.safeSet(value);
3723 if (currentUIVersion < 82) {
3724 this._migrateXULStoreForDocument(
3725 "chrome://browser/content/browser.xul",
3726 "chrome://browser/content/browser.xhtml"
3730 if (currentUIVersion < 83) {
3731 Services.prefs.clearUserPref("browser.search.reset.status");
3734 if (currentUIVersion < 84) {
3735 // Reset flash "always allow/block" permissions
3736 // We keep session and policy permissions, which could both be
3737 // the result of enterprise policy settings. "Never/Always allow"
3738 // settings for flash were actually time-bound on recent-ish Firefoxen,
3739 // so we remove EXPIRE_TIME entries, too.
3740 const { EXPIRE_NEVER, EXPIRE_TIME } = Services.perms;
3741 let flashPermissions = Services.perms
3742 .getAllWithTypePrefix("plugin:flash")
3745 p.type == "plugin:flash" &&
3746 (p.expireType == EXPIRE_NEVER || p.expireType == EXPIRE_TIME)
3748 flashPermissions.forEach(p => Services.perms.removePermission(p));
3751 // currentUIVersion < 85 is missing due to the following:
3752 // Origianlly, Bug #1568900 added currentUIVersion 85 but was targeting FF70 release.
3753 // In between it landing in FF70, Bug #1562601 (currentUIVersion 86) landed and
3754 // was uplifted to Beta. To make sure the migration doesn't get skipped, the
3755 // code block that was at 85 has been moved/bumped to currentUIVersion 87.
3757 if (currentUIVersion < 86) {
3758 // If the user has set "media.autoplay.allow-muted" to false
3759 // migrate that to media.autoplay.default=BLOCKED_ALL.
3761 Services.prefs.prefHasUserValue("media.autoplay.allow-muted") &&
3762 !Services.prefs.getBoolPref("media.autoplay.allow-muted") &&
3763 !Services.prefs.prefHasUserValue("media.autoplay.default") &&
3764 Services.prefs.getIntPref("media.autoplay.default") ==
3765 Ci.nsIAutoplay.BLOCKED
3767 Services.prefs.setIntPref(
3768 "media.autoplay.default",
3769 Ci.nsIAutoplay.BLOCKED_ALL
3772 Services.prefs.clearUserPref("media.autoplay.allow-muted");
3775 if (currentUIVersion < 87) {
3776 const TRACKING_TABLE_PREF = "urlclassifier.trackingTable";
3777 const CUSTOM_BLOCKING_PREF =
3778 "browser.contentblocking.customBlockList.preferences.ui.enabled";
3779 // Check if user has set custom tables pref, and show custom block list UI
3780 // in the about:preferences#privacy custom panel.
3781 if (Services.prefs.prefHasUserValue(TRACKING_TABLE_PREF)) {
3782 Services.prefs.setBoolPref(CUSTOM_BLOCKING_PREF, true);
3786 if (currentUIVersion < 88) {
3787 // If the user the has "browser.contentblocking.category = custom", but has
3788 // the exact same settings as "standard", move them once to "standard". This is
3789 // to reset users who we may have moved accidentally, or moved to get ETP early.
3790 let category_prefs = [
3791 "network.cookie.cookieBehavior",
3792 "privacy.trackingprotection.pbmode.enabled",
3793 "privacy.trackingprotection.enabled",
3794 "privacy.trackingprotection.socialtracking.enabled",
3795 "privacy.trackingprotection.fingerprinting.enabled",
3796 "privacy.trackingprotection.cryptomining.enabled",
3799 Services.prefs.getStringPref(
3800 "browser.contentblocking.category",
3804 let shouldMigrate = true;
3805 for (let pref of category_prefs) {
3806 if (Services.prefs.prefHasUserValue(pref)) {
3807 shouldMigrate = false;
3810 if (shouldMigrate) {
3811 Services.prefs.setStringPref(
3812 "browser.contentblocking.category",
3819 if (currentUIVersion < 89) {
3820 // This file was renamed in https://bugzilla.mozilla.org/show_bug.cgi?id=1595636.
3821 this._migrateXULStoreForDocument(
3822 "chrome://devtools/content/framework/toolbox-window.xul",
3823 "chrome://devtools/content/framework/toolbox-window.xhtml"
3827 if (currentUIVersion < 90) {
3828 this._migrateXULStoreForDocument(
3829 "chrome://browser/content/places/historySidebar.xul",
3830 "chrome://browser/content/places/historySidebar.xhtml"
3832 this._migrateXULStoreForDocument(
3833 "chrome://browser/content/places/places.xul",
3834 "chrome://browser/content/places/places.xhtml"
3836 this._migrateXULStoreForDocument(
3837 "chrome://browser/content/places/bookmarksSidebar.xul",
3838 "chrome://browser/content/places/bookmarksSidebar.xhtml"
3842 // Clear socks proxy values if they were shared from http, to prevent
3843 // websocket breakage after bug 1577862 (see bug 969282).
3845 currentUIVersion < 91 &&
3846 Services.prefs.getBoolPref("network.proxy.share_proxy_settings", false) &&
3847 Services.prefs.getIntPref("network.proxy.type", 0) == 1
3849 let httpProxy = Services.prefs.getCharPref("network.proxy.http", "");
3850 let httpPort = Services.prefs.getIntPref("network.proxy.http_port", 0);
3851 let socksProxy = Services.prefs.getCharPref("network.proxy.socks", "");
3852 let socksPort = Services.prefs.getIntPref("network.proxy.socks_port", 0);
3853 if (httpProxy && httpProxy == socksProxy && httpPort == socksPort) {
3854 Services.prefs.setCharPref(
3855 "network.proxy.socks",
3856 Services.prefs.getCharPref("network.proxy.backup.socks", "")
3858 Services.prefs.setIntPref(
3859 "network.proxy.socks_port",
3860 Services.prefs.getIntPref("network.proxy.backup.socks_port", 0)
3865 if (currentUIVersion < 92) {
3866 // privacy.userContext.longPressBehavior pref was renamed and changed to a boolean
3867 let longpress = Services.prefs.getIntPref(
3868 "privacy.userContext.longPressBehavior",
3871 if (longpress == 1) {
3872 Services.prefs.setBoolPref(
3873 "privacy.userContext.newTabContainerOnLeftClick.enabled",
3879 if (currentUIVersion < 93) {
3880 // The Gecko Profiler Addon is now an internal component. Remove the old
3881 // addon, and enable the new UI.
3883 function enableProfilerButton(wasAddonActive) {
3884 // Enable the feature pref. This will add it to the customization palette,
3885 // but not to the the navbar.
3886 Services.prefs.setBoolPref(
3887 "devtools.performance.popup.feature-flag",
3891 if (wasAddonActive) {
3892 const { ProfilerMenuButton } = ChromeUtils.import(
3893 "resource://devtools/client/performance-new/popup/menu-button.jsm.js"
3895 if (!ProfilerMenuButton.isInNavbar()) {
3896 // The profiler menu button is not enabled. Turn it on now.
3897 const win = lazy.BrowserWindowTracker.getTopWindow();
3898 if (win && win.document) {
3899 ProfilerMenuButton.addToNavbar(win.document);
3907 addonPromise = lazy.AddonManager.getAddonByID(
3908 "geckoprofiler@mozilla.com"
3912 "Could not access the AddonManager to upgrade the profile. This is most " +
3913 "likely because the upgrader is being run from an xpcshell test where " +
3914 "the AddonManager is not initialized."
3917 Promise.resolve(addonPromise).then(addon => {
3919 // Either the addon wasn't installed, or the call to getAddonByID failed.
3922 // Remove the old addon.
3923 const wasAddonActive = addon.isActive;
3926 .catch(console.error)
3927 .then(() => enableProfilerButton(wasAddonActive))
3928 .catch(console.error);
3932 // Clear unused socks proxy backup values - see bug 1625773.
3933 if (currentUIVersion < 94) {
3934 let backup = Services.prefs.getCharPref("network.proxy.backup.socks", "");
3935 let backupPort = Services.prefs.getIntPref(
3936 "network.proxy.backup.socks_port",
3939 let socksProxy = Services.prefs.getCharPref("network.proxy.socks", "");
3940 let socksPort = Services.prefs.getIntPref("network.proxy.socks_port", 0);
3941 if (backup == socksProxy) {
3942 Services.prefs.clearUserPref("network.proxy.backup.socks");
3944 if (backupPort == socksPort) {
3945 Services.prefs.clearUserPref("network.proxy.backup.socks_port");
3949 if (currentUIVersion < 95) {
3950 const oldPrefName = "media.autoplay.enabled.user-gestures-needed";
3951 const oldPrefValue = Services.prefs.getBoolPref(oldPrefName, true);
3952 const newPrefValue = oldPrefValue ? 0 : 1;
3953 Services.prefs.setIntPref("media.autoplay.blocking_policy", newPrefValue);
3954 Services.prefs.clearUserPref(oldPrefName);
3957 if (currentUIVersion < 96) {
3958 const oldPrefName = "browser.urlbar.openViewOnFocus";
3959 const oldPrefValue = Services.prefs.getBoolPref(oldPrefName, true);
3960 Services.prefs.setBoolPref(
3961 "browser.urlbar.suggest.topsites",
3964 Services.prefs.clearUserPref(oldPrefName);
3967 if (currentUIVersion < 97) {
3968 let userCustomizedWheelMax = Services.prefs.prefHasUserValue(
3969 "general.smoothScroll.mouseWheel.durationMaxMS"
3971 let userCustomizedWheelMin = Services.prefs.prefHasUserValue(
3972 "general.smoothScroll.mouseWheel.durationMinMS"
3975 if (!userCustomizedWheelMin && !userCustomizedWheelMax) {
3976 // If the user has an existing profile but hasn't customized the wheel
3977 // animation duration, they will now get the new default values. This
3978 // condition used to set a migrationPercent pref to 0, so that users
3979 // upgrading an older profile would gradually have their wheel animation
3980 // speed migrated to the new values. However, that "gradual migration"
3981 // was phased out by FF 86, so we don't need to set that pref anymore.
3982 } else if (userCustomizedWheelMin && !userCustomizedWheelMax) {
3983 // If they customized just one of the two, save the old value for the
3984 // other one as well, because the two values go hand-in-hand and we
3985 // don't want to move just one to a new value and leave the other one
3986 // at a customized value. In both of these cases, we leave the "migration
3987 // complete" percentage at 100, because they have customized this and
3988 // don't need any further migration.
3989 Services.prefs.setIntPref(
3990 "general.smoothScroll.mouseWheel.durationMaxMS",
3993 } else if (!userCustomizedWheelMin && userCustomizedWheelMax) {
3994 // Same as above case, but for the other pref.
3995 Services.prefs.setIntPref(
3996 "general.smoothScroll.mouseWheel.durationMinMS",
4000 // The last remaining case is if they customized both values, in which
4001 // case also don't need to do anything; the user's customized values
4002 // will be retained and respected.
4006 if (currentUIVersion < 98) {
4007 Services.prefs.clearUserPref("browser.search.cohort");
4010 if (currentUIVersion < 99) {
4011 Services.prefs.clearUserPref("security.tls.version.enable-deprecated");
4014 if (currentUIVersion < 102) {
4015 // In Firefox 83, we moved to a dynamic button, so it needs to be removed
4016 // from default placement. This is done early enough that it doesn't
4017 // impact adding new managed bookmarks.
4018 const { CustomizableUI } = ChromeUtils.importESModule(
4019 "resource:///modules/CustomizableUI.sys.mjs"
4021 CustomizableUI.removeWidgetFromArea("managed-bookmarks");
4024 // We have to rerun these because we had to use 102 on beta.
4025 // They were 101 and 102 before.
4026 if (currentUIVersion < 103) {
4027 // Set a pref if the bookmarks toolbar was already visible,
4028 // so we can keep it visible when navigating away from newtab
4029 let bookmarksToolbarWasVisible =
4030 Services.xulStore.getValue(
4035 if (bookmarksToolbarWasVisible) {
4036 // Migrate the user to the "always visible" value. See firefox.js for
4037 // the other possible states.
4038 Services.prefs.setCharPref(
4039 "browser.toolbars.bookmarks.visibility",
4043 Services.xulStore.removeValue(
4049 Services.prefs.clearUserPref(
4050 "browser.livebookmarks.migrationAttemptsLeft"
4054 // For existing profiles, continue putting bookmarks in the
4055 // "other bookmarks" folder.
4056 if (currentUIVersion < 104) {
4057 Services.prefs.setCharPref(
4058 "browser.bookmarks.defaultLocation",
4063 // Renamed and flipped the logic of a pref to make its purpose more clear.
4064 if (currentUIVersion < 105) {
4065 const oldPrefName = "browser.urlbar.imeCompositionClosesPanel";
4066 const oldPrefValue = Services.prefs.getBoolPref(oldPrefName, true);
4067 Services.prefs.setBoolPref(
4068 "browser.urlbar.keepPanelOpenDuringImeComposition",
4071 Services.prefs.clearUserPref(oldPrefName);
4074 // Initialize the new browser.urlbar.showSuggestionsBeforeGeneral pref.
4075 if (currentUIVersion < 106) {
4076 lazy.UrlbarPrefs.initializeShowSearchSuggestionsFirstPref();
4079 if (currentUIVersion < 107) {
4080 // Migrate old http URIs for mailto handlers to their https equivalents.
4081 // The handler service will do this. We need to wait with migrating
4082 // until the handler service has started up, so just set a pref here.
4083 const kPref = "browser.handlers.migrations";
4084 // We might have set up another migration further up. Create an array,
4085 // and drop empty strings resulting from the `split`:
4086 let migrations = Services.prefs
4087 .getCharPref(kPref, "")
4090 migrations.push("secure-mail");
4091 Services.prefs.setCharPref(kPref, migrations.join(","));
4094 if (currentUIVersion < 108) {
4095 // Migrate old ctrlTab pref to new ctrlTab pref
4096 let defaultValue = false;
4097 let oldPrefName = "browser.ctrlTab.recentlyUsedOrder";
4098 let oldPrefDefault = true;
4099 // Use old pref value if the user used Ctrl+Tab before, elsewise use new default value
4100 if (Services.prefs.getBoolPref("browser.engagement.ctrlTab.has-used")) {
4101 let newPrefValue = Services.prefs.getBoolPref(
4105 Services.prefs.setBoolPref(
4106 "browser.ctrlTab.sortByRecentlyUsed",
4110 Services.prefs.setBoolPref(
4111 "browser.ctrlTab.sortByRecentlyUsed",
4117 if (currentUIVersion < 109) {
4118 // Migrate old pref to new pref
4120 Services.prefs.prefHasUserValue("signon.recipes.remoteRecipesEnabled")
4122 // Fetch the previous value of signon.recipes.remoteRecipesEnabled and assign it to signon.recipes.remoteRecipes.enabled.
4123 Services.prefs.setBoolPref(
4124 "signon.recipes.remoteRecipes.enabled",
4125 Services.prefs.getBoolPref(
4126 "signon.recipes.remoteRecipesEnabled",
4130 //Then clear user pref
4131 Services.prefs.clearUserPref("signon.recipes.remoteRecipesEnabled");
4135 if (currentUIVersion < 120) {
4136 // Migrate old titlebar bool pref to new int-based one.
4137 const oldPref = "browser.tabs.drawInTitlebar";
4138 const newPref = "browser.tabs.inTitlebar";
4139 if (Services.prefs.prefHasUserValue(oldPref)) {
4140 // We may have int prefs for builds between bug 1736518 and bug 1739539.
4141 const oldPrefType = Services.prefs.getPrefType(oldPref);
4142 if (oldPrefType == Services.prefs.PREF_BOOL) {
4143 Services.prefs.setIntPref(
4145 Services.prefs.getBoolPref(oldPref) ? 1 : 0
4148 Services.prefs.setIntPref(
4150 Services.prefs.getIntPref(oldPref)
4153 Services.prefs.clearUserPref(oldPref);
4157 if (currentUIVersion < 121) {
4158 // Migrate stored uris and convert them to use hashed keys
4159 this._migrateHashedKeysForXULStoreForDocument(BROWSER_DOCURL);
4160 this._migrateHashedKeysForXULStoreForDocument(
4161 "chrome://browser/content/places/bookmarksSidebar.xhtml"
4163 this._migrateHashedKeysForXULStoreForDocument(
4164 "chrome://browser/content/places/historySidebar.xhtml"
4168 if (currentUIVersion < 122) {
4169 // Migrate xdg-desktop-portal pref from old to new prefs.
4171 const oldPref = "widget.use-xdg-desktop-portal";
4172 if (Services.prefs.getBoolPref(oldPref)) {
4173 Services.prefs.setIntPref(
4174 "widget.use-xdg-desktop-portal.file-picker",
4177 Services.prefs.setIntPref(
4178 "widget.use-xdg-desktop-portal.mime-handler",
4182 Services.prefs.clearUserPref(oldPref);
4186 // Bug 1745248: Due to multiple backouts, do not use UI Version 123
4187 // as this version is most likely set for the Nightly channel
4189 if (currentUIVersion < 124) {
4190 // Migrate "extensions.formautofill.available" and
4191 // "extensions.formautofill.creditCards.available" from old to new prefs
4192 const oldFormAutofillModule = "extensions.formautofill.available";
4193 const oldCreditCardsAvailable =
4194 "extensions.formautofill.creditCards.available";
4195 const newCreditCardsAvailable =
4196 "extensions.formautofill.creditCards.supported";
4197 const newAddressesAvailable =
4198 "extensions.formautofill.addresses.supported";
4199 if (Services.prefs.prefHasUserValue(oldFormAutofillModule)) {
4200 let moduleAvailability = Services.prefs.getCharPref(
4201 oldFormAutofillModule
4203 if (moduleAvailability == "on") {
4204 Services.prefs.setCharPref(newAddressesAvailable, moduleAvailability);
4205 Services.prefs.setCharPref(
4206 newCreditCardsAvailable,
4207 Services.prefs.getBoolPref(oldCreditCardsAvailable) ? "on" : "off"
4211 if (moduleAvailability == "off") {
4212 Services.prefs.setCharPref(
4213 newCreditCardsAvailable,
4216 Services.prefs.setCharPref(newAddressesAvailable, moduleAvailability);
4220 // after migrating, clear old prefs so we can remove them later.
4221 Services.prefs.clearUserPref(oldFormAutofillModule);
4222 Services.prefs.clearUserPref(oldCreditCardsAvailable);
4225 if (currentUIVersion < 125) {
4226 // Bug 1756243 - Clear PiP cached coordinates since we changed their
4227 // coordinate space.
4228 const PIP_PLAYER_URI =
4229 "chrome://global/content/pictureinpicture/player.xhtml";
4231 for (let value of ["left", "top", "width", "height"]) {
4232 Services.xulStore.removeValue(
4234 "picture-in-picture",
4239 console.error("Failed to clear XULStore PiP values: ", ex);
4243 if (currentUIVersion < 126) {
4244 // Bug 1747343 - Add a pref to set the default download action to "Always
4245 // ask" so the UCT dialog will be opened for mime types that are not
4246 // stored already. Users who wanted this behavior would have disabled the
4247 // experimental pref browser.download.improvements_to_download_panel so we
4248 // can migrate its inverted value to this new pref.
4250 !Services.prefs.getBoolPref(
4251 "browser.download.improvements_to_download_panel",
4255 Services.prefs.setBoolPref(
4256 "browser.download.always_ask_before_handling_new_types",
4262 // Bug 1769071: The UI Version 127 was used for a clean up code that is not
4263 // necessary anymore. Please do not use 127 because this number is probably
4264 // set in Nightly and Beta channel.
4266 // Non-macOS only (on macOS we've never used the tmp folder for downloads):
4267 if (AppConstants.platform != "macosx" && currentUIVersion < 128) {
4268 // Bug 1738574 - Add a pref to start downloads in the tmp folder.
4269 // Users who wanted this behavior would have disabled the experimental
4270 // pref browser.download.improvements_to_download_panel so we
4271 // can migrate its inverted value to this new pref.
4273 !Services.prefs.getBoolPref(
4274 "browser.download.improvements_to_download_panel",
4278 Services.prefs.setBoolPref(
4279 "browser.download.start_downloads_in_tmp_dir",
4285 function migrateXULAttributeToStyle(url, id, attr) {
4287 let value = Services.xulStore.getValue(url, id, attr);
4289 Services.xulStore.setValue(url, id, "style", `${attr}: ${value}px;`);
4292 console.error(`Error migrating ${id}'s ${attr} value: `, ex);
4296 // Bug 1792748 used version 129 with a buggy variant of the sidebar width
4297 // migration. This version is already in use in the nightly channel, so it
4298 // shouldn't be used.
4300 // Bug 1793366: migrate sidebar persisted attribute from width to style.
4301 if (currentUIVersion < 130) {
4302 migrateXULAttributeToStyle(BROWSER_DOCURL, "sidebar-box", "width");
4305 // Migration 131 was moved to 133 to allow for an uplift.
4307 if (currentUIVersion < 132) {
4308 // These attributes are no longer persisted, thus remove them from xulstore.
4310 "chrome://browser/content/places/bookmarkProperties.xhtml",
4311 "chrome://browser/content/places/bookmarkProperties2.xhtml",
4313 for (let attr of ["width", "screenX", "screenY"]) {
4314 xulStore.removeValue(url, "bookmarkproperties", attr);
4319 if (currentUIVersion < 133) {
4320 xulStore.removeValue(BROWSER_DOCURL, "urlbar-container", "width");
4323 // Migration 134 was removed because it was no longer necessary.
4325 if (currentUIVersion < 135 && AppConstants.platform == "linux") {
4326 // Avoid changing titlebar setting for users that used to had it off.
4328 if (!Services.prefs.prefHasUserValue("browser.tabs.inTitlebar")) {
4329 let de = Services.appinfo.desktopEnvironment;
4330 let oldDefault = de.includes("gnome") || de.includes("pantheon");
4332 Services.prefs.setIntPref("browser.tabs.inTitlebar", 0);
4336 console.error("Error migrating tabsInTitlebar setting", e);
4340 if (currentUIVersion < 136) {
4341 migrateXULAttributeToStyle(
4342 "chrome://browser/content/places/places.xhtml",
4348 if (currentUIVersion < 137) {
4349 // The default value for enabling smooth scrolls is now false if the
4350 // user prefers reduced motion. If the value was previously set, do
4351 // not reset it, but if it was not explicitly set preserve the old
4354 !Services.prefs.prefHasUserValue("general.smoothScroll") &&
4355 Services.appinfo.prefersReducedMotion
4357 Services.prefs.setBoolPref("general.smoothScroll", true);
4361 // Update the migration version.
4362 Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
4365 async _showUpgradeDialog() {
4366 const data = await lazy.OnboardingMessageProvider.getUpgradeMessage();
4367 const { gBrowser } = lazy.BrowserWindowTracker.getTopWindow();
4369 // We'll be adding a new tab open the tab-modal dialog in.
4372 const upgradeTabsProgressListener = {
4373 onLocationChange(aBrowser) {
4374 if (aBrowser === tab.linkedBrowser) {
4375 lazy.setTimeout(() => {
4376 // We're now far enough along in the load that we no longer have to
4377 // worry about a call to onLocationChange triggering SubDialog.abort,
4378 // so display the dialog
4380 type: "SHOW_SPOTLIGHT",
4383 lazy.SpecialMessageActions.handleAction(config, tab.linkedBrowser);
4385 gBrowser.removeTabsProgressListener(upgradeTabsProgressListener);
4391 // Make sure we're ready to show the dialog once onLocationChange gets
4393 gBrowser.addTabsProgressListener(upgradeTabsProgressListener);
4395 tab = gBrowser.addTrustedTab("about:home", {
4396 relatedToCurrent: true,
4399 gBrowser.selectedTab = tab;
4402 async _showAboutWelcomeModal() {
4403 const { gBrowser } = lazy.BrowserWindowTracker.getTopWindow();
4404 const data = await lazy.NimbusFeatures.aboutwelcome.getAllVariables();
4407 type: "SHOW_SPOTLIGHT",
4410 template: "multistage",
4411 id: data?.id || "ABOUT_WELCOME_MODAL",
4412 backdrop: data?.backdrop,
4413 screens: data?.screens,
4414 UTMTerm: data?.UTMTerm,
4419 lazy.SpecialMessageActions.handleAction(config, gBrowser);
4422 async _maybeShowDefaultBrowserPrompt() {
4423 // Highest priority is about:welcome window modal experiment
4424 // Second highest priority is the upgrade dialog, which can include a "primary
4425 // browser" request and is limited in various ways, e.g., major upgrades.
4427 lazy.BrowserHandler.firstRunProfile &&
4428 lazy.NimbusFeatures.aboutwelcome.getVariable("showModal")
4430 this._showAboutWelcomeModal();
4433 const dialogVersion = 106;
4434 const dialogVersionPref = "browser.startup.upgradeDialog.version";
4435 const dialogReason = await (async () => {
4436 if (!lazy.BrowserHandler.majorUpgrade) {
4439 const lastVersion = Services.prefs.getIntPref(dialogVersionPref, 0);
4440 if (lastVersion > dialogVersion) {
4441 return "newer-shown";
4443 if (lastVersion === dialogVersion) {
4444 return "already-shown";
4447 // Check the default branch as enterprise policies can set prefs there.
4448 const defaultPrefs = Services.prefs.getDefaultBranch("");
4450 !defaultPrefs.getBoolPref(
4451 "browser.messaging-system.whatsNewPanel.enabled",
4455 return "no-whatsNew";
4457 if (!defaultPrefs.getBoolPref("browser.aboutwelcome.enabled", true)) {
4458 return "no-welcome";
4460 if (!Services.policies.isAllowed("postUpdateCustomPage")) {
4461 return "disallow-postUpdate";
4464 const useMROnboarding =
4465 lazy.NimbusFeatures.majorRelease2022.getVariable("onboarding");
4466 const showUpgradeDialog =
4468 lazy.NimbusFeatures.upgradeDialog.getVariable("enabled");
4470 return showUpgradeDialog ? "" : "disabled";
4473 // Record why the dialog is showing or not.
4474 Services.telemetry.setEventRecordingEnabled("upgrade_dialog", true);
4475 Services.telemetry.recordEvent(
4479 dialogReason || "satisfied"
4482 // Show the upgrade dialog if allowed and remember the version.
4483 if (!dialogReason) {
4484 Services.prefs.setIntPref(dialogVersionPref, dialogVersion);
4485 this._showUpgradeDialog();
4489 const willPrompt = await DefaultBrowserCheck.willCheckDefaultBrowser(
4490 /* isStartupCheck */ true
4493 let win = lazy.BrowserWindowTracker.getTopWindow();
4494 DefaultBrowserCheck.prompt(win);
4495 } else if (await lazy.QuickSuggest.maybeShowOnboardingDialog()) {
4499 await lazy.ASRouter.waitForInitialized;
4500 lazy.ASRouter.sendTriggerMessage({
4502 lazy.BrowserWindowTracker.getTopWindow()?.gBrowser.selectedBrowser,
4503 // triggerId and triggerContext
4504 id: "defaultBrowserCheck",
4505 context: { willShowDefaultPrompt: willPrompt, source: "startup" },
4510 * Only show the infobar when canRestoreLastSession and the pref value == 1
4512 async _maybeShowRestoreSessionInfoBar() {
4513 let count = Services.prefs.getIntPref(
4514 "browser.startup.couldRestoreSession.count",
4517 if (count < 0 || count >= 2) {
4521 // We don't show the infobar right after the update which establishes this pref
4522 // Increment the counter so we can consider it next time
4523 Services.prefs.setIntPref(
4524 "browser.startup.couldRestoreSession.count",
4530 const win = lazy.BrowserWindowTracker.getTopWindow();
4531 // We've restarted at least once; we will show the notification if possible.
4532 // We can't do that if there's no session to restore, or this is a private window.
4534 !lazy.SessionStore.canRestoreLastSession ||
4535 lazy.PrivateBrowsingUtils.isWindowPrivate(win)
4540 Services.prefs.setIntPref(
4541 "browser.startup.couldRestoreSession.count",
4545 const messageFragment = win.document.createDocumentFragment();
4546 const message = win.document.createElement("span");
4547 const icon = win.document.createElement("img");
4548 icon.src = "chrome://browser/skin/menu.svg";
4549 icon.setAttribute("data-l10n-name", "icon");
4550 icon.className = "inline-icon";
4551 message.appendChild(icon);
4552 messageFragment.appendChild(message);
4553 win.document.l10n.setAttributes(
4555 "restore-session-startup-suggestion-message"
4560 "l10n-id": "restore-session-startup-suggestion-button",
4563 win.PanelUI.selectAndMarkItem([
4564 "appMenu-history-button",
4565 "appMenu-restoreSession",
4571 const notifyBox = win.gBrowser.getNotificationBox();
4572 const notification = notifyBox.appendNotification(
4573 "startup-restore-session-suggestion",
4575 label: messageFragment,
4576 priority: notifyBox.PRIORITY_INFO_MEDIUM,
4580 // Don't allow it to be immediately hidden:
4581 notification.timeout = Date.now() + 3000;
4585 * Open preferences even if there are no open windows.
4587 _openPreferences(...args) {
4588 let chromeWindow = lazy.BrowserWindowTracker.getTopWindow();
4590 chromeWindow.openPreferences(...args);
4594 if (Services.appShell.hiddenDOMWindow.openPreferences) {
4595 Services.appShell.hiddenDOMWindow.openPreferences(...args);
4599 _openURLInNewWindow(url) {
4600 let urlString = Cc["@mozilla.org/supports-string;1"].createInstance(
4601 Ci.nsISupportsString
4603 urlString.data = url;
4604 return new Promise(resolve => {
4605 let win = Services.ww.openWindow(
4607 AppConstants.BROWSER_CHROME_URL,
4609 "chrome,all,dialog=no",
4612 win.addEventListener(
4623 * Called as an observer when Sync's "display URIs" notification is fired.
4625 * We open the received URIs in background tabs.
4627 async _onDisplaySyncURIs(data) {
4629 // The payload is wrapped weirdly because of how Sync does notifications.
4630 const URIs = data.wrappedJSObject.object;
4632 // win can be null, but it's ok, we'll assign it later in openTab()
4633 let win = lazy.BrowserWindowTracker.getTopWindow({ private: false });
4635 const openTab = async URI => {
4638 win = await this._openURLInNewWindow(URI.uri);
4639 let tabs = win.gBrowser.tabs;
4640 tab = tabs[tabs.length - 1];
4642 tab = win.gBrowser.addWebTab(URI.uri);
4644 tab.attention = true;
4648 const firstTab = await openTab(URIs[0]);
4649 await Promise.all(URIs.slice(1).map(URI => openTab(URI)));
4651 const deviceName = URIs[0].sender && URIs[0].sender.name;
4652 let titleL10nId, body;
4653 if (URIs.length == 1) {
4654 // Due to bug 1305895, tabs from iOS may not have device information, so
4655 // we have separate strings to handle those cases. (See Also
4656 // unnamedTabsArrivingNotificationNoDevice.body below)
4657 titleL10nId = deviceName
4659 id: "account-single-tab-arriving-from-device-title",
4660 args: { deviceName },
4662 : { id: "account-single-tab-arriving-title" };
4663 // Use the page URL as the body. We strip the fragment and query (after
4664 // the `?` and `#` respectively) to reduce size, and also format it the
4665 // same way that the url bar would.
4666 let url = URIs[0].uri.replace(/([?#]).*$/, "$1");
4667 const wasTruncated = url.length < URIs[0].uri.length;
4668 url = lazy.BrowserUIUtils.trimURL(url);
4670 body = await lazy.accountsL10n.formatValue(
4671 "account-single-tab-arriving-truncated-url",
4678 titleL10nId = { id: "account-multiple-tabs-arriving-title" };
4679 const allKnownSender = URIs.every(URI => URI.sender != null);
4680 const allSameDevice =
4682 URIs.every(URI => URI.sender.id == URIs[0].sender.id);
4684 if (allSameDevice) {
4685 bodyL10nId = deviceName
4686 ? "account-multiple-tabs-arriving-from-single-device"
4687 : "account-multiple-tabs-arriving-from-unknown-device";
4689 bodyL10nId = "account-multiple-tabs-arriving-from-multiple-devices";
4692 body = await lazy.accountsL10n.formatValue(bodyL10nId, {
4694 tabCount: URIs.length,
4697 const title = await lazy.accountsL10n.formatValue(titleL10nId);
4699 const clickCallback = (obsSubject, obsTopic, obsData) => {
4700 if (obsTopic == "alertclickcallback") {
4701 win.gBrowser.selectedTab = firstTab;
4705 // Specify an icon because on Windows no icon is shown at the moment
4707 if (AppConstants.platform == "win") {
4708 imageURL = "chrome://branding/content/icon64.png";
4710 this.AlertsService.showAlertNotification(
4719 console.error("Error displaying tab(s) received by Sync: ", ex);
4723 async _onVerifyLoginNotification({ body, title, url }) {
4726 if (AppConstants.platform == "win") {
4727 imageURL = "chrome://branding/content/icon64.png";
4729 let win = lazy.BrowserWindowTracker.getTopWindow({ private: false });
4731 win = await this._openURLInNewWindow(url);
4732 let tabs = win.gBrowser.tabs;
4733 tab = tabs[tabs.length - 1];
4735 tab = win.gBrowser.addWebTab(url);
4737 tab.attention = true;
4738 let clickCallback = (subject, topic, data) => {
4739 if (topic != "alertclickcallback") {
4742 win.gBrowser.selectedTab = tab;
4746 this.AlertsService.showAlertNotification(
4755 console.error("Error notifying of a verify login event: ", ex);
4759 _onDeviceConnected(deviceName) {
4760 const [title, body] = lazy.accountsL10n.formatValuesSync([
4761 { id: "account-connection-title" },
4763 ? { id: "account-connection-connected-with", args: { deviceName } }
4764 : { id: "account-connection-connected-with-noname" },
4767 let clickCallback = async (subject, topic, data) => {
4768 if (topic != "alertclickcallback") {
4771 let url = await lazy.FxAccounts.config.promiseManageDevicesURI(
4772 "device-connected-notification"
4774 let win = lazy.BrowserWindowTracker.getTopWindow({ private: false });
4776 this._openURLInNewWindow(url);
4778 win.gBrowser.addWebTab(url);
4783 this.AlertsService.showAlertNotification(
4792 console.error("Error notifying of a new Sync device: ", ex);
4796 _onDeviceDisconnected() {
4797 const [title, body] = lazy.accountsL10n.formatValuesSync([
4798 "account-connection-title",
4799 "account-connection-disconnected",
4802 let clickCallback = (subject, topic, data) => {
4803 if (topic != "alertclickcallback") {
4806 this._openPreferences("sync");
4808 this.AlertsService.showAlertNotification(
4818 _updateFxaBadges(win) {
4819 let fxaButton = win.document.getElementById("fxa-toolbar-menu-button");
4820 let badge = fxaButton?.querySelector(".toolbarbutton-badge");
4822 let state = lazy.UIState.get();
4824 state.status == lazy.UIState.STATUS_LOGIN_FAILED ||
4825 state.status == lazy.UIState.STATUS_NOT_VERIFIED
4827 // If the fxa toolbar button is in the toolbox, we display the notification
4828 // on the fxa button instead of the app menu.
4829 let navToolbox = win.document.getElementById("navigator-toolbox");
4830 let isFxAButtonShown = navToolbox.contains(fxaButton);
4831 if (isFxAButtonShown) {
4832 state.status == lazy.UIState.STATUS_LOGIN_FAILED
4833 ? fxaButton?.setAttribute("badge-status", state.status)
4834 : badge?.classList.add("feature-callout");
4836 lazy.AppMenuNotifications.showBadgeOnlyNotification(
4837 "fxa-needs-authentication"
4841 fxaButton?.removeAttribute("badge-status");
4842 badge?.classList.remove("feature-callout");
4843 lazy.AppMenuNotifications.removeNotification("fxa-needs-authentication");
4847 _collectTelemetryPiPEnabled() {
4848 Services.telemetry.setEventRecordingEnabled(
4849 "pictureinpicture.settings",
4852 Services.telemetry.setEventRecordingEnabled("pictureinpicture", true);
4854 const TOGGLE_ENABLED_PREF =
4855 "media.videocontrols.picture-in-picture.video-toggle.enabled";
4857 const observe = (subject, topic, data) => {
4858 const enabled = Services.prefs.getBoolPref(TOGGLE_ENABLED_PREF, false);
4859 Services.telemetry.scalarSet("pictureinpicture.toggle_enabled", enabled);
4861 // Record events when preferences change
4862 if (topic === "nsPref:changed") {
4864 Services.telemetry.recordEvent(
4865 "pictureinpicture.settings",
4873 Services.prefs.addObserver(TOGGLE_ENABLED_PREF, observe);
4877 QueryInterface: ChromeUtils.generateQI([
4879 "nsISupportsWeakReference",
4883 var ContentBlockingCategoriesPrefs = {
4884 PREF_CB_CATEGORY: "browser.contentblocking.category",
4885 PREF_STRICT_DEF: "browser.contentblocking.features.strict",
4886 switchingCategory: false,
4888 setPrefExpectations() {
4889 // The prefs inside CATEGORY_PREFS are initial values.
4890 // If the pref remains null, then it will expect the default value.
4891 // The "standard" category is defined as expecting all 5 default values.
4892 this.CATEGORY_PREFS = {
4894 "network.cookie.cookieBehavior": null,
4895 "network.cookie.cookieBehavior.pbmode": null,
4896 "privacy.trackingprotection.pbmode.enabled": null,
4897 "privacy.trackingprotection.enabled": null,
4898 "privacy.trackingprotection.socialtracking.enabled": null,
4899 "privacy.trackingprotection.fingerprinting.enabled": null,
4900 "privacy.trackingprotection.cryptomining.enabled": null,
4901 "privacy.trackingprotection.emailtracking.enabled": null,
4902 "privacy.trackingprotection.emailtracking.pbmode.enabled": null,
4903 "privacy.annotate_channels.strict_list.enabled": null,
4904 "network.http.referer.disallowCrossSiteRelaxingDefault": null,
4905 "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation":
4907 "privacy.partition.network_state.ocsp_cache": null,
4908 "privacy.query_stripping.enabled": null,
4909 "privacy.query_stripping.enabled.pbmode": null,
4912 "network.cookie.cookieBehavior": null,
4913 "network.cookie.cookieBehavior.pbmode": null,
4914 "privacy.trackingprotection.pbmode.enabled": null,
4915 "privacy.trackingprotection.enabled": null,
4916 "privacy.trackingprotection.socialtracking.enabled": null,
4917 "privacy.trackingprotection.fingerprinting.enabled": null,
4918 "privacy.trackingprotection.cryptomining.enabled": null,
4919 "privacy.trackingprotection.emailtracking.enabled": null,
4920 "privacy.trackingprotection.emailtracking.pbmode.enabled": null,
4921 "privacy.annotate_channels.strict_list.enabled": null,
4922 "network.http.referer.disallowCrossSiteRelaxingDefault": null,
4923 "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation":
4925 "privacy.partition.network_state.ocsp_cache": null,
4926 "privacy.query_stripping.enabled": null,
4927 "privacy.query_stripping.enabled.pbmode": null,
4930 let type = "strict";
4931 let rulesArray = Services.prefs
4932 .getStringPref(this.PREF_STRICT_DEF)
4934 for (let item of rulesArray) {
4937 this.CATEGORY_PREFS[type][
4938 "privacy.trackingprotection.enabled"
4942 this.CATEGORY_PREFS[type][
4943 "privacy.trackingprotection.enabled"
4947 this.CATEGORY_PREFS[type][
4948 "privacy.trackingprotection.pbmode.enabled"
4952 this.CATEGORY_PREFS[type][
4953 "privacy.trackingprotection.pbmode.enabled"
4957 this.CATEGORY_PREFS[type][
4958 "privacy.trackingprotection.fingerprinting.enabled"
4962 this.CATEGORY_PREFS[type][
4963 "privacy.trackingprotection.fingerprinting.enabled"
4967 this.CATEGORY_PREFS[type][
4968 "privacy.trackingprotection.cryptomining.enabled"
4972 this.CATEGORY_PREFS[type][
4973 "privacy.trackingprotection.cryptomining.enabled"
4977 this.CATEGORY_PREFS[type][
4978 "privacy.trackingprotection.socialtracking.enabled"
4982 this.CATEGORY_PREFS[type][
4983 "privacy.trackingprotection.socialtracking.enabled"
4987 this.CATEGORY_PREFS[type][
4988 "privacy.trackingprotection.emailtracking.enabled"
4992 this.CATEGORY_PREFS[type][
4993 "privacy.trackingprotection.emailtracking.enabled"
4996 case "emailTPPrivate":
4997 this.CATEGORY_PREFS[type][
4998 "privacy.trackingprotection.emailtracking.pbmode.enabled"
5001 case "-emailTPPrivate":
5002 this.CATEGORY_PREFS[type][
5003 "privacy.trackingprotection.emailtracking.pbmode.enabled"
5007 this.CATEGORY_PREFS[type][
5008 "privacy.annotate_channels.strict_list.enabled"
5012 this.CATEGORY_PREFS[type][
5013 "privacy.annotate_channels.strict_list.enabled"
5017 this.CATEGORY_PREFS[type][
5018 "network.http.referer.disallowCrossSiteRelaxingDefault"
5022 this.CATEGORY_PREFS[type][
5023 "network.http.referer.disallowCrossSiteRelaxingDefault"
5027 this.CATEGORY_PREFS[type][
5028 "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation"
5032 this.CATEGORY_PREFS[type][
5033 "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation"
5037 this.CATEGORY_PREFS[type][
5038 "privacy.partition.network_state.ocsp_cache"
5042 this.CATEGORY_PREFS[type][
5043 "privacy.partition.network_state.ocsp_cache"
5047 this.CATEGORY_PREFS[type]["privacy.query_stripping.enabled"] = true;
5050 this.CATEGORY_PREFS[type]["privacy.query_stripping.enabled"] = false;
5053 this.CATEGORY_PREFS[type][
5054 "privacy.query_stripping.enabled.pbmode"
5058 this.CATEGORY_PREFS[type][
5059 "privacy.query_stripping.enabled.pbmode"
5062 case "cookieBehavior0":
5063 this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] =
5064 Ci.nsICookieService.BEHAVIOR_ACCEPT;
5066 case "cookieBehavior1":
5067 this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] =
5068 Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN;
5070 case "cookieBehavior2":
5071 this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] =
5072 Ci.nsICookieService.BEHAVIOR_REJECT;
5074 case "cookieBehavior3":
5075 this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] =
5076 Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN;
5078 case "cookieBehavior4":
5079 this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] =
5080 Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER;
5082 case "cookieBehavior5":
5083 this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] =
5084 Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
5086 case "cookieBehaviorPBM0":
5087 this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] =
5088 Ci.nsICookieService.BEHAVIOR_ACCEPT;
5090 case "cookieBehaviorPBM1":
5091 this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] =
5092 Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN;
5094 case "cookieBehaviorPBM2":
5095 this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] =
5096 Ci.nsICookieService.BEHAVIOR_REJECT;
5098 case "cookieBehaviorPBM3":
5099 this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] =
5100 Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN;
5102 case "cookieBehaviorPBM4":
5103 this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] =
5104 Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER;
5106 case "cookieBehaviorPBM5":
5107 this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] =
5108 Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
5111 console.error(`Error: Unknown rule observed ${item}`);
5117 * Checks if CB prefs match perfectly with one of our pre-defined categories.
5119 prefsMatch(category) {
5120 // The category pref must be either unset, or match.
5122 Services.prefs.prefHasUserValue(this.PREF_CB_CATEGORY) &&
5123 Services.prefs.getStringPref(this.PREF_CB_CATEGORY) != category
5127 for (let pref in this.CATEGORY_PREFS[category]) {
5128 let value = this.CATEGORY_PREFS[category][pref];
5129 if (value == null) {
5130 if (Services.prefs.prefHasUserValue(pref)) {
5134 let prefType = Services.prefs.getPrefType(pref);
5136 (prefType == Services.prefs.PREF_BOOL &&
5137 Services.prefs.getBoolPref(pref) != value) ||
5138 (prefType == Services.prefs.PREF_INT &&
5139 Services.prefs.getIntPref(pref) != value) ||
5140 (prefType == Services.prefs.PREF_STRING &&
5141 Services.prefs.getStringPref(pref) != value)
5151 if (this.switchingCategory) {
5154 // If PREF_CB_CATEGORY is not set match users to a Content Blocking category. Check if prefs fit
5155 // perfectly into strict or standard, otherwise match with custom. If PREF_CB_CATEGORY has previously been set,
5156 // a change of one of these prefs necessarily puts us in "custom".
5157 if (this.prefsMatch("standard")) {
5158 Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "standard");
5159 } else if (this.prefsMatch("strict")) {
5160 Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "strict");
5162 Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "custom");
5165 // If there is a custom policy which changes a related pref, then put the user in custom so
5166 // they still have access to other content blocking prefs, and to keep our default definitions
5168 let policy = Services.policies.getActivePolicies();
5169 if (policy && (policy.EnableTrackingProtection || policy.Cookies)) {
5170 Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "custom");
5174 updateCBCategory() {
5176 this.switchingCategory ||
5177 !Services.prefs.prefHasUserValue(this.PREF_CB_CATEGORY)
5181 // Turn on switchingCategory flag, to ensure that when the individual prefs that change as a result
5182 // of the category change do not trigger yet another category change.
5183 this.switchingCategory = true;
5184 let value = Services.prefs.getStringPref(this.PREF_CB_CATEGORY);
5185 this.setPrefsToCategory(value);
5186 this.switchingCategory = false;
5190 * Sets all user-exposed content blocking preferences to values that match the selected category.
5192 setPrefsToCategory(category) {
5193 // Leave prefs as they were if we are switching to "custom" category.
5194 if (category == "custom") {
5198 for (let pref in this.CATEGORY_PREFS[category]) {
5199 let value = this.CATEGORY_PREFS[category][pref];
5200 if (!Services.prefs.prefIsLocked(pref)) {
5201 if (value == null) {
5202 Services.prefs.clearUserPref(pref);
5204 switch (Services.prefs.getPrefType(pref)) {
5205 case Services.prefs.PREF_BOOL:
5206 Services.prefs.setBoolPref(pref, value);
5208 case Services.prefs.PREF_INT:
5209 Services.prefs.setIntPref(pref, value);
5211 case Services.prefs.PREF_STRING:
5212 Services.prefs.setStringPref(pref, value);
5222 * ContentPermissionIntegration is responsible for showing the user
5223 * simple permission prompts when content requests additional
5226 * While there are some built-in permission prompts, createPermissionPrompt
5227 * can also be overridden by system add-ons or tests to provide new ones.
5229 * This override ability is provided by Integration.sys.mjs. See
5230 * PermissionUI.sys.mjs for an example of how to provide a new prompt
5233 const ContentPermissionIntegration = {
5235 * Creates a PermissionPrompt for a given permission type and
5236 * nsIContentPermissionRequest.
5238 * @param {string} type
5239 * The type of the permission request from content. This normally
5240 * matches the "type" field of an nsIContentPermissionType, but it
5241 * can be something else if the permission does not use the
5242 * nsIContentPermissionRequest model. Note that this type might also
5243 * be different from the permission key used in the permissions
5245 * Example: "geolocation"
5246 * @param {nsIContentPermissionRequest} request
5247 * The request for a permission from content.
5248 * @return {PermissionPrompt} (see PermissionUI.sys.mjs),
5249 * or undefined if the type cannot be handled.
5251 createPermissionPrompt(type, request) {
5253 case "geolocation": {
5254 return new lazy.PermissionUI.GeolocationPermissionPrompt(request);
5257 return new lazy.PermissionUI.XRPermissionPrompt(request);
5259 case "desktop-notification": {
5260 return new lazy.PermissionUI.DesktopNotificationPermissionPrompt(
5264 case "persistent-storage": {
5265 return new lazy.PermissionUI.PersistentStoragePermissionPrompt(request);
5268 return new lazy.PermissionUI.MIDIPermissionPrompt(request);
5270 case "storage-access": {
5271 return new lazy.PermissionUI.StorageAccessPermissionPrompt(request);
5278 export function ContentPermissionPrompt() {}
5280 ContentPermissionPrompt.prototype = {
5281 classID: Components.ID("{d8903bf6-68d5-4e97-bcd1-e4d3012f721a}"),
5283 QueryInterface: ChromeUtils.generateQI(["nsIContentPermissionPrompt"]),
5286 * This implementation of nsIContentPermissionPrompt.prompt ensures
5287 * that there's only one nsIContentPermissionType in the request,
5288 * and that it's of type nsIContentPermissionType. Failing to
5289 * satisfy either of these conditions will result in this method
5290 * throwing NS_ERRORs. If the combined ContentPermissionIntegration
5291 * cannot construct a prompt for this particular request, an
5292 * NS_ERROR_FAILURE will be thrown.
5294 * Any time an error is thrown, the nsIContentPermissionRequest is
5295 * cancelled automatically.
5297 * @param {nsIContentPermissionRequest} request
5298 * The request that we're to show a prompt for.
5301 if (request.element && request.element.fxrPermissionPrompt) {
5302 // For Firefox Reality on Desktop, switch to a different mechanism to
5303 // prompt the user since fewer permissions are available and since many
5304 // UI dependencies are not availabe.
5305 request.element.fxrPermissionPrompt(request);
5311 // Only allow exactly one permission request here.
5312 let types = request.types.QueryInterface(Ci.nsIArray);
5313 if (types.length != 1) {
5314 throw Components.Exception(
5315 "Expected an nsIContentPermissionRequest with only 1 type.",
5316 Cr.NS_ERROR_UNEXPECTED
5320 type = types.queryElementAt(0, Ci.nsIContentPermissionType).type;
5321 let combinedIntegration = lazy.Integration.contentPermission.getCombined(
5322 ContentPermissionIntegration
5325 let permissionPrompt = combinedIntegration.createPermissionPrompt(
5329 if (!permissionPrompt) {
5330 throw Components.Exception(
5331 `Failed to handle permission of type ${type}`,
5336 permissionPrompt.prompt();
5343 let schemeHistogram = Services.telemetry.getKeyedHistogramById(
5344 "PERMISSION_REQUEST_ORIGIN_SCHEME"
5348 if (request.principal.schemeIs("http")) {
5350 } else if (request.principal.schemeIs("https")) {
5354 // If the request principal is not available at this point,
5355 // the request has likely been cancelled before being shown to the
5356 // user. We shouldn't record this request.
5357 if (ex.result != Cr.NS_ERROR_FAILURE) {
5362 schemeHistogram.add(type, scheme);
5364 let userInputHistogram = Services.telemetry.getKeyedHistogramById(
5365 "PERMISSION_REQUEST_HANDLING_USER_INPUT"
5367 userInputHistogram.add(
5369 request.hasValidTransientUserGestureActivation
5374 export var DefaultBrowserCheck = {
5376 const shellService = win.getShellService();
5377 const needPin = await shellService.doesAppNeedPin();
5379 win.MozXULElement.insertFTLIfNeeded("branding/brand.ftl");
5380 win.MozXULElement.insertFTLIfNeeded(
5381 "browser/defaultBrowserNotification.ftl"
5383 // Resolve the translations for the prompt elements and return only the
5386 AppConstants.platform == "macosx"
5387 ? "default-browser-prompt-message-pin-mac"
5388 : "default-browser-prompt-message-pin";
5389 let [promptTitle, promptMessage, askLabel, yesButton, notNowButton] = (
5390 await win.document.l10n.formatMessages([
5393 ? "default-browser-prompt-title-pin"
5394 : "default-browser-prompt-title-alt",
5397 id: needPin ? pinMessage : "default-browser-prompt-message-alt",
5399 { id: "default-browser-prompt-checkbox-not-again-label" },
5402 ? "default-browser-prompt-button-primary-pin"
5403 : "default-browser-prompt-button-primary-alt",
5405 { id: "default-browser-prompt-button-secondary" },
5407 ).map(({ value }) => value);
5409 let ps = Services.prompt;
5411 ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0 +
5412 ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_1 +
5413 ps.BUTTON_POS_0_DEFAULT;
5414 let rv = await ps.asyncConfirmEx(
5415 win.browsingContext,
5416 ps.MODAL_TYPE_INTERNAL_WINDOW,
5424 false, // checkbox state
5425 { headerIconURL: "chrome://branding/content/icon32.png" }
5427 let buttonNumClicked = rv.get("buttonNumClicked");
5428 let checkboxState = rv.get("checked");
5429 if (buttonNumClicked == 0) {
5430 shellService.setAsDefault();
5431 shellService.pinToTaskbar();
5433 if (checkboxState) {
5434 shellService.shouldCheckDefaultBrowser = false;
5438 let resultEnum = buttonNumClicked * 2 + !checkboxState;
5440 .getHistogramById("BROWSER_SET_DEFAULT_RESULT")
5443 /* Don't break if Telemetry is acting up. */
5448 * Checks if the default browser check prompt will be shown.
5449 * @param {boolean} isStartupCheck
5450 * If true, prefs will be set and telemetry will be recorded.
5451 * @returns {boolean} True if the default browser check prompt will be shown.
5453 async willCheckDefaultBrowser(isStartupCheck) {
5454 let win = lazy.BrowserWindowTracker.getTopWindow();
5455 let shellService = win.getShellService();
5457 // Perform default browser checking.
5458 if (!shellService) {
5463 !AppConstants.DEBUG && shellService.shouldCheckDefaultBrowser;
5465 // Even if we shouldn't check the default browser, we still continue when
5466 // isStartupCheck = true to set prefs and telemetry.
5467 if (!shouldCheck && !isStartupCheck) {
5471 // Skip the "Set Default Browser" check during first-run or after the
5472 // browser has been run a few times.
5473 const skipDefaultBrowserCheck =
5474 Services.prefs.getBoolPref(
5475 "browser.shell.skipDefaultBrowserCheckOnFirstRun"
5477 !Services.prefs.getBoolPref(
5478 "browser.shell.didSkipDefaultBrowserCheckOnFirstRun"
5481 let promptCount = Services.prefs.getIntPref(
5482 "browser.shell.defaultBrowserCheckCount",
5486 // If SessionStartup's state is not initialized, checking sessionType will set
5487 // its internal state to "do not restore".
5488 await lazy.SessionStartup.onceInitialized;
5489 let willRecoverSession =
5490 lazy.SessionStartup.sessionType == lazy.SessionStartup.RECOVER_SESSION;
5492 // Don't show the prompt if we're already the default browser.
5493 let isDefault = false;
5494 let isDefaultError = false;
5496 isDefault = shellService.isDefaultBrowser(isStartupCheck, false);
5498 isDefaultError = true;
5501 if (isDefault && isStartupCheck) {
5502 let now = Math.floor(Date.now() / 1000).toString();
5503 Services.prefs.setCharPref(
5504 "browser.shell.mostRecentDateSetAsDefault",
5509 let willPrompt = shouldCheck && !isDefault && !willRecoverSession;
5512 if (skipDefaultBrowserCheck) {
5513 if (isStartupCheck) {
5514 Services.prefs.setBoolPref(
5515 "browser.shell.didSkipDefaultBrowserCheckOnFirstRun",
5522 if (isStartupCheck) {
5523 Services.prefs.setIntPref(
5524 "browser.shell.defaultBrowserCheckCount",
5528 if (!AppConstants.RELEASE_OR_BETA && promptCount > 3) {
5534 if (isStartupCheck) {
5536 // Report default browser status on startup to telemetry
5537 // so we can track whether we are the default.
5539 .getHistogramById("BROWSER_IS_USER_DEFAULT")
5542 .getHistogramById("BROWSER_IS_USER_DEFAULT_ERROR")
5543 .add(isDefaultError);
5545 .getHistogramById("BROWSER_SET_DEFAULT_ALWAYS_CHECK")
5548 .getHistogramById("BROWSER_SET_DEFAULT_DIALOG_PROMPT_RAWCOUNT")
5551 /* Don't break the default prompt if telemetry is broken. */
5560 * Prompts users who have an outdated JAWS screen reader informing
5561 * them they need to update JAWS or switch to esr. Can be removed
5564 var JawsScreenReaderVersionCheck = {
5568 Services.obs.addObserver(this, "a11y-init-or-shutdown", true);
5571 QueryInterface: ChromeUtils.generateQI([
5573 "nsISupportsWeakReference",
5576 observe(subject, topic, data) {
5577 if (topic == "a11y-init-or-shutdown" && data == "1") {
5578 Services.tm.dispatchToMainThread(() => this._checkVersionAndPrompt());
5582 onWindowsRestored() {
5583 Services.tm.dispatchToMainThread(() => this._checkVersionAndPrompt());
5586 _checkVersionAndPrompt() {
5587 // Make sure we only prompt for versions of JAWS we do not
5588 // support and never prompt if e10s is disabled or if we're on
5591 !Services.appinfo.shouldBlockIncompatJaws ||
5592 !Services.appinfo.browserTabsRemoteAutostart ||
5593 AppConstants.NIGHTLY_BUILD
5598 let win = lazy.BrowserWindowTracker.getTopWindow();
5599 if (!win || !win.gBrowser || !win.gBrowser.selectedBrowser) {
5600 Services.console.logStringMessage(
5601 "Content access support for older versions of JAWS is disabled " +
5602 "due to compatibility issues with this version of Firefox."
5604 this._prompted = false;
5608 // Only prompt once per session
5609 if (this._prompted) {
5612 this._prompted = true;
5614 let browser = win.gBrowser.selectedBrowser;
5616 // Prompt JAWS users to let them know they need to update
5617 let promptMessage = win.gNavigatorBundle.getFormattedString(
5618 "e10s.accessibilityNotice.jawsMessage",
5619 [lazy.gBrandBundle.GetStringFromName("brandShortName")]
5622 // main option: an Ok button, keeps running with content accessibility disabled
5624 label: win.gNavigatorBundle.getString(
5625 "e10s.accessibilityNotice.acceptButton.label"
5627 accessKey: win.gNavigatorBundle.getString(
5628 "e10s.accessibilityNotice.acceptButton.accesskey"
5631 // If the user invoked the button option remove the notification,
5632 // otherwise keep the alert icon around in the address bar.
5633 notification.remove();
5637 popupIconURL: "chrome://browser/skin/e10s-64@2x.png",
5638 persistWhileVisible: true,
5643 notification = win.PopupNotifications.show(
5645 "e10s_enabled_with_incompat_jaws",
5656 * AboutHomeStartupCache is responsible for reading and writing the
5657 * initial about:home document from the HTTP cache as a startup
5658 * performance optimization. It only works when the "privileged about
5659 * content process" is enabled and when ENABLED_PREF is set to true.
5661 * See https://firefox-source-docs.mozilla.org/browser/components/newtab/docs/v2-system-addon/about_home_startup_cache.html
5662 * for further details.
5664 export var AboutHomeStartupCache = {
5665 ABOUT_HOME_URI_STRING: "about:home",
5666 SCRIPT_EXTENSION: "script",
5667 ENABLED_PREF: "browser.startup.homepage.abouthome_cache.enabled",
5668 PRELOADED_NEWTAB_PREF: "browser.newtab.preload",
5669 LOG_LEVEL_PREF: "browser.startup.homepage.abouthome_cache.loglevel",
5671 // It's possible that the layout of about:home will change such that
5672 // we want to invalidate any pre-existing caches. We do this by setting
5673 // this meta key in the nsICacheEntry for the page.
5675 // The version is currently set to the build ID, meaning that the cache
5676 // is invalidated after every upgrade (like the main startup cache).
5677 CACHE_VERSION_META_KEY: "version",
5679 LOG_NAME: "AboutHomeStartupCache",
5681 // These messages are used to request the "privileged about content process"
5682 // to create the cached document, and then to receive that document.
5683 CACHE_REQUEST_MESSAGE: "AboutHomeStartupCache:CacheRequest",
5684 CACHE_RESPONSE_MESSAGE: "AboutHomeStartupCache:CacheResponse",
5685 CACHE_USAGE_RESULT_MESSAGE: "AboutHomeStartupCache:UsageResult",
5687 // When a "privileged about content process" is launched, this message is
5688 // sent to give it some nsIInputStream's for the about:home document they
5690 SEND_STREAMS_MESSAGE: "AboutHomeStartupCache:InputStreams",
5692 // This time in ms is used to debounce messages that are broadcast to
5693 // all about:newtab's, or the preloaded about:newtab. We use those
5694 // messages as a signal that it's likely time to refresh the cache.
5695 CACHE_DEBOUNCE_RATE_MS: 5000,
5697 // This is how long we'll block the AsyncShutdown while waiting for
5698 // the cache to write. If we fail to write within that time, we will
5699 // allow the shutdown to proceed.
5700 SHUTDOWN_CACHE_WRITE_TIMEOUT_MS: 1000,
5702 // The following values are as possible values for the
5703 // browser.startup.abouthome_cache_result scalar. Keep these in sync with the
5704 // scalar definition in Scalars.yaml. See setDeferredResult for more
5706 CACHE_RESULT_SCALARS: {
5715 NOT_LOADING_ABOUTHOME: 8,
5716 PRELOADING_DISABLED: 9,
5719 // This will be set to one of the values of CACHE_RESULT_SCALARS
5720 // once it is determined which result best suits what occurred.
5721 _cacheDeferredResultScalar: -1,
5723 // A reference to the nsICacheEntry to read from and write to.
5726 // These nsIPipe's are sent down to the "privileged about content process"
5727 // immediately after the process launches. This allows us to race the loading
5728 // of the cache entry in the parent process with the load of the about:home
5729 // page in the content process, since we'll connect the InputStream's to
5730 // the pipes as soon as the nsICacheEntry is available.
5732 // The page pipe is for the HTML markup for the page.
5734 // The script pipe is for the JavaScript that the HTML markup loads
5735 // to set its internal state.
5737 _cacheDeferred: null,
5741 _hasWrittenThisSession: false,
5743 _firstPrivilegedProcessCreated: false,
5746 if (this._initted) {
5747 throw new Error("AboutHomeStartupCache already initted.");
5750 this.setDeferredResult(this.CACHE_RESULT_SCALARS.UNSET);
5752 this._enabled = !!lazy.NimbusFeatures.abouthomecache.getVariable("enabled");
5754 if (!this._enabled) {
5755 this.recordResult(this.CACHE_RESULT_SCALARS.DISABLED);
5759 this.log = lazy.Log.repository.getLogger(this.LOG_NAME);
5760 this.log.manageLevelFromPref(this.LOG_LEVEL_PREF);
5761 this._appender = new lazy.Log.ConsoleAppender(
5762 new lazy.Log.BasicFormatter()
5764 this.log.addAppender(this._appender);
5766 this.log.trace("Initting.");
5768 // If the user is not configured to load about:home at startup, then
5769 // let's not bother with the cache - loading it needlessly is more likely
5770 // to hinder what we're actually trying to load.
5771 let willLoadAboutHome =
5772 !lazy.HomePage.overridden &&
5773 Services.prefs.getIntPref("browser.startup.page") === 1;
5775 if (!willLoadAboutHome) {
5776 this.log.trace("Not configured to load about:home by default.");
5777 this.recordResult(this.CACHE_RESULT_SCALARS.NOT_LOADING_ABOUTHOME);
5781 if (!Services.prefs.getBoolPref(this.PRELOADED_NEWTAB_PREF, false)) {
5782 this.log.trace("Preloaded about:newtab disabled.");
5783 this.recordResult(this.CACHE_RESULT_SCALARS.PRELOADING_DISABLED);
5787 Services.obs.addObserver(this, "ipc:content-created");
5788 Services.obs.addObserver(this, "process-type-set");
5789 Services.obs.addObserver(this, "ipc:content-shutdown");
5790 Services.obs.addObserver(this, "intl:app-locales-changed");
5792 this.log.trace("Constructing pipes.");
5793 this._pagePipe = this.makePipe();
5794 this._scriptPipe = this.makePipe();
5796 this._cacheEntryPromise = new Promise(resolve => {
5797 this._cacheEntryResolver = resolve;
5800 let lci = Services.loadContextInfo.default;
5801 let storage = Services.cache2.diskCacheStorage(lci);
5803 storage.asyncOpenURI(
5806 Ci.nsICacheStorage.OPEN_PRIORITY,
5810 this.log.error("Failed to open about:home cache entry", e);
5813 this._cacheTask = new lazy.DeferredTask(async () => {
5814 await this.cacheNow();
5815 }, this.CACHE_DEBOUNCE_RATE_MS);
5817 lazy.AsyncShutdown.quitApplicationGranted.addBlocker(
5818 "AboutHomeStartupCache: Writing cache",
5820 await this.onShutdown();
5822 () => this._cacheProgress
5825 this._cacheDeferred = null;
5826 this._initted = true;
5827 this.log.trace("Initialized.");
5831 return this._initted;
5835 if (!this._enabled) {
5840 Services.obs.removeObserver(this, "ipc:content-created");
5841 Services.obs.removeObserver(this, "process-type-set");
5842 Services.obs.removeObserver(this, "ipc:content-shutdown");
5843 Services.obs.removeObserver(this, "intl:app-locales-changed");
5845 // If we failed to initialize and register for these observer
5846 // notifications, then attempting to remove them will throw.
5847 // It's fine to ignore that case on shutdown.
5850 if (this._cacheTask) {
5851 this._cacheTask.disarm();
5852 this._cacheTask = null;
5855 this._pagePipe = null;
5856 this._scriptPipe = null;
5857 this._initted = false;
5858 this._cacheEntry = null;
5859 this._hasWrittenThisSession = false;
5860 this._cacheEntryPromise = null;
5861 this._cacheEntryResolver = null;
5862 this._cacheDeferredResultScalar = -1;
5865 this.log.trace("Uninitialized.");
5866 this.log.removeAppender(this._appender);
5870 this._procManager = null;
5871 this._procManagerID = null;
5872 this._appender = null;
5873 this._cacheDeferred = null;
5874 this._finalized = false;
5875 this._firstPrivilegedProcessCreated = false;
5878 _aboutHomeURI: null,
5880 get aboutHomeURI() {
5881 if (this._aboutHomeURI) {
5882 return this._aboutHomeURI;
5885 this._aboutHomeURI = Services.io.newURI(this.ABOUT_HOME_URI_STRING);
5886 return this._aboutHomeURI;
5889 // For the AsyncShutdown blocker, this is used to populate the progress
5891 _cacheProgress: "Not yet begun",
5894 * Called by the AsyncShutdown blocker on quit-application-granted
5895 * to potentially flush the most recent cache to disk. If one was
5896 * never written during the session, one is generated and written
5897 * before the async function resolves.
5899 * @param withTimeout (boolean)
5900 * Whether or not the timeout mechanism should be used. Defaults
5904 * If a cache has never been written, or a cache write is in
5905 * progress, resolves true when the cache has been written. Also
5906 * resolves to true if a cache didn't need to be written.
5908 * Resolves to false if a cache write unexpectedly timed out.
5910 async onShutdown(withTimeout = true) {
5911 // If we never wrote this session, arm the task so that the next
5912 // step can finalize.
5913 if (!this._hasWrittenThisSession) {
5914 this.log.trace("Never wrote a cache this session. Arming cache task.");
5915 this._cacheTask.arm();
5918 Services.telemetry.scalarSet(
5919 "browser.startup.abouthome_cache_shutdownwrite",
5920 this._cacheTask.isArmed
5923 if (this._cacheTask.isArmed) {
5924 this.log.trace("Finalizing cache task on shutdown");
5925 this._finalized = true;
5927 // To avoid hanging shutdowns, we'll ensure that we wait a maximum of
5928 // SHUTDOWN_CACHE_WRITE_TIMEOUT_MS millseconds before giving up.
5929 const TIMED_OUT = Symbol();
5932 let timeoutPromise = new Promise(resolve => {
5933 timeoutID = lazy.setTimeout(
5934 () => resolve(TIMED_OUT),
5935 this.SHUTDOWN_CACHE_WRITE_TIMEOUT_MS
5939 let promises = [this._cacheTask.finalize()];
5941 this.log.trace("Using timeout mechanism.");
5942 promises.push(timeoutPromise);
5944 this.log.trace("Skipping timeout mechanism.");
5947 let result = await Promise.race(promises);
5948 this.log.trace("Done blocking shutdown.");
5949 lazy.clearTimeout(timeoutID);
5950 if (result === TIMED_OUT) {
5951 this.log.error("Timed out getting cache streams. Skipping cache task.");
5955 this.log.trace("onShutdown is exiting");
5960 * Called by the _cacheTask DeferredTask to actually do the work of
5961 * caching the about:home document.
5964 * @resolves undefined
5965 * Resolves when a fresh version of the cache has been written.
5968 this.log.trace("Caching now.");
5969 this._cacheProgress = "Getting cache streams";
5971 let { pageInputStream, scriptInputStream } = await this.requestCache();
5973 if (!pageInputStream || !scriptInputStream) {
5974 this.log.trace("Failed to get cache streams.");
5975 this._cacheProgress = "Failed to get streams";
5979 this.log.trace("Got cache streams.");
5981 this._cacheProgress = "Writing to cache";
5984 this.log.trace("Populating cache.");
5985 await this.populateCache(pageInputStream, scriptInputStream);
5987 this._cacheProgress = "Failed to populate cache";
5988 this.log.error("Populating the cache failed: ", e);
5992 this._cacheProgress = "Done";
5993 this.log.trace("Done writing to cache.");
5994 this._hasWrittenThisSession = true;
5998 * Requests the cached document streams from the "privileged about content
6003 * Resolves with an Object with the following properties:
6005 * pageInputStream (nsIInputStream)
6006 * The page content to write to the cache, or null if request the streams
6009 * scriptInputStream (nsIInputStream)
6010 * The script content to write to the cache, or null if request the streams
6014 this.log.trace("Parent is requesting Activity Stream state object.");
6015 if (!this._procManager) {
6016 this.log.error("requestCache called with no _procManager!");
6017 return { pageInputStream: null, scriptInputStream: null };
6021 this._procManager.remoteType != lazy.E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE
6023 this.log.error("Somehow got the wrong process type.");
6024 return { pageInputStream: null, scriptInputStream: null };
6027 let state = lazy.AboutNewTab.activityStream.store.getState();
6028 return new Promise(resolve => {
6029 this._cacheDeferred = resolve;
6030 this.log.trace("Parent is requesting cache streams.");
6031 this._procManager.sendAsyncMessage(this.CACHE_REQUEST_MESSAGE, { state });
6036 * Helper function that returns a newly constructed nsIPipe instance.
6041 let pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
6043 true /* non-blocking input */,
6044 true /* non-blocking output */,
6045 0 /* segment size */,
6046 0 /* max segments */
6052 return this._pagePipe;
6056 return this._scriptPipe;
6060 * Called when the nsICacheEntry has been accessed. If the nsICacheEntry
6061 * has content that we want to send down to the "privileged about content
6062 * process", then we connect that content to the nsIPipe's that may or
6063 * may not have already been sent down to the process.
6065 * In the event that the nsICacheEntry doesn't contain anything usable,
6066 * the nsInputStreams on the nsIPipe's are closed.
6069 this.log.trace(`Connecting nsICacheEntry to pipes.`);
6071 // If the cache doesn't yet exist, we'll know because the version metadata
6076 version = this._cacheEntry.getMetaDataElement(
6077 this.CACHE_VERSION_META_KEY
6080 if (e.result == Cr.NS_ERROR_NOT_AVAILABLE) {
6081 this.log.debug("Cache meta data does not exist. Closing streams.");
6082 this.pagePipe.outputStream.close();
6083 this.scriptPipe.outputStream.close();
6084 this.setDeferredResult(this.CACHE_RESULT_SCALARS.DOES_NOT_EXIST);
6091 this.log.info("Version retrieved is", version);
6093 if (version != Services.appinfo.appBuildID) {
6094 this.log.info("Version does not match! Dooming and closing streams.\n");
6095 // This cache is no good - doom it, and prepare for a new one.
6097 this.pagePipe.outputStream.close();
6098 this.scriptPipe.outputStream.close();
6099 this.setDeferredResult(this.CACHE_RESULT_SCALARS.INVALIDATED);
6103 let cachePageInputStream;
6106 cachePageInputStream = this._cacheEntry.openInputStream(0);
6108 this.log.error("Failed to open main input stream for cache entry", e);
6109 this.pagePipe.outputStream.close();
6110 this.scriptPipe.outputStream.close();
6111 this.setDeferredResult(this.CACHE_RESULT_SCALARS.CORRUPT_PAGE);
6115 this.log.trace("Connecting page stream to pipe.");
6116 lazy.NetUtil.asyncCopy(
6117 cachePageInputStream,
6118 this.pagePipe.outputStream,
6120 this.log.info("Page stream connected to pipe.");
6124 let cacheScriptInputStream;
6126 this.log.trace("Connecting script stream to pipe.");
6127 cacheScriptInputStream =
6128 this._cacheEntry.openAlternativeInputStream("script");
6129 lazy.NetUtil.asyncCopy(
6130 cacheScriptInputStream,
6131 this.scriptPipe.outputStream,
6133 this.log.info("Script stream connected to pipe.");
6137 if (e.result == Cr.NS_ERROR_NOT_AVAILABLE) {
6138 // For some reason, the script was not available. We'll close the pipe
6139 // without sending anything into it. The privileged about content process
6140 // will notice that there's nothing available in the pipe, and fall back
6141 // to dynamically generating the page.
6142 this.log.error("Script stream not available! Closing pipe.");
6143 this.scriptPipe.outputStream.close();
6144 this.setDeferredResult(this.CACHE_RESULT_SCALARS.CORRUPT_SCRIPT);
6150 this.setDeferredResult(this.CACHE_RESULT_SCALARS.VALID_AND_USED);
6151 this.log.trace("Streams connected to pipes.");
6155 * Called when we have received a the cache values from the "privileged
6156 * about content process". The page and script streams are written to
6157 * the nsICacheEntry.
6159 * This writing is asynchronous, and if a write happens to already be
6160 * underway when this function is called, that latter call will be
6163 * @param pageInputStream (nsIInputStream)
6164 * A stream containing the HTML markup to be saved to the cache.
6165 * @param scriptInputStream (nsIInputStream)
6166 * A stream containing the JS hydration script to be saved to the cache.
6168 * @resolves undefined
6169 * When the cache has been successfully written to.
6171 * Rejects with a JS Error if writing any part of the cache happens to
6174 async populateCache(pageInputStream, scriptInputStream) {
6175 await this.ensureCacheEntry();
6177 await new Promise((resolve, reject) => {
6178 // Doom the old cache entry, so we can start writing to a new one.
6179 this.log.trace("Populating the cache. Dooming old entry.");
6182 this.log.trace("Opening the page output stream.");
6183 let pageOutputStream;
6185 pageOutputStream = this._cacheEntry.openOutputStream(0, -1);
6191 this.log.info("Writing the page cache.");
6192 lazy.NetUtil.asyncCopy(pageInputStream, pageOutputStream, pageResult => {
6193 if (!Components.isSuccessCode(pageResult)) {
6194 this.log.error("Failed to write page. Result: " + pageResult);
6195 reject(new Error(pageResult));
6200 "Writing the page data is complete. Now opening the " +
6201 "script output stream."
6204 let scriptOutputStream;
6206 scriptOutputStream = this._cacheEntry.openAlternativeOutputStream(
6215 this.log.info("Writing the script cache.");
6216 lazy.NetUtil.asyncCopy(
6220 if (!Components.isSuccessCode(scriptResult)) {
6221 this.log.error("Failed to write script. Result: " + scriptResult);
6222 reject(new Error(scriptResult));
6227 "Writing the script cache is done. Setting version."
6230 this._cacheEntry.setMetaDataElement(
6232 Services.appinfo.appBuildID
6235 this.log.error("Failed to write version.");
6239 this.log.trace(`Version is set to ${Services.appinfo.appBuildID}.`);
6240 this.log.info("Caching of page and script is done.");
6247 this.log.trace("populateCache has finished.");
6251 * Returns a Promise that resolves once the nsICacheEntry for the cache
6252 * is available to write to and read from.
6255 * @resolves nsICacheEntry
6256 * Once the cache entry has become available.
6258 * Rejects with an error message if getting the cache entry is attempted
6259 * before the AboutHomeStartupCache component has been initialized.
6261 ensureCacheEntry() {
6262 if (!this._initted) {
6263 return Promise.reject(
6264 "Cannot ensureCacheEntry - AboutHomeStartupCache is not initted"
6268 return this._cacheEntryPromise;
6272 * Clears the contents of the cache.
6275 this.log.trace("Clearing the cache.");
6276 this._cacheEntry = this._cacheEntry.recreate();
6277 this._cacheEntryPromise = new Promise(resolve => {
6278 resolve(this._cacheEntry);
6280 this._hasWrittenThisSession = false;
6284 * Called when a content process is created. If this is the "privileged
6285 * about content process", then the cache streams will be sent to it.
6287 * @param childID (Number)
6288 * The unique ID for the content process that was created, as passed by
6289 * ipc:content-created.
6290 * @param procManager (ProcessMessageManager)
6291 * The ProcessMessageManager for the created content process.
6292 * @param processParent
6293 * The nsIDOMProcessParent for the tab.
6295 onContentProcessCreated(childID, procManager, processParent) {
6296 if (procManager.remoteType == lazy.E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE) {
6297 if (this._finalized) {
6299 "Ignoring privileged about content process launch after finalization."
6304 if (this._firstPrivilegedProcessCreated) {
6306 "Ignoring non-first privileged about content processes."
6312 `A privileged about content process is launching with ID ${childID}.`
6315 this.log.info("Sending input streams down to content process.");
6316 let actor = processParent.getActor("BrowserProcess");
6317 actor.sendAsyncMessage(this.SEND_STREAMS_MESSAGE, {
6318 pageInputStream: this.pagePipe.inputStream,
6319 scriptInputStream: this.scriptPipe.inputStream,
6322 procManager.addMessageListener(this.CACHE_RESPONSE_MESSAGE, this);
6323 procManager.addMessageListener(this.CACHE_USAGE_RESULT_MESSAGE, this);
6324 this._procManager = procManager;
6325 this._procManagerID = childID;
6326 this._firstPrivilegedProcessCreated = true;
6331 * Called when a content process is destroyed. Either it shut down normally,
6332 * or it crashed. If this is the "privileged about content process", then some
6333 * internal state is cleared.
6335 * @param childID (Number)
6336 * The unique ID for the content process that was created, as passed by
6337 * ipc:content-shutdown.
6339 onContentProcessShutdown(childID) {
6340 this.log.info(`Content process shutdown: ${childID}`);
6341 if (this._procManagerID == childID) {
6342 this.log.info("It was the current privileged about process.");
6343 if (this._cacheDeferred) {
6345 "A privileged about content process shut down while cache streams " +
6346 "were still en route."
6348 // The crash occurred while we were waiting on cache input streams to
6349 // be returned to us. Resolve with null streams instead.
6350 this._cacheDeferred({ pageInputStream: null, scriptInputStream: null });
6351 this._cacheDeferred = null;
6354 this._procManager.removeMessageListener(
6355 this.CACHE_RESPONSE_MESSAGE,
6358 this._procManager.removeMessageListener(
6359 this.CACHE_USAGE_RESULT_MESSAGE,
6362 this._procManager = null;
6363 this._procManagerID = null;
6368 * Called externally by ActivityStreamMessageChannel anytime
6369 * a message is broadcast to all about:newtabs, or sent to the
6370 * preloaded about:newtab. This is used to determine if we need
6371 * to refresh the cache.
6373 onPreloadedNewTabMessage() {
6374 if (!this._initted || !this._enabled) {
6378 if (this._finalized) {
6379 this.log.trace("Ignoring preloaded newtab update after finalization.");
6383 this.log.trace("Preloaded about:newtab was updated.");
6385 this._cacheTask.disarm();
6386 this._cacheTask.arm();
6390 * Stores the CACHE_RESULT_SCALARS value that most accurately represents
6391 * the current notion of how the cache has operated so far. It is stored
6392 * temporarily like this because we need to hear from the privileged
6393 * about content process to hear whether or not retrieving the cache
6394 * actually worked on that end. The success state reported back from
6395 * the privileged about content process will be compared against the
6396 * deferred result scalar to compute what will be recorded to
6399 * Note that this value will only be recorded if its value is GREATER
6400 * than the currently recorded value. This is because it's possible for
6401 * certain functions that record results to re-enter - but we want to record
6402 * the _first_ condition that caused the cache to not be read from.
6404 * @param result (Number)
6405 * One of the CACHE_RESULT_SCALARS values. If this value is less than
6406 * the currently recorded value, it is ignored.
6408 setDeferredResult(result) {
6409 if (this._cacheDeferredResultScalar < result) {
6410 this._cacheDeferredResultScalar = result;
6415 * Records the final result of how the cache operated for the user
6416 * during this session to Telemetry.
6418 recordResult(result) {
6419 // Note: this can be called very early on in the lifetime of
6420 // AboutHomeStartupCache, so things like this.log might not exist yet.
6421 Services.telemetry.scalarSet(
6422 "browser.startup.abouthome_cache_result",
6428 * Called when the parent process receives a message from the privileged
6429 * about content process saying whether or not reading from the cache
6432 * @param success (boolean)
6433 * True if reading from the cache succeeded.
6435 onUsageResult(success) {
6436 this.log.trace(`Received usage result. Success = ${success}`);
6439 this._cacheDeferredResultScalar !=
6440 this.CACHE_RESULT_SCALARS.VALID_AND_USED
6443 "Somehow got a success result despite having never " +
6444 "successfully sent down the cache streams"
6446 this.recordResult(this._cacheDeferredResultScalar);
6448 this.recordResult(this.CACHE_RESULT_SCALARS.VALID_AND_USED);
6455 this._cacheDeferredResultScalar ==
6456 this.CACHE_RESULT_SCALARS.VALID_AND_USED
6458 // We failed to read from the cache despite having successfully
6459 // sent it down to the content process. We presume then that the
6460 // streams just didn't provide any bytes in time.
6461 this.recordResult(this.CACHE_RESULT_SCALARS.LATE);
6463 // We failed to read the cache, but already knew why. We can
6464 // now record that value.
6465 this.recordResult(this._cacheDeferredResultScalar);
6469 QueryInterface: ChromeUtils.generateQI([
6470 "nsICacheEntryOpenallback",
6474 /** MessageListener **/
6476 receiveMessage(message) {
6477 // Only the privileged about content process can write to the cache.
6479 message.target.remoteType != lazy.E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE
6482 "Received a message from a non-privileged content process!"
6487 switch (message.name) {
6488 case this.CACHE_RESPONSE_MESSAGE: {
6489 this.log.trace("Parent received cache streams.");
6490 if (!this._cacheDeferred) {
6491 this.log.error("Parent doesn't have _cacheDeferred set up!");
6495 this._cacheDeferred(message.data);
6496 this._cacheDeferred = null;
6499 case this.CACHE_USAGE_RESULT_MESSAGE: {
6500 this.onUsageResult(message.data.success);
6508 observe(aSubject, aTopic, aData) {
6510 case "intl:app-locales-changed": {
6514 case "process-type-set":
6515 // Intentional fall-through
6516 case "ipc:content-created": {
6517 let childID = aData;
6518 let procManager = aSubject
6519 .QueryInterface(Ci.nsIInterfaceRequestor)
6520 .getInterface(Ci.nsIMessageSender);
6521 let pp = aSubject.QueryInterface(Ci.nsIDOMProcessParent);
6522 this.onContentProcessCreated(childID, procManager, pp);
6526 case "ipc:content-shutdown": {
6527 let childID = aData;
6528 this.onContentProcessShutdown(childID);
6534 /** nsICacheEntryOpenCallback **/
6536 onCacheEntryCheck(aEntry) {
6537 return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
6540 onCacheEntryAvailable(aEntry, aNew, aResult) {
6541 this.log.trace("Cache entry is available.");
6543 this._cacheEntry = aEntry;
6544 this.connectToPipes();
6545 this._cacheEntryResolver(this._cacheEntry);