michael@0: # -*- indent-tabs-mode: nil -*- michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: const Ci = Components.interfaces; michael@0: const Cc = Components.classes; michael@0: const Cr = Components.results; michael@0: const Cu = Components.utils; michael@0: michael@0: const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "AboutHome", michael@0: "resource:///modules/AboutHome.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", michael@0: "resource://gre/modules/AddonManager.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "ContentClick", michael@0: "resource:///modules/ContentClick.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "DirectoryLinksProvider", michael@0: "resource://gre/modules/DirectoryLinksProvider.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", michael@0: "resource://gre/modules/NetUtil.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", michael@0: "resource://gre/modules/FileUtils.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", michael@0: "resource://gre/modules/PlacesUtils.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils", michael@0: "resource://gre/modules/BookmarkHTMLUtils.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "BookmarkJSONUtils", michael@0: "resource://gre/modules/BookmarkJSONUtils.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "WebappManager", michael@0: "resource:///modules/WebappManager.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs", michael@0: "resource://gre/modules/PageThumbs.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils", michael@0: "resource://gre/modules/NewTabUtils.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "BrowserNewTabPreloader", michael@0: "resource:///modules/BrowserNewTabPreloader.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "CustomizationTabPreloader", michael@0: "resource:///modules/CustomizationTabPreloader.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "PdfJs", michael@0: "resource://pdf.js/PdfJs.jsm"); michael@0: michael@0: #ifdef NIGHTLY_BUILD michael@0: XPCOMUtils.defineLazyModuleGetter(this, "ShumwayUtils", michael@0: "resource://shumway/ShumwayUtils.jsm"); michael@0: #endif michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "webrtcUI", michael@0: "resource:///modules/webrtcUI.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", michael@0: "resource://gre/modules/PrivateBrowsingUtils.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", michael@0: "resource:///modules/RecentWindow.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "Task", michael@0: "resource://gre/modules/Task.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "PlacesBackups", michael@0: "resource://gre/modules/PlacesBackups.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "OS", michael@0: "resource://gre/modules/osfile.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "RemotePrompt", michael@0: "resource:///modules/RemotePrompt.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "SessionStore", michael@0: "resource:///modules/sessionstore/SessionStore.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry", michael@0: "resource:///modules/BrowserUITelemetry.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown", michael@0: "resource://gre/modules/AsyncShutdown.jsm"); michael@0: michael@0: #ifdef NIGHTLY_BUILD michael@0: XPCOMUtils.defineLazyModuleGetter(this, "SignInToWebsiteUX", michael@0: "resource:///modules/SignInToWebsite.jsm"); michael@0: #endif michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch", michael@0: "resource:///modules/ContentSearch.jsm"); michael@0: michael@0: const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser"; michael@0: const PREF_PLUGINS_UPDATEURL = "plugins.update.url"; michael@0: michael@0: // Seconds of idle before trying to create a bookmarks backup. michael@0: const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 8 * 60; michael@0: // Minimum interval between backups. We try to not create more than one backup michael@0: // per interval. michael@0: const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1; michael@0: // Maximum interval between backups. If the last backup is older than these michael@0: // days we will try to create a new one more aggressively. michael@0: const BOOKMARKS_BACKUP_MAX_INTERVAL_DAYS = 3; michael@0: michael@0: // Factory object michael@0: const BrowserGlueServiceFactory = { michael@0: _instance: null, michael@0: createInstance: function BGSF_createInstance(outer, iid) { michael@0: if (outer != null) michael@0: throw Components.results.NS_ERROR_NO_AGGREGATION; michael@0: return this._instance == null ? michael@0: this._instance = new BrowserGlue() : this._instance; michael@0: } michael@0: }; michael@0: michael@0: // Constructor michael@0: michael@0: function BrowserGlue() { michael@0: XPCOMUtils.defineLazyServiceGetter(this, "_idleService", michael@0: "@mozilla.org/widget/idleservice;1", michael@0: "nsIIdleService"); michael@0: michael@0: XPCOMUtils.defineLazyGetter(this, "_distributionCustomizer", function() { michael@0: Cu.import("resource:///modules/distribution.js"); michael@0: return new DistributionCustomizer(); michael@0: }); michael@0: michael@0: XPCOMUtils.defineLazyGetter(this, "_sanitizer", michael@0: function() { michael@0: let sanitizerScope = {}; michael@0: Services.scriptloader.loadSubScript("chrome://browser/content/sanitize.js", sanitizerScope); michael@0: return sanitizerScope.Sanitizer; michael@0: }); michael@0: michael@0: this._init(); michael@0: } michael@0: michael@0: #ifndef XP_MACOSX michael@0: # OS X has the concept of zero-window sessions and therefore ignores the michael@0: # browser-lastwindow-close-* topics. michael@0: #define OBSERVE_LASTWINDOW_CLOSE_TOPICS 1 michael@0: #endif michael@0: michael@0: BrowserGlue.prototype = { michael@0: _saveSession: false, michael@0: _isPlacesInitObserver: false, michael@0: _isPlacesLockedObserver: false, michael@0: _isPlacesShutdownObserver: false, michael@0: _isPlacesDatabaseLocked: false, michael@0: _migrationImportsDefaultBookmarks: false, michael@0: michael@0: _setPrefToSaveSession: function BG__setPrefToSaveSession(aForce) { michael@0: if (!this._saveSession && !aForce) michael@0: return; michael@0: michael@0: Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true); michael@0: michael@0: // This method can be called via [NSApplication terminate:] on Mac, which michael@0: // ends up causing prefs not to be flushed to disk, so we need to do that michael@0: // explicitly here. See bug 497652. michael@0: Services.prefs.savePrefFile(null); michael@0: }, michael@0: michael@0: #ifdef MOZ_SERVICES_SYNC michael@0: _setSyncAutoconnectDelay: function BG__setSyncAutoconnectDelay() { michael@0: // Assume that a non-zero value for services.sync.autoconnectDelay should override michael@0: if (Services.prefs.prefHasUserValue("services.sync.autoconnectDelay")) { michael@0: let prefDelay = Services.prefs.getIntPref("services.sync.autoconnectDelay"); michael@0: michael@0: if (prefDelay > 0) michael@0: return; michael@0: } michael@0: michael@0: // delays are in seconds michael@0: const MAX_DELAY = 300; michael@0: let delay = 3; michael@0: let browserEnum = Services.wm.getEnumerator("navigator:browser"); michael@0: while (browserEnum.hasMoreElements()) { michael@0: delay += browserEnum.getNext().gBrowser.tabs.length; michael@0: } michael@0: delay = delay <= MAX_DELAY ? delay : MAX_DELAY; michael@0: michael@0: Cu.import("resource://services-sync/main.js"); michael@0: Weave.Service.scheduler.delayedAutoConnect(delay); michael@0: }, michael@0: #endif michael@0: michael@0: // nsIObserver implementation michael@0: observe: function BG_observe(subject, topic, data) { michael@0: switch (topic) { michael@0: case "prefservice:after-app-defaults": michael@0: this._onAppDefaults(); michael@0: break; michael@0: case "final-ui-startup": michael@0: this._finalUIStartup(); michael@0: break; michael@0: case "browser-delayed-startup-finished": michael@0: this._onFirstWindowLoaded(subject); michael@0: Services.obs.removeObserver(this, "browser-delayed-startup-finished"); michael@0: break; michael@0: case "sessionstore-windows-restored": michael@0: this._onWindowsRestored(); michael@0: break; michael@0: case "browser:purge-session-history": michael@0: // reset the console service's error buffer michael@0: Services.console.logStringMessage(null); // clear the console (in case it's open) michael@0: Services.console.reset(); michael@0: break; michael@0: case "quit-application-requested": michael@0: this._onQuitRequest(subject, data); michael@0: break; michael@0: case "quit-application-granted": michael@0: this._onQuitApplicationGranted(); michael@0: break; michael@0: #ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS michael@0: case "browser-lastwindow-close-requested": michael@0: // The application is not actually quitting, but the last full browser michael@0: // window is about to be closed. michael@0: this._onQuitRequest(subject, "lastwindow"); michael@0: break; michael@0: case "browser-lastwindow-close-granted": michael@0: this._setPrefToSaveSession(); michael@0: break; michael@0: #endif michael@0: #ifdef MOZ_SERVICES_SYNC michael@0: case "weave:service:ready": michael@0: this._setSyncAutoconnectDelay(); michael@0: break; michael@0: case "weave:engine:clients:display-uri": michael@0: this._onDisplaySyncURI(subject); michael@0: break; michael@0: #endif michael@0: case "session-save": michael@0: this._setPrefToSaveSession(true); michael@0: subject.QueryInterface(Ci.nsISupportsPRBool); michael@0: subject.data = true; michael@0: break; michael@0: case "places-init-complete": michael@0: if (!this._migrationImportsDefaultBookmarks) michael@0: this._initPlaces(false); michael@0: michael@0: Services.obs.removeObserver(this, "places-init-complete"); michael@0: this._isPlacesInitObserver = false; michael@0: // no longer needed, since history was initialized completely. michael@0: Services.obs.removeObserver(this, "places-database-locked"); michael@0: this._isPlacesLockedObserver = false; michael@0: break; michael@0: case "places-database-locked": michael@0: this._isPlacesDatabaseLocked = true; michael@0: // Stop observing, so further attempts to load history service michael@0: // will not show the prompt. michael@0: Services.obs.removeObserver(this, "places-database-locked"); michael@0: this._isPlacesLockedObserver = false; michael@0: break; michael@0: case "places-shutdown": michael@0: if (this._isPlacesShutdownObserver) { michael@0: Services.obs.removeObserver(this, "places-shutdown"); michael@0: this._isPlacesShutdownObserver = false; michael@0: } michael@0: // places-shutdown is fired when the profile is about to disappear. michael@0: this._onPlacesShutdown(); michael@0: break; michael@0: case "idle": michael@0: this._backupBookmarks(); michael@0: break; michael@0: case "distribution-customization-complete": michael@0: Services.obs.removeObserver(this, "distribution-customization-complete"); michael@0: // Customization has finished, we don't need the customizer anymore. michael@0: delete this._distributionCustomizer; michael@0: break; michael@0: case "browser-glue-test": // used by tests michael@0: if (data == "post-update-notification") { michael@0: if (Services.prefs.prefHasUserValue("app.update.postupdate")) michael@0: this._showUpdateNotification(); michael@0: } michael@0: else if (data == "force-ui-migration") { michael@0: this._migrateUI(); michael@0: } michael@0: else if (data == "force-distribution-customization") { michael@0: this._distributionCustomizer.applyPrefDefaults(); michael@0: this._distributionCustomizer.applyCustomizations(); michael@0: // To apply distribution bookmarks use "places-init-complete". michael@0: } michael@0: else if (data == "force-places-init") { michael@0: this._initPlaces(false); michael@0: } michael@0: break; michael@0: case "initial-migration-will-import-default-bookmarks": michael@0: this._migrationImportsDefaultBookmarks = true; michael@0: break; michael@0: case "initial-migration-did-import-default-bookmarks": michael@0: this._initPlaces(true); michael@0: break; michael@0: case "handle-xul-text-link": michael@0: let linkHandled = subject.QueryInterface(Ci.nsISupportsPRBool); michael@0: if (!linkHandled.data) { michael@0: let win = this.getMostRecentBrowserWindow(); michael@0: if (win) { michael@0: win.openUILinkIn(data, "tab"); michael@0: linkHandled.data = true; michael@0: } michael@0: } michael@0: break; michael@0: case "profile-before-change": michael@0: // Any component depending on Places should be finalized in michael@0: // _onPlacesShutdown. Any component that doesn't need to act after michael@0: // the UI has gone should be finalized in _onQuitApplicationGranted. michael@0: this._dispose(); michael@0: break; michael@0: #ifdef MOZ_SERVICES_HEALTHREPORT michael@0: case "keyword-search": michael@0: // This is very similar to code in michael@0: // browser.js:BrowserSearch.recordSearchInHealthReport(). The code could michael@0: // be consolidated if there is will. We need the observer in michael@0: // nsBrowserGlue to prevent double counting. michael@0: let reporter = Cc["@mozilla.org/datareporting/service;1"] michael@0: .getService() michael@0: .wrappedJSObject michael@0: .healthReporter; michael@0: michael@0: if (!reporter) { michael@0: return; michael@0: } michael@0: michael@0: reporter.onInit().then(function record() { michael@0: try { michael@0: let engine = subject.QueryInterface(Ci.nsISearchEngine); michael@0: reporter.getProvider("org.mozilla.searches").recordSearch(engine, "urlbar"); michael@0: } catch (ex) { michael@0: Cu.reportError(ex); michael@0: } michael@0: }); michael@0: break; michael@0: #endif michael@0: case "browser-search-engine-modified": michael@0: if (data != "engine-default" && data != "engine-current") { michael@0: break; michael@0: } michael@0: // Enforce that the search service's defaultEngine is always equal to michael@0: // its currentEngine. The search service will notify us any time either michael@0: // of them are changed (either by directly setting the relevant prefs, michael@0: // i.e. if add-ons try to change this directly, or if the michael@0: // nsIBrowserSearchService setters are called). michael@0: // No need to initialize the search service, since it's guaranteed to be michael@0: // initialized already when this notification fires. michael@0: let ss = Services.search; michael@0: if (ss.currentEngine.name == ss.defaultEngine.name) michael@0: return; michael@0: if (data == "engine-current") michael@0: ss.defaultEngine = ss.currentEngine; michael@0: else michael@0: ss.currentEngine = ss.defaultEngine; michael@0: break; michael@0: case "browser-search-service": michael@0: if (data != "init-complete") michael@0: return; michael@0: Services.obs.removeObserver(this, "browser-search-service"); michael@0: this._syncSearchEngines(); michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: _syncSearchEngines: function () { michael@0: // Only do this if the search service is already initialized. This function michael@0: // gets called in finalUIStartup and from a browser-search-service observer, michael@0: // to catch both cases (search service initialization occurring before and michael@0: // after final-ui-startup) michael@0: if (Services.search.isInitialized) { michael@0: Services.search.defaultEngine = Services.search.currentEngine; michael@0: } michael@0: }, michael@0: michael@0: // initialization (called on application startup) michael@0: _init: function BG__init() { michael@0: let os = Services.obs; michael@0: os.addObserver(this, "prefservice:after-app-defaults", false); michael@0: os.addObserver(this, "final-ui-startup", false); michael@0: os.addObserver(this, "browser-delayed-startup-finished", false); michael@0: os.addObserver(this, "sessionstore-windows-restored", false); michael@0: os.addObserver(this, "browser:purge-session-history", false); michael@0: os.addObserver(this, "quit-application-requested", false); michael@0: os.addObserver(this, "quit-application-granted", false); michael@0: #ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS michael@0: os.addObserver(this, "browser-lastwindow-close-requested", false); michael@0: os.addObserver(this, "browser-lastwindow-close-granted", false); michael@0: #endif michael@0: #ifdef MOZ_SERVICES_SYNC michael@0: os.addObserver(this, "weave:service:ready", false); michael@0: os.addObserver(this, "weave:engine:clients:display-uri", false); michael@0: #endif michael@0: os.addObserver(this, "session-save", false); michael@0: os.addObserver(this, "places-init-complete", false); michael@0: this._isPlacesInitObserver = true; michael@0: os.addObserver(this, "places-database-locked", false); michael@0: this._isPlacesLockedObserver = true; michael@0: os.addObserver(this, "distribution-customization-complete", false); michael@0: os.addObserver(this, "places-shutdown", false); michael@0: this._isPlacesShutdownObserver = true; michael@0: os.addObserver(this, "handle-xul-text-link", false); michael@0: os.addObserver(this, "profile-before-change", false); michael@0: #ifdef MOZ_SERVICES_HEALTHREPORT michael@0: os.addObserver(this, "keyword-search", false); michael@0: #endif michael@0: os.addObserver(this, "browser-search-engine-modified", false); michael@0: os.addObserver(this, "browser-search-service", false); michael@0: }, michael@0: michael@0: // cleanup (called on application shutdown) michael@0: _dispose: function BG__dispose() { michael@0: let os = Services.obs; michael@0: os.removeObserver(this, "prefservice:after-app-defaults"); michael@0: os.removeObserver(this, "final-ui-startup"); michael@0: os.removeObserver(this, "sessionstore-windows-restored"); michael@0: os.removeObserver(this, "browser:purge-session-history"); michael@0: os.removeObserver(this, "quit-application-requested"); michael@0: os.removeObserver(this, "quit-application-granted"); michael@0: #ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS michael@0: os.removeObserver(this, "browser-lastwindow-close-requested"); michael@0: os.removeObserver(this, "browser-lastwindow-close-granted"); michael@0: #endif michael@0: #ifdef MOZ_SERVICES_SYNC michael@0: os.removeObserver(this, "weave:service:ready"); michael@0: os.removeObserver(this, "weave:engine:clients:display-uri"); michael@0: #endif michael@0: os.removeObserver(this, "session-save"); michael@0: if (this._bookmarksBackupIdleTime) { michael@0: this._idleService.removeIdleObserver(this, this._bookmarksBackupIdleTime); michael@0: delete this._bookmarksBackupIdleTime; michael@0: } michael@0: if (this._isPlacesInitObserver) michael@0: os.removeObserver(this, "places-init-complete"); michael@0: if (this._isPlacesLockedObserver) michael@0: os.removeObserver(this, "places-database-locked"); michael@0: if (this._isPlacesShutdownObserver) michael@0: os.removeObserver(this, "places-shutdown"); michael@0: os.removeObserver(this, "handle-xul-text-link"); michael@0: os.removeObserver(this, "profile-before-change"); michael@0: #ifdef MOZ_SERVICES_HEALTHREPORT michael@0: os.removeObserver(this, "keyword-search"); michael@0: #endif michael@0: os.removeObserver(this, "browser-search-engine-modified"); michael@0: try { michael@0: os.removeObserver(this, "browser-search-service"); michael@0: // may have already been removed by the observer michael@0: } catch (ex) {} michael@0: }, michael@0: michael@0: _onAppDefaults: function BG__onAppDefaults() { michael@0: // apply distribution customizations (prefs) michael@0: // other customizations are applied in _finalUIStartup() michael@0: this._distributionCustomizer.applyPrefDefaults(); michael@0: }, michael@0: michael@0: // runs on startup, before the first command line handler is invoked michael@0: // (i.e. before the first window is opened) michael@0: _finalUIStartup: function BG__finalUIStartup() { michael@0: this._sanitizer.onStartup(); michael@0: // check if we're in safe mode michael@0: if (Services.appinfo.inSafeMode) { michael@0: Services.ww.openWindow(null, "chrome://browser/content/safeMode.xul", michael@0: "_blank", "chrome,centerscreen,modal,resizable=no", null); michael@0: } michael@0: michael@0: // apply distribution customizations michael@0: // prefs are applied in _onAppDefaults() michael@0: this._distributionCustomizer.applyCustomizations(); michael@0: michael@0: // handle any UI migration michael@0: this._migrateUI(); michael@0: michael@0: this._syncSearchEngines(); michael@0: michael@0: WebappManager.init(); michael@0: PageThumbs.init(); michael@0: NewTabUtils.init(); michael@0: DirectoryLinksProvider.init(); michael@0: NewTabUtils.links.addProvider(DirectoryLinksProvider); michael@0: BrowserNewTabPreloader.init(); michael@0: #ifdef NIGHTLY_BUILD michael@0: if (Services.prefs.getBoolPref("dom.identity.enabled")) { michael@0: SignInToWebsiteUX.init(); michael@0: } michael@0: #endif michael@0: PdfJs.init(); michael@0: #ifdef NIGHTLY_BUILD michael@0: ShumwayUtils.init(); michael@0: #endif michael@0: webrtcUI.init(); michael@0: AboutHome.init(); michael@0: SessionStore.init(); michael@0: BrowserUITelemetry.init(); michael@0: ContentSearch.init(); michael@0: michael@0: if (Services.appinfo.browserTabsRemote) { michael@0: ContentClick.init(); michael@0: RemotePrompt.init(); michael@0: } michael@0: michael@0: Services.obs.notifyObservers(null, "browser-ui-startup-complete", ""); michael@0: }, michael@0: michael@0: _checkForOldBuildUpdates: function () { michael@0: // check for update if our build is old michael@0: if (Services.prefs.getBoolPref("app.update.enabled") && michael@0: Services.prefs.getBoolPref("app.update.checkInstallTime")) { michael@0: michael@0: let buildID = Services.appinfo.appBuildID; michael@0: let today = new Date().getTime(); michael@0: let buildDate = new Date(buildID.slice(0,4), // year michael@0: buildID.slice(4,6) - 1, // months are zero-based. michael@0: buildID.slice(6,8), // day michael@0: buildID.slice(8,10), // hour michael@0: buildID.slice(10,12), // min michael@0: buildID.slice(12,14)) // ms michael@0: .getTime(); michael@0: michael@0: const millisecondsIn24Hours = 86400000; michael@0: let acceptableAge = Services.prefs.getIntPref("app.update.checkInstallTime.days") * millisecondsIn24Hours; michael@0: michael@0: if (buildDate + acceptableAge < today) { michael@0: Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService).checkForBackgroundUpdates(); michael@0: } michael@0: } michael@0: }, michael@0: michael@0: _trackSlowStartup: function () { michael@0: if (Services.startup.interrupted || michael@0: Services.prefs.getBoolPref("browser.slowStartup.notificationDisabled")) michael@0: return; michael@0: michael@0: let currentTime = Date.now() - Services.startup.getStartupInfo().process; michael@0: let averageTime = 0; michael@0: let samples = 0; michael@0: try { michael@0: averageTime = Services.prefs.getIntPref("browser.slowStartup.averageTime"); michael@0: samples = Services.prefs.getIntPref("browser.slowStartup.samples"); michael@0: } catch (e) { } michael@0: michael@0: let totalTime = (averageTime * samples) + currentTime; michael@0: samples++; michael@0: averageTime = totalTime / samples; michael@0: michael@0: if (samples >= Services.prefs.getIntPref("browser.slowStartup.maxSamples")) { michael@0: if (averageTime > Services.prefs.getIntPref("browser.slowStartup.timeThreshold")) michael@0: this._showSlowStartupNotification(); michael@0: averageTime = 0; michael@0: samples = 0; michael@0: } michael@0: michael@0: Services.prefs.setIntPref("browser.slowStartup.averageTime", averageTime); michael@0: Services.prefs.setIntPref("browser.slowStartup.samples", samples); michael@0: }, michael@0: michael@0: _showSlowStartupNotification: function () { michael@0: let win = this.getMostRecentBrowserWindow(); michael@0: if (!win) michael@0: return; michael@0: michael@0: let productName = Services.strings michael@0: .createBundle("chrome://branding/locale/brand.properties") michael@0: .GetStringFromName("brandFullName"); michael@0: let message = win.gNavigatorBundle.getFormattedString("slowStartup.message", [productName]); michael@0: michael@0: let buttons = [ michael@0: { michael@0: label: win.gNavigatorBundle.getString("slowStartup.helpButton.label"), michael@0: accessKey: win.gNavigatorBundle.getString("slowStartup.helpButton.accesskey"), michael@0: callback: function () { michael@0: win.openUILinkIn("https://support.mozilla.org/kb/reset-firefox-easily-fix-most-problems", "tab"); michael@0: } michael@0: }, michael@0: { michael@0: label: win.gNavigatorBundle.getString("slowStartup.disableNotificationButton.label"), michael@0: accessKey: win.gNavigatorBundle.getString("slowStartup.disableNotificationButton.accesskey"), michael@0: callback: function () { michael@0: Services.prefs.setBoolPref("browser.slowStartup.notificationDisabled", true); michael@0: } michael@0: } michael@0: ]; michael@0: michael@0: let nb = win.document.getElementById("global-notificationbox"); michael@0: nb.appendNotification(message, "slow-startup", michael@0: "chrome://browser/skin/slowStartup-16.png", michael@0: nb.PRIORITY_INFO_LOW, buttons); michael@0: }, michael@0: michael@0: /** michael@0: * Show a notification bar offering a reset if the profile has been unused for some time. michael@0: */ michael@0: _resetUnusedProfileNotification: function () { michael@0: let win = this.getMostRecentBrowserWindow(); michael@0: if (!win) michael@0: return; michael@0: michael@0: Cu.import("resource://gre/modules/ResetProfile.jsm"); michael@0: if (!ResetProfile.resetSupported()) michael@0: return; michael@0: michael@0: let productName = Services.strings michael@0: .createBundle("chrome://branding/locale/brand.properties") michael@0: .GetStringFromName("brandShortName"); michael@0: let resetBundle = Services.strings michael@0: .createBundle("chrome://global/locale/resetProfile.properties"); michael@0: michael@0: let message = resetBundle.formatStringFromName("resetUnusedProfile.message", [productName], 1); michael@0: let buttons = [ michael@0: { michael@0: label: resetBundle.formatStringFromName("resetProfile.resetButton.label", [productName], 1), michael@0: accessKey: resetBundle.GetStringFromName("resetProfile.resetButton.accesskey"), michael@0: callback: function () { michael@0: ResetProfile.openConfirmationDialog(win); michael@0: } michael@0: }, michael@0: ]; michael@0: michael@0: let nb = win.document.getElementById("global-notificationbox"); michael@0: nb.appendNotification(message, "reset-unused-profile", michael@0: "chrome://global/skin/icons/question-16.png", michael@0: nb.PRIORITY_INFO_LOW, buttons); michael@0: }, michael@0: michael@0: // the first browser window has finished initializing michael@0: _onFirstWindowLoaded: function BG__onFirstWindowLoaded(aWindow) { michael@0: #ifdef XP_WIN michael@0: // For windows seven, initialize the jump list module. michael@0: const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1"; michael@0: if (WINTASKBAR_CONTRACTID in Cc && michael@0: Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) { michael@0: let temp = {}; michael@0: Cu.import("resource:///modules/WindowsJumpLists.jsm", temp); michael@0: temp.WinTaskbarJumpList.startup(); michael@0: } michael@0: #endif michael@0: michael@0: this._trackSlowStartup(); michael@0: michael@0: // Offer to reset a user's profile if it hasn't been used for 60 days. michael@0: const OFFER_PROFILE_RESET_INTERVAL_MS = 60 * 24 * 60 * 60 * 1000; michael@0: let lastUse = Services.appinfo.replacedLockTime; michael@0: let disableResetPrompt = false; michael@0: try { michael@0: disableResetPrompt = Services.prefs.getBoolPref("browser.disableResetPrompt"); michael@0: } catch(e) {} michael@0: if (!disableResetPrompt && lastUse && michael@0: Date.now() - lastUse >= OFFER_PROFILE_RESET_INTERVAL_MS) { michael@0: this._resetUnusedProfileNotification(); michael@0: } michael@0: michael@0: this._checkForOldBuildUpdates(); michael@0: }, michael@0: michael@0: /** michael@0: * Application shutdown handler. michael@0: */ michael@0: _onQuitApplicationGranted: function () { michael@0: // This pref must be set here because SessionStore will use its value michael@0: // on quit-application. michael@0: this._setPrefToSaveSession(); michael@0: michael@0: // Call trackStartupCrashEnd here in case the delayed call on startup hasn't michael@0: // yet occurred (see trackStartupCrashEnd caller in browser.js). michael@0: try { michael@0: let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"] michael@0: .getService(Ci.nsIAppStartup); michael@0: appStartup.trackStartupCrashEnd(); michael@0: } catch (e) { michael@0: Cu.reportError("Could not end startup crash tracking in quit-application-granted: " + e); michael@0: } michael@0: michael@0: BrowserNewTabPreloader.uninit(); michael@0: CustomizationTabPreloader.uninit(); michael@0: WebappManager.uninit(); michael@0: #ifdef NIGHTLY_BUILD michael@0: if (Services.prefs.getBoolPref("dom.identity.enabled")) { michael@0: SignInToWebsiteUX.uninit(); michael@0: } michael@0: #endif michael@0: webrtcUI.uninit(); michael@0: }, michael@0: michael@0: // All initial windows have opened. michael@0: _onWindowsRestored: function BG__onWindowsRestored() { michael@0: // Show update notification, if needed. michael@0: if (Services.prefs.prefHasUserValue("app.update.postupdate")) michael@0: this._showUpdateNotification(); michael@0: michael@0: // Load the "more info" page for a locked places.sqlite michael@0: // This property is set earlier by places-database-locked topic. michael@0: if (this._isPlacesDatabaseLocked) { michael@0: this._showPlacesLockedNotificationBox(); michael@0: } michael@0: michael@0: // If there are plugins installed that are outdated, and the user hasn't michael@0: // been warned about them yet, open the plugins update page. michael@0: if (Services.prefs.getBoolPref(PREF_PLUGINS_NOTIFYUSER)) michael@0: this._showPluginUpdatePage(); michael@0: michael@0: // For any add-ons that were installed disabled and can be enabled offer michael@0: // them to the user. michael@0: let changedIDs = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED); michael@0: if (changedIDs.length > 0) { michael@0: let win = this.getMostRecentBrowserWindow(); michael@0: AddonManager.getAddonsByIDs(changedIDs, function(aAddons) { michael@0: aAddons.forEach(function(aAddon) { michael@0: // If the add-on isn't user disabled or can't be enabled then skip it. michael@0: if (!aAddon.userDisabled || !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE)) michael@0: return; michael@0: michael@0: win.openUILinkIn("about:newaddon?id=" + aAddon.id, "tab"); michael@0: }) michael@0: }); michael@0: } michael@0: michael@0: // Perform default browser checking. michael@0: var shell; michael@0: try { michael@0: shell = Components.classes["@mozilla.org/browser/shell-service;1"] michael@0: .getService(Components.interfaces.nsIShellService); michael@0: } catch (e) { } michael@0: if (shell) { michael@0: #ifdef DEBUG michael@0: let shouldCheck = false; michael@0: #else michael@0: let shouldCheck = shell.shouldCheckDefaultBrowser; michael@0: #endif michael@0: let willRecoverSession = false; michael@0: try { michael@0: let ss = Cc["@mozilla.org/browser/sessionstartup;1"]. michael@0: getService(Ci.nsISessionStartup); michael@0: willRecoverSession = michael@0: (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION); michael@0: } michael@0: catch (ex) { /* never mind; suppose SessionStore is broken */ } michael@0: michael@0: let isDefault = shell.isDefaultBrowser(true, false); // startup check, check all assoc michael@0: try { michael@0: // Report default browser status on startup to telemetry michael@0: // so we can track whether we are the default. michael@0: Services.telemetry.getHistogramById("BROWSER_IS_USER_DEFAULT") michael@0: .add(isDefault); michael@0: } michael@0: catch (ex) { /* Don't break the default prompt if telemetry is broken. */ } michael@0: michael@0: if (shouldCheck && !isDefault && !willRecoverSession) { michael@0: Services.tm.mainThread.dispatch(function() { michael@0: var win = this.getMostRecentBrowserWindow(); michael@0: var brandBundle = win.document.getElementById("bundle_brand"); michael@0: var shellBundle = win.document.getElementById("bundle_shell"); michael@0: michael@0: var brandShortName = brandBundle.getString("brandShortName"); michael@0: var promptTitle = shellBundle.getString("setDefaultBrowserTitle"); michael@0: var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage", michael@0: [brandShortName]); michael@0: var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk", michael@0: [brandShortName]); michael@0: var checkEveryTime = { value: shouldCheck }; michael@0: var ps = Services.prompt; michael@0: var rv = ps.confirmEx(win, promptTitle, promptMessage, michael@0: ps.STD_YES_NO_BUTTONS, michael@0: null, null, null, checkboxLabel, checkEveryTime); michael@0: if (rv == 0) { michael@0: var claimAllTypes = true; michael@0: #ifdef XP_WIN michael@0: try { michael@0: // In Windows 8, the UI for selecting default protocol is much michael@0: // nicer than the UI for setting file type associations. So we michael@0: // only show the protocol association screen on Windows 8. michael@0: // Windows 8 is version 6.2. michael@0: let version = Cc["@mozilla.org/system-info;1"] michael@0: .getService(Ci.nsIPropertyBag2) michael@0: .getProperty("version"); michael@0: claimAllTypes = (parseFloat(version) < 6.2); michael@0: } catch (ex) { } michael@0: #endif michael@0: shell.setDefaultBrowser(claimAllTypes, false); michael@0: } michael@0: shell.shouldCheckDefaultBrowser = checkEveryTime.value; michael@0: }.bind(this), Ci.nsIThread.DISPATCH_NORMAL); michael@0: } michael@0: } michael@0: }, michael@0: michael@0: _onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) { michael@0: // If user has already dismissed quit request, then do nothing michael@0: if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data) michael@0: return; michael@0: michael@0: // There are several cases where we won't show a dialog here: michael@0: // 1. There is only 1 tab open in 1 window michael@0: // 2. The session will be restored at startup, indicated by michael@0: // browser.startup.page == 3 or browser.sessionstore.resume_session_once == true michael@0: // 3. browser.warnOnQuit == false michael@0: // 4. The browser is currently in Private Browsing mode michael@0: // 5. The browser will be restarted. michael@0: // michael@0: // Otherwise these are the conditions and the associated dialogs that will be shown: michael@0: // 1. aQuitType == "lastwindow" or "quit" and browser.showQuitWarning == true michael@0: // - The quit dialog will be shown michael@0: // 2. aQuitType == "lastwindow" && browser.tabs.warnOnClose == true michael@0: // - The "closing multiple tabs" dialog will be shown michael@0: // michael@0: // aQuitType == "lastwindow" is overloaded. "lastwindow" is used to indicate michael@0: // "the last window is closing but we're not quitting (a non-browser window is open)" michael@0: // and also "we're quitting by closing the last window". michael@0: michael@0: if (aQuitType == "restart") michael@0: return; michael@0: michael@0: var windowcount = 0; michael@0: var pagecount = 0; michael@0: var browserEnum = Services.wm.getEnumerator("navigator:browser"); michael@0: let allWindowsPrivate = true; michael@0: while (browserEnum.hasMoreElements()) { michael@0: // XXXbz should we skip closed windows here? michael@0: windowcount++; michael@0: michael@0: var browser = browserEnum.getNext(); michael@0: if (!PrivateBrowsingUtils.isWindowPrivate(browser)) michael@0: allWindowsPrivate = false; michael@0: var tabbrowser = browser.document.getElementById("content"); michael@0: if (tabbrowser) michael@0: pagecount += tabbrowser.browsers.length - tabbrowser._numPinnedTabs; michael@0: } michael@0: michael@0: this._saveSession = false; michael@0: if (pagecount < 2) michael@0: return; michael@0: michael@0: if (!aQuitType) michael@0: aQuitType = "quit"; michael@0: michael@0: var mostRecentBrowserWindow; michael@0: michael@0: // browser.warnOnQuit is a hidden global boolean to override all quit prompts michael@0: // browser.showQuitWarning specifically covers quitting michael@0: // browser.tabs.warnOnClose is the global "warn when closing multiple tabs" pref michael@0: michael@0: var sessionWillBeRestored = Services.prefs.getIntPref("browser.startup.page") == 3 || michael@0: Services.prefs.getBoolPref("browser.sessionstore.resume_session_once"); michael@0: if (sessionWillBeRestored || !Services.prefs.getBoolPref("browser.warnOnQuit")) michael@0: return; michael@0: michael@0: // On last window close or quit && showQuitWarning, we want to show the michael@0: // quit warning. michael@0: if (!Services.prefs.getBoolPref("browser.showQuitWarning")) { michael@0: if (aQuitType == "lastwindow") { michael@0: // If aQuitType is "lastwindow" and we aren't showing the quit warning, michael@0: // we should show the window closing warning instead. warnAboutClosing michael@0: // tabs checks browser.tabs.warnOnClose and returns if it's ok to close michael@0: // the window. It doesn't actually close the window. michael@0: mostRecentBrowserWindow = Services.wm.getMostRecentWindow("navigator:browser"); michael@0: let allTabs = mostRecentBrowserWindow.gBrowser.closingTabsEnum.ALL; michael@0: aCancelQuit.data = !mostRecentBrowserWindow.gBrowser.warnAboutClosingTabs(allTabs) michael@0: } michael@0: return; michael@0: } michael@0: michael@0: // Never show a prompt inside private browsing mode michael@0: if (allWindowsPrivate) michael@0: return; michael@0: michael@0: var quitBundle = Services.strings.createBundle("chrome://browser/locale/quitDialog.properties"); michael@0: var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties"); michael@0: michael@0: var appName = brandBundle.GetStringFromName("brandShortName"); michael@0: var quitTitleString = "quitDialogTitle"; michael@0: var quitDialogTitle = quitBundle.formatStringFromName(quitTitleString, [appName], 1); michael@0: michael@0: var message; michael@0: if (windowcount == 1) michael@0: message = quitBundle.formatStringFromName("messageNoWindows", michael@0: [appName], 1); michael@0: else michael@0: message = quitBundle.formatStringFromName("message", michael@0: [appName], 1); michael@0: michael@0: var promptService = Services.prompt; michael@0: michael@0: var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0 + michael@0: promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 + michael@0: promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_2 + michael@0: promptService.BUTTON_POS_0_DEFAULT; michael@0: michael@0: var neverAsk = {value:false}; michael@0: var button0Title = quitBundle.GetStringFromName("saveTitle"); michael@0: var button1Title = quitBundle.GetStringFromName("cancelTitle"); michael@0: var button2Title = quitBundle.GetStringFromName("quitTitle"); michael@0: var neverAskText = quitBundle.GetStringFromName("neverAsk2"); michael@0: michael@0: // This wouldn't have been set above since we shouldn't be here for michael@0: // aQuitType == "lastwindow" michael@0: mostRecentBrowserWindow = Services.wm.getMostRecentWindow("navigator:browser"); michael@0: michael@0: var buttonChoice = michael@0: promptService.confirmEx(mostRecentBrowserWindow, quitDialogTitle, message, michael@0: flags, button0Title, button1Title, button2Title, michael@0: neverAskText, neverAsk); michael@0: michael@0: switch (buttonChoice) { michael@0: case 2: // Quit michael@0: if (neverAsk.value) michael@0: Services.prefs.setBoolPref("browser.showQuitWarning", false); michael@0: break; michael@0: case 1: // Cancel michael@0: aCancelQuit.QueryInterface(Ci.nsISupportsPRBool); michael@0: aCancelQuit.data = true; michael@0: break; michael@0: case 0: // Save & Quit michael@0: this._saveSession = true; michael@0: if (neverAsk.value) { michael@0: // always save state when shutting down michael@0: Services.prefs.setIntPref("browser.startup.page", 3); michael@0: } michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: _showUpdateNotification: function BG__showUpdateNotification() { michael@0: Services.prefs.clearUserPref("app.update.postupdate"); michael@0: michael@0: var um = Cc["@mozilla.org/updates/update-manager;1"]. michael@0: getService(Ci.nsIUpdateManager); michael@0: try { michael@0: // If the updates.xml file is deleted then getUpdateAt will throw. michael@0: var update = um.getUpdateAt(0).QueryInterface(Ci.nsIPropertyBag); michael@0: } michael@0: catch (e) { michael@0: // This should never happen. michael@0: Cu.reportError("Unable to find update: " + e); michael@0: return; michael@0: } michael@0: michael@0: var actions = update.getProperty("actions"); michael@0: if (!actions || actions.indexOf("silent") != -1) michael@0: return; michael@0: michael@0: var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. michael@0: getService(Ci.nsIURLFormatter); michael@0: var browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); michael@0: var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties"); michael@0: var appName = brandBundle.GetStringFromName("brandShortName"); michael@0: michael@0: function getNotifyString(aPropData) { michael@0: var propValue = update.getProperty(aPropData.propName); michael@0: if (!propValue) { michael@0: if (aPropData.prefName) michael@0: propValue = formatter.formatURLPref(aPropData.prefName); michael@0: else if (aPropData.stringParams) michael@0: propValue = browserBundle.formatStringFromName(aPropData.stringName, michael@0: aPropData.stringParams, michael@0: aPropData.stringParams.length); michael@0: else michael@0: propValue = browserBundle.GetStringFromName(aPropData.stringName); michael@0: } michael@0: return propValue; michael@0: } michael@0: michael@0: if (actions.indexOf("showNotification") != -1) { michael@0: let text = getNotifyString({propName: "notificationText", michael@0: stringName: "puNotifyText", michael@0: stringParams: [appName]}); michael@0: let url = getNotifyString({propName: "notificationURL", michael@0: prefName: "startup.homepage_override_url"}); michael@0: let label = getNotifyString({propName: "notificationButtonLabel", michael@0: stringName: "pu.notifyButton.label"}); michael@0: let key = getNotifyString({propName: "notificationButtonAccessKey", michael@0: stringName: "pu.notifyButton.accesskey"}); michael@0: michael@0: let win = this.getMostRecentBrowserWindow(); michael@0: let notifyBox = win.gBrowser.getNotificationBox(); michael@0: michael@0: let buttons = [ michael@0: { michael@0: label: label, michael@0: accessKey: key, michael@0: popup: null, michael@0: callback: function(aNotificationBar, aButton) { michael@0: win.openUILinkIn(url, "tab"); michael@0: } michael@0: } michael@0: ]; michael@0: michael@0: let notification = notifyBox.appendNotification(text, "post-update-notification", michael@0: null, notifyBox.PRIORITY_INFO_LOW, michael@0: buttons); michael@0: notification.persistence = -1; // Until user closes it michael@0: } michael@0: michael@0: if (actions.indexOf("showAlert") == -1) michael@0: return; michael@0: michael@0: let notifier; michael@0: try { michael@0: notifier = Cc["@mozilla.org/alerts-service;1"]. michael@0: getService(Ci.nsIAlertsService); michael@0: } michael@0: catch (e) { michael@0: // nsIAlertsService is not available for this platform michael@0: return; michael@0: } michael@0: michael@0: let title = getNotifyString({propName: "alertTitle", michael@0: stringName: "puAlertTitle", michael@0: stringParams: [appName]}); michael@0: let text = getNotifyString({propName: "alertText", michael@0: stringName: "puAlertText", michael@0: stringParams: [appName]}); michael@0: let url = getNotifyString({propName: "alertURL", michael@0: prefName: "startup.homepage_override_url"}); michael@0: michael@0: var self = this; michael@0: function clickCallback(subject, topic, data) { michael@0: // This callback will be called twice but only once with this topic michael@0: if (topic != "alertclickcallback") michael@0: return; michael@0: let win = self.getMostRecentBrowserWindow(); michael@0: win.openUILinkIn(data, "tab"); michael@0: } michael@0: michael@0: try { michael@0: // This will throw NS_ERROR_NOT_AVAILABLE if the notification cannot michael@0: // be displayed per the idl. michael@0: notifier.showAlertNotification(null, title, text, michael@0: true, url, clickCallback); michael@0: } michael@0: catch (e) { michael@0: } michael@0: }, michael@0: michael@0: _showPluginUpdatePage: function BG__showPluginUpdatePage() { michael@0: Services.prefs.setBoolPref(PREF_PLUGINS_NOTIFYUSER, false); michael@0: michael@0: var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. michael@0: getService(Ci.nsIURLFormatter); michael@0: var updateUrl = formatter.formatURLPref(PREF_PLUGINS_UPDATEURL); michael@0: michael@0: var win = this.getMostRecentBrowserWindow(); michael@0: win.openUILinkIn(updateUrl, "tab"); michael@0: }, michael@0: michael@0: /** michael@0: * Initialize Places michael@0: * - imports the bookmarks html file if bookmarks database is empty, try to michael@0: * restore bookmarks from a JSON backup if the backend indicates that the michael@0: * database was corrupt. michael@0: * michael@0: * These prefs can be set up by the frontend: michael@0: * michael@0: * WARNING: setting these preferences to true will overwite existing bookmarks michael@0: * michael@0: * - browser.places.importBookmarksHTML michael@0: * Set to true will import the bookmarks.html file from the profile folder. michael@0: * - browser.places.smartBookmarksVersion michael@0: * Set during HTML import to indicate that Smart Bookmarks were created. michael@0: * Set to -1 to disable Smart Bookmarks creation. michael@0: * Set to 0 to restore current Smart Bookmarks. michael@0: * - browser.bookmarks.restore_default_bookmarks michael@0: * Set to true by safe-mode dialog to indicate we must restore default michael@0: * bookmarks. michael@0: */ michael@0: _initPlaces: function BG__initPlaces(aInitialMigrationPerformed) { michael@0: // We must instantiate the history service since it will tell us if we michael@0: // need to import or restore bookmarks due to first-run, corruption or michael@0: // forced migration (due to a major schema change). michael@0: // If the database is corrupt or has been newly created we should michael@0: // import bookmarks. michael@0: let dbStatus = PlacesUtils.history.databaseStatus; michael@0: let importBookmarks = !aInitialMigrationPerformed && michael@0: (dbStatus == PlacesUtils.history.DATABASE_STATUS_CREATE || michael@0: dbStatus == PlacesUtils.history.DATABASE_STATUS_CORRUPT); michael@0: michael@0: // Check if user or an extension has required to import bookmarks.html michael@0: let importBookmarksHTML = false; michael@0: try { michael@0: importBookmarksHTML = michael@0: Services.prefs.getBoolPref("browser.places.importBookmarksHTML"); michael@0: if (importBookmarksHTML) michael@0: importBookmarks = true; michael@0: } catch(ex) {} michael@0: michael@0: // Support legacy bookmarks.html format for apps that depend on that format. michael@0: let autoExportHTML = false; michael@0: try { michael@0: autoExportHTML = Services.prefs.getBoolPref("browser.bookmarks.autoExportHTML"); michael@0: } catch (ex) {} // Do not export. michael@0: if (autoExportHTML) { michael@0: // Sqlite.jsm and Places shutdown happen at profile-before-change, thus, michael@0: // to be on the safe side, this should run earlier. michael@0: AsyncShutdown.profileChangeTeardown.addBlocker( michael@0: "Places: export bookmarks.html", michael@0: () => BookmarkHTMLUtils.exportToFile(BookmarkHTMLUtils.defaultPath)); michael@0: } michael@0: michael@0: Task.spawn(function() { michael@0: // Check if Safe Mode or the user has required to restore bookmarks from michael@0: // default profile's bookmarks.html michael@0: let restoreDefaultBookmarks = false; michael@0: try { michael@0: restoreDefaultBookmarks = michael@0: Services.prefs.getBoolPref("browser.bookmarks.restore_default_bookmarks"); michael@0: if (restoreDefaultBookmarks) { michael@0: // Ensure that we already have a bookmarks backup for today. michael@0: yield this._backupBookmarks(); michael@0: importBookmarks = true; michael@0: } michael@0: } catch(ex) {} michael@0: michael@0: // This may be reused later, check for "=== undefined" to see if it has michael@0: // been populated already. michael@0: let lastBackupFile; michael@0: michael@0: // If the user did not require to restore default bookmarks, or import michael@0: // from bookmarks.html, we will try to restore from JSON michael@0: if (importBookmarks && !restoreDefaultBookmarks && !importBookmarksHTML) { michael@0: // get latest JSON backup michael@0: lastBackupFile = yield PlacesBackups.getMostRecentBackup("json"); michael@0: if (lastBackupFile) { michael@0: // restore from JSON backup michael@0: yield BookmarkJSONUtils.importFromFile(lastBackupFile, true); michael@0: importBookmarks = false; michael@0: } michael@0: else { michael@0: // We have created a new database but we don't have any backup available michael@0: importBookmarks = true; michael@0: if (yield OS.File.exists(BookmarkHTMLUtils.defaultPath)) { michael@0: // If bookmarks.html is available in current profile import it... michael@0: importBookmarksHTML = true; michael@0: } michael@0: else { michael@0: // ...otherwise we will restore defaults michael@0: restoreDefaultBookmarks = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // If bookmarks are not imported, then initialize smart bookmarks. This michael@0: // happens during a common startup. michael@0: // Otherwise, if any kind of import runs, smart bookmarks creation should be michael@0: // delayed till the import operations has finished. Not doing so would michael@0: // cause them to be overwritten by the newly imported bookmarks. michael@0: if (!importBookmarks) { michael@0: // Now apply distribution customized bookmarks. michael@0: // This should always run after Places initialization. michael@0: this._distributionCustomizer.applyBookmarks(); michael@0: this.ensurePlacesDefaultQueriesInitialized(); michael@0: } michael@0: else { michael@0: // An import operation is about to run. michael@0: // Don't try to recreate smart bookmarks if autoExportHTML is true or michael@0: // smart bookmarks are disabled. michael@0: let smartBookmarksVersion = 0; michael@0: try { michael@0: smartBookmarksVersion = Services.prefs.getIntPref("browser.places.smartBookmarksVersion"); michael@0: } catch(ex) {} michael@0: if (!autoExportHTML && smartBookmarksVersion != -1) michael@0: Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0); michael@0: michael@0: let bookmarksUrl = null; michael@0: if (restoreDefaultBookmarks) { michael@0: // User wants to restore bookmarks.html file from default profile folder michael@0: bookmarksUrl = "resource:///defaults/profile/bookmarks.html"; michael@0: } michael@0: else if (yield OS.File.exists(BookmarkHTMLUtils.defaultPath)) { michael@0: bookmarksUrl = OS.Path.toFileURI(BookmarkHTMLUtils.defaultPath); michael@0: } michael@0: michael@0: if (bookmarksUrl) { michael@0: // Import from bookmarks.html file. michael@0: try { michael@0: BookmarkHTMLUtils.importFromURL(bookmarksUrl, true).then(null, michael@0: function onFailure() { michael@0: Cu.reportError("Bookmarks.html file could be corrupt."); michael@0: } michael@0: ).then( michael@0: function onComplete() { michael@0: // Now apply distribution customized bookmarks. michael@0: // This should always run after Places initialization. michael@0: this._distributionCustomizer.applyBookmarks(); michael@0: // Ensure that smart bookmarks are created once the operation is michael@0: // complete. michael@0: this.ensurePlacesDefaultQueriesInitialized(); michael@0: }.bind(this) michael@0: ); michael@0: } catch (err) { michael@0: Cu.reportError("Bookmarks.html file could be corrupt. " + err); michael@0: } michael@0: } michael@0: else { michael@0: Cu.reportError("Unable to find bookmarks.html file."); michael@0: } michael@0: michael@0: // Reset preferences, so we won't try to import again at next run michael@0: if (importBookmarksHTML) michael@0: Services.prefs.setBoolPref("browser.places.importBookmarksHTML", false); michael@0: if (restoreDefaultBookmarks) michael@0: Services.prefs.setBoolPref("browser.bookmarks.restore_default_bookmarks", michael@0: false); michael@0: } michael@0: michael@0: // Initialize bookmark archiving on idle. michael@0: if (!this._bookmarksBackupIdleTime) { michael@0: this._bookmarksBackupIdleTime = BOOKMARKS_BACKUP_IDLE_TIME_SEC; michael@0: michael@0: // If there is no backup, or the last bookmarks backup is too old, use michael@0: // a more aggressive idle observer. michael@0: if (lastBackupFile === undefined) michael@0: lastBackupFile = yield PlacesBackups.getMostRecentBackup(); michael@0: if (!lastBackupFile) { michael@0: this._bookmarksBackupIdleTime /= 2; michael@0: } michael@0: else { michael@0: let lastBackupTime = PlacesBackups.getDateForFile(lastBackupFile); michael@0: let profileLastUse = Services.appinfo.replacedLockTime || Date.now(); michael@0: michael@0: // If there is a backup after the last profile usage date it's fine, michael@0: // regardless its age. Otherwise check how old is the last michael@0: // available backup compared to that session. michael@0: if (profileLastUse > lastBackupTime) { michael@0: let backupAge = Math.round((profileLastUse - lastBackupTime) / 86400000); michael@0: // Report the age of the last available backup. michael@0: try { michael@0: Services.telemetry michael@0: .getHistogramById("PLACES_BACKUPS_DAYSFROMLAST") michael@0: .add(backupAge); michael@0: } catch (ex) { michael@0: Components.utils.reportError("Unable to report telemetry."); michael@0: } michael@0: michael@0: if (backupAge > BOOKMARKS_BACKUP_MAX_INTERVAL_DAYS) michael@0: this._bookmarksBackupIdleTime /= 2; michael@0: } michael@0: } michael@0: this._idleService.addIdleObserver(this, this._bookmarksBackupIdleTime); michael@0: } michael@0: michael@0: Services.obs.notifyObservers(null, "places-browser-init-complete", ""); michael@0: }.bind(this)); michael@0: }, michael@0: michael@0: /** michael@0: * Places shut-down tasks michael@0: * - finalize components depending on Places. michael@0: * - export bookmarks as HTML, if so configured. michael@0: */ michael@0: _onPlacesShutdown: function BG__onPlacesShutdown() { michael@0: this._sanitizer.onShutdown(); michael@0: PageThumbs.uninit(); michael@0: michael@0: if (this._bookmarksBackupIdleTime) { michael@0: this._idleService.removeIdleObserver(this, this._bookmarksBackupIdleTime); michael@0: delete this._bookmarksBackupIdleTime; michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * If a backup for today doesn't exist, this creates one. michael@0: */ michael@0: _backupBookmarks: function BG__backupBookmarks() { michael@0: return Task.spawn(function() { michael@0: let lastBackupFile = yield PlacesBackups.getMostRecentBackup(); michael@0: // Should backup bookmarks if there are no backups or the maximum michael@0: // interval between backups elapsed. michael@0: if (!lastBackupFile || michael@0: new Date() - PlacesBackups.getDateForFile(lastBackupFile) > BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS * 86400000) { michael@0: let maxBackups = Services.prefs.getIntPref("browser.bookmarks.max_backups"); michael@0: yield PlacesBackups.create(maxBackups); michael@0: } michael@0: }); michael@0: }, michael@0: michael@0: /** michael@0: * Show the notificationBox for a locked places database. michael@0: */ michael@0: _showPlacesLockedNotificationBox: function BG__showPlacesLockedNotificationBox() { michael@0: var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties"); michael@0: var applicationName = brandBundle.GetStringFromName("brandShortName"); michael@0: var placesBundle = Services.strings.createBundle("chrome://browser/locale/places/places.properties"); michael@0: var title = placesBundle.GetStringFromName("lockPrompt.title"); michael@0: var text = placesBundle.formatStringFromName("lockPrompt.text", [applicationName], 1); michael@0: var buttonText = placesBundle.GetStringFromName("lockPromptInfoButton.label"); michael@0: var accessKey = placesBundle.GetStringFromName("lockPromptInfoButton.accessKey"); michael@0: michael@0: var helpTopic = "places-locked"; michael@0: var url = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. michael@0: getService(Components.interfaces.nsIURLFormatter). michael@0: formatURLPref("app.support.baseURL"); michael@0: url += helpTopic; michael@0: michael@0: var win = this.getMostRecentBrowserWindow(); michael@0: michael@0: var buttons = [ michael@0: { michael@0: label: buttonText, michael@0: accessKey: accessKey, michael@0: popup: null, michael@0: callback: function(aNotificationBar, aButton) { michael@0: win.openUILinkIn(url, "tab"); michael@0: } michael@0: } michael@0: ]; michael@0: michael@0: var notifyBox = win.gBrowser.getNotificationBox(); michael@0: var notification = notifyBox.appendNotification(text, title, null, michael@0: notifyBox.PRIORITY_CRITICAL_MEDIUM, michael@0: buttons); michael@0: notification.persistence = -1; // Until user closes it michael@0: }, michael@0: michael@0: _migrateUI: function BG__migrateUI() { michael@0: const UI_VERSION = 22; michael@0: const BROWSER_DOCURL = "chrome://browser/content/browser.xul#"; michael@0: let currentUIVersion = 0; michael@0: try { michael@0: currentUIVersion = Services.prefs.getIntPref("browser.migration.version"); michael@0: } catch(ex) {} michael@0: if (currentUIVersion >= UI_VERSION) michael@0: return; michael@0: michael@0: this._rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService); michael@0: this._dataSource = this._rdf.GetDataSource("rdf:local-store"); michael@0: this._dirty = false; michael@0: michael@0: if (currentUIVersion < 2) { michael@0: // This code adds the customizable bookmarks button. michael@0: let currentsetResource = this._rdf.GetResource("currentset"); michael@0: let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); michael@0: let currentset = this._getPersist(toolbarResource, currentsetResource); michael@0: // Need to migrate only if toolbar is customized and the element is not found. michael@0: if (currentset && michael@0: currentset.indexOf("bookmarks-menu-button-container") == -1) { michael@0: currentset += ",bookmarks-menu-button-container"; michael@0: this._setPersist(toolbarResource, currentsetResource, currentset); michael@0: } michael@0: } michael@0: michael@0: if (currentUIVersion < 3) { michael@0: // This code merges the reload/stop/go button into the url bar. michael@0: let currentsetResource = this._rdf.GetResource("currentset"); michael@0: let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); michael@0: let currentset = this._getPersist(toolbarResource, currentsetResource); michael@0: // Need to migrate only if toolbar is customized and all 3 elements are found. michael@0: if (currentset && michael@0: currentset.indexOf("reload-button") != -1 && michael@0: currentset.indexOf("stop-button") != -1 && michael@0: currentset.indexOf("urlbar-container") != -1 && michael@0: currentset.indexOf("urlbar-container,reload-button,stop-button") == -1) { michael@0: currentset = currentset.replace(/(^|,)reload-button($|,)/, "$1$2") michael@0: .replace(/(^|,)stop-button($|,)/, "$1$2") michael@0: .replace(/(^|,)urlbar-container($|,)/, michael@0: "$1urlbar-container,reload-button,stop-button$2"); michael@0: this._setPersist(toolbarResource, currentsetResource, currentset); michael@0: } michael@0: } michael@0: michael@0: if (currentUIVersion < 4) { michael@0: // This code moves the home button to the immediate left of the bookmarks menu button. michael@0: let currentsetResource = this._rdf.GetResource("currentset"); michael@0: let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); michael@0: let currentset = this._getPersist(toolbarResource, currentsetResource); michael@0: // Need to migrate only if toolbar is customized and the elements are found. michael@0: if (currentset && michael@0: currentset.indexOf("home-button") != -1 && michael@0: currentset.indexOf("bookmarks-menu-button-container") != -1) { michael@0: currentset = currentset.replace(/(^|,)home-button($|,)/, "$1$2") michael@0: .replace(/(^|,)bookmarks-menu-button-container($|,)/, michael@0: "$1home-button,bookmarks-menu-button-container$2"); michael@0: this._setPersist(toolbarResource, currentsetResource, currentset); michael@0: } michael@0: } michael@0: michael@0: if (currentUIVersion < 5) { michael@0: // This code uncollapses PersonalToolbar if its collapsed status is not michael@0: // persisted, and user customized it or changed default bookmarks. michael@0: let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "PersonalToolbar"); michael@0: let collapsedResource = this._rdf.GetResource("collapsed"); michael@0: let collapsed = this._getPersist(toolbarResource, collapsedResource); michael@0: // If the user does not have a persisted value for the toolbar's michael@0: // "collapsed" attribute, try to determine whether it's customized. michael@0: if (collapsed === null) { michael@0: // We consider the toolbar customized if it has more than michael@0: // 3 children, or if it has a persisted currentset value. michael@0: let currentsetResource = this._rdf.GetResource("currentset"); michael@0: let toolbarIsCustomized = !!this._getPersist(toolbarResource, michael@0: currentsetResource); michael@0: function getToolbarFolderCount() { michael@0: let toolbarFolder = michael@0: PlacesUtils.getFolderContents(PlacesUtils.toolbarFolderId).root; michael@0: let toolbarChildCount = toolbarFolder.childCount; michael@0: toolbarFolder.containerOpen = false; michael@0: return toolbarChildCount; michael@0: } michael@0: michael@0: if (toolbarIsCustomized || getToolbarFolderCount() > 3) { michael@0: this._setPersist(toolbarResource, collapsedResource, "false"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (currentUIVersion < 6) { michael@0: // convert tabsontop attribute to pref michael@0: let toolboxResource = this._rdf.GetResource(BROWSER_DOCURL + "navigator-toolbox"); michael@0: let tabsOnTopResource = this._rdf.GetResource("tabsontop"); michael@0: let tabsOnTopAttribute = this._getPersist(toolboxResource, tabsOnTopResource); michael@0: if (tabsOnTopAttribute) michael@0: Services.prefs.setBoolPref("browser.tabs.onTop", tabsOnTopAttribute == "true"); michael@0: } michael@0: michael@0: // Migration at version 7 only occurred for users who wanted to try the new michael@0: // Downloads Panel feature before its release. Since migration at version michael@0: // 9 adds the button by default, this step has been removed. michael@0: michael@0: if (currentUIVersion < 8) { michael@0: // Reset homepage pref for users who have it set to google.com/firefox michael@0: let uri = Services.prefs.getComplexValue("browser.startup.homepage", michael@0: Ci.nsIPrefLocalizedString).data; michael@0: if (uri && /^https?:\/\/(www\.)?google(\.\w{2,3}){1,2}\/firefox\/?$/.test(uri)) { michael@0: Services.prefs.clearUserPref("browser.startup.homepage"); michael@0: } michael@0: } michael@0: michael@0: if (currentUIVersion < 9) { michael@0: // This code adds the customizable downloads buttons. michael@0: let currentsetResource = this._rdf.GetResource("currentset"); michael@0: let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); michael@0: let currentset = this._getPersist(toolbarResource, currentsetResource); michael@0: michael@0: // Since the Downloads button is located in the navigation bar by default, michael@0: // migration needs to happen only if the toolbar was customized using a michael@0: // previous UI version, and the button was not already placed on the michael@0: // toolbar manually. michael@0: if (currentset && michael@0: currentset.indexOf("downloads-button") == -1) { michael@0: // The element is added either after the search bar or before the home michael@0: // button. As a last resort, the element is added just before the michael@0: // non-customizable window controls. michael@0: if (currentset.indexOf("search-container") != -1) { michael@0: currentset = currentset.replace(/(^|,)search-container($|,)/, michael@0: "$1search-container,downloads-button$2") michael@0: } else if (currentset.indexOf("home-button") != -1) { michael@0: currentset = currentset.replace(/(^|,)home-button($|,)/, michael@0: "$1downloads-button,home-button$2") michael@0: } else { michael@0: currentset = currentset.replace(/(^|,)window-controls($|,)/, michael@0: "$1downloads-button,window-controls$2") michael@0: } michael@0: this._setPersist(toolbarResource, currentsetResource, currentset); michael@0: } michael@0: } michael@0: michael@0: #ifdef XP_WIN michael@0: if (currentUIVersion < 10) { michael@0: // For Windows systems with display set to > 96dpi (i.e. systemDefaultScale michael@0: // will return a value > 1.0), we want to discard any saved full-zoom settings, michael@0: // as we'll now be scaling the content according to the system resolution michael@0: // scale factor (Windows "logical DPI" setting) michael@0: let sm = Cc["@mozilla.org/gfx/screenmanager;1"].getService(Ci.nsIScreenManager); michael@0: if (sm.systemDefaultScale > 1.0) { michael@0: let cps2 = Cc["@mozilla.org/content-pref/service;1"]. michael@0: getService(Ci.nsIContentPrefService2); michael@0: cps2.removeByName("browser.content.full-zoom", null); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: if (currentUIVersion < 11) { michael@0: Services.prefs.clearUserPref("dom.disable_window_move_resize"); michael@0: Services.prefs.clearUserPref("dom.disable_window_flip"); michael@0: Services.prefs.clearUserPref("dom.event.contextmenu.enabled"); michael@0: Services.prefs.clearUserPref("javascript.enabled"); michael@0: Services.prefs.clearUserPref("permissions.default.image"); michael@0: } michael@0: michael@0: if (currentUIVersion < 12) { michael@0: // Remove bookmarks-menu-button-container, then place michael@0: // bookmarks-menu-button into its position. michael@0: let currentsetResource = this._rdf.GetResource("currentset"); michael@0: let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); michael@0: let currentset = this._getPersist(toolbarResource, currentsetResource); michael@0: // Need to migrate only if toolbar is customized. michael@0: if (currentset) { michael@0: if (currentset.contains("bookmarks-menu-button-container")) { michael@0: currentset = currentset.replace(/(^|,)bookmarks-menu-button-container($|,)/, michael@0: "$1bookmarks-menu-button$2"); michael@0: this._setPersist(toolbarResource, currentsetResource, currentset); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (currentUIVersion < 13) { michael@0: try { michael@0: if (Services.prefs.getBoolPref("plugins.hide_infobar_for_missing_plugin")) michael@0: Services.prefs.setBoolPref("plugins.notifyMissingFlash", false); michael@0: } michael@0: catch (ex) {} michael@0: } michael@0: michael@0: if (currentUIVersion < 14) { michael@0: // DOM Storage doesn't specially handle about: pages anymore. michael@0: let path = OS.Path.join(OS.Constants.Path.profileDir, michael@0: "chromeappsstore.sqlite"); michael@0: OS.File.remove(path); michael@0: } michael@0: michael@0: // Version 15 was obsoleted in favour of 18. michael@0: michael@0: if (currentUIVersion < 16) { michael@0: let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); michael@0: let collapsedResource = this._rdf.GetResource("collapsed"); michael@0: let isCollapsed = this._getPersist(toolbarResource, collapsedResource); michael@0: if (isCollapsed == "true") { michael@0: this._setPersist(toolbarResource, collapsedResource, "false"); michael@0: } michael@0: } michael@0: michael@0: // Insert the bookmarks-menu-button into the nav-bar if it isn't already michael@0: // there. michael@0: if (currentUIVersion < 17) { michael@0: let currentsetResource = this._rdf.GetResource("currentset"); michael@0: let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); michael@0: let currentset = this._getPersist(toolbarResource, currentsetResource); michael@0: // Need to migrate only if toolbar is customized. michael@0: if (currentset) { michael@0: if (!currentset.contains("bookmarks-menu-button")) { michael@0: // The button isn't in the nav-bar, so let's look for an appropriate michael@0: // place to put it. michael@0: if (currentset.contains("downloads-button")) { michael@0: currentset = currentset.replace(/(^|,)downloads-button($|,)/, michael@0: "$1bookmarks-menu-button,downloads-button$2"); michael@0: } else if (currentset.contains("home-button")) { michael@0: currentset = currentset.replace(/(^|,)home-button($|,)/, michael@0: "$1bookmarks-menu-button,home-button$2"); michael@0: } else { michael@0: // Just append. michael@0: currentset = currentset.replace(/(^|,)window-controls($|,)/, michael@0: "$1bookmarks-menu-button,window-controls$2") michael@0: } michael@0: this._setPersist(toolbarResource, currentsetResource, currentset); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (currentUIVersion < 18) { michael@0: // Remove iconsize and mode from all the toolbars michael@0: let toolbars = ["navigator-toolbox", "nav-bar", "PersonalToolbar", michael@0: "addon-bar", "TabsToolbar", "toolbar-menubar"]; michael@0: for (let resourceName of ["mode", "iconsize"]) { michael@0: let resource = this._rdf.GetResource(resourceName); michael@0: for (let toolbarId of toolbars) { michael@0: let toolbar = this._rdf.GetResource(BROWSER_DOCURL + toolbarId); michael@0: if (this._getPersist(toolbar, resource)) { michael@0: this._setPersist(toolbar, resource); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (currentUIVersion < 19) { michael@0: let detector = null; michael@0: try { michael@0: detector = Services.prefs.getComplexValue("intl.charset.detector", michael@0: Ci.nsIPrefLocalizedString).data; michael@0: } catch (ex) {} michael@0: if (!(detector == "" || michael@0: detector == "ja_parallel_state_machine" || michael@0: detector == "ruprob" || michael@0: detector == "ukprob")) { michael@0: // If the encoding detector pref value is not reachable from the UI, michael@0: // reset to default (varies by localization). michael@0: Services.prefs.clearUserPref("intl.charset.detector"); michael@0: } michael@0: } michael@0: michael@0: if (currentUIVersion < 20) { michael@0: // Remove persisted collapsed state from TabsToolbar. michael@0: let resource = this._rdf.GetResource("collapsed"); michael@0: let toolbar = this._rdf.GetResource(BROWSER_DOCURL + "TabsToolbar"); michael@0: if (this._getPersist(toolbar, resource)) { michael@0: this._setPersist(toolbar, resource); michael@0: } michael@0: } michael@0: michael@0: if (currentUIVersion < 21) { michael@0: // Make sure the 'toolbarbutton-1' class will always be present from here michael@0: // on out. michael@0: let button = this._rdf.GetResource(BROWSER_DOCURL + "bookmarks-menu-button"); michael@0: let classResource = this._rdf.GetResource("class"); michael@0: if (this._getPersist(button, classResource)) { michael@0: this._setPersist(button, classResource); michael@0: } michael@0: } michael@0: michael@0: if (currentUIVersion < 22) { michael@0: // Reset the Sync promobox count to promote the new FxAccount-based Sync. michael@0: Services.prefs.clearUserPref("browser.syncPromoViewsLeft"); michael@0: Services.prefs.clearUserPref("browser.syncPromoViewsLeftMap"); michael@0: } michael@0: michael@0: if (this._dirty) michael@0: this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush(); michael@0: michael@0: delete this._rdf; michael@0: delete this._dataSource; michael@0: michael@0: // Update the migration version. michael@0: Services.prefs.setIntPref("browser.migration.version", UI_VERSION); michael@0: }, michael@0: michael@0: _getPersist: function BG__getPersist(aSource, aProperty) { michael@0: var target = this._dataSource.GetTarget(aSource, aProperty, true); michael@0: if (target instanceof Ci.nsIRDFLiteral) michael@0: return target.Value; michael@0: return null; michael@0: }, michael@0: michael@0: _setPersist: function BG__setPersist(aSource, aProperty, aTarget) { michael@0: this._dirty = true; michael@0: try { michael@0: var oldTarget = this._dataSource.GetTarget(aSource, aProperty, true); michael@0: if (oldTarget) { michael@0: if (aTarget) michael@0: this._dataSource.Change(aSource, aProperty, oldTarget, this._rdf.GetLiteral(aTarget)); michael@0: else michael@0: this._dataSource.Unassert(aSource, aProperty, oldTarget); michael@0: } michael@0: else { michael@0: this._dataSource.Assert(aSource, aProperty, this._rdf.GetLiteral(aTarget), true); michael@0: } michael@0: michael@0: // Add the entry to the persisted set for this document if it's not there. michael@0: // This code is mostly borrowed from XULDocument::Persist. michael@0: let docURL = aSource.ValueUTF8.split("#")[0]; michael@0: let docResource = this._rdf.GetResource(docURL); michael@0: let persistResource = this._rdf.GetResource("http://home.netscape.com/NC-rdf#persist"); michael@0: if (!this._dataSource.HasAssertion(docResource, persistResource, aSource, true)) { michael@0: this._dataSource.Assert(docResource, persistResource, aSource, true); michael@0: } michael@0: } michael@0: catch(ex) {} michael@0: }, michael@0: michael@0: // ------------------------------ michael@0: // public nsIBrowserGlue members michael@0: // ------------------------------ michael@0: michael@0: sanitize: function BG_sanitize(aParentWindow) { michael@0: this._sanitizer.sanitize(aParentWindow); michael@0: }, michael@0: michael@0: ensurePlacesDefaultQueriesInitialized: michael@0: function BG_ensurePlacesDefaultQueriesInitialized() { michael@0: // This is actual version of the smart bookmarks, must be increased every michael@0: // time smart bookmarks change. michael@0: // When adding a new smart bookmark below, its newInVersion property must michael@0: // be set to the version it has been added in, we will compare its value michael@0: // to users' smartBookmarksVersion and add new smart bookmarks without michael@0: // recreating old deleted ones. michael@0: const SMART_BOOKMARKS_VERSION = 7; michael@0: const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark"; michael@0: const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion"; michael@0: michael@0: // TODO bug 399268: should this be a pref? michael@0: const MAX_RESULTS = 10; michael@0: michael@0: // Get current smart bookmarks version. If not set, create them. michael@0: let smartBookmarksCurrentVersion = 0; michael@0: try { michael@0: smartBookmarksCurrentVersion = Services.prefs.getIntPref(SMART_BOOKMARKS_PREF); michael@0: } catch(ex) {} michael@0: michael@0: // If version is current or smart bookmarks are disabled, just bail out. michael@0: if (smartBookmarksCurrentVersion == -1 || michael@0: smartBookmarksCurrentVersion >= SMART_BOOKMARKS_VERSION) { michael@0: return; michael@0: } michael@0: michael@0: let batch = { michael@0: runBatched: function BG_EPDQI_runBatched() { michael@0: let menuIndex = 0; michael@0: let toolbarIndex = 0; michael@0: let bundle = Services.strings.createBundle("chrome://browser/locale/places/places.properties"); michael@0: michael@0: let smartBookmarks = { michael@0: MostVisited: { michael@0: title: bundle.GetStringFromName("mostVisitedTitle"), michael@0: uri: NetUtil.newURI("place:sort=" + michael@0: Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING + michael@0: "&maxResults=" + MAX_RESULTS), michael@0: parent: PlacesUtils.toolbarFolderId, michael@0: get position() { return toolbarIndex++; }, michael@0: newInVersion: 1 michael@0: }, michael@0: RecentlyBookmarked: { michael@0: title: bundle.GetStringFromName("recentlyBookmarkedTitle"), michael@0: uri: NetUtil.newURI("place:folder=BOOKMARKS_MENU" + michael@0: "&folder=UNFILED_BOOKMARKS" + michael@0: "&folder=TOOLBAR" + michael@0: "&queryType=" + michael@0: Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS + michael@0: "&sort=" + michael@0: Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING + michael@0: "&maxResults=" + MAX_RESULTS + michael@0: "&excludeQueries=1"), michael@0: parent: PlacesUtils.bookmarksMenuFolderId, michael@0: get position() { return menuIndex++; }, michael@0: newInVersion: 1 michael@0: }, michael@0: RecentTags: { michael@0: title: bundle.GetStringFromName("recentTagsTitle"), michael@0: uri: NetUtil.newURI("place:"+ michael@0: "type=" + michael@0: Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY + michael@0: "&sort=" + michael@0: Ci.nsINavHistoryQueryOptions.SORT_BY_LASTMODIFIED_DESCENDING + michael@0: "&maxResults=" + MAX_RESULTS), michael@0: parent: PlacesUtils.bookmarksMenuFolderId, michael@0: get position() { return menuIndex++; }, michael@0: newInVersion: 1 michael@0: }, michael@0: }; michael@0: michael@0: if (Services.metro && Services.metro.supported) { michael@0: smartBookmarks.Windows8Touch = { michael@0: title: PlacesUtils.getString("windows8TouchTitle"), michael@0: get uri() { michael@0: let metroBookmarksRoot = PlacesUtils.annotations.getItemsWithAnnotation('metro/bookmarksRoot', {}); michael@0: if (metroBookmarksRoot.length > 0) { michael@0: return NetUtil.newURI("place:folder=" + michael@0: metroBookmarksRoot[0] + michael@0: "&queryType=" + michael@0: Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS + michael@0: "&sort=" + michael@0: Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING + michael@0: "&maxResults=" + MAX_RESULTS + michael@0: "&excludeQueries=1") michael@0: } michael@0: return null; michael@0: }, michael@0: parent: PlacesUtils.bookmarksMenuFolderId, michael@0: get position() { return menuIndex++; }, michael@0: newInVersion: 7 michael@0: }; michael@0: } michael@0: michael@0: // Set current itemId, parent and position if Smart Bookmark exists, michael@0: // we will use these informations to create the new version at the same michael@0: // position. michael@0: let smartBookmarkItemIds = PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO); michael@0: smartBookmarkItemIds.forEach(function (itemId) { michael@0: let queryId = PlacesUtils.annotations.getItemAnnotation(itemId, SMART_BOOKMARKS_ANNO); michael@0: if (queryId in smartBookmarks) { michael@0: let smartBookmark = smartBookmarks[queryId]; michael@0: if (!smartBookmark.uri) { michael@0: PlacesUtils.bookmarks.removeItem(itemId); michael@0: return; michael@0: } michael@0: smartBookmark.itemId = itemId; michael@0: smartBookmark.parent = PlacesUtils.bookmarks.getFolderIdForItem(itemId); michael@0: smartBookmark.updatedPosition = PlacesUtils.bookmarks.getItemIndex(itemId); michael@0: } michael@0: else { michael@0: // We don't remove old Smart Bookmarks because user could still michael@0: // find them useful, or could have personalized them. michael@0: // Instead we remove the Smart Bookmark annotation. michael@0: PlacesUtils.annotations.removeItemAnnotation(itemId, SMART_BOOKMARKS_ANNO); michael@0: } michael@0: }); michael@0: michael@0: for (let queryId in smartBookmarks) { michael@0: let smartBookmark = smartBookmarks[queryId]; michael@0: michael@0: // We update or create only changed or new smart bookmarks. michael@0: // Also we respect user choices, so we won't try to create a smart michael@0: // bookmark if it has been removed. michael@0: if (smartBookmarksCurrentVersion > 0 && michael@0: smartBookmark.newInVersion <= smartBookmarksCurrentVersion && michael@0: !smartBookmark.itemId || !smartBookmark.uri) michael@0: continue; michael@0: michael@0: // Remove old version of the smart bookmark if it exists, since it michael@0: // will be replaced in place. michael@0: if (smartBookmark.itemId) { michael@0: PlacesUtils.bookmarks.removeItem(smartBookmark.itemId); michael@0: } michael@0: michael@0: // Create the new smart bookmark and store its updated itemId. michael@0: smartBookmark.itemId = michael@0: PlacesUtils.bookmarks.insertBookmark(smartBookmark.parent, michael@0: smartBookmark.uri, michael@0: smartBookmark.updatedPosition || smartBookmark.position, michael@0: smartBookmark.title); michael@0: PlacesUtils.annotations.setItemAnnotation(smartBookmark.itemId, michael@0: SMART_BOOKMARKS_ANNO, michael@0: queryId, 0, michael@0: PlacesUtils.annotations.EXPIRE_NEVER); michael@0: } michael@0: michael@0: // If we are creating all Smart Bookmarks from ground up, add a michael@0: // separator below them in the bookmarks menu. michael@0: if (smartBookmarksCurrentVersion == 0 && michael@0: smartBookmarkItemIds.length == 0) { michael@0: let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, michael@0: menuIndex); michael@0: // Don't add a separator if the menu was empty or there is one already. michael@0: if (id != -1 && michael@0: PlacesUtils.bookmarks.getItemType(id) != PlacesUtils.bookmarks.TYPE_SEPARATOR) { michael@0: PlacesUtils.bookmarks.insertSeparator(PlacesUtils.bookmarksMenuFolderId, michael@0: menuIndex); michael@0: } michael@0: } michael@0: } michael@0: }; michael@0: michael@0: try { michael@0: PlacesUtils.bookmarks.runInBatchMode(batch, null); michael@0: } michael@0: catch(ex) { michael@0: Components.utils.reportError(ex); michael@0: } michael@0: finally { michael@0: Services.prefs.setIntPref(SMART_BOOKMARKS_PREF, SMART_BOOKMARKS_VERSION); michael@0: Services.prefs.savePrefFile(null); michael@0: } michael@0: }, michael@0: michael@0: // this returns the most recent non-popup browser window michael@0: getMostRecentBrowserWindow: function BG_getMostRecentBrowserWindow() { michael@0: return RecentWindow.getMostRecentBrowserWindow(); michael@0: }, michael@0: michael@0: #ifdef MOZ_SERVICES_SYNC michael@0: /** michael@0: * Called as an observer when Sync's "display URI" notification is fired. michael@0: * michael@0: * We open the received URI in a background tab. michael@0: * michael@0: * Eventually, this will likely be replaced by a more robust tab syncing michael@0: * feature. This functionality is considered somewhat evil by UX because it michael@0: * opens a new tab automatically without any prompting. However, it is a michael@0: * lesser evil than sending a tab to a specific device (from e.g. Fennec) michael@0: * and having nothing happen on the receiving end. michael@0: */ michael@0: _onDisplaySyncURI: function _onDisplaySyncURI(data) { michael@0: try { michael@0: let tabbrowser = RecentWindow.getMostRecentBrowserWindow({private: false}).gBrowser; michael@0: michael@0: // The payload is wrapped weirdly because of how Sync does notifications. michael@0: tabbrowser.addTab(data.wrappedJSObject.object.uri); michael@0: } catch (ex) { michael@0: Cu.reportError("Error displaying tab received by Sync: " + ex); michael@0: } michael@0: }, michael@0: #endif michael@0: michael@0: // for XPCOM michael@0: classID: Components.ID("{eab9012e-5f74-4cbc-b2b5-a590235513cc}"), michael@0: michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, michael@0: Ci.nsISupportsWeakReference, michael@0: Ci.nsIBrowserGlue]), michael@0: michael@0: // redefine the default factory for XPCOMUtils michael@0: _xpcom_factory: BrowserGlueServiceFactory, michael@0: } michael@0: michael@0: function ContentPermissionPrompt() {} michael@0: michael@0: ContentPermissionPrompt.prototype = { michael@0: classID: Components.ID("{d8903bf6-68d5-4e97-bcd1-e4d3012f721a}"), michael@0: michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]), michael@0: michael@0: _getBrowserForRequest: function (aRequest) { michael@0: // "element" is only defined in e10s mode. michael@0: let browser = aRequest.element; michael@0: if (!browser) { michael@0: // Find the requesting browser. michael@0: browser = aRequest.window.QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIWebNavigation) michael@0: .QueryInterface(Ci.nsIDocShell) michael@0: .chromeEventHandler; michael@0: } michael@0: return browser; michael@0: }, michael@0: michael@0: /** michael@0: * Show a permission prompt. michael@0: * michael@0: * @param aRequest The permission request. michael@0: * @param aMessage The message to display on the prompt. michael@0: * @param aPermission The type of permission to prompt. michael@0: * @param aActions An array of actions of the form: michael@0: * [main action, secondary actions, ...] michael@0: * Actions are of the form { stringId, action, expireType, callback } michael@0: * Permission is granted if action is null or ALLOW_ACTION. michael@0: * @param aNotificationId The id of the PopupNotification. michael@0: * @param aAnchorId The id for the PopupNotification anchor. michael@0: * @param aOptions Options for the PopupNotification michael@0: */ michael@0: _showPrompt: function CPP_showPrompt(aRequest, aMessage, aPermission, aActions, michael@0: aNotificationId, aAnchorId, aOptions) { michael@0: function onFullScreen() { michael@0: popup.remove(); michael@0: } michael@0: michael@0: var browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); michael@0: michael@0: var browser = this._getBrowserForRequest(aRequest); michael@0: var chromeWin = browser.ownerDocument.defaultView; michael@0: var requestPrincipal = aRequest.principal; michael@0: michael@0: // Transform the prompt actions into PopupNotification actions. michael@0: var popupNotificationActions = []; michael@0: for (var i = 0; i < aActions.length; i++) { michael@0: let promptAction = aActions[i]; michael@0: michael@0: // Don't offer action in PB mode if the action remembers permission for more than a session. michael@0: if (PrivateBrowsingUtils.isWindowPrivate(chromeWin) && michael@0: promptAction.expireType != Ci.nsIPermissionManager.EXPIRE_SESSION && michael@0: promptAction.action) { michael@0: continue; michael@0: } michael@0: michael@0: var action = { michael@0: label: browserBundle.GetStringFromName(promptAction.stringId), michael@0: accessKey: browserBundle.GetStringFromName(promptAction.stringId + ".accesskey"), michael@0: callback: function() { michael@0: if (promptAction.callback) { michael@0: promptAction.callback(); michael@0: } michael@0: michael@0: // Remember permissions. michael@0: if (promptAction.action) { michael@0: Services.perms.addFromPrincipal(requestPrincipal, aPermission, michael@0: promptAction.action, promptAction.expireType); michael@0: } michael@0: michael@0: // Grant permission if action is null or ALLOW_ACTION. michael@0: if (!promptAction.action || promptAction.action == Ci.nsIPermissionManager.ALLOW_ACTION) { michael@0: aRequest.allow(); michael@0: } else { michael@0: aRequest.cancel(); michael@0: } michael@0: }, michael@0: }; michael@0: michael@0: popupNotificationActions.push(action); michael@0: } michael@0: michael@0: var mainAction = popupNotificationActions.length ? michael@0: popupNotificationActions[0] : null; michael@0: var secondaryActions = popupNotificationActions.splice(1); michael@0: michael@0: // Only allow exactly one permission rquest here. michael@0: let types = aRequest.types.QueryInterface(Ci.nsIArray); michael@0: if (types.length != 1) { michael@0: aRequest.cancel(); michael@0: return; michael@0: } michael@0: michael@0: let perm = types.queryElementAt(0, Ci.nsIContentPermissionType); michael@0: michael@0: if (perm.type == "pointerLock") { michael@0: // If there's no mainAction, this is the autoAllow warning prompt. michael@0: let autoAllow = !mainAction; michael@0: michael@0: if (!aOptions) michael@0: aOptions = {}; michael@0: michael@0: aOptions.removeOnDismissal = autoAllow; michael@0: aOptions.eventCallback = type => { michael@0: if (type == "removed") { michael@0: browser.removeEventListener("mozfullscreenchange", onFullScreen, true); michael@0: if (autoAllow) { michael@0: aRequest.allow(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: } michael@0: michael@0: var popup = chromeWin.PopupNotifications.show(browser, aNotificationId, aMessage, aAnchorId, michael@0: mainAction, secondaryActions, aOptions); michael@0: if (perm.type == "pointerLock") { michael@0: // pointerLock is automatically allowed in fullscreen mode (and revoked michael@0: // upon exit), so if the page enters fullscreen mode after requesting michael@0: // pointerLock (but before the user has granted permission), we should michael@0: // remove the now-impotent notification. michael@0: browser.addEventListener("mozfullscreenchange", onFullScreen, true); michael@0: } michael@0: }, michael@0: michael@0: _promptGeo : function(aRequest) { michael@0: var secHistogram = Services.telemetry.getHistogramById("SECURITY_UI"); michael@0: var browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); michael@0: var requestingURI = aRequest.principal.URI; michael@0: michael@0: var message; michael@0: michael@0: // Share location action. michael@0: var actions = [{ michael@0: stringId: "geolocation.shareLocation", michael@0: action: null, michael@0: expireType: null, michael@0: callback: function() { michael@0: secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_SHARE_LOCATION); michael@0: }, michael@0: }]; michael@0: michael@0: if (requestingURI.schemeIs("file")) { michael@0: message = browserBundle.formatStringFromName("geolocation.shareWithFile", michael@0: [requestingURI.path], 1); michael@0: } else { michael@0: message = browserBundle.formatStringFromName("geolocation.shareWithSite", michael@0: [requestingURI.host], 1); michael@0: // Always share location action. michael@0: actions.push({ michael@0: stringId: "geolocation.alwaysShareLocation", michael@0: action: Ci.nsIPermissionManager.ALLOW_ACTION, michael@0: expireType: null, michael@0: callback: function() { michael@0: secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_ALWAYS_SHARE); michael@0: }, michael@0: }); michael@0: michael@0: // Never share location action. michael@0: actions.push({ michael@0: stringId: "geolocation.neverShareLocation", michael@0: action: Ci.nsIPermissionManager.DENY_ACTION, michael@0: expireType: null, michael@0: callback: function() { michael@0: secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_NEVER_SHARE); michael@0: }, michael@0: }); michael@0: } michael@0: michael@0: var options = { michael@0: learnMoreURL: Services.urlFormatter.formatURLPref("browser.geolocation.warning.infoURL"), michael@0: }; michael@0: michael@0: secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST); michael@0: michael@0: this._showPrompt(aRequest, message, "geo", actions, "geolocation", michael@0: "geo-notification-icon", options); michael@0: }, michael@0: michael@0: _promptWebNotifications : function(aRequest) { michael@0: var browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); michael@0: var requestingURI = aRequest.principal.URI; michael@0: michael@0: var message = browserBundle.formatStringFromName("webNotifications.showFromSite", michael@0: [requestingURI.host], 1); michael@0: michael@0: var actions = [ michael@0: { michael@0: stringId: "webNotifications.showForSession", michael@0: action: Ci.nsIPermissionManager.ALLOW_ACTION, michael@0: expireType: Ci.nsIPermissionManager.EXPIRE_SESSION, michael@0: callback: function() {}, michael@0: }, michael@0: { michael@0: stringId: "webNotifications.alwaysShow", michael@0: action: Ci.nsIPermissionManager.ALLOW_ACTION, michael@0: expireType: null, michael@0: callback: function() {}, michael@0: }, michael@0: { michael@0: stringId: "webNotifications.neverShow", michael@0: action: Ci.nsIPermissionManager.DENY_ACTION, michael@0: expireType: null, michael@0: callback: function() {}, michael@0: }, michael@0: ]; michael@0: michael@0: this._showPrompt(aRequest, message, "desktop-notification", actions, michael@0: "web-notifications", michael@0: "web-notifications-notification-icon", null); michael@0: }, michael@0: michael@0: _promptPointerLock: function CPP_promtPointerLock(aRequest, autoAllow) { michael@0: michael@0: let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); michael@0: let requestingURI = aRequest.principal.URI; michael@0: michael@0: let originString = requestingURI.schemeIs("file") ? requestingURI.path : requestingURI.host; michael@0: let message = browserBundle.formatStringFromName(autoAllow ? michael@0: "pointerLock.autoLock.title2" : "pointerLock.title2", michael@0: [originString], 1); michael@0: // If this is an autoAllow info prompt, offer no actions. michael@0: // _showPrompt() will allow the request when it's dismissed. michael@0: let actions = []; michael@0: if (!autoAllow) { michael@0: actions = [ michael@0: { michael@0: stringId: "pointerLock.allow2", michael@0: action: null, michael@0: expireType: null, michael@0: callback: function() {}, michael@0: }, michael@0: { michael@0: stringId: "pointerLock.alwaysAllow", michael@0: action: Ci.nsIPermissionManager.ALLOW_ACTION, michael@0: expireType: null, michael@0: callback: function() {}, michael@0: }, michael@0: { michael@0: stringId: "pointerLock.neverAllow", michael@0: action: Ci.nsIPermissionManager.DENY_ACTION, michael@0: expireType: null, michael@0: callback: function() {}, michael@0: }, michael@0: ]; michael@0: } michael@0: michael@0: this._showPrompt(aRequest, message, "pointerLock", actions, "pointerLock", michael@0: "pointerLock-notification-icon", null); michael@0: }, michael@0: michael@0: prompt: function CPP_prompt(request) { michael@0: michael@0: // Only allow exactly one permission rquest here. michael@0: let types = request.types.QueryInterface(Ci.nsIArray); michael@0: if (types.length != 1) { michael@0: request.cancel(); michael@0: return; michael@0: } michael@0: let perm = types.queryElementAt(0, Ci.nsIContentPermissionType); michael@0: michael@0: const kFeatureKeys = { "geolocation" : "geo", michael@0: "desktop-notification" : "desktop-notification", michael@0: "pointerLock" : "pointerLock", michael@0: }; michael@0: michael@0: // Make sure that we support the request. michael@0: if (!(perm.type in kFeatureKeys)) { michael@0: return; michael@0: } michael@0: michael@0: var requestingPrincipal = request.principal; michael@0: var requestingURI = requestingPrincipal.URI; michael@0: michael@0: // Ignore requests from non-nsIStandardURLs michael@0: if (!(requestingURI instanceof Ci.nsIStandardURL)) michael@0: return; michael@0: michael@0: var autoAllow = false; michael@0: var permissionKey = kFeatureKeys[perm.type]; michael@0: var result = Services.perms.testExactPermissionFromPrincipal(requestingPrincipal, permissionKey); michael@0: michael@0: if (result == Ci.nsIPermissionManager.DENY_ACTION) { michael@0: request.cancel(); michael@0: return; michael@0: } michael@0: michael@0: if (result == Ci.nsIPermissionManager.ALLOW_ACTION) { michael@0: autoAllow = true; michael@0: // For pointerLock, we still want to show a warning prompt. michael@0: if (perm.type != "pointerLock") { michael@0: request.allow(); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: var browser = this._getBrowserForRequest(request); michael@0: var chromeWin = browser.ownerDocument.defaultView; michael@0: if (!chromeWin.PopupNotifications) michael@0: // Ignore requests from browsers hosted in windows that don't support michael@0: // PopupNotifications. michael@0: return; michael@0: michael@0: // Show the prompt. michael@0: switch (perm.type) { michael@0: case "geolocation": michael@0: this._promptGeo(request); michael@0: break; michael@0: case "desktop-notification": michael@0: this._promptWebNotifications(request); michael@0: break; michael@0: case "pointerLock": michael@0: this._promptPointerLock(request, autoAllow); michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: }; michael@0: michael@0: var components = [BrowserGlue, ContentPermissionPrompt]; michael@0: this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);