1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/components/nsBrowserGlue.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2198 @@ 1.4 +# -*- indent-tabs-mode: nil -*- 1.5 +# This Source Code Form is subject to the terms of the Mozilla Public 1.6 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.8 + 1.9 +const Ci = Components.interfaces; 1.10 +const Cc = Components.classes; 1.11 +const Cr = Components.results; 1.12 +const Cu = Components.utils; 1.13 + 1.14 +const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 1.15 + 1.16 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.17 +Cu.import("resource://gre/modules/Services.jsm"); 1.18 + 1.19 +XPCOMUtils.defineLazyModuleGetter(this, "AboutHome", 1.20 + "resource:///modules/AboutHome.jsm"); 1.21 + 1.22 +XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", 1.23 + "resource://gre/modules/AddonManager.jsm"); 1.24 + 1.25 +XPCOMUtils.defineLazyModuleGetter(this, "ContentClick", 1.26 + "resource:///modules/ContentClick.jsm"); 1.27 + 1.28 +XPCOMUtils.defineLazyModuleGetter(this, "DirectoryLinksProvider", 1.29 + "resource://gre/modules/DirectoryLinksProvider.jsm"); 1.30 + 1.31 +XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", 1.32 + "resource://gre/modules/NetUtil.jsm"); 1.33 + 1.34 +XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", 1.35 + "resource://gre/modules/FileUtils.jsm"); 1.36 + 1.37 +XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", 1.38 + "resource://gre/modules/PlacesUtils.jsm"); 1.39 + 1.40 +XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils", 1.41 + "resource://gre/modules/BookmarkHTMLUtils.jsm"); 1.42 + 1.43 +XPCOMUtils.defineLazyModuleGetter(this, "BookmarkJSONUtils", 1.44 + "resource://gre/modules/BookmarkJSONUtils.jsm"); 1.45 + 1.46 +XPCOMUtils.defineLazyModuleGetter(this, "WebappManager", 1.47 + "resource:///modules/WebappManager.jsm"); 1.48 + 1.49 +XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs", 1.50 + "resource://gre/modules/PageThumbs.jsm"); 1.51 + 1.52 +XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils", 1.53 + "resource://gre/modules/NewTabUtils.jsm"); 1.54 + 1.55 +XPCOMUtils.defineLazyModuleGetter(this, "BrowserNewTabPreloader", 1.56 + "resource:///modules/BrowserNewTabPreloader.jsm"); 1.57 + 1.58 +XPCOMUtils.defineLazyModuleGetter(this, "CustomizationTabPreloader", 1.59 + "resource:///modules/CustomizationTabPreloader.jsm"); 1.60 + 1.61 +XPCOMUtils.defineLazyModuleGetter(this, "PdfJs", 1.62 + "resource://pdf.js/PdfJs.jsm"); 1.63 + 1.64 +#ifdef NIGHTLY_BUILD 1.65 +XPCOMUtils.defineLazyModuleGetter(this, "ShumwayUtils", 1.66 + "resource://shumway/ShumwayUtils.jsm"); 1.67 +#endif 1.68 + 1.69 +XPCOMUtils.defineLazyModuleGetter(this, "webrtcUI", 1.70 + "resource:///modules/webrtcUI.jsm"); 1.71 + 1.72 +XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", 1.73 + "resource://gre/modules/PrivateBrowsingUtils.jsm"); 1.74 + 1.75 +XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", 1.76 + "resource:///modules/RecentWindow.jsm"); 1.77 + 1.78 +XPCOMUtils.defineLazyModuleGetter(this, "Task", 1.79 + "resource://gre/modules/Task.jsm"); 1.80 + 1.81 +XPCOMUtils.defineLazyModuleGetter(this, "PlacesBackups", 1.82 + "resource://gre/modules/PlacesBackups.jsm"); 1.83 + 1.84 +XPCOMUtils.defineLazyModuleGetter(this, "OS", 1.85 + "resource://gre/modules/osfile.jsm"); 1.86 + 1.87 + XPCOMUtils.defineLazyModuleGetter(this, "RemotePrompt", 1.88 + "resource:///modules/RemotePrompt.jsm"); 1.89 + 1.90 +XPCOMUtils.defineLazyModuleGetter(this, "SessionStore", 1.91 + "resource:///modules/sessionstore/SessionStore.jsm"); 1.92 + 1.93 +XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry", 1.94 + "resource:///modules/BrowserUITelemetry.jsm"); 1.95 + 1.96 +XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown", 1.97 + "resource://gre/modules/AsyncShutdown.jsm"); 1.98 + 1.99 +#ifdef NIGHTLY_BUILD 1.100 +XPCOMUtils.defineLazyModuleGetter(this, "SignInToWebsiteUX", 1.101 + "resource:///modules/SignInToWebsite.jsm"); 1.102 +#endif 1.103 + 1.104 +XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch", 1.105 + "resource:///modules/ContentSearch.jsm"); 1.106 + 1.107 +const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser"; 1.108 +const PREF_PLUGINS_UPDATEURL = "plugins.update.url"; 1.109 + 1.110 +// Seconds of idle before trying to create a bookmarks backup. 1.111 +const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 8 * 60; 1.112 +// Minimum interval between backups. We try to not create more than one backup 1.113 +// per interval. 1.114 +const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1; 1.115 +// Maximum interval between backups. If the last backup is older than these 1.116 +// days we will try to create a new one more aggressively. 1.117 +const BOOKMARKS_BACKUP_MAX_INTERVAL_DAYS = 3; 1.118 + 1.119 +// Factory object 1.120 +const BrowserGlueServiceFactory = { 1.121 + _instance: null, 1.122 + createInstance: function BGSF_createInstance(outer, iid) { 1.123 + if (outer != null) 1.124 + throw Components.results.NS_ERROR_NO_AGGREGATION; 1.125 + return this._instance == null ? 1.126 + this._instance = new BrowserGlue() : this._instance; 1.127 + } 1.128 +}; 1.129 + 1.130 +// Constructor 1.131 + 1.132 +function BrowserGlue() { 1.133 + XPCOMUtils.defineLazyServiceGetter(this, "_idleService", 1.134 + "@mozilla.org/widget/idleservice;1", 1.135 + "nsIIdleService"); 1.136 + 1.137 + XPCOMUtils.defineLazyGetter(this, "_distributionCustomizer", function() { 1.138 + Cu.import("resource:///modules/distribution.js"); 1.139 + return new DistributionCustomizer(); 1.140 + }); 1.141 + 1.142 + XPCOMUtils.defineLazyGetter(this, "_sanitizer", 1.143 + function() { 1.144 + let sanitizerScope = {}; 1.145 + Services.scriptloader.loadSubScript("chrome://browser/content/sanitize.js", sanitizerScope); 1.146 + return sanitizerScope.Sanitizer; 1.147 + }); 1.148 + 1.149 + this._init(); 1.150 +} 1.151 + 1.152 +#ifndef XP_MACOSX 1.153 +# OS X has the concept of zero-window sessions and therefore ignores the 1.154 +# browser-lastwindow-close-* topics. 1.155 +#define OBSERVE_LASTWINDOW_CLOSE_TOPICS 1 1.156 +#endif 1.157 + 1.158 +BrowserGlue.prototype = { 1.159 + _saveSession: false, 1.160 + _isPlacesInitObserver: false, 1.161 + _isPlacesLockedObserver: false, 1.162 + _isPlacesShutdownObserver: false, 1.163 + _isPlacesDatabaseLocked: false, 1.164 + _migrationImportsDefaultBookmarks: false, 1.165 + 1.166 + _setPrefToSaveSession: function BG__setPrefToSaveSession(aForce) { 1.167 + if (!this._saveSession && !aForce) 1.168 + return; 1.169 + 1.170 + Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true); 1.171 + 1.172 + // This method can be called via [NSApplication terminate:] on Mac, which 1.173 + // ends up causing prefs not to be flushed to disk, so we need to do that 1.174 + // explicitly here. See bug 497652. 1.175 + Services.prefs.savePrefFile(null); 1.176 + }, 1.177 + 1.178 +#ifdef MOZ_SERVICES_SYNC 1.179 + _setSyncAutoconnectDelay: function BG__setSyncAutoconnectDelay() { 1.180 + // Assume that a non-zero value for services.sync.autoconnectDelay should override 1.181 + if (Services.prefs.prefHasUserValue("services.sync.autoconnectDelay")) { 1.182 + let prefDelay = Services.prefs.getIntPref("services.sync.autoconnectDelay"); 1.183 + 1.184 + if (prefDelay > 0) 1.185 + return; 1.186 + } 1.187 + 1.188 + // delays are in seconds 1.189 + const MAX_DELAY = 300; 1.190 + let delay = 3; 1.191 + let browserEnum = Services.wm.getEnumerator("navigator:browser"); 1.192 + while (browserEnum.hasMoreElements()) { 1.193 + delay += browserEnum.getNext().gBrowser.tabs.length; 1.194 + } 1.195 + delay = delay <= MAX_DELAY ? delay : MAX_DELAY; 1.196 + 1.197 + Cu.import("resource://services-sync/main.js"); 1.198 + Weave.Service.scheduler.delayedAutoConnect(delay); 1.199 + }, 1.200 +#endif 1.201 + 1.202 + // nsIObserver implementation 1.203 + observe: function BG_observe(subject, topic, data) { 1.204 + switch (topic) { 1.205 + case "prefservice:after-app-defaults": 1.206 + this._onAppDefaults(); 1.207 + break; 1.208 + case "final-ui-startup": 1.209 + this._finalUIStartup(); 1.210 + break; 1.211 + case "browser-delayed-startup-finished": 1.212 + this._onFirstWindowLoaded(subject); 1.213 + Services.obs.removeObserver(this, "browser-delayed-startup-finished"); 1.214 + break; 1.215 + case "sessionstore-windows-restored": 1.216 + this._onWindowsRestored(); 1.217 + break; 1.218 + case "browser:purge-session-history": 1.219 + // reset the console service's error buffer 1.220 + Services.console.logStringMessage(null); // clear the console (in case it's open) 1.221 + Services.console.reset(); 1.222 + break; 1.223 + case "quit-application-requested": 1.224 + this._onQuitRequest(subject, data); 1.225 + break; 1.226 + case "quit-application-granted": 1.227 + this._onQuitApplicationGranted(); 1.228 + break; 1.229 +#ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS 1.230 + case "browser-lastwindow-close-requested": 1.231 + // The application is not actually quitting, but the last full browser 1.232 + // window is about to be closed. 1.233 + this._onQuitRequest(subject, "lastwindow"); 1.234 + break; 1.235 + case "browser-lastwindow-close-granted": 1.236 + this._setPrefToSaveSession(); 1.237 + break; 1.238 +#endif 1.239 +#ifdef MOZ_SERVICES_SYNC 1.240 + case "weave:service:ready": 1.241 + this._setSyncAutoconnectDelay(); 1.242 + break; 1.243 + case "weave:engine:clients:display-uri": 1.244 + this._onDisplaySyncURI(subject); 1.245 + break; 1.246 +#endif 1.247 + case "session-save": 1.248 + this._setPrefToSaveSession(true); 1.249 + subject.QueryInterface(Ci.nsISupportsPRBool); 1.250 + subject.data = true; 1.251 + break; 1.252 + case "places-init-complete": 1.253 + if (!this._migrationImportsDefaultBookmarks) 1.254 + this._initPlaces(false); 1.255 + 1.256 + Services.obs.removeObserver(this, "places-init-complete"); 1.257 + this._isPlacesInitObserver = false; 1.258 + // no longer needed, since history was initialized completely. 1.259 + Services.obs.removeObserver(this, "places-database-locked"); 1.260 + this._isPlacesLockedObserver = false; 1.261 + break; 1.262 + case "places-database-locked": 1.263 + this._isPlacesDatabaseLocked = true; 1.264 + // Stop observing, so further attempts to load history service 1.265 + // will not show the prompt. 1.266 + Services.obs.removeObserver(this, "places-database-locked"); 1.267 + this._isPlacesLockedObserver = false; 1.268 + break; 1.269 + case "places-shutdown": 1.270 + if (this._isPlacesShutdownObserver) { 1.271 + Services.obs.removeObserver(this, "places-shutdown"); 1.272 + this._isPlacesShutdownObserver = false; 1.273 + } 1.274 + // places-shutdown is fired when the profile is about to disappear. 1.275 + this._onPlacesShutdown(); 1.276 + break; 1.277 + case "idle": 1.278 + this._backupBookmarks(); 1.279 + break; 1.280 + case "distribution-customization-complete": 1.281 + Services.obs.removeObserver(this, "distribution-customization-complete"); 1.282 + // Customization has finished, we don't need the customizer anymore. 1.283 + delete this._distributionCustomizer; 1.284 + break; 1.285 + case "browser-glue-test": // used by tests 1.286 + if (data == "post-update-notification") { 1.287 + if (Services.prefs.prefHasUserValue("app.update.postupdate")) 1.288 + this._showUpdateNotification(); 1.289 + } 1.290 + else if (data == "force-ui-migration") { 1.291 + this._migrateUI(); 1.292 + } 1.293 + else if (data == "force-distribution-customization") { 1.294 + this._distributionCustomizer.applyPrefDefaults(); 1.295 + this._distributionCustomizer.applyCustomizations(); 1.296 + // To apply distribution bookmarks use "places-init-complete". 1.297 + } 1.298 + else if (data == "force-places-init") { 1.299 + this._initPlaces(false); 1.300 + } 1.301 + break; 1.302 + case "initial-migration-will-import-default-bookmarks": 1.303 + this._migrationImportsDefaultBookmarks = true; 1.304 + break; 1.305 + case "initial-migration-did-import-default-bookmarks": 1.306 + this._initPlaces(true); 1.307 + break; 1.308 + case "handle-xul-text-link": 1.309 + let linkHandled = subject.QueryInterface(Ci.nsISupportsPRBool); 1.310 + if (!linkHandled.data) { 1.311 + let win = this.getMostRecentBrowserWindow(); 1.312 + if (win) { 1.313 + win.openUILinkIn(data, "tab"); 1.314 + linkHandled.data = true; 1.315 + } 1.316 + } 1.317 + break; 1.318 + case "profile-before-change": 1.319 + // Any component depending on Places should be finalized in 1.320 + // _onPlacesShutdown. Any component that doesn't need to act after 1.321 + // the UI has gone should be finalized in _onQuitApplicationGranted. 1.322 + this._dispose(); 1.323 + break; 1.324 +#ifdef MOZ_SERVICES_HEALTHREPORT 1.325 + case "keyword-search": 1.326 + // This is very similar to code in 1.327 + // browser.js:BrowserSearch.recordSearchInHealthReport(). The code could 1.328 + // be consolidated if there is will. We need the observer in 1.329 + // nsBrowserGlue to prevent double counting. 1.330 + let reporter = Cc["@mozilla.org/datareporting/service;1"] 1.331 + .getService() 1.332 + .wrappedJSObject 1.333 + .healthReporter; 1.334 + 1.335 + if (!reporter) { 1.336 + return; 1.337 + } 1.338 + 1.339 + reporter.onInit().then(function record() { 1.340 + try { 1.341 + let engine = subject.QueryInterface(Ci.nsISearchEngine); 1.342 + reporter.getProvider("org.mozilla.searches").recordSearch(engine, "urlbar"); 1.343 + } catch (ex) { 1.344 + Cu.reportError(ex); 1.345 + } 1.346 + }); 1.347 + break; 1.348 +#endif 1.349 + case "browser-search-engine-modified": 1.350 + if (data != "engine-default" && data != "engine-current") { 1.351 + break; 1.352 + } 1.353 + // Enforce that the search service's defaultEngine is always equal to 1.354 + // its currentEngine. The search service will notify us any time either 1.355 + // of them are changed (either by directly setting the relevant prefs, 1.356 + // i.e. if add-ons try to change this directly, or if the 1.357 + // nsIBrowserSearchService setters are called). 1.358 + // No need to initialize the search service, since it's guaranteed to be 1.359 + // initialized already when this notification fires. 1.360 + let ss = Services.search; 1.361 + if (ss.currentEngine.name == ss.defaultEngine.name) 1.362 + return; 1.363 + if (data == "engine-current") 1.364 + ss.defaultEngine = ss.currentEngine; 1.365 + else 1.366 + ss.currentEngine = ss.defaultEngine; 1.367 + break; 1.368 + case "browser-search-service": 1.369 + if (data != "init-complete") 1.370 + return; 1.371 + Services.obs.removeObserver(this, "browser-search-service"); 1.372 + this._syncSearchEngines(); 1.373 + break; 1.374 + } 1.375 + }, 1.376 + 1.377 + _syncSearchEngines: function () { 1.378 + // Only do this if the search service is already initialized. This function 1.379 + // gets called in finalUIStartup and from a browser-search-service observer, 1.380 + // to catch both cases (search service initialization occurring before and 1.381 + // after final-ui-startup) 1.382 + if (Services.search.isInitialized) { 1.383 + Services.search.defaultEngine = Services.search.currentEngine; 1.384 + } 1.385 + }, 1.386 + 1.387 + // initialization (called on application startup) 1.388 + _init: function BG__init() { 1.389 + let os = Services.obs; 1.390 + os.addObserver(this, "prefservice:after-app-defaults", false); 1.391 + os.addObserver(this, "final-ui-startup", false); 1.392 + os.addObserver(this, "browser-delayed-startup-finished", false); 1.393 + os.addObserver(this, "sessionstore-windows-restored", false); 1.394 + os.addObserver(this, "browser:purge-session-history", false); 1.395 + os.addObserver(this, "quit-application-requested", false); 1.396 + os.addObserver(this, "quit-application-granted", false); 1.397 +#ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS 1.398 + os.addObserver(this, "browser-lastwindow-close-requested", false); 1.399 + os.addObserver(this, "browser-lastwindow-close-granted", false); 1.400 +#endif 1.401 +#ifdef MOZ_SERVICES_SYNC 1.402 + os.addObserver(this, "weave:service:ready", false); 1.403 + os.addObserver(this, "weave:engine:clients:display-uri", false); 1.404 +#endif 1.405 + os.addObserver(this, "session-save", false); 1.406 + os.addObserver(this, "places-init-complete", false); 1.407 + this._isPlacesInitObserver = true; 1.408 + os.addObserver(this, "places-database-locked", false); 1.409 + this._isPlacesLockedObserver = true; 1.410 + os.addObserver(this, "distribution-customization-complete", false); 1.411 + os.addObserver(this, "places-shutdown", false); 1.412 + this._isPlacesShutdownObserver = true; 1.413 + os.addObserver(this, "handle-xul-text-link", false); 1.414 + os.addObserver(this, "profile-before-change", false); 1.415 +#ifdef MOZ_SERVICES_HEALTHREPORT 1.416 + os.addObserver(this, "keyword-search", false); 1.417 +#endif 1.418 + os.addObserver(this, "browser-search-engine-modified", false); 1.419 + os.addObserver(this, "browser-search-service", false); 1.420 + }, 1.421 + 1.422 + // cleanup (called on application shutdown) 1.423 + _dispose: function BG__dispose() { 1.424 + let os = Services.obs; 1.425 + os.removeObserver(this, "prefservice:after-app-defaults"); 1.426 + os.removeObserver(this, "final-ui-startup"); 1.427 + os.removeObserver(this, "sessionstore-windows-restored"); 1.428 + os.removeObserver(this, "browser:purge-session-history"); 1.429 + os.removeObserver(this, "quit-application-requested"); 1.430 + os.removeObserver(this, "quit-application-granted"); 1.431 +#ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS 1.432 + os.removeObserver(this, "browser-lastwindow-close-requested"); 1.433 + os.removeObserver(this, "browser-lastwindow-close-granted"); 1.434 +#endif 1.435 +#ifdef MOZ_SERVICES_SYNC 1.436 + os.removeObserver(this, "weave:service:ready"); 1.437 + os.removeObserver(this, "weave:engine:clients:display-uri"); 1.438 +#endif 1.439 + os.removeObserver(this, "session-save"); 1.440 + if (this._bookmarksBackupIdleTime) { 1.441 + this._idleService.removeIdleObserver(this, this._bookmarksBackupIdleTime); 1.442 + delete this._bookmarksBackupIdleTime; 1.443 + } 1.444 + if (this._isPlacesInitObserver) 1.445 + os.removeObserver(this, "places-init-complete"); 1.446 + if (this._isPlacesLockedObserver) 1.447 + os.removeObserver(this, "places-database-locked"); 1.448 + if (this._isPlacesShutdownObserver) 1.449 + os.removeObserver(this, "places-shutdown"); 1.450 + os.removeObserver(this, "handle-xul-text-link"); 1.451 + os.removeObserver(this, "profile-before-change"); 1.452 +#ifdef MOZ_SERVICES_HEALTHREPORT 1.453 + os.removeObserver(this, "keyword-search"); 1.454 +#endif 1.455 + os.removeObserver(this, "browser-search-engine-modified"); 1.456 + try { 1.457 + os.removeObserver(this, "browser-search-service"); 1.458 + // may have already been removed by the observer 1.459 + } catch (ex) {} 1.460 + }, 1.461 + 1.462 + _onAppDefaults: function BG__onAppDefaults() { 1.463 + // apply distribution customizations (prefs) 1.464 + // other customizations are applied in _finalUIStartup() 1.465 + this._distributionCustomizer.applyPrefDefaults(); 1.466 + }, 1.467 + 1.468 + // runs on startup, before the first command line handler is invoked 1.469 + // (i.e. before the first window is opened) 1.470 + _finalUIStartup: function BG__finalUIStartup() { 1.471 + this._sanitizer.onStartup(); 1.472 + // check if we're in safe mode 1.473 + if (Services.appinfo.inSafeMode) { 1.474 + Services.ww.openWindow(null, "chrome://browser/content/safeMode.xul", 1.475 + "_blank", "chrome,centerscreen,modal,resizable=no", null); 1.476 + } 1.477 + 1.478 + // apply distribution customizations 1.479 + // prefs are applied in _onAppDefaults() 1.480 + this._distributionCustomizer.applyCustomizations(); 1.481 + 1.482 + // handle any UI migration 1.483 + this._migrateUI(); 1.484 + 1.485 + this._syncSearchEngines(); 1.486 + 1.487 + WebappManager.init(); 1.488 + PageThumbs.init(); 1.489 + NewTabUtils.init(); 1.490 + DirectoryLinksProvider.init(); 1.491 + NewTabUtils.links.addProvider(DirectoryLinksProvider); 1.492 + BrowserNewTabPreloader.init(); 1.493 +#ifdef NIGHTLY_BUILD 1.494 + if (Services.prefs.getBoolPref("dom.identity.enabled")) { 1.495 + SignInToWebsiteUX.init(); 1.496 + } 1.497 +#endif 1.498 + PdfJs.init(); 1.499 +#ifdef NIGHTLY_BUILD 1.500 + ShumwayUtils.init(); 1.501 +#endif 1.502 + webrtcUI.init(); 1.503 + AboutHome.init(); 1.504 + SessionStore.init(); 1.505 + BrowserUITelemetry.init(); 1.506 + ContentSearch.init(); 1.507 + 1.508 + if (Services.appinfo.browserTabsRemote) { 1.509 + ContentClick.init(); 1.510 + RemotePrompt.init(); 1.511 + } 1.512 + 1.513 + Services.obs.notifyObservers(null, "browser-ui-startup-complete", ""); 1.514 + }, 1.515 + 1.516 + _checkForOldBuildUpdates: function () { 1.517 + // check for update if our build is old 1.518 + if (Services.prefs.getBoolPref("app.update.enabled") && 1.519 + Services.prefs.getBoolPref("app.update.checkInstallTime")) { 1.520 + 1.521 + let buildID = Services.appinfo.appBuildID; 1.522 + let today = new Date().getTime(); 1.523 + let buildDate = new Date(buildID.slice(0,4), // year 1.524 + buildID.slice(4,6) - 1, // months are zero-based. 1.525 + buildID.slice(6,8), // day 1.526 + buildID.slice(8,10), // hour 1.527 + buildID.slice(10,12), // min 1.528 + buildID.slice(12,14)) // ms 1.529 + .getTime(); 1.530 + 1.531 + const millisecondsIn24Hours = 86400000; 1.532 + let acceptableAge = Services.prefs.getIntPref("app.update.checkInstallTime.days") * millisecondsIn24Hours; 1.533 + 1.534 + if (buildDate + acceptableAge < today) { 1.535 + Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService).checkForBackgroundUpdates(); 1.536 + } 1.537 + } 1.538 + }, 1.539 + 1.540 + _trackSlowStartup: function () { 1.541 + if (Services.startup.interrupted || 1.542 + Services.prefs.getBoolPref("browser.slowStartup.notificationDisabled")) 1.543 + return; 1.544 + 1.545 + let currentTime = Date.now() - Services.startup.getStartupInfo().process; 1.546 + let averageTime = 0; 1.547 + let samples = 0; 1.548 + try { 1.549 + averageTime = Services.prefs.getIntPref("browser.slowStartup.averageTime"); 1.550 + samples = Services.prefs.getIntPref("browser.slowStartup.samples"); 1.551 + } catch (e) { } 1.552 + 1.553 + let totalTime = (averageTime * samples) + currentTime; 1.554 + samples++; 1.555 + averageTime = totalTime / samples; 1.556 + 1.557 + if (samples >= Services.prefs.getIntPref("browser.slowStartup.maxSamples")) { 1.558 + if (averageTime > Services.prefs.getIntPref("browser.slowStartup.timeThreshold")) 1.559 + this._showSlowStartupNotification(); 1.560 + averageTime = 0; 1.561 + samples = 0; 1.562 + } 1.563 + 1.564 + Services.prefs.setIntPref("browser.slowStartup.averageTime", averageTime); 1.565 + Services.prefs.setIntPref("browser.slowStartup.samples", samples); 1.566 + }, 1.567 + 1.568 + _showSlowStartupNotification: function () { 1.569 + let win = this.getMostRecentBrowserWindow(); 1.570 + if (!win) 1.571 + return; 1.572 + 1.573 + let productName = Services.strings 1.574 + .createBundle("chrome://branding/locale/brand.properties") 1.575 + .GetStringFromName("brandFullName"); 1.576 + let message = win.gNavigatorBundle.getFormattedString("slowStartup.message", [productName]); 1.577 + 1.578 + let buttons = [ 1.579 + { 1.580 + label: win.gNavigatorBundle.getString("slowStartup.helpButton.label"), 1.581 + accessKey: win.gNavigatorBundle.getString("slowStartup.helpButton.accesskey"), 1.582 + callback: function () { 1.583 + win.openUILinkIn("https://support.mozilla.org/kb/reset-firefox-easily-fix-most-problems", "tab"); 1.584 + } 1.585 + }, 1.586 + { 1.587 + label: win.gNavigatorBundle.getString("slowStartup.disableNotificationButton.label"), 1.588 + accessKey: win.gNavigatorBundle.getString("slowStartup.disableNotificationButton.accesskey"), 1.589 + callback: function () { 1.590 + Services.prefs.setBoolPref("browser.slowStartup.notificationDisabled", true); 1.591 + } 1.592 + } 1.593 + ]; 1.594 + 1.595 + let nb = win.document.getElementById("global-notificationbox"); 1.596 + nb.appendNotification(message, "slow-startup", 1.597 + "chrome://browser/skin/slowStartup-16.png", 1.598 + nb.PRIORITY_INFO_LOW, buttons); 1.599 + }, 1.600 + 1.601 + /** 1.602 + * Show a notification bar offering a reset if the profile has been unused for some time. 1.603 + */ 1.604 + _resetUnusedProfileNotification: function () { 1.605 + let win = this.getMostRecentBrowserWindow(); 1.606 + if (!win) 1.607 + return; 1.608 + 1.609 + Cu.import("resource://gre/modules/ResetProfile.jsm"); 1.610 + if (!ResetProfile.resetSupported()) 1.611 + return; 1.612 + 1.613 + let productName = Services.strings 1.614 + .createBundle("chrome://branding/locale/brand.properties") 1.615 + .GetStringFromName("brandShortName"); 1.616 + let resetBundle = Services.strings 1.617 + .createBundle("chrome://global/locale/resetProfile.properties"); 1.618 + 1.619 + let message = resetBundle.formatStringFromName("resetUnusedProfile.message", [productName], 1); 1.620 + let buttons = [ 1.621 + { 1.622 + label: resetBundle.formatStringFromName("resetProfile.resetButton.label", [productName], 1), 1.623 + accessKey: resetBundle.GetStringFromName("resetProfile.resetButton.accesskey"), 1.624 + callback: function () { 1.625 + ResetProfile.openConfirmationDialog(win); 1.626 + } 1.627 + }, 1.628 + ]; 1.629 + 1.630 + let nb = win.document.getElementById("global-notificationbox"); 1.631 + nb.appendNotification(message, "reset-unused-profile", 1.632 + "chrome://global/skin/icons/question-16.png", 1.633 + nb.PRIORITY_INFO_LOW, buttons); 1.634 + }, 1.635 + 1.636 + // the first browser window has finished initializing 1.637 + _onFirstWindowLoaded: function BG__onFirstWindowLoaded(aWindow) { 1.638 +#ifdef XP_WIN 1.639 + // For windows seven, initialize the jump list module. 1.640 + const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1"; 1.641 + if (WINTASKBAR_CONTRACTID in Cc && 1.642 + Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) { 1.643 + let temp = {}; 1.644 + Cu.import("resource:///modules/WindowsJumpLists.jsm", temp); 1.645 + temp.WinTaskbarJumpList.startup(); 1.646 + } 1.647 +#endif 1.648 + 1.649 + this._trackSlowStartup(); 1.650 + 1.651 + // Offer to reset a user's profile if it hasn't been used for 60 days. 1.652 + const OFFER_PROFILE_RESET_INTERVAL_MS = 60 * 24 * 60 * 60 * 1000; 1.653 + let lastUse = Services.appinfo.replacedLockTime; 1.654 + let disableResetPrompt = false; 1.655 + try { 1.656 + disableResetPrompt = Services.prefs.getBoolPref("browser.disableResetPrompt"); 1.657 + } catch(e) {} 1.658 + if (!disableResetPrompt && lastUse && 1.659 + Date.now() - lastUse >= OFFER_PROFILE_RESET_INTERVAL_MS) { 1.660 + this._resetUnusedProfileNotification(); 1.661 + } 1.662 + 1.663 + this._checkForOldBuildUpdates(); 1.664 + }, 1.665 + 1.666 + /** 1.667 + * Application shutdown handler. 1.668 + */ 1.669 + _onQuitApplicationGranted: function () { 1.670 + // This pref must be set here because SessionStore will use its value 1.671 + // on quit-application. 1.672 + this._setPrefToSaveSession(); 1.673 + 1.674 + // Call trackStartupCrashEnd here in case the delayed call on startup hasn't 1.675 + // yet occurred (see trackStartupCrashEnd caller in browser.js). 1.676 + try { 1.677 + let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"] 1.678 + .getService(Ci.nsIAppStartup); 1.679 + appStartup.trackStartupCrashEnd(); 1.680 + } catch (e) { 1.681 + Cu.reportError("Could not end startup crash tracking in quit-application-granted: " + e); 1.682 + } 1.683 + 1.684 + BrowserNewTabPreloader.uninit(); 1.685 + CustomizationTabPreloader.uninit(); 1.686 + WebappManager.uninit(); 1.687 +#ifdef NIGHTLY_BUILD 1.688 + if (Services.prefs.getBoolPref("dom.identity.enabled")) { 1.689 + SignInToWebsiteUX.uninit(); 1.690 + } 1.691 +#endif 1.692 + webrtcUI.uninit(); 1.693 + }, 1.694 + 1.695 + // All initial windows have opened. 1.696 + _onWindowsRestored: function BG__onWindowsRestored() { 1.697 + // Show update notification, if needed. 1.698 + if (Services.prefs.prefHasUserValue("app.update.postupdate")) 1.699 + this._showUpdateNotification(); 1.700 + 1.701 + // Load the "more info" page for a locked places.sqlite 1.702 + // This property is set earlier by places-database-locked topic. 1.703 + if (this._isPlacesDatabaseLocked) { 1.704 + this._showPlacesLockedNotificationBox(); 1.705 + } 1.706 + 1.707 + // If there are plugins installed that are outdated, and the user hasn't 1.708 + // been warned about them yet, open the plugins update page. 1.709 + if (Services.prefs.getBoolPref(PREF_PLUGINS_NOTIFYUSER)) 1.710 + this._showPluginUpdatePage(); 1.711 + 1.712 + // For any add-ons that were installed disabled and can be enabled offer 1.713 + // them to the user. 1.714 + let changedIDs = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED); 1.715 + if (changedIDs.length > 0) { 1.716 + let win = this.getMostRecentBrowserWindow(); 1.717 + AddonManager.getAddonsByIDs(changedIDs, function(aAddons) { 1.718 + aAddons.forEach(function(aAddon) { 1.719 + // If the add-on isn't user disabled or can't be enabled then skip it. 1.720 + if (!aAddon.userDisabled || !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE)) 1.721 + return; 1.722 + 1.723 + win.openUILinkIn("about:newaddon?id=" + aAddon.id, "tab"); 1.724 + }) 1.725 + }); 1.726 + } 1.727 + 1.728 + // Perform default browser checking. 1.729 + var shell; 1.730 + try { 1.731 + shell = Components.classes["@mozilla.org/browser/shell-service;1"] 1.732 + .getService(Components.interfaces.nsIShellService); 1.733 + } catch (e) { } 1.734 + if (shell) { 1.735 +#ifdef DEBUG 1.736 + let shouldCheck = false; 1.737 +#else 1.738 + let shouldCheck = shell.shouldCheckDefaultBrowser; 1.739 +#endif 1.740 + let willRecoverSession = false; 1.741 + try { 1.742 + let ss = Cc["@mozilla.org/browser/sessionstartup;1"]. 1.743 + getService(Ci.nsISessionStartup); 1.744 + willRecoverSession = 1.745 + (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION); 1.746 + } 1.747 + catch (ex) { /* never mind; suppose SessionStore is broken */ } 1.748 + 1.749 + let isDefault = shell.isDefaultBrowser(true, false); // startup check, check all assoc 1.750 + try { 1.751 + // Report default browser status on startup to telemetry 1.752 + // so we can track whether we are the default. 1.753 + Services.telemetry.getHistogramById("BROWSER_IS_USER_DEFAULT") 1.754 + .add(isDefault); 1.755 + } 1.756 + catch (ex) { /* Don't break the default prompt if telemetry is broken. */ } 1.757 + 1.758 + if (shouldCheck && !isDefault && !willRecoverSession) { 1.759 + Services.tm.mainThread.dispatch(function() { 1.760 + var win = this.getMostRecentBrowserWindow(); 1.761 + var brandBundle = win.document.getElementById("bundle_brand"); 1.762 + var shellBundle = win.document.getElementById("bundle_shell"); 1.763 + 1.764 + var brandShortName = brandBundle.getString("brandShortName"); 1.765 + var promptTitle = shellBundle.getString("setDefaultBrowserTitle"); 1.766 + var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage", 1.767 + [brandShortName]); 1.768 + var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk", 1.769 + [brandShortName]); 1.770 + var checkEveryTime = { value: shouldCheck }; 1.771 + var ps = Services.prompt; 1.772 + var rv = ps.confirmEx(win, promptTitle, promptMessage, 1.773 + ps.STD_YES_NO_BUTTONS, 1.774 + null, null, null, checkboxLabel, checkEveryTime); 1.775 + if (rv == 0) { 1.776 + var claimAllTypes = true; 1.777 +#ifdef XP_WIN 1.778 + try { 1.779 + // In Windows 8, the UI for selecting default protocol is much 1.780 + // nicer than the UI for setting file type associations. So we 1.781 + // only show the protocol association screen on Windows 8. 1.782 + // Windows 8 is version 6.2. 1.783 + let version = Cc["@mozilla.org/system-info;1"] 1.784 + .getService(Ci.nsIPropertyBag2) 1.785 + .getProperty("version"); 1.786 + claimAllTypes = (parseFloat(version) < 6.2); 1.787 + } catch (ex) { } 1.788 +#endif 1.789 + shell.setDefaultBrowser(claimAllTypes, false); 1.790 + } 1.791 + shell.shouldCheckDefaultBrowser = checkEveryTime.value; 1.792 + }.bind(this), Ci.nsIThread.DISPATCH_NORMAL); 1.793 + } 1.794 + } 1.795 + }, 1.796 + 1.797 + _onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) { 1.798 + // If user has already dismissed quit request, then do nothing 1.799 + if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data) 1.800 + return; 1.801 + 1.802 + // There are several cases where we won't show a dialog here: 1.803 + // 1. There is only 1 tab open in 1 window 1.804 + // 2. The session will be restored at startup, indicated by 1.805 + // browser.startup.page == 3 or browser.sessionstore.resume_session_once == true 1.806 + // 3. browser.warnOnQuit == false 1.807 + // 4. The browser is currently in Private Browsing mode 1.808 + // 5. The browser will be restarted. 1.809 + // 1.810 + // Otherwise these are the conditions and the associated dialogs that will be shown: 1.811 + // 1. aQuitType == "lastwindow" or "quit" and browser.showQuitWarning == true 1.812 + // - The quit dialog will be shown 1.813 + // 2. aQuitType == "lastwindow" && browser.tabs.warnOnClose == true 1.814 + // - The "closing multiple tabs" dialog will be shown 1.815 + // 1.816 + // aQuitType == "lastwindow" is overloaded. "lastwindow" is used to indicate 1.817 + // "the last window is closing but we're not quitting (a non-browser window is open)" 1.818 + // and also "we're quitting by closing the last window". 1.819 + 1.820 + if (aQuitType == "restart") 1.821 + return; 1.822 + 1.823 + var windowcount = 0; 1.824 + var pagecount = 0; 1.825 + var browserEnum = Services.wm.getEnumerator("navigator:browser"); 1.826 + let allWindowsPrivate = true; 1.827 + while (browserEnum.hasMoreElements()) { 1.828 + // XXXbz should we skip closed windows here? 1.829 + windowcount++; 1.830 + 1.831 + var browser = browserEnum.getNext(); 1.832 + if (!PrivateBrowsingUtils.isWindowPrivate(browser)) 1.833 + allWindowsPrivate = false; 1.834 + var tabbrowser = browser.document.getElementById("content"); 1.835 + if (tabbrowser) 1.836 + pagecount += tabbrowser.browsers.length - tabbrowser._numPinnedTabs; 1.837 + } 1.838 + 1.839 + this._saveSession = false; 1.840 + if (pagecount < 2) 1.841 + return; 1.842 + 1.843 + if (!aQuitType) 1.844 + aQuitType = "quit"; 1.845 + 1.846 + var mostRecentBrowserWindow; 1.847 + 1.848 + // browser.warnOnQuit is a hidden global boolean to override all quit prompts 1.849 + // browser.showQuitWarning specifically covers quitting 1.850 + // browser.tabs.warnOnClose is the global "warn when closing multiple tabs" pref 1.851 + 1.852 + var sessionWillBeRestored = Services.prefs.getIntPref("browser.startup.page") == 3 || 1.853 + Services.prefs.getBoolPref("browser.sessionstore.resume_session_once"); 1.854 + if (sessionWillBeRestored || !Services.prefs.getBoolPref("browser.warnOnQuit")) 1.855 + return; 1.856 + 1.857 + // On last window close or quit && showQuitWarning, we want to show the 1.858 + // quit warning. 1.859 + if (!Services.prefs.getBoolPref("browser.showQuitWarning")) { 1.860 + if (aQuitType == "lastwindow") { 1.861 + // If aQuitType is "lastwindow" and we aren't showing the quit warning, 1.862 + // we should show the window closing warning instead. warnAboutClosing 1.863 + // tabs checks browser.tabs.warnOnClose and returns if it's ok to close 1.864 + // the window. It doesn't actually close the window. 1.865 + mostRecentBrowserWindow = Services.wm.getMostRecentWindow("navigator:browser"); 1.866 + let allTabs = mostRecentBrowserWindow.gBrowser.closingTabsEnum.ALL; 1.867 + aCancelQuit.data = !mostRecentBrowserWindow.gBrowser.warnAboutClosingTabs(allTabs) 1.868 + } 1.869 + return; 1.870 + } 1.871 + 1.872 + // Never show a prompt inside private browsing mode 1.873 + if (allWindowsPrivate) 1.874 + return; 1.875 + 1.876 + var quitBundle = Services.strings.createBundle("chrome://browser/locale/quitDialog.properties"); 1.877 + var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties"); 1.878 + 1.879 + var appName = brandBundle.GetStringFromName("brandShortName"); 1.880 + var quitTitleString = "quitDialogTitle"; 1.881 + var quitDialogTitle = quitBundle.formatStringFromName(quitTitleString, [appName], 1); 1.882 + 1.883 + var message; 1.884 + if (windowcount == 1) 1.885 + message = quitBundle.formatStringFromName("messageNoWindows", 1.886 + [appName], 1); 1.887 + else 1.888 + message = quitBundle.formatStringFromName("message", 1.889 + [appName], 1); 1.890 + 1.891 + var promptService = Services.prompt; 1.892 + 1.893 + var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0 + 1.894 + promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 + 1.895 + promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_2 + 1.896 + promptService.BUTTON_POS_0_DEFAULT; 1.897 + 1.898 + var neverAsk = {value:false}; 1.899 + var button0Title = quitBundle.GetStringFromName("saveTitle"); 1.900 + var button1Title = quitBundle.GetStringFromName("cancelTitle"); 1.901 + var button2Title = quitBundle.GetStringFromName("quitTitle"); 1.902 + var neverAskText = quitBundle.GetStringFromName("neverAsk2"); 1.903 + 1.904 + // This wouldn't have been set above since we shouldn't be here for 1.905 + // aQuitType == "lastwindow" 1.906 + mostRecentBrowserWindow = Services.wm.getMostRecentWindow("navigator:browser"); 1.907 + 1.908 + var buttonChoice = 1.909 + promptService.confirmEx(mostRecentBrowserWindow, quitDialogTitle, message, 1.910 + flags, button0Title, button1Title, button2Title, 1.911 + neverAskText, neverAsk); 1.912 + 1.913 + switch (buttonChoice) { 1.914 + case 2: // Quit 1.915 + if (neverAsk.value) 1.916 + Services.prefs.setBoolPref("browser.showQuitWarning", false); 1.917 + break; 1.918 + case 1: // Cancel 1.919 + aCancelQuit.QueryInterface(Ci.nsISupportsPRBool); 1.920 + aCancelQuit.data = true; 1.921 + break; 1.922 + case 0: // Save & Quit 1.923 + this._saveSession = true; 1.924 + if (neverAsk.value) { 1.925 + // always save state when shutting down 1.926 + Services.prefs.setIntPref("browser.startup.page", 3); 1.927 + } 1.928 + break; 1.929 + } 1.930 + }, 1.931 + 1.932 + _showUpdateNotification: function BG__showUpdateNotification() { 1.933 + Services.prefs.clearUserPref("app.update.postupdate"); 1.934 + 1.935 + var um = Cc["@mozilla.org/updates/update-manager;1"]. 1.936 + getService(Ci.nsIUpdateManager); 1.937 + try { 1.938 + // If the updates.xml file is deleted then getUpdateAt will throw. 1.939 + var update = um.getUpdateAt(0).QueryInterface(Ci.nsIPropertyBag); 1.940 + } 1.941 + catch (e) { 1.942 + // This should never happen. 1.943 + Cu.reportError("Unable to find update: " + e); 1.944 + return; 1.945 + } 1.946 + 1.947 + var actions = update.getProperty("actions"); 1.948 + if (!actions || actions.indexOf("silent") != -1) 1.949 + return; 1.950 + 1.951 + var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. 1.952 + getService(Ci.nsIURLFormatter); 1.953 + var browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); 1.954 + var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties"); 1.955 + var appName = brandBundle.GetStringFromName("brandShortName"); 1.956 + 1.957 + function getNotifyString(aPropData) { 1.958 + var propValue = update.getProperty(aPropData.propName); 1.959 + if (!propValue) { 1.960 + if (aPropData.prefName) 1.961 + propValue = formatter.formatURLPref(aPropData.prefName); 1.962 + else if (aPropData.stringParams) 1.963 + propValue = browserBundle.formatStringFromName(aPropData.stringName, 1.964 + aPropData.stringParams, 1.965 + aPropData.stringParams.length); 1.966 + else 1.967 + propValue = browserBundle.GetStringFromName(aPropData.stringName); 1.968 + } 1.969 + return propValue; 1.970 + } 1.971 + 1.972 + if (actions.indexOf("showNotification") != -1) { 1.973 + let text = getNotifyString({propName: "notificationText", 1.974 + stringName: "puNotifyText", 1.975 + stringParams: [appName]}); 1.976 + let url = getNotifyString({propName: "notificationURL", 1.977 + prefName: "startup.homepage_override_url"}); 1.978 + let label = getNotifyString({propName: "notificationButtonLabel", 1.979 + stringName: "pu.notifyButton.label"}); 1.980 + let key = getNotifyString({propName: "notificationButtonAccessKey", 1.981 + stringName: "pu.notifyButton.accesskey"}); 1.982 + 1.983 + let win = this.getMostRecentBrowserWindow(); 1.984 + let notifyBox = win.gBrowser.getNotificationBox(); 1.985 + 1.986 + let buttons = [ 1.987 + { 1.988 + label: label, 1.989 + accessKey: key, 1.990 + popup: null, 1.991 + callback: function(aNotificationBar, aButton) { 1.992 + win.openUILinkIn(url, "tab"); 1.993 + } 1.994 + } 1.995 + ]; 1.996 + 1.997 + let notification = notifyBox.appendNotification(text, "post-update-notification", 1.998 + null, notifyBox.PRIORITY_INFO_LOW, 1.999 + buttons); 1.1000 + notification.persistence = -1; // Until user closes it 1.1001 + } 1.1002 + 1.1003 + if (actions.indexOf("showAlert") == -1) 1.1004 + return; 1.1005 + 1.1006 + let notifier; 1.1007 + try { 1.1008 + notifier = Cc["@mozilla.org/alerts-service;1"]. 1.1009 + getService(Ci.nsIAlertsService); 1.1010 + } 1.1011 + catch (e) { 1.1012 + // nsIAlertsService is not available for this platform 1.1013 + return; 1.1014 + } 1.1015 + 1.1016 + let title = getNotifyString({propName: "alertTitle", 1.1017 + stringName: "puAlertTitle", 1.1018 + stringParams: [appName]}); 1.1019 + let text = getNotifyString({propName: "alertText", 1.1020 + stringName: "puAlertText", 1.1021 + stringParams: [appName]}); 1.1022 + let url = getNotifyString({propName: "alertURL", 1.1023 + prefName: "startup.homepage_override_url"}); 1.1024 + 1.1025 + var self = this; 1.1026 + function clickCallback(subject, topic, data) { 1.1027 + // This callback will be called twice but only once with this topic 1.1028 + if (topic != "alertclickcallback") 1.1029 + return; 1.1030 + let win = self.getMostRecentBrowserWindow(); 1.1031 + win.openUILinkIn(data, "tab"); 1.1032 + } 1.1033 + 1.1034 + try { 1.1035 + // This will throw NS_ERROR_NOT_AVAILABLE if the notification cannot 1.1036 + // be displayed per the idl. 1.1037 + notifier.showAlertNotification(null, title, text, 1.1038 + true, url, clickCallback); 1.1039 + } 1.1040 + catch (e) { 1.1041 + } 1.1042 + }, 1.1043 + 1.1044 + _showPluginUpdatePage: function BG__showPluginUpdatePage() { 1.1045 + Services.prefs.setBoolPref(PREF_PLUGINS_NOTIFYUSER, false); 1.1046 + 1.1047 + var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. 1.1048 + getService(Ci.nsIURLFormatter); 1.1049 + var updateUrl = formatter.formatURLPref(PREF_PLUGINS_UPDATEURL); 1.1050 + 1.1051 + var win = this.getMostRecentBrowserWindow(); 1.1052 + win.openUILinkIn(updateUrl, "tab"); 1.1053 + }, 1.1054 + 1.1055 + /** 1.1056 + * Initialize Places 1.1057 + * - imports the bookmarks html file if bookmarks database is empty, try to 1.1058 + * restore bookmarks from a JSON backup if the backend indicates that the 1.1059 + * database was corrupt. 1.1060 + * 1.1061 + * These prefs can be set up by the frontend: 1.1062 + * 1.1063 + * WARNING: setting these preferences to true will overwite existing bookmarks 1.1064 + * 1.1065 + * - browser.places.importBookmarksHTML 1.1066 + * Set to true will import the bookmarks.html file from the profile folder. 1.1067 + * - browser.places.smartBookmarksVersion 1.1068 + * Set during HTML import to indicate that Smart Bookmarks were created. 1.1069 + * Set to -1 to disable Smart Bookmarks creation. 1.1070 + * Set to 0 to restore current Smart Bookmarks. 1.1071 + * - browser.bookmarks.restore_default_bookmarks 1.1072 + * Set to true by safe-mode dialog to indicate we must restore default 1.1073 + * bookmarks. 1.1074 + */ 1.1075 + _initPlaces: function BG__initPlaces(aInitialMigrationPerformed) { 1.1076 + // We must instantiate the history service since it will tell us if we 1.1077 + // need to import or restore bookmarks due to first-run, corruption or 1.1078 + // forced migration (due to a major schema change). 1.1079 + // If the database is corrupt or has been newly created we should 1.1080 + // import bookmarks. 1.1081 + let dbStatus = PlacesUtils.history.databaseStatus; 1.1082 + let importBookmarks = !aInitialMigrationPerformed && 1.1083 + (dbStatus == PlacesUtils.history.DATABASE_STATUS_CREATE || 1.1084 + dbStatus == PlacesUtils.history.DATABASE_STATUS_CORRUPT); 1.1085 + 1.1086 + // Check if user or an extension has required to import bookmarks.html 1.1087 + let importBookmarksHTML = false; 1.1088 + try { 1.1089 + importBookmarksHTML = 1.1090 + Services.prefs.getBoolPref("browser.places.importBookmarksHTML"); 1.1091 + if (importBookmarksHTML) 1.1092 + importBookmarks = true; 1.1093 + } catch(ex) {} 1.1094 + 1.1095 + // Support legacy bookmarks.html format for apps that depend on that format. 1.1096 + let autoExportHTML = false; 1.1097 + try { 1.1098 + autoExportHTML = Services.prefs.getBoolPref("browser.bookmarks.autoExportHTML"); 1.1099 + } catch (ex) {} // Do not export. 1.1100 + if (autoExportHTML) { 1.1101 + // Sqlite.jsm and Places shutdown happen at profile-before-change, thus, 1.1102 + // to be on the safe side, this should run earlier. 1.1103 + AsyncShutdown.profileChangeTeardown.addBlocker( 1.1104 + "Places: export bookmarks.html", 1.1105 + () => BookmarkHTMLUtils.exportToFile(BookmarkHTMLUtils.defaultPath)); 1.1106 + } 1.1107 + 1.1108 + Task.spawn(function() { 1.1109 + // Check if Safe Mode or the user has required to restore bookmarks from 1.1110 + // default profile's bookmarks.html 1.1111 + let restoreDefaultBookmarks = false; 1.1112 + try { 1.1113 + restoreDefaultBookmarks = 1.1114 + Services.prefs.getBoolPref("browser.bookmarks.restore_default_bookmarks"); 1.1115 + if (restoreDefaultBookmarks) { 1.1116 + // Ensure that we already have a bookmarks backup for today. 1.1117 + yield this._backupBookmarks(); 1.1118 + importBookmarks = true; 1.1119 + } 1.1120 + } catch(ex) {} 1.1121 + 1.1122 + // This may be reused later, check for "=== undefined" to see if it has 1.1123 + // been populated already. 1.1124 + let lastBackupFile; 1.1125 + 1.1126 + // If the user did not require to restore default bookmarks, or import 1.1127 + // from bookmarks.html, we will try to restore from JSON 1.1128 + if (importBookmarks && !restoreDefaultBookmarks && !importBookmarksHTML) { 1.1129 + // get latest JSON backup 1.1130 + lastBackupFile = yield PlacesBackups.getMostRecentBackup("json"); 1.1131 + if (lastBackupFile) { 1.1132 + // restore from JSON backup 1.1133 + yield BookmarkJSONUtils.importFromFile(lastBackupFile, true); 1.1134 + importBookmarks = false; 1.1135 + } 1.1136 + else { 1.1137 + // We have created a new database but we don't have any backup available 1.1138 + importBookmarks = true; 1.1139 + if (yield OS.File.exists(BookmarkHTMLUtils.defaultPath)) { 1.1140 + // If bookmarks.html is available in current profile import it... 1.1141 + importBookmarksHTML = true; 1.1142 + } 1.1143 + else { 1.1144 + // ...otherwise we will restore defaults 1.1145 + restoreDefaultBookmarks = true; 1.1146 + } 1.1147 + } 1.1148 + } 1.1149 + 1.1150 + // If bookmarks are not imported, then initialize smart bookmarks. This 1.1151 + // happens during a common startup. 1.1152 + // Otherwise, if any kind of import runs, smart bookmarks creation should be 1.1153 + // delayed till the import operations has finished. Not doing so would 1.1154 + // cause them to be overwritten by the newly imported bookmarks. 1.1155 + if (!importBookmarks) { 1.1156 + // Now apply distribution customized bookmarks. 1.1157 + // This should always run after Places initialization. 1.1158 + this._distributionCustomizer.applyBookmarks(); 1.1159 + this.ensurePlacesDefaultQueriesInitialized(); 1.1160 + } 1.1161 + else { 1.1162 + // An import operation is about to run. 1.1163 + // Don't try to recreate smart bookmarks if autoExportHTML is true or 1.1164 + // smart bookmarks are disabled. 1.1165 + let smartBookmarksVersion = 0; 1.1166 + try { 1.1167 + smartBookmarksVersion = Services.prefs.getIntPref("browser.places.smartBookmarksVersion"); 1.1168 + } catch(ex) {} 1.1169 + if (!autoExportHTML && smartBookmarksVersion != -1) 1.1170 + Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0); 1.1171 + 1.1172 + let bookmarksUrl = null; 1.1173 + if (restoreDefaultBookmarks) { 1.1174 + // User wants to restore bookmarks.html file from default profile folder 1.1175 + bookmarksUrl = "resource:///defaults/profile/bookmarks.html"; 1.1176 + } 1.1177 + else if (yield OS.File.exists(BookmarkHTMLUtils.defaultPath)) { 1.1178 + bookmarksUrl = OS.Path.toFileURI(BookmarkHTMLUtils.defaultPath); 1.1179 + } 1.1180 + 1.1181 + if (bookmarksUrl) { 1.1182 + // Import from bookmarks.html file. 1.1183 + try { 1.1184 + BookmarkHTMLUtils.importFromURL(bookmarksUrl, true).then(null, 1.1185 + function onFailure() { 1.1186 + Cu.reportError("Bookmarks.html file could be corrupt."); 1.1187 + } 1.1188 + ).then( 1.1189 + function onComplete() { 1.1190 + // Now apply distribution customized bookmarks. 1.1191 + // This should always run after Places initialization. 1.1192 + this._distributionCustomizer.applyBookmarks(); 1.1193 + // Ensure that smart bookmarks are created once the operation is 1.1194 + // complete. 1.1195 + this.ensurePlacesDefaultQueriesInitialized(); 1.1196 + }.bind(this) 1.1197 + ); 1.1198 + } catch (err) { 1.1199 + Cu.reportError("Bookmarks.html file could be corrupt. " + err); 1.1200 + } 1.1201 + } 1.1202 + else { 1.1203 + Cu.reportError("Unable to find bookmarks.html file."); 1.1204 + } 1.1205 + 1.1206 + // Reset preferences, so we won't try to import again at next run 1.1207 + if (importBookmarksHTML) 1.1208 + Services.prefs.setBoolPref("browser.places.importBookmarksHTML", false); 1.1209 + if (restoreDefaultBookmarks) 1.1210 + Services.prefs.setBoolPref("browser.bookmarks.restore_default_bookmarks", 1.1211 + false); 1.1212 + } 1.1213 + 1.1214 + // Initialize bookmark archiving on idle. 1.1215 + if (!this._bookmarksBackupIdleTime) { 1.1216 + this._bookmarksBackupIdleTime = BOOKMARKS_BACKUP_IDLE_TIME_SEC; 1.1217 + 1.1218 + // If there is no backup, or the last bookmarks backup is too old, use 1.1219 + // a more aggressive idle observer. 1.1220 + if (lastBackupFile === undefined) 1.1221 + lastBackupFile = yield PlacesBackups.getMostRecentBackup(); 1.1222 + if (!lastBackupFile) { 1.1223 + this._bookmarksBackupIdleTime /= 2; 1.1224 + } 1.1225 + else { 1.1226 + let lastBackupTime = PlacesBackups.getDateForFile(lastBackupFile); 1.1227 + let profileLastUse = Services.appinfo.replacedLockTime || Date.now(); 1.1228 + 1.1229 + // If there is a backup after the last profile usage date it's fine, 1.1230 + // regardless its age. Otherwise check how old is the last 1.1231 + // available backup compared to that session. 1.1232 + if (profileLastUse > lastBackupTime) { 1.1233 + let backupAge = Math.round((profileLastUse - lastBackupTime) / 86400000); 1.1234 + // Report the age of the last available backup. 1.1235 + try { 1.1236 + Services.telemetry 1.1237 + .getHistogramById("PLACES_BACKUPS_DAYSFROMLAST") 1.1238 + .add(backupAge); 1.1239 + } catch (ex) { 1.1240 + Components.utils.reportError("Unable to report telemetry."); 1.1241 + } 1.1242 + 1.1243 + if (backupAge > BOOKMARKS_BACKUP_MAX_INTERVAL_DAYS) 1.1244 + this._bookmarksBackupIdleTime /= 2; 1.1245 + } 1.1246 + } 1.1247 + this._idleService.addIdleObserver(this, this._bookmarksBackupIdleTime); 1.1248 + } 1.1249 + 1.1250 + Services.obs.notifyObservers(null, "places-browser-init-complete", ""); 1.1251 + }.bind(this)); 1.1252 + }, 1.1253 + 1.1254 + /** 1.1255 + * Places shut-down tasks 1.1256 + * - finalize components depending on Places. 1.1257 + * - export bookmarks as HTML, if so configured. 1.1258 + */ 1.1259 + _onPlacesShutdown: function BG__onPlacesShutdown() { 1.1260 + this._sanitizer.onShutdown(); 1.1261 + PageThumbs.uninit(); 1.1262 + 1.1263 + if (this._bookmarksBackupIdleTime) { 1.1264 + this._idleService.removeIdleObserver(this, this._bookmarksBackupIdleTime); 1.1265 + delete this._bookmarksBackupIdleTime; 1.1266 + } 1.1267 + }, 1.1268 + 1.1269 + /** 1.1270 + * If a backup for today doesn't exist, this creates one. 1.1271 + */ 1.1272 + _backupBookmarks: function BG__backupBookmarks() { 1.1273 + return Task.spawn(function() { 1.1274 + let lastBackupFile = yield PlacesBackups.getMostRecentBackup(); 1.1275 + // Should backup bookmarks if there are no backups or the maximum 1.1276 + // interval between backups elapsed. 1.1277 + if (!lastBackupFile || 1.1278 + new Date() - PlacesBackups.getDateForFile(lastBackupFile) > BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS * 86400000) { 1.1279 + let maxBackups = Services.prefs.getIntPref("browser.bookmarks.max_backups"); 1.1280 + yield PlacesBackups.create(maxBackups); 1.1281 + } 1.1282 + }); 1.1283 + }, 1.1284 + 1.1285 + /** 1.1286 + * Show the notificationBox for a locked places database. 1.1287 + */ 1.1288 + _showPlacesLockedNotificationBox: function BG__showPlacesLockedNotificationBox() { 1.1289 + var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties"); 1.1290 + var applicationName = brandBundle.GetStringFromName("brandShortName"); 1.1291 + var placesBundle = Services.strings.createBundle("chrome://browser/locale/places/places.properties"); 1.1292 + var title = placesBundle.GetStringFromName("lockPrompt.title"); 1.1293 + var text = placesBundle.formatStringFromName("lockPrompt.text", [applicationName], 1); 1.1294 + var buttonText = placesBundle.GetStringFromName("lockPromptInfoButton.label"); 1.1295 + var accessKey = placesBundle.GetStringFromName("lockPromptInfoButton.accessKey"); 1.1296 + 1.1297 + var helpTopic = "places-locked"; 1.1298 + var url = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. 1.1299 + getService(Components.interfaces.nsIURLFormatter). 1.1300 + formatURLPref("app.support.baseURL"); 1.1301 + url += helpTopic; 1.1302 + 1.1303 + var win = this.getMostRecentBrowserWindow(); 1.1304 + 1.1305 + var buttons = [ 1.1306 + { 1.1307 + label: buttonText, 1.1308 + accessKey: accessKey, 1.1309 + popup: null, 1.1310 + callback: function(aNotificationBar, aButton) { 1.1311 + win.openUILinkIn(url, "tab"); 1.1312 + } 1.1313 + } 1.1314 + ]; 1.1315 + 1.1316 + var notifyBox = win.gBrowser.getNotificationBox(); 1.1317 + var notification = notifyBox.appendNotification(text, title, null, 1.1318 + notifyBox.PRIORITY_CRITICAL_MEDIUM, 1.1319 + buttons); 1.1320 + notification.persistence = -1; // Until user closes it 1.1321 + }, 1.1322 + 1.1323 + _migrateUI: function BG__migrateUI() { 1.1324 + const UI_VERSION = 22; 1.1325 + const BROWSER_DOCURL = "chrome://browser/content/browser.xul#"; 1.1326 + let currentUIVersion = 0; 1.1327 + try { 1.1328 + currentUIVersion = Services.prefs.getIntPref("browser.migration.version"); 1.1329 + } catch(ex) {} 1.1330 + if (currentUIVersion >= UI_VERSION) 1.1331 + return; 1.1332 + 1.1333 + this._rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService); 1.1334 + this._dataSource = this._rdf.GetDataSource("rdf:local-store"); 1.1335 + this._dirty = false; 1.1336 + 1.1337 + if (currentUIVersion < 2) { 1.1338 + // This code adds the customizable bookmarks button. 1.1339 + let currentsetResource = this._rdf.GetResource("currentset"); 1.1340 + let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); 1.1341 + let currentset = this._getPersist(toolbarResource, currentsetResource); 1.1342 + // Need to migrate only if toolbar is customized and the element is not found. 1.1343 + if (currentset && 1.1344 + currentset.indexOf("bookmarks-menu-button-container") == -1) { 1.1345 + currentset += ",bookmarks-menu-button-container"; 1.1346 + this._setPersist(toolbarResource, currentsetResource, currentset); 1.1347 + } 1.1348 + } 1.1349 + 1.1350 + if (currentUIVersion < 3) { 1.1351 + // This code merges the reload/stop/go button into the url bar. 1.1352 + let currentsetResource = this._rdf.GetResource("currentset"); 1.1353 + let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); 1.1354 + let currentset = this._getPersist(toolbarResource, currentsetResource); 1.1355 + // Need to migrate only if toolbar is customized and all 3 elements are found. 1.1356 + if (currentset && 1.1357 + currentset.indexOf("reload-button") != -1 && 1.1358 + currentset.indexOf("stop-button") != -1 && 1.1359 + currentset.indexOf("urlbar-container") != -1 && 1.1360 + currentset.indexOf("urlbar-container,reload-button,stop-button") == -1) { 1.1361 + currentset = currentset.replace(/(^|,)reload-button($|,)/, "$1$2") 1.1362 + .replace(/(^|,)stop-button($|,)/, "$1$2") 1.1363 + .replace(/(^|,)urlbar-container($|,)/, 1.1364 + "$1urlbar-container,reload-button,stop-button$2"); 1.1365 + this._setPersist(toolbarResource, currentsetResource, currentset); 1.1366 + } 1.1367 + } 1.1368 + 1.1369 + if (currentUIVersion < 4) { 1.1370 + // This code moves the home button to the immediate left of the bookmarks menu button. 1.1371 + let currentsetResource = this._rdf.GetResource("currentset"); 1.1372 + let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); 1.1373 + let currentset = this._getPersist(toolbarResource, currentsetResource); 1.1374 + // Need to migrate only if toolbar is customized and the elements are found. 1.1375 + if (currentset && 1.1376 + currentset.indexOf("home-button") != -1 && 1.1377 + currentset.indexOf("bookmarks-menu-button-container") != -1) { 1.1378 + currentset = currentset.replace(/(^|,)home-button($|,)/, "$1$2") 1.1379 + .replace(/(^|,)bookmarks-menu-button-container($|,)/, 1.1380 + "$1home-button,bookmarks-menu-button-container$2"); 1.1381 + this._setPersist(toolbarResource, currentsetResource, currentset); 1.1382 + } 1.1383 + } 1.1384 + 1.1385 + if (currentUIVersion < 5) { 1.1386 + // This code uncollapses PersonalToolbar if its collapsed status is not 1.1387 + // persisted, and user customized it or changed default bookmarks. 1.1388 + let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "PersonalToolbar"); 1.1389 + let collapsedResource = this._rdf.GetResource("collapsed"); 1.1390 + let collapsed = this._getPersist(toolbarResource, collapsedResource); 1.1391 + // If the user does not have a persisted value for the toolbar's 1.1392 + // "collapsed" attribute, try to determine whether it's customized. 1.1393 + if (collapsed === null) { 1.1394 + // We consider the toolbar customized if it has more than 1.1395 + // 3 children, or if it has a persisted currentset value. 1.1396 + let currentsetResource = this._rdf.GetResource("currentset"); 1.1397 + let toolbarIsCustomized = !!this._getPersist(toolbarResource, 1.1398 + currentsetResource); 1.1399 + function getToolbarFolderCount() { 1.1400 + let toolbarFolder = 1.1401 + PlacesUtils.getFolderContents(PlacesUtils.toolbarFolderId).root; 1.1402 + let toolbarChildCount = toolbarFolder.childCount; 1.1403 + toolbarFolder.containerOpen = false; 1.1404 + return toolbarChildCount; 1.1405 + } 1.1406 + 1.1407 + if (toolbarIsCustomized || getToolbarFolderCount() > 3) { 1.1408 + this._setPersist(toolbarResource, collapsedResource, "false"); 1.1409 + } 1.1410 + } 1.1411 + } 1.1412 + 1.1413 + if (currentUIVersion < 6) { 1.1414 + // convert tabsontop attribute to pref 1.1415 + let toolboxResource = this._rdf.GetResource(BROWSER_DOCURL + "navigator-toolbox"); 1.1416 + let tabsOnTopResource = this._rdf.GetResource("tabsontop"); 1.1417 + let tabsOnTopAttribute = this._getPersist(toolboxResource, tabsOnTopResource); 1.1418 + if (tabsOnTopAttribute) 1.1419 + Services.prefs.setBoolPref("browser.tabs.onTop", tabsOnTopAttribute == "true"); 1.1420 + } 1.1421 + 1.1422 + // Migration at version 7 only occurred for users who wanted to try the new 1.1423 + // Downloads Panel feature before its release. Since migration at version 1.1424 + // 9 adds the button by default, this step has been removed. 1.1425 + 1.1426 + if (currentUIVersion < 8) { 1.1427 + // Reset homepage pref for users who have it set to google.com/firefox 1.1428 + let uri = Services.prefs.getComplexValue("browser.startup.homepage", 1.1429 + Ci.nsIPrefLocalizedString).data; 1.1430 + if (uri && /^https?:\/\/(www\.)?google(\.\w{2,3}){1,2}\/firefox\/?$/.test(uri)) { 1.1431 + Services.prefs.clearUserPref("browser.startup.homepage"); 1.1432 + } 1.1433 + } 1.1434 + 1.1435 + if (currentUIVersion < 9) { 1.1436 + // This code adds the customizable downloads buttons. 1.1437 + let currentsetResource = this._rdf.GetResource("currentset"); 1.1438 + let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); 1.1439 + let currentset = this._getPersist(toolbarResource, currentsetResource); 1.1440 + 1.1441 + // Since the Downloads button is located in the navigation bar by default, 1.1442 + // migration needs to happen only if the toolbar was customized using a 1.1443 + // previous UI version, and the button was not already placed on the 1.1444 + // toolbar manually. 1.1445 + if (currentset && 1.1446 + currentset.indexOf("downloads-button") == -1) { 1.1447 + // The element is added either after the search bar or before the home 1.1448 + // button. As a last resort, the element is added just before the 1.1449 + // non-customizable window controls. 1.1450 + if (currentset.indexOf("search-container") != -1) { 1.1451 + currentset = currentset.replace(/(^|,)search-container($|,)/, 1.1452 + "$1search-container,downloads-button$2") 1.1453 + } else if (currentset.indexOf("home-button") != -1) { 1.1454 + currentset = currentset.replace(/(^|,)home-button($|,)/, 1.1455 + "$1downloads-button,home-button$2") 1.1456 + } else { 1.1457 + currentset = currentset.replace(/(^|,)window-controls($|,)/, 1.1458 + "$1downloads-button,window-controls$2") 1.1459 + } 1.1460 + this._setPersist(toolbarResource, currentsetResource, currentset); 1.1461 + } 1.1462 + } 1.1463 + 1.1464 +#ifdef XP_WIN 1.1465 + if (currentUIVersion < 10) { 1.1466 + // For Windows systems with display set to > 96dpi (i.e. systemDefaultScale 1.1467 + // will return a value > 1.0), we want to discard any saved full-zoom settings, 1.1468 + // as we'll now be scaling the content according to the system resolution 1.1469 + // scale factor (Windows "logical DPI" setting) 1.1470 + let sm = Cc["@mozilla.org/gfx/screenmanager;1"].getService(Ci.nsIScreenManager); 1.1471 + if (sm.systemDefaultScale > 1.0) { 1.1472 + let cps2 = Cc["@mozilla.org/content-pref/service;1"]. 1.1473 + getService(Ci.nsIContentPrefService2); 1.1474 + cps2.removeByName("browser.content.full-zoom", null); 1.1475 + } 1.1476 + } 1.1477 +#endif 1.1478 + 1.1479 + if (currentUIVersion < 11) { 1.1480 + Services.prefs.clearUserPref("dom.disable_window_move_resize"); 1.1481 + Services.prefs.clearUserPref("dom.disable_window_flip"); 1.1482 + Services.prefs.clearUserPref("dom.event.contextmenu.enabled"); 1.1483 + Services.prefs.clearUserPref("javascript.enabled"); 1.1484 + Services.prefs.clearUserPref("permissions.default.image"); 1.1485 + } 1.1486 + 1.1487 + if (currentUIVersion < 12) { 1.1488 + // Remove bookmarks-menu-button-container, then place 1.1489 + // bookmarks-menu-button into its position. 1.1490 + let currentsetResource = this._rdf.GetResource("currentset"); 1.1491 + let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); 1.1492 + let currentset = this._getPersist(toolbarResource, currentsetResource); 1.1493 + // Need to migrate only if toolbar is customized. 1.1494 + if (currentset) { 1.1495 + if (currentset.contains("bookmarks-menu-button-container")) { 1.1496 + currentset = currentset.replace(/(^|,)bookmarks-menu-button-container($|,)/, 1.1497 + "$1bookmarks-menu-button$2"); 1.1498 + this._setPersist(toolbarResource, currentsetResource, currentset); 1.1499 + } 1.1500 + } 1.1501 + } 1.1502 + 1.1503 + if (currentUIVersion < 13) { 1.1504 + try { 1.1505 + if (Services.prefs.getBoolPref("plugins.hide_infobar_for_missing_plugin")) 1.1506 + Services.prefs.setBoolPref("plugins.notifyMissingFlash", false); 1.1507 + } 1.1508 + catch (ex) {} 1.1509 + } 1.1510 + 1.1511 + if (currentUIVersion < 14) { 1.1512 + // DOM Storage doesn't specially handle about: pages anymore. 1.1513 + let path = OS.Path.join(OS.Constants.Path.profileDir, 1.1514 + "chromeappsstore.sqlite"); 1.1515 + OS.File.remove(path); 1.1516 + } 1.1517 + 1.1518 + // Version 15 was obsoleted in favour of 18. 1.1519 + 1.1520 + if (currentUIVersion < 16) { 1.1521 + let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); 1.1522 + let collapsedResource = this._rdf.GetResource("collapsed"); 1.1523 + let isCollapsed = this._getPersist(toolbarResource, collapsedResource); 1.1524 + if (isCollapsed == "true") { 1.1525 + this._setPersist(toolbarResource, collapsedResource, "false"); 1.1526 + } 1.1527 + } 1.1528 + 1.1529 + // Insert the bookmarks-menu-button into the nav-bar if it isn't already 1.1530 + // there. 1.1531 + if (currentUIVersion < 17) { 1.1532 + let currentsetResource = this._rdf.GetResource("currentset"); 1.1533 + let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); 1.1534 + let currentset = this._getPersist(toolbarResource, currentsetResource); 1.1535 + // Need to migrate only if toolbar is customized. 1.1536 + if (currentset) { 1.1537 + if (!currentset.contains("bookmarks-menu-button")) { 1.1538 + // The button isn't in the nav-bar, so let's look for an appropriate 1.1539 + // place to put it. 1.1540 + if (currentset.contains("downloads-button")) { 1.1541 + currentset = currentset.replace(/(^|,)downloads-button($|,)/, 1.1542 + "$1bookmarks-menu-button,downloads-button$2"); 1.1543 + } else if (currentset.contains("home-button")) { 1.1544 + currentset = currentset.replace(/(^|,)home-button($|,)/, 1.1545 + "$1bookmarks-menu-button,home-button$2"); 1.1546 + } else { 1.1547 + // Just append. 1.1548 + currentset = currentset.replace(/(^|,)window-controls($|,)/, 1.1549 + "$1bookmarks-menu-button,window-controls$2") 1.1550 + } 1.1551 + this._setPersist(toolbarResource, currentsetResource, currentset); 1.1552 + } 1.1553 + } 1.1554 + } 1.1555 + 1.1556 + if (currentUIVersion < 18) { 1.1557 + // Remove iconsize and mode from all the toolbars 1.1558 + let toolbars = ["navigator-toolbox", "nav-bar", "PersonalToolbar", 1.1559 + "addon-bar", "TabsToolbar", "toolbar-menubar"]; 1.1560 + for (let resourceName of ["mode", "iconsize"]) { 1.1561 + let resource = this._rdf.GetResource(resourceName); 1.1562 + for (let toolbarId of toolbars) { 1.1563 + let toolbar = this._rdf.GetResource(BROWSER_DOCURL + toolbarId); 1.1564 + if (this._getPersist(toolbar, resource)) { 1.1565 + this._setPersist(toolbar, resource); 1.1566 + } 1.1567 + } 1.1568 + } 1.1569 + } 1.1570 + 1.1571 + if (currentUIVersion < 19) { 1.1572 + let detector = null; 1.1573 + try { 1.1574 + detector = Services.prefs.getComplexValue("intl.charset.detector", 1.1575 + Ci.nsIPrefLocalizedString).data; 1.1576 + } catch (ex) {} 1.1577 + if (!(detector == "" || 1.1578 + detector == "ja_parallel_state_machine" || 1.1579 + detector == "ruprob" || 1.1580 + detector == "ukprob")) { 1.1581 + // If the encoding detector pref value is not reachable from the UI, 1.1582 + // reset to default (varies by localization). 1.1583 + Services.prefs.clearUserPref("intl.charset.detector"); 1.1584 + } 1.1585 + } 1.1586 + 1.1587 + if (currentUIVersion < 20) { 1.1588 + // Remove persisted collapsed state from TabsToolbar. 1.1589 + let resource = this._rdf.GetResource("collapsed"); 1.1590 + let toolbar = this._rdf.GetResource(BROWSER_DOCURL + "TabsToolbar"); 1.1591 + if (this._getPersist(toolbar, resource)) { 1.1592 + this._setPersist(toolbar, resource); 1.1593 + } 1.1594 + } 1.1595 + 1.1596 + if (currentUIVersion < 21) { 1.1597 + // Make sure the 'toolbarbutton-1' class will always be present from here 1.1598 + // on out. 1.1599 + let button = this._rdf.GetResource(BROWSER_DOCURL + "bookmarks-menu-button"); 1.1600 + let classResource = this._rdf.GetResource("class"); 1.1601 + if (this._getPersist(button, classResource)) { 1.1602 + this._setPersist(button, classResource); 1.1603 + } 1.1604 + } 1.1605 + 1.1606 + if (currentUIVersion < 22) { 1.1607 + // Reset the Sync promobox count to promote the new FxAccount-based Sync. 1.1608 + Services.prefs.clearUserPref("browser.syncPromoViewsLeft"); 1.1609 + Services.prefs.clearUserPref("browser.syncPromoViewsLeftMap"); 1.1610 + } 1.1611 + 1.1612 + if (this._dirty) 1.1613 + this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush(); 1.1614 + 1.1615 + delete this._rdf; 1.1616 + delete this._dataSource; 1.1617 + 1.1618 + // Update the migration version. 1.1619 + Services.prefs.setIntPref("browser.migration.version", UI_VERSION); 1.1620 + }, 1.1621 + 1.1622 + _getPersist: function BG__getPersist(aSource, aProperty) { 1.1623 + var target = this._dataSource.GetTarget(aSource, aProperty, true); 1.1624 + if (target instanceof Ci.nsIRDFLiteral) 1.1625 + return target.Value; 1.1626 + return null; 1.1627 + }, 1.1628 + 1.1629 + _setPersist: function BG__setPersist(aSource, aProperty, aTarget) { 1.1630 + this._dirty = true; 1.1631 + try { 1.1632 + var oldTarget = this._dataSource.GetTarget(aSource, aProperty, true); 1.1633 + if (oldTarget) { 1.1634 + if (aTarget) 1.1635 + this._dataSource.Change(aSource, aProperty, oldTarget, this._rdf.GetLiteral(aTarget)); 1.1636 + else 1.1637 + this._dataSource.Unassert(aSource, aProperty, oldTarget); 1.1638 + } 1.1639 + else { 1.1640 + this._dataSource.Assert(aSource, aProperty, this._rdf.GetLiteral(aTarget), true); 1.1641 + } 1.1642 + 1.1643 + // Add the entry to the persisted set for this document if it's not there. 1.1644 + // This code is mostly borrowed from XULDocument::Persist. 1.1645 + let docURL = aSource.ValueUTF8.split("#")[0]; 1.1646 + let docResource = this._rdf.GetResource(docURL); 1.1647 + let persistResource = this._rdf.GetResource("http://home.netscape.com/NC-rdf#persist"); 1.1648 + if (!this._dataSource.HasAssertion(docResource, persistResource, aSource, true)) { 1.1649 + this._dataSource.Assert(docResource, persistResource, aSource, true); 1.1650 + } 1.1651 + } 1.1652 + catch(ex) {} 1.1653 + }, 1.1654 + 1.1655 + // ------------------------------ 1.1656 + // public nsIBrowserGlue members 1.1657 + // ------------------------------ 1.1658 + 1.1659 + sanitize: function BG_sanitize(aParentWindow) { 1.1660 + this._sanitizer.sanitize(aParentWindow); 1.1661 + }, 1.1662 + 1.1663 + ensurePlacesDefaultQueriesInitialized: 1.1664 + function BG_ensurePlacesDefaultQueriesInitialized() { 1.1665 + // This is actual version of the smart bookmarks, must be increased every 1.1666 + // time smart bookmarks change. 1.1667 + // When adding a new smart bookmark below, its newInVersion property must 1.1668 + // be set to the version it has been added in, we will compare its value 1.1669 + // to users' smartBookmarksVersion and add new smart bookmarks without 1.1670 + // recreating old deleted ones. 1.1671 + const SMART_BOOKMARKS_VERSION = 7; 1.1672 + const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark"; 1.1673 + const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion"; 1.1674 + 1.1675 + // TODO bug 399268: should this be a pref? 1.1676 + const MAX_RESULTS = 10; 1.1677 + 1.1678 + // Get current smart bookmarks version. If not set, create them. 1.1679 + let smartBookmarksCurrentVersion = 0; 1.1680 + try { 1.1681 + smartBookmarksCurrentVersion = Services.prefs.getIntPref(SMART_BOOKMARKS_PREF); 1.1682 + } catch(ex) {} 1.1683 + 1.1684 + // If version is current or smart bookmarks are disabled, just bail out. 1.1685 + if (smartBookmarksCurrentVersion == -1 || 1.1686 + smartBookmarksCurrentVersion >= SMART_BOOKMARKS_VERSION) { 1.1687 + return; 1.1688 + } 1.1689 + 1.1690 + let batch = { 1.1691 + runBatched: function BG_EPDQI_runBatched() { 1.1692 + let menuIndex = 0; 1.1693 + let toolbarIndex = 0; 1.1694 + let bundle = Services.strings.createBundle("chrome://browser/locale/places/places.properties"); 1.1695 + 1.1696 + let smartBookmarks = { 1.1697 + MostVisited: { 1.1698 + title: bundle.GetStringFromName("mostVisitedTitle"), 1.1699 + uri: NetUtil.newURI("place:sort=" + 1.1700 + Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING + 1.1701 + "&maxResults=" + MAX_RESULTS), 1.1702 + parent: PlacesUtils.toolbarFolderId, 1.1703 + get position() { return toolbarIndex++; }, 1.1704 + newInVersion: 1 1.1705 + }, 1.1706 + RecentlyBookmarked: { 1.1707 + title: bundle.GetStringFromName("recentlyBookmarkedTitle"), 1.1708 + uri: NetUtil.newURI("place:folder=BOOKMARKS_MENU" + 1.1709 + "&folder=UNFILED_BOOKMARKS" + 1.1710 + "&folder=TOOLBAR" + 1.1711 + "&queryType=" + 1.1712 + Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS + 1.1713 + "&sort=" + 1.1714 + Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING + 1.1715 + "&maxResults=" + MAX_RESULTS + 1.1716 + "&excludeQueries=1"), 1.1717 + parent: PlacesUtils.bookmarksMenuFolderId, 1.1718 + get position() { return menuIndex++; }, 1.1719 + newInVersion: 1 1.1720 + }, 1.1721 + RecentTags: { 1.1722 + title: bundle.GetStringFromName("recentTagsTitle"), 1.1723 + uri: NetUtil.newURI("place:"+ 1.1724 + "type=" + 1.1725 + Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY + 1.1726 + "&sort=" + 1.1727 + Ci.nsINavHistoryQueryOptions.SORT_BY_LASTMODIFIED_DESCENDING + 1.1728 + "&maxResults=" + MAX_RESULTS), 1.1729 + parent: PlacesUtils.bookmarksMenuFolderId, 1.1730 + get position() { return menuIndex++; }, 1.1731 + newInVersion: 1 1.1732 + }, 1.1733 + }; 1.1734 + 1.1735 + if (Services.metro && Services.metro.supported) { 1.1736 + smartBookmarks.Windows8Touch = { 1.1737 + title: PlacesUtils.getString("windows8TouchTitle"), 1.1738 + get uri() { 1.1739 + let metroBookmarksRoot = PlacesUtils.annotations.getItemsWithAnnotation('metro/bookmarksRoot', {}); 1.1740 + if (metroBookmarksRoot.length > 0) { 1.1741 + return NetUtil.newURI("place:folder=" + 1.1742 + metroBookmarksRoot[0] + 1.1743 + "&queryType=" + 1.1744 + Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS + 1.1745 + "&sort=" + 1.1746 + Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING + 1.1747 + "&maxResults=" + MAX_RESULTS + 1.1748 + "&excludeQueries=1") 1.1749 + } 1.1750 + return null; 1.1751 + }, 1.1752 + parent: PlacesUtils.bookmarksMenuFolderId, 1.1753 + get position() { return menuIndex++; }, 1.1754 + newInVersion: 7 1.1755 + }; 1.1756 + } 1.1757 + 1.1758 + // Set current itemId, parent and position if Smart Bookmark exists, 1.1759 + // we will use these informations to create the new version at the same 1.1760 + // position. 1.1761 + let smartBookmarkItemIds = PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO); 1.1762 + smartBookmarkItemIds.forEach(function (itemId) { 1.1763 + let queryId = PlacesUtils.annotations.getItemAnnotation(itemId, SMART_BOOKMARKS_ANNO); 1.1764 + if (queryId in smartBookmarks) { 1.1765 + let smartBookmark = smartBookmarks[queryId]; 1.1766 + if (!smartBookmark.uri) { 1.1767 + PlacesUtils.bookmarks.removeItem(itemId); 1.1768 + return; 1.1769 + } 1.1770 + smartBookmark.itemId = itemId; 1.1771 + smartBookmark.parent = PlacesUtils.bookmarks.getFolderIdForItem(itemId); 1.1772 + smartBookmark.updatedPosition = PlacesUtils.bookmarks.getItemIndex(itemId); 1.1773 + } 1.1774 + else { 1.1775 + // We don't remove old Smart Bookmarks because user could still 1.1776 + // find them useful, or could have personalized them. 1.1777 + // Instead we remove the Smart Bookmark annotation. 1.1778 + PlacesUtils.annotations.removeItemAnnotation(itemId, SMART_BOOKMARKS_ANNO); 1.1779 + } 1.1780 + }); 1.1781 + 1.1782 + for (let queryId in smartBookmarks) { 1.1783 + let smartBookmark = smartBookmarks[queryId]; 1.1784 + 1.1785 + // We update or create only changed or new smart bookmarks. 1.1786 + // Also we respect user choices, so we won't try to create a smart 1.1787 + // bookmark if it has been removed. 1.1788 + if (smartBookmarksCurrentVersion > 0 && 1.1789 + smartBookmark.newInVersion <= smartBookmarksCurrentVersion && 1.1790 + !smartBookmark.itemId || !smartBookmark.uri) 1.1791 + continue; 1.1792 + 1.1793 + // Remove old version of the smart bookmark if it exists, since it 1.1794 + // will be replaced in place. 1.1795 + if (smartBookmark.itemId) { 1.1796 + PlacesUtils.bookmarks.removeItem(smartBookmark.itemId); 1.1797 + } 1.1798 + 1.1799 + // Create the new smart bookmark and store its updated itemId. 1.1800 + smartBookmark.itemId = 1.1801 + PlacesUtils.bookmarks.insertBookmark(smartBookmark.parent, 1.1802 + smartBookmark.uri, 1.1803 + smartBookmark.updatedPosition || smartBookmark.position, 1.1804 + smartBookmark.title); 1.1805 + PlacesUtils.annotations.setItemAnnotation(smartBookmark.itemId, 1.1806 + SMART_BOOKMARKS_ANNO, 1.1807 + queryId, 0, 1.1808 + PlacesUtils.annotations.EXPIRE_NEVER); 1.1809 + } 1.1810 + 1.1811 + // If we are creating all Smart Bookmarks from ground up, add a 1.1812 + // separator below them in the bookmarks menu. 1.1813 + if (smartBookmarksCurrentVersion == 0 && 1.1814 + smartBookmarkItemIds.length == 0) { 1.1815 + let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 1.1816 + menuIndex); 1.1817 + // Don't add a separator if the menu was empty or there is one already. 1.1818 + if (id != -1 && 1.1819 + PlacesUtils.bookmarks.getItemType(id) != PlacesUtils.bookmarks.TYPE_SEPARATOR) { 1.1820 + PlacesUtils.bookmarks.insertSeparator(PlacesUtils.bookmarksMenuFolderId, 1.1821 + menuIndex); 1.1822 + } 1.1823 + } 1.1824 + } 1.1825 + }; 1.1826 + 1.1827 + try { 1.1828 + PlacesUtils.bookmarks.runInBatchMode(batch, null); 1.1829 + } 1.1830 + catch(ex) { 1.1831 + Components.utils.reportError(ex); 1.1832 + } 1.1833 + finally { 1.1834 + Services.prefs.setIntPref(SMART_BOOKMARKS_PREF, SMART_BOOKMARKS_VERSION); 1.1835 + Services.prefs.savePrefFile(null); 1.1836 + } 1.1837 + }, 1.1838 + 1.1839 + // this returns the most recent non-popup browser window 1.1840 + getMostRecentBrowserWindow: function BG_getMostRecentBrowserWindow() { 1.1841 + return RecentWindow.getMostRecentBrowserWindow(); 1.1842 + }, 1.1843 + 1.1844 +#ifdef MOZ_SERVICES_SYNC 1.1845 + /** 1.1846 + * Called as an observer when Sync's "display URI" notification is fired. 1.1847 + * 1.1848 + * We open the received URI in a background tab. 1.1849 + * 1.1850 + * Eventually, this will likely be replaced by a more robust tab syncing 1.1851 + * feature. This functionality is considered somewhat evil by UX because it 1.1852 + * opens a new tab automatically without any prompting. However, it is a 1.1853 + * lesser evil than sending a tab to a specific device (from e.g. Fennec) 1.1854 + * and having nothing happen on the receiving end. 1.1855 + */ 1.1856 + _onDisplaySyncURI: function _onDisplaySyncURI(data) { 1.1857 + try { 1.1858 + let tabbrowser = RecentWindow.getMostRecentBrowserWindow({private: false}).gBrowser; 1.1859 + 1.1860 + // The payload is wrapped weirdly because of how Sync does notifications. 1.1861 + tabbrowser.addTab(data.wrappedJSObject.object.uri); 1.1862 + } catch (ex) { 1.1863 + Cu.reportError("Error displaying tab received by Sync: " + ex); 1.1864 + } 1.1865 + }, 1.1866 +#endif 1.1867 + 1.1868 + // for XPCOM 1.1869 + classID: Components.ID("{eab9012e-5f74-4cbc-b2b5-a590235513cc}"), 1.1870 + 1.1871 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, 1.1872 + Ci.nsISupportsWeakReference, 1.1873 + Ci.nsIBrowserGlue]), 1.1874 + 1.1875 + // redefine the default factory for XPCOMUtils 1.1876 + _xpcom_factory: BrowserGlueServiceFactory, 1.1877 +} 1.1878 + 1.1879 +function ContentPermissionPrompt() {} 1.1880 + 1.1881 +ContentPermissionPrompt.prototype = { 1.1882 + classID: Components.ID("{d8903bf6-68d5-4e97-bcd1-e4d3012f721a}"), 1.1883 + 1.1884 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]), 1.1885 + 1.1886 + _getBrowserForRequest: function (aRequest) { 1.1887 + // "element" is only defined in e10s mode. 1.1888 + let browser = aRequest.element; 1.1889 + if (!browser) { 1.1890 + // Find the requesting browser. 1.1891 + browser = aRequest.window.QueryInterface(Ci.nsIInterfaceRequestor) 1.1892 + .getInterface(Ci.nsIWebNavigation) 1.1893 + .QueryInterface(Ci.nsIDocShell) 1.1894 + .chromeEventHandler; 1.1895 + } 1.1896 + return browser; 1.1897 + }, 1.1898 + 1.1899 + /** 1.1900 + * Show a permission prompt. 1.1901 + * 1.1902 + * @param aRequest The permission request. 1.1903 + * @param aMessage The message to display on the prompt. 1.1904 + * @param aPermission The type of permission to prompt. 1.1905 + * @param aActions An array of actions of the form: 1.1906 + * [main action, secondary actions, ...] 1.1907 + * Actions are of the form { stringId, action, expireType, callback } 1.1908 + * Permission is granted if action is null or ALLOW_ACTION. 1.1909 + * @param aNotificationId The id of the PopupNotification. 1.1910 + * @param aAnchorId The id for the PopupNotification anchor. 1.1911 + * @param aOptions Options for the PopupNotification 1.1912 + */ 1.1913 + _showPrompt: function CPP_showPrompt(aRequest, aMessage, aPermission, aActions, 1.1914 + aNotificationId, aAnchorId, aOptions) { 1.1915 + function onFullScreen() { 1.1916 + popup.remove(); 1.1917 + } 1.1918 + 1.1919 + var browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); 1.1920 + 1.1921 + var browser = this._getBrowserForRequest(aRequest); 1.1922 + var chromeWin = browser.ownerDocument.defaultView; 1.1923 + var requestPrincipal = aRequest.principal; 1.1924 + 1.1925 + // Transform the prompt actions into PopupNotification actions. 1.1926 + var popupNotificationActions = []; 1.1927 + for (var i = 0; i < aActions.length; i++) { 1.1928 + let promptAction = aActions[i]; 1.1929 + 1.1930 + // Don't offer action in PB mode if the action remembers permission for more than a session. 1.1931 + if (PrivateBrowsingUtils.isWindowPrivate(chromeWin) && 1.1932 + promptAction.expireType != Ci.nsIPermissionManager.EXPIRE_SESSION && 1.1933 + promptAction.action) { 1.1934 + continue; 1.1935 + } 1.1936 + 1.1937 + var action = { 1.1938 + label: browserBundle.GetStringFromName(promptAction.stringId), 1.1939 + accessKey: browserBundle.GetStringFromName(promptAction.stringId + ".accesskey"), 1.1940 + callback: function() { 1.1941 + if (promptAction.callback) { 1.1942 + promptAction.callback(); 1.1943 + } 1.1944 + 1.1945 + // Remember permissions. 1.1946 + if (promptAction.action) { 1.1947 + Services.perms.addFromPrincipal(requestPrincipal, aPermission, 1.1948 + promptAction.action, promptAction.expireType); 1.1949 + } 1.1950 + 1.1951 + // Grant permission if action is null or ALLOW_ACTION. 1.1952 + if (!promptAction.action || promptAction.action == Ci.nsIPermissionManager.ALLOW_ACTION) { 1.1953 + aRequest.allow(); 1.1954 + } else { 1.1955 + aRequest.cancel(); 1.1956 + } 1.1957 + }, 1.1958 + }; 1.1959 + 1.1960 + popupNotificationActions.push(action); 1.1961 + } 1.1962 + 1.1963 + var mainAction = popupNotificationActions.length ? 1.1964 + popupNotificationActions[0] : null; 1.1965 + var secondaryActions = popupNotificationActions.splice(1); 1.1966 + 1.1967 + // Only allow exactly one permission rquest here. 1.1968 + let types = aRequest.types.QueryInterface(Ci.nsIArray); 1.1969 + if (types.length != 1) { 1.1970 + aRequest.cancel(); 1.1971 + return; 1.1972 + } 1.1973 + 1.1974 + let perm = types.queryElementAt(0, Ci.nsIContentPermissionType); 1.1975 + 1.1976 + if (perm.type == "pointerLock") { 1.1977 + // If there's no mainAction, this is the autoAllow warning prompt. 1.1978 + let autoAllow = !mainAction; 1.1979 + 1.1980 + if (!aOptions) 1.1981 + aOptions = {}; 1.1982 + 1.1983 + aOptions.removeOnDismissal = autoAllow; 1.1984 + aOptions.eventCallback = type => { 1.1985 + if (type == "removed") { 1.1986 + browser.removeEventListener("mozfullscreenchange", onFullScreen, true); 1.1987 + if (autoAllow) { 1.1988 + aRequest.allow(); 1.1989 + } 1.1990 + } 1.1991 + } 1.1992 + 1.1993 + } 1.1994 + 1.1995 + var popup = chromeWin.PopupNotifications.show(browser, aNotificationId, aMessage, aAnchorId, 1.1996 + mainAction, secondaryActions, aOptions); 1.1997 + if (perm.type == "pointerLock") { 1.1998 + // pointerLock is automatically allowed in fullscreen mode (and revoked 1.1999 + // upon exit), so if the page enters fullscreen mode after requesting 1.2000 + // pointerLock (but before the user has granted permission), we should 1.2001 + // remove the now-impotent notification. 1.2002 + browser.addEventListener("mozfullscreenchange", onFullScreen, true); 1.2003 + } 1.2004 + }, 1.2005 + 1.2006 + _promptGeo : function(aRequest) { 1.2007 + var secHistogram = Services.telemetry.getHistogramById("SECURITY_UI"); 1.2008 + var browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); 1.2009 + var requestingURI = aRequest.principal.URI; 1.2010 + 1.2011 + var message; 1.2012 + 1.2013 + // Share location action. 1.2014 + var actions = [{ 1.2015 + stringId: "geolocation.shareLocation", 1.2016 + action: null, 1.2017 + expireType: null, 1.2018 + callback: function() { 1.2019 + secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_SHARE_LOCATION); 1.2020 + }, 1.2021 + }]; 1.2022 + 1.2023 + if (requestingURI.schemeIs("file")) { 1.2024 + message = browserBundle.formatStringFromName("geolocation.shareWithFile", 1.2025 + [requestingURI.path], 1); 1.2026 + } else { 1.2027 + message = browserBundle.formatStringFromName("geolocation.shareWithSite", 1.2028 + [requestingURI.host], 1); 1.2029 + // Always share location action. 1.2030 + actions.push({ 1.2031 + stringId: "geolocation.alwaysShareLocation", 1.2032 + action: Ci.nsIPermissionManager.ALLOW_ACTION, 1.2033 + expireType: null, 1.2034 + callback: function() { 1.2035 + secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_ALWAYS_SHARE); 1.2036 + }, 1.2037 + }); 1.2038 + 1.2039 + // Never share location action. 1.2040 + actions.push({ 1.2041 + stringId: "geolocation.neverShareLocation", 1.2042 + action: Ci.nsIPermissionManager.DENY_ACTION, 1.2043 + expireType: null, 1.2044 + callback: function() { 1.2045 + secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_NEVER_SHARE); 1.2046 + }, 1.2047 + }); 1.2048 + } 1.2049 + 1.2050 + var options = { 1.2051 + learnMoreURL: Services.urlFormatter.formatURLPref("browser.geolocation.warning.infoURL"), 1.2052 + }; 1.2053 + 1.2054 + secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST); 1.2055 + 1.2056 + this._showPrompt(aRequest, message, "geo", actions, "geolocation", 1.2057 + "geo-notification-icon", options); 1.2058 + }, 1.2059 + 1.2060 + _promptWebNotifications : function(aRequest) { 1.2061 + var browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); 1.2062 + var requestingURI = aRequest.principal.URI; 1.2063 + 1.2064 + var message = browserBundle.formatStringFromName("webNotifications.showFromSite", 1.2065 + [requestingURI.host], 1); 1.2066 + 1.2067 + var actions = [ 1.2068 + { 1.2069 + stringId: "webNotifications.showForSession", 1.2070 + action: Ci.nsIPermissionManager.ALLOW_ACTION, 1.2071 + expireType: Ci.nsIPermissionManager.EXPIRE_SESSION, 1.2072 + callback: function() {}, 1.2073 + }, 1.2074 + { 1.2075 + stringId: "webNotifications.alwaysShow", 1.2076 + action: Ci.nsIPermissionManager.ALLOW_ACTION, 1.2077 + expireType: null, 1.2078 + callback: function() {}, 1.2079 + }, 1.2080 + { 1.2081 + stringId: "webNotifications.neverShow", 1.2082 + action: Ci.nsIPermissionManager.DENY_ACTION, 1.2083 + expireType: null, 1.2084 + callback: function() {}, 1.2085 + }, 1.2086 + ]; 1.2087 + 1.2088 + this._showPrompt(aRequest, message, "desktop-notification", actions, 1.2089 + "web-notifications", 1.2090 + "web-notifications-notification-icon", null); 1.2091 + }, 1.2092 + 1.2093 + _promptPointerLock: function CPP_promtPointerLock(aRequest, autoAllow) { 1.2094 + 1.2095 + let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); 1.2096 + let requestingURI = aRequest.principal.URI; 1.2097 + 1.2098 + let originString = requestingURI.schemeIs("file") ? requestingURI.path : requestingURI.host; 1.2099 + let message = browserBundle.formatStringFromName(autoAllow ? 1.2100 + "pointerLock.autoLock.title2" : "pointerLock.title2", 1.2101 + [originString], 1); 1.2102 + // If this is an autoAllow info prompt, offer no actions. 1.2103 + // _showPrompt() will allow the request when it's dismissed. 1.2104 + let actions = []; 1.2105 + if (!autoAllow) { 1.2106 + actions = [ 1.2107 + { 1.2108 + stringId: "pointerLock.allow2", 1.2109 + action: null, 1.2110 + expireType: null, 1.2111 + callback: function() {}, 1.2112 + }, 1.2113 + { 1.2114 + stringId: "pointerLock.alwaysAllow", 1.2115 + action: Ci.nsIPermissionManager.ALLOW_ACTION, 1.2116 + expireType: null, 1.2117 + callback: function() {}, 1.2118 + }, 1.2119 + { 1.2120 + stringId: "pointerLock.neverAllow", 1.2121 + action: Ci.nsIPermissionManager.DENY_ACTION, 1.2122 + expireType: null, 1.2123 + callback: function() {}, 1.2124 + }, 1.2125 + ]; 1.2126 + } 1.2127 + 1.2128 + this._showPrompt(aRequest, message, "pointerLock", actions, "pointerLock", 1.2129 + "pointerLock-notification-icon", null); 1.2130 + }, 1.2131 + 1.2132 + prompt: function CPP_prompt(request) { 1.2133 + 1.2134 + // Only allow exactly one permission rquest here. 1.2135 + let types = request.types.QueryInterface(Ci.nsIArray); 1.2136 + if (types.length != 1) { 1.2137 + request.cancel(); 1.2138 + return; 1.2139 + } 1.2140 + let perm = types.queryElementAt(0, Ci.nsIContentPermissionType); 1.2141 + 1.2142 + const kFeatureKeys = { "geolocation" : "geo", 1.2143 + "desktop-notification" : "desktop-notification", 1.2144 + "pointerLock" : "pointerLock", 1.2145 + }; 1.2146 + 1.2147 + // Make sure that we support the request. 1.2148 + if (!(perm.type in kFeatureKeys)) { 1.2149 + return; 1.2150 + } 1.2151 + 1.2152 + var requestingPrincipal = request.principal; 1.2153 + var requestingURI = requestingPrincipal.URI; 1.2154 + 1.2155 + // Ignore requests from non-nsIStandardURLs 1.2156 + if (!(requestingURI instanceof Ci.nsIStandardURL)) 1.2157 + return; 1.2158 + 1.2159 + var autoAllow = false; 1.2160 + var permissionKey = kFeatureKeys[perm.type]; 1.2161 + var result = Services.perms.testExactPermissionFromPrincipal(requestingPrincipal, permissionKey); 1.2162 + 1.2163 + if (result == Ci.nsIPermissionManager.DENY_ACTION) { 1.2164 + request.cancel(); 1.2165 + return; 1.2166 + } 1.2167 + 1.2168 + if (result == Ci.nsIPermissionManager.ALLOW_ACTION) { 1.2169 + autoAllow = true; 1.2170 + // For pointerLock, we still want to show a warning prompt. 1.2171 + if (perm.type != "pointerLock") { 1.2172 + request.allow(); 1.2173 + return; 1.2174 + } 1.2175 + } 1.2176 + 1.2177 + var browser = this._getBrowserForRequest(request); 1.2178 + var chromeWin = browser.ownerDocument.defaultView; 1.2179 + if (!chromeWin.PopupNotifications) 1.2180 + // Ignore requests from browsers hosted in windows that don't support 1.2181 + // PopupNotifications. 1.2182 + return; 1.2183 + 1.2184 + // Show the prompt. 1.2185 + switch (perm.type) { 1.2186 + case "geolocation": 1.2187 + this._promptGeo(request); 1.2188 + break; 1.2189 + case "desktop-notification": 1.2190 + this._promptWebNotifications(request); 1.2191 + break; 1.2192 + case "pointerLock": 1.2193 + this._promptPointerLock(request, autoAllow); 1.2194 + break; 1.2195 + } 1.2196 + }, 1.2197 + 1.2198 +}; 1.2199 + 1.2200 +var components = [BrowserGlue, ContentPermissionPrompt]; 1.2201 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);