Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | # -*- indent-tabs-mode: nil -*- |
michael@0 | 2 | # This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 5 | |
michael@0 | 6 | const Ci = Components.interfaces; |
michael@0 | 7 | const Cc = Components.classes; |
michael@0 | 8 | const Cr = Components.results; |
michael@0 | 9 | const Cu = Components.utils; |
michael@0 | 10 | |
michael@0 | 11 | const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; |
michael@0 | 12 | |
michael@0 | 13 | Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
michael@0 | 14 | Cu.import("resource://gre/modules/Services.jsm"); |
michael@0 | 15 | |
michael@0 | 16 | XPCOMUtils.defineLazyModuleGetter(this, "AboutHome", |
michael@0 | 17 | "resource:///modules/AboutHome.jsm"); |
michael@0 | 18 | |
michael@0 | 19 | XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", |
michael@0 | 20 | "resource://gre/modules/AddonManager.jsm"); |
michael@0 | 21 | |
michael@0 | 22 | XPCOMUtils.defineLazyModuleGetter(this, "ContentClick", |
michael@0 | 23 | "resource:///modules/ContentClick.jsm"); |
michael@0 | 24 | |
michael@0 | 25 | XPCOMUtils.defineLazyModuleGetter(this, "DirectoryLinksProvider", |
michael@0 | 26 | "resource://gre/modules/DirectoryLinksProvider.jsm"); |
michael@0 | 27 | |
michael@0 | 28 | XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", |
michael@0 | 29 | "resource://gre/modules/NetUtil.jsm"); |
michael@0 | 30 | |
michael@0 | 31 | XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", |
michael@0 | 32 | "resource://gre/modules/FileUtils.jsm"); |
michael@0 | 33 | |
michael@0 | 34 | XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", |
michael@0 | 35 | "resource://gre/modules/PlacesUtils.jsm"); |
michael@0 | 36 | |
michael@0 | 37 | XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils", |
michael@0 | 38 | "resource://gre/modules/BookmarkHTMLUtils.jsm"); |
michael@0 | 39 | |
michael@0 | 40 | XPCOMUtils.defineLazyModuleGetter(this, "BookmarkJSONUtils", |
michael@0 | 41 | "resource://gre/modules/BookmarkJSONUtils.jsm"); |
michael@0 | 42 | |
michael@0 | 43 | XPCOMUtils.defineLazyModuleGetter(this, "WebappManager", |
michael@0 | 44 | "resource:///modules/WebappManager.jsm"); |
michael@0 | 45 | |
michael@0 | 46 | XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs", |
michael@0 | 47 | "resource://gre/modules/PageThumbs.jsm"); |
michael@0 | 48 | |
michael@0 | 49 | XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils", |
michael@0 | 50 | "resource://gre/modules/NewTabUtils.jsm"); |
michael@0 | 51 | |
michael@0 | 52 | XPCOMUtils.defineLazyModuleGetter(this, "BrowserNewTabPreloader", |
michael@0 | 53 | "resource:///modules/BrowserNewTabPreloader.jsm"); |
michael@0 | 54 | |
michael@0 | 55 | XPCOMUtils.defineLazyModuleGetter(this, "CustomizationTabPreloader", |
michael@0 | 56 | "resource:///modules/CustomizationTabPreloader.jsm"); |
michael@0 | 57 | |
michael@0 | 58 | XPCOMUtils.defineLazyModuleGetter(this, "PdfJs", |
michael@0 | 59 | "resource://pdf.js/PdfJs.jsm"); |
michael@0 | 60 | |
michael@0 | 61 | #ifdef NIGHTLY_BUILD |
michael@0 | 62 | XPCOMUtils.defineLazyModuleGetter(this, "ShumwayUtils", |
michael@0 | 63 | "resource://shumway/ShumwayUtils.jsm"); |
michael@0 | 64 | #endif |
michael@0 | 65 | |
michael@0 | 66 | XPCOMUtils.defineLazyModuleGetter(this, "webrtcUI", |
michael@0 | 67 | "resource:///modules/webrtcUI.jsm"); |
michael@0 | 68 | |
michael@0 | 69 | XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", |
michael@0 | 70 | "resource://gre/modules/PrivateBrowsingUtils.jsm"); |
michael@0 | 71 | |
michael@0 | 72 | XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", |
michael@0 | 73 | "resource:///modules/RecentWindow.jsm"); |
michael@0 | 74 | |
michael@0 | 75 | XPCOMUtils.defineLazyModuleGetter(this, "Task", |
michael@0 | 76 | "resource://gre/modules/Task.jsm"); |
michael@0 | 77 | |
michael@0 | 78 | XPCOMUtils.defineLazyModuleGetter(this, "PlacesBackups", |
michael@0 | 79 | "resource://gre/modules/PlacesBackups.jsm"); |
michael@0 | 80 | |
michael@0 | 81 | XPCOMUtils.defineLazyModuleGetter(this, "OS", |
michael@0 | 82 | "resource://gre/modules/osfile.jsm"); |
michael@0 | 83 | |
michael@0 | 84 | XPCOMUtils.defineLazyModuleGetter(this, "RemotePrompt", |
michael@0 | 85 | "resource:///modules/RemotePrompt.jsm"); |
michael@0 | 86 | |
michael@0 | 87 | XPCOMUtils.defineLazyModuleGetter(this, "SessionStore", |
michael@0 | 88 | "resource:///modules/sessionstore/SessionStore.jsm"); |
michael@0 | 89 | |
michael@0 | 90 | XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry", |
michael@0 | 91 | "resource:///modules/BrowserUITelemetry.jsm"); |
michael@0 | 92 | |
michael@0 | 93 | XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown", |
michael@0 | 94 | "resource://gre/modules/AsyncShutdown.jsm"); |
michael@0 | 95 | |
michael@0 | 96 | #ifdef NIGHTLY_BUILD |
michael@0 | 97 | XPCOMUtils.defineLazyModuleGetter(this, "SignInToWebsiteUX", |
michael@0 | 98 | "resource:///modules/SignInToWebsite.jsm"); |
michael@0 | 99 | #endif |
michael@0 | 100 | |
michael@0 | 101 | XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch", |
michael@0 | 102 | "resource:///modules/ContentSearch.jsm"); |
michael@0 | 103 | |
michael@0 | 104 | const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser"; |
michael@0 | 105 | const PREF_PLUGINS_UPDATEURL = "plugins.update.url"; |
michael@0 | 106 | |
michael@0 | 107 | // Seconds of idle before trying to create a bookmarks backup. |
michael@0 | 108 | const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 8 * 60; |
michael@0 | 109 | // Minimum interval between backups. We try to not create more than one backup |
michael@0 | 110 | // per interval. |
michael@0 | 111 | const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1; |
michael@0 | 112 | // Maximum interval between backups. If the last backup is older than these |
michael@0 | 113 | // days we will try to create a new one more aggressively. |
michael@0 | 114 | const BOOKMARKS_BACKUP_MAX_INTERVAL_DAYS = 3; |
michael@0 | 115 | |
michael@0 | 116 | // Factory object |
michael@0 | 117 | const BrowserGlueServiceFactory = { |
michael@0 | 118 | _instance: null, |
michael@0 | 119 | createInstance: function BGSF_createInstance(outer, iid) { |
michael@0 | 120 | if (outer != null) |
michael@0 | 121 | throw Components.results.NS_ERROR_NO_AGGREGATION; |
michael@0 | 122 | return this._instance == null ? |
michael@0 | 123 | this._instance = new BrowserGlue() : this._instance; |
michael@0 | 124 | } |
michael@0 | 125 | }; |
michael@0 | 126 | |
michael@0 | 127 | // Constructor |
michael@0 | 128 | |
michael@0 | 129 | function BrowserGlue() { |
michael@0 | 130 | XPCOMUtils.defineLazyServiceGetter(this, "_idleService", |
michael@0 | 131 | "@mozilla.org/widget/idleservice;1", |
michael@0 | 132 | "nsIIdleService"); |
michael@0 | 133 | |
michael@0 | 134 | XPCOMUtils.defineLazyGetter(this, "_distributionCustomizer", function() { |
michael@0 | 135 | Cu.import("resource:///modules/distribution.js"); |
michael@0 | 136 | return new DistributionCustomizer(); |
michael@0 | 137 | }); |
michael@0 | 138 | |
michael@0 | 139 | XPCOMUtils.defineLazyGetter(this, "_sanitizer", |
michael@0 | 140 | function() { |
michael@0 | 141 | let sanitizerScope = {}; |
michael@0 | 142 | Services.scriptloader.loadSubScript("chrome://browser/content/sanitize.js", sanitizerScope); |
michael@0 | 143 | return sanitizerScope.Sanitizer; |
michael@0 | 144 | }); |
michael@0 | 145 | |
michael@0 | 146 | this._init(); |
michael@0 | 147 | } |
michael@0 | 148 | |
michael@0 | 149 | #ifndef XP_MACOSX |
michael@0 | 150 | # OS X has the concept of zero-window sessions and therefore ignores the |
michael@0 | 151 | # browser-lastwindow-close-* topics. |
michael@0 | 152 | #define OBSERVE_LASTWINDOW_CLOSE_TOPICS 1 |
michael@0 | 153 | #endif |
michael@0 | 154 | |
michael@0 | 155 | BrowserGlue.prototype = { |
michael@0 | 156 | _saveSession: false, |
michael@0 | 157 | _isPlacesInitObserver: false, |
michael@0 | 158 | _isPlacesLockedObserver: false, |
michael@0 | 159 | _isPlacesShutdownObserver: false, |
michael@0 | 160 | _isPlacesDatabaseLocked: false, |
michael@0 | 161 | _migrationImportsDefaultBookmarks: false, |
michael@0 | 162 | |
michael@0 | 163 | _setPrefToSaveSession: function BG__setPrefToSaveSession(aForce) { |
michael@0 | 164 | if (!this._saveSession && !aForce) |
michael@0 | 165 | return; |
michael@0 | 166 | |
michael@0 | 167 | Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true); |
michael@0 | 168 | |
michael@0 | 169 | // This method can be called via [NSApplication terminate:] on Mac, which |
michael@0 | 170 | // ends up causing prefs not to be flushed to disk, so we need to do that |
michael@0 | 171 | // explicitly here. See bug 497652. |
michael@0 | 172 | Services.prefs.savePrefFile(null); |
michael@0 | 173 | }, |
michael@0 | 174 | |
michael@0 | 175 | #ifdef MOZ_SERVICES_SYNC |
michael@0 | 176 | _setSyncAutoconnectDelay: function BG__setSyncAutoconnectDelay() { |
michael@0 | 177 | // Assume that a non-zero value for services.sync.autoconnectDelay should override |
michael@0 | 178 | if (Services.prefs.prefHasUserValue("services.sync.autoconnectDelay")) { |
michael@0 | 179 | let prefDelay = Services.prefs.getIntPref("services.sync.autoconnectDelay"); |
michael@0 | 180 | |
michael@0 | 181 | if (prefDelay > 0) |
michael@0 | 182 | return; |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | // delays are in seconds |
michael@0 | 186 | const MAX_DELAY = 300; |
michael@0 | 187 | let delay = 3; |
michael@0 | 188 | let browserEnum = Services.wm.getEnumerator("navigator:browser"); |
michael@0 | 189 | while (browserEnum.hasMoreElements()) { |
michael@0 | 190 | delay += browserEnum.getNext().gBrowser.tabs.length; |
michael@0 | 191 | } |
michael@0 | 192 | delay = delay <= MAX_DELAY ? delay : MAX_DELAY; |
michael@0 | 193 | |
michael@0 | 194 | Cu.import("resource://services-sync/main.js"); |
michael@0 | 195 | Weave.Service.scheduler.delayedAutoConnect(delay); |
michael@0 | 196 | }, |
michael@0 | 197 | #endif |
michael@0 | 198 | |
michael@0 | 199 | // nsIObserver implementation |
michael@0 | 200 | observe: function BG_observe(subject, topic, data) { |
michael@0 | 201 | switch (topic) { |
michael@0 | 202 | case "prefservice:after-app-defaults": |
michael@0 | 203 | this._onAppDefaults(); |
michael@0 | 204 | break; |
michael@0 | 205 | case "final-ui-startup": |
michael@0 | 206 | this._finalUIStartup(); |
michael@0 | 207 | break; |
michael@0 | 208 | case "browser-delayed-startup-finished": |
michael@0 | 209 | this._onFirstWindowLoaded(subject); |
michael@0 | 210 | Services.obs.removeObserver(this, "browser-delayed-startup-finished"); |
michael@0 | 211 | break; |
michael@0 | 212 | case "sessionstore-windows-restored": |
michael@0 | 213 | this._onWindowsRestored(); |
michael@0 | 214 | break; |
michael@0 | 215 | case "browser:purge-session-history": |
michael@0 | 216 | // reset the console service's error buffer |
michael@0 | 217 | Services.console.logStringMessage(null); // clear the console (in case it's open) |
michael@0 | 218 | Services.console.reset(); |
michael@0 | 219 | break; |
michael@0 | 220 | case "quit-application-requested": |
michael@0 | 221 | this._onQuitRequest(subject, data); |
michael@0 | 222 | break; |
michael@0 | 223 | case "quit-application-granted": |
michael@0 | 224 | this._onQuitApplicationGranted(); |
michael@0 | 225 | break; |
michael@0 | 226 | #ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS |
michael@0 | 227 | case "browser-lastwindow-close-requested": |
michael@0 | 228 | // The application is not actually quitting, but the last full browser |
michael@0 | 229 | // window is about to be closed. |
michael@0 | 230 | this._onQuitRequest(subject, "lastwindow"); |
michael@0 | 231 | break; |
michael@0 | 232 | case "browser-lastwindow-close-granted": |
michael@0 | 233 | this._setPrefToSaveSession(); |
michael@0 | 234 | break; |
michael@0 | 235 | #endif |
michael@0 | 236 | #ifdef MOZ_SERVICES_SYNC |
michael@0 | 237 | case "weave:service:ready": |
michael@0 | 238 | this._setSyncAutoconnectDelay(); |
michael@0 | 239 | break; |
michael@0 | 240 | case "weave:engine:clients:display-uri": |
michael@0 | 241 | this._onDisplaySyncURI(subject); |
michael@0 | 242 | break; |
michael@0 | 243 | #endif |
michael@0 | 244 | case "session-save": |
michael@0 | 245 | this._setPrefToSaveSession(true); |
michael@0 | 246 | subject.QueryInterface(Ci.nsISupportsPRBool); |
michael@0 | 247 | subject.data = true; |
michael@0 | 248 | break; |
michael@0 | 249 | case "places-init-complete": |
michael@0 | 250 | if (!this._migrationImportsDefaultBookmarks) |
michael@0 | 251 | this._initPlaces(false); |
michael@0 | 252 | |
michael@0 | 253 | Services.obs.removeObserver(this, "places-init-complete"); |
michael@0 | 254 | this._isPlacesInitObserver = false; |
michael@0 | 255 | // no longer needed, since history was initialized completely. |
michael@0 | 256 | Services.obs.removeObserver(this, "places-database-locked"); |
michael@0 | 257 | this._isPlacesLockedObserver = false; |
michael@0 | 258 | break; |
michael@0 | 259 | case "places-database-locked": |
michael@0 | 260 | this._isPlacesDatabaseLocked = true; |
michael@0 | 261 | // Stop observing, so further attempts to load history service |
michael@0 | 262 | // will not show the prompt. |
michael@0 | 263 | Services.obs.removeObserver(this, "places-database-locked"); |
michael@0 | 264 | this._isPlacesLockedObserver = false; |
michael@0 | 265 | break; |
michael@0 | 266 | case "places-shutdown": |
michael@0 | 267 | if (this._isPlacesShutdownObserver) { |
michael@0 | 268 | Services.obs.removeObserver(this, "places-shutdown"); |
michael@0 | 269 | this._isPlacesShutdownObserver = false; |
michael@0 | 270 | } |
michael@0 | 271 | // places-shutdown is fired when the profile is about to disappear. |
michael@0 | 272 | this._onPlacesShutdown(); |
michael@0 | 273 | break; |
michael@0 | 274 | case "idle": |
michael@0 | 275 | this._backupBookmarks(); |
michael@0 | 276 | break; |
michael@0 | 277 | case "distribution-customization-complete": |
michael@0 | 278 | Services.obs.removeObserver(this, "distribution-customization-complete"); |
michael@0 | 279 | // Customization has finished, we don't need the customizer anymore. |
michael@0 | 280 | delete this._distributionCustomizer; |
michael@0 | 281 | break; |
michael@0 | 282 | case "browser-glue-test": // used by tests |
michael@0 | 283 | if (data == "post-update-notification") { |
michael@0 | 284 | if (Services.prefs.prefHasUserValue("app.update.postupdate")) |
michael@0 | 285 | this._showUpdateNotification(); |
michael@0 | 286 | } |
michael@0 | 287 | else if (data == "force-ui-migration") { |
michael@0 | 288 | this._migrateUI(); |
michael@0 | 289 | } |
michael@0 | 290 | else if (data == "force-distribution-customization") { |
michael@0 | 291 | this._distributionCustomizer.applyPrefDefaults(); |
michael@0 | 292 | this._distributionCustomizer.applyCustomizations(); |
michael@0 | 293 | // To apply distribution bookmarks use "places-init-complete". |
michael@0 | 294 | } |
michael@0 | 295 | else if (data == "force-places-init") { |
michael@0 | 296 | this._initPlaces(false); |
michael@0 | 297 | } |
michael@0 | 298 | break; |
michael@0 | 299 | case "initial-migration-will-import-default-bookmarks": |
michael@0 | 300 | this._migrationImportsDefaultBookmarks = true; |
michael@0 | 301 | break; |
michael@0 | 302 | case "initial-migration-did-import-default-bookmarks": |
michael@0 | 303 | this._initPlaces(true); |
michael@0 | 304 | break; |
michael@0 | 305 | case "handle-xul-text-link": |
michael@0 | 306 | let linkHandled = subject.QueryInterface(Ci.nsISupportsPRBool); |
michael@0 | 307 | if (!linkHandled.data) { |
michael@0 | 308 | let win = this.getMostRecentBrowserWindow(); |
michael@0 | 309 | if (win) { |
michael@0 | 310 | win.openUILinkIn(data, "tab"); |
michael@0 | 311 | linkHandled.data = true; |
michael@0 | 312 | } |
michael@0 | 313 | } |
michael@0 | 314 | break; |
michael@0 | 315 | case "profile-before-change": |
michael@0 | 316 | // Any component depending on Places should be finalized in |
michael@0 | 317 | // _onPlacesShutdown. Any component that doesn't need to act after |
michael@0 | 318 | // the UI has gone should be finalized in _onQuitApplicationGranted. |
michael@0 | 319 | this._dispose(); |
michael@0 | 320 | break; |
michael@0 | 321 | #ifdef MOZ_SERVICES_HEALTHREPORT |
michael@0 | 322 | case "keyword-search": |
michael@0 | 323 | // This is very similar to code in |
michael@0 | 324 | // browser.js:BrowserSearch.recordSearchInHealthReport(). The code could |
michael@0 | 325 | // be consolidated if there is will. We need the observer in |
michael@0 | 326 | // nsBrowserGlue to prevent double counting. |
michael@0 | 327 | let reporter = Cc["@mozilla.org/datareporting/service;1"] |
michael@0 | 328 | .getService() |
michael@0 | 329 | .wrappedJSObject |
michael@0 | 330 | .healthReporter; |
michael@0 | 331 | |
michael@0 | 332 | if (!reporter) { |
michael@0 | 333 | return; |
michael@0 | 334 | } |
michael@0 | 335 | |
michael@0 | 336 | reporter.onInit().then(function record() { |
michael@0 | 337 | try { |
michael@0 | 338 | let engine = subject.QueryInterface(Ci.nsISearchEngine); |
michael@0 | 339 | reporter.getProvider("org.mozilla.searches").recordSearch(engine, "urlbar"); |
michael@0 | 340 | } catch (ex) { |
michael@0 | 341 | Cu.reportError(ex); |
michael@0 | 342 | } |
michael@0 | 343 | }); |
michael@0 | 344 | break; |
michael@0 | 345 | #endif |
michael@0 | 346 | case "browser-search-engine-modified": |
michael@0 | 347 | if (data != "engine-default" && data != "engine-current") { |
michael@0 | 348 | break; |
michael@0 | 349 | } |
michael@0 | 350 | // Enforce that the search service's defaultEngine is always equal to |
michael@0 | 351 | // its currentEngine. The search service will notify us any time either |
michael@0 | 352 | // of them are changed (either by directly setting the relevant prefs, |
michael@0 | 353 | // i.e. if add-ons try to change this directly, or if the |
michael@0 | 354 | // nsIBrowserSearchService setters are called). |
michael@0 | 355 | // No need to initialize the search service, since it's guaranteed to be |
michael@0 | 356 | // initialized already when this notification fires. |
michael@0 | 357 | let ss = Services.search; |
michael@0 | 358 | if (ss.currentEngine.name == ss.defaultEngine.name) |
michael@0 | 359 | return; |
michael@0 | 360 | if (data == "engine-current") |
michael@0 | 361 | ss.defaultEngine = ss.currentEngine; |
michael@0 | 362 | else |
michael@0 | 363 | ss.currentEngine = ss.defaultEngine; |
michael@0 | 364 | break; |
michael@0 | 365 | case "browser-search-service": |
michael@0 | 366 | if (data != "init-complete") |
michael@0 | 367 | return; |
michael@0 | 368 | Services.obs.removeObserver(this, "browser-search-service"); |
michael@0 | 369 | this._syncSearchEngines(); |
michael@0 | 370 | break; |
michael@0 | 371 | } |
michael@0 | 372 | }, |
michael@0 | 373 | |
michael@0 | 374 | _syncSearchEngines: function () { |
michael@0 | 375 | // Only do this if the search service is already initialized. This function |
michael@0 | 376 | // gets called in finalUIStartup and from a browser-search-service observer, |
michael@0 | 377 | // to catch both cases (search service initialization occurring before and |
michael@0 | 378 | // after final-ui-startup) |
michael@0 | 379 | if (Services.search.isInitialized) { |
michael@0 | 380 | Services.search.defaultEngine = Services.search.currentEngine; |
michael@0 | 381 | } |
michael@0 | 382 | }, |
michael@0 | 383 | |
michael@0 | 384 | // initialization (called on application startup) |
michael@0 | 385 | _init: function BG__init() { |
michael@0 | 386 | let os = Services.obs; |
michael@0 | 387 | os.addObserver(this, "prefservice:after-app-defaults", false); |
michael@0 | 388 | os.addObserver(this, "final-ui-startup", false); |
michael@0 | 389 | os.addObserver(this, "browser-delayed-startup-finished", false); |
michael@0 | 390 | os.addObserver(this, "sessionstore-windows-restored", false); |
michael@0 | 391 | os.addObserver(this, "browser:purge-session-history", false); |
michael@0 | 392 | os.addObserver(this, "quit-application-requested", false); |
michael@0 | 393 | os.addObserver(this, "quit-application-granted", false); |
michael@0 | 394 | #ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS |
michael@0 | 395 | os.addObserver(this, "browser-lastwindow-close-requested", false); |
michael@0 | 396 | os.addObserver(this, "browser-lastwindow-close-granted", false); |
michael@0 | 397 | #endif |
michael@0 | 398 | #ifdef MOZ_SERVICES_SYNC |
michael@0 | 399 | os.addObserver(this, "weave:service:ready", false); |
michael@0 | 400 | os.addObserver(this, "weave:engine:clients:display-uri", false); |
michael@0 | 401 | #endif |
michael@0 | 402 | os.addObserver(this, "session-save", false); |
michael@0 | 403 | os.addObserver(this, "places-init-complete", false); |
michael@0 | 404 | this._isPlacesInitObserver = true; |
michael@0 | 405 | os.addObserver(this, "places-database-locked", false); |
michael@0 | 406 | this._isPlacesLockedObserver = true; |
michael@0 | 407 | os.addObserver(this, "distribution-customization-complete", false); |
michael@0 | 408 | os.addObserver(this, "places-shutdown", false); |
michael@0 | 409 | this._isPlacesShutdownObserver = true; |
michael@0 | 410 | os.addObserver(this, "handle-xul-text-link", false); |
michael@0 | 411 | os.addObserver(this, "profile-before-change", false); |
michael@0 | 412 | #ifdef MOZ_SERVICES_HEALTHREPORT |
michael@0 | 413 | os.addObserver(this, "keyword-search", false); |
michael@0 | 414 | #endif |
michael@0 | 415 | os.addObserver(this, "browser-search-engine-modified", false); |
michael@0 | 416 | os.addObserver(this, "browser-search-service", false); |
michael@0 | 417 | }, |
michael@0 | 418 | |
michael@0 | 419 | // cleanup (called on application shutdown) |
michael@0 | 420 | _dispose: function BG__dispose() { |
michael@0 | 421 | let os = Services.obs; |
michael@0 | 422 | os.removeObserver(this, "prefservice:after-app-defaults"); |
michael@0 | 423 | os.removeObserver(this, "final-ui-startup"); |
michael@0 | 424 | os.removeObserver(this, "sessionstore-windows-restored"); |
michael@0 | 425 | os.removeObserver(this, "browser:purge-session-history"); |
michael@0 | 426 | os.removeObserver(this, "quit-application-requested"); |
michael@0 | 427 | os.removeObserver(this, "quit-application-granted"); |
michael@0 | 428 | #ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS |
michael@0 | 429 | os.removeObserver(this, "browser-lastwindow-close-requested"); |
michael@0 | 430 | os.removeObserver(this, "browser-lastwindow-close-granted"); |
michael@0 | 431 | #endif |
michael@0 | 432 | #ifdef MOZ_SERVICES_SYNC |
michael@0 | 433 | os.removeObserver(this, "weave:service:ready"); |
michael@0 | 434 | os.removeObserver(this, "weave:engine:clients:display-uri"); |
michael@0 | 435 | #endif |
michael@0 | 436 | os.removeObserver(this, "session-save"); |
michael@0 | 437 | if (this._bookmarksBackupIdleTime) { |
michael@0 | 438 | this._idleService.removeIdleObserver(this, this._bookmarksBackupIdleTime); |
michael@0 | 439 | delete this._bookmarksBackupIdleTime; |
michael@0 | 440 | } |
michael@0 | 441 | if (this._isPlacesInitObserver) |
michael@0 | 442 | os.removeObserver(this, "places-init-complete"); |
michael@0 | 443 | if (this._isPlacesLockedObserver) |
michael@0 | 444 | os.removeObserver(this, "places-database-locked"); |
michael@0 | 445 | if (this._isPlacesShutdownObserver) |
michael@0 | 446 | os.removeObserver(this, "places-shutdown"); |
michael@0 | 447 | os.removeObserver(this, "handle-xul-text-link"); |
michael@0 | 448 | os.removeObserver(this, "profile-before-change"); |
michael@0 | 449 | #ifdef MOZ_SERVICES_HEALTHREPORT |
michael@0 | 450 | os.removeObserver(this, "keyword-search"); |
michael@0 | 451 | #endif |
michael@0 | 452 | os.removeObserver(this, "browser-search-engine-modified"); |
michael@0 | 453 | try { |
michael@0 | 454 | os.removeObserver(this, "browser-search-service"); |
michael@0 | 455 | // may have already been removed by the observer |
michael@0 | 456 | } catch (ex) {} |
michael@0 | 457 | }, |
michael@0 | 458 | |
michael@0 | 459 | _onAppDefaults: function BG__onAppDefaults() { |
michael@0 | 460 | // apply distribution customizations (prefs) |
michael@0 | 461 | // other customizations are applied in _finalUIStartup() |
michael@0 | 462 | this._distributionCustomizer.applyPrefDefaults(); |
michael@0 | 463 | }, |
michael@0 | 464 | |
michael@0 | 465 | // runs on startup, before the first command line handler is invoked |
michael@0 | 466 | // (i.e. before the first window is opened) |
michael@0 | 467 | _finalUIStartup: function BG__finalUIStartup() { |
michael@0 | 468 | this._sanitizer.onStartup(); |
michael@0 | 469 | // check if we're in safe mode |
michael@0 | 470 | if (Services.appinfo.inSafeMode) { |
michael@0 | 471 | Services.ww.openWindow(null, "chrome://browser/content/safeMode.xul", |
michael@0 | 472 | "_blank", "chrome,centerscreen,modal,resizable=no", null); |
michael@0 | 473 | } |
michael@0 | 474 | |
michael@0 | 475 | // apply distribution customizations |
michael@0 | 476 | // prefs are applied in _onAppDefaults() |
michael@0 | 477 | this._distributionCustomizer.applyCustomizations(); |
michael@0 | 478 | |
michael@0 | 479 | // handle any UI migration |
michael@0 | 480 | this._migrateUI(); |
michael@0 | 481 | |
michael@0 | 482 | this._syncSearchEngines(); |
michael@0 | 483 | |
michael@0 | 484 | WebappManager.init(); |
michael@0 | 485 | PageThumbs.init(); |
michael@0 | 486 | NewTabUtils.init(); |
michael@0 | 487 | DirectoryLinksProvider.init(); |
michael@0 | 488 | NewTabUtils.links.addProvider(DirectoryLinksProvider); |
michael@0 | 489 | BrowserNewTabPreloader.init(); |
michael@0 | 490 | #ifdef NIGHTLY_BUILD |
michael@0 | 491 | if (Services.prefs.getBoolPref("dom.identity.enabled")) { |
michael@0 | 492 | SignInToWebsiteUX.init(); |
michael@0 | 493 | } |
michael@0 | 494 | #endif |
michael@0 | 495 | PdfJs.init(); |
michael@0 | 496 | #ifdef NIGHTLY_BUILD |
michael@0 | 497 | ShumwayUtils.init(); |
michael@0 | 498 | #endif |
michael@0 | 499 | webrtcUI.init(); |
michael@0 | 500 | AboutHome.init(); |
michael@0 | 501 | SessionStore.init(); |
michael@0 | 502 | BrowserUITelemetry.init(); |
michael@0 | 503 | ContentSearch.init(); |
michael@0 | 504 | |
michael@0 | 505 | if (Services.appinfo.browserTabsRemote) { |
michael@0 | 506 | ContentClick.init(); |
michael@0 | 507 | RemotePrompt.init(); |
michael@0 | 508 | } |
michael@0 | 509 | |
michael@0 | 510 | Services.obs.notifyObservers(null, "browser-ui-startup-complete", ""); |
michael@0 | 511 | }, |
michael@0 | 512 | |
michael@0 | 513 | _checkForOldBuildUpdates: function () { |
michael@0 | 514 | // check for update if our build is old |
michael@0 | 515 | if (Services.prefs.getBoolPref("app.update.enabled") && |
michael@0 | 516 | Services.prefs.getBoolPref("app.update.checkInstallTime")) { |
michael@0 | 517 | |
michael@0 | 518 | let buildID = Services.appinfo.appBuildID; |
michael@0 | 519 | let today = new Date().getTime(); |
michael@0 | 520 | let buildDate = new Date(buildID.slice(0,4), // year |
michael@0 | 521 | buildID.slice(4,6) - 1, // months are zero-based. |
michael@0 | 522 | buildID.slice(6,8), // day |
michael@0 | 523 | buildID.slice(8,10), // hour |
michael@0 | 524 | buildID.slice(10,12), // min |
michael@0 | 525 | buildID.slice(12,14)) // ms |
michael@0 | 526 | .getTime(); |
michael@0 | 527 | |
michael@0 | 528 | const millisecondsIn24Hours = 86400000; |
michael@0 | 529 | let acceptableAge = Services.prefs.getIntPref("app.update.checkInstallTime.days") * millisecondsIn24Hours; |
michael@0 | 530 | |
michael@0 | 531 | if (buildDate + acceptableAge < today) { |
michael@0 | 532 | Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService).checkForBackgroundUpdates(); |
michael@0 | 533 | } |
michael@0 | 534 | } |
michael@0 | 535 | }, |
michael@0 | 536 | |
michael@0 | 537 | _trackSlowStartup: function () { |
michael@0 | 538 | if (Services.startup.interrupted || |
michael@0 | 539 | Services.prefs.getBoolPref("browser.slowStartup.notificationDisabled")) |
michael@0 | 540 | return; |
michael@0 | 541 | |
michael@0 | 542 | let currentTime = Date.now() - Services.startup.getStartupInfo().process; |
michael@0 | 543 | let averageTime = 0; |
michael@0 | 544 | let samples = 0; |
michael@0 | 545 | try { |
michael@0 | 546 | averageTime = Services.prefs.getIntPref("browser.slowStartup.averageTime"); |
michael@0 | 547 | samples = Services.prefs.getIntPref("browser.slowStartup.samples"); |
michael@0 | 548 | } catch (e) { } |
michael@0 | 549 | |
michael@0 | 550 | let totalTime = (averageTime * samples) + currentTime; |
michael@0 | 551 | samples++; |
michael@0 | 552 | averageTime = totalTime / samples; |
michael@0 | 553 | |
michael@0 | 554 | if (samples >= Services.prefs.getIntPref("browser.slowStartup.maxSamples")) { |
michael@0 | 555 | if (averageTime > Services.prefs.getIntPref("browser.slowStartup.timeThreshold")) |
michael@0 | 556 | this._showSlowStartupNotification(); |
michael@0 | 557 | averageTime = 0; |
michael@0 | 558 | samples = 0; |
michael@0 | 559 | } |
michael@0 | 560 | |
michael@0 | 561 | Services.prefs.setIntPref("browser.slowStartup.averageTime", averageTime); |
michael@0 | 562 | Services.prefs.setIntPref("browser.slowStartup.samples", samples); |
michael@0 | 563 | }, |
michael@0 | 564 | |
michael@0 | 565 | _showSlowStartupNotification: function () { |
michael@0 | 566 | let win = this.getMostRecentBrowserWindow(); |
michael@0 | 567 | if (!win) |
michael@0 | 568 | return; |
michael@0 | 569 | |
michael@0 | 570 | let productName = Services.strings |
michael@0 | 571 | .createBundle("chrome://branding/locale/brand.properties") |
michael@0 | 572 | .GetStringFromName("brandFullName"); |
michael@0 | 573 | let message = win.gNavigatorBundle.getFormattedString("slowStartup.message", [productName]); |
michael@0 | 574 | |
michael@0 | 575 | let buttons = [ |
michael@0 | 576 | { |
michael@0 | 577 | label: win.gNavigatorBundle.getString("slowStartup.helpButton.label"), |
michael@0 | 578 | accessKey: win.gNavigatorBundle.getString("slowStartup.helpButton.accesskey"), |
michael@0 | 579 | callback: function () { |
michael@0 | 580 | win.openUILinkIn("https://support.mozilla.org/kb/reset-firefox-easily-fix-most-problems", "tab"); |
michael@0 | 581 | } |
michael@0 | 582 | }, |
michael@0 | 583 | { |
michael@0 | 584 | label: win.gNavigatorBundle.getString("slowStartup.disableNotificationButton.label"), |
michael@0 | 585 | accessKey: win.gNavigatorBundle.getString("slowStartup.disableNotificationButton.accesskey"), |
michael@0 | 586 | callback: function () { |
michael@0 | 587 | Services.prefs.setBoolPref("browser.slowStartup.notificationDisabled", true); |
michael@0 | 588 | } |
michael@0 | 589 | } |
michael@0 | 590 | ]; |
michael@0 | 591 | |
michael@0 | 592 | let nb = win.document.getElementById("global-notificationbox"); |
michael@0 | 593 | nb.appendNotification(message, "slow-startup", |
michael@0 | 594 | "chrome://browser/skin/slowStartup-16.png", |
michael@0 | 595 | nb.PRIORITY_INFO_LOW, buttons); |
michael@0 | 596 | }, |
michael@0 | 597 | |
michael@0 | 598 | /** |
michael@0 | 599 | * Show a notification bar offering a reset if the profile has been unused for some time. |
michael@0 | 600 | */ |
michael@0 | 601 | _resetUnusedProfileNotification: function () { |
michael@0 | 602 | let win = this.getMostRecentBrowserWindow(); |
michael@0 | 603 | if (!win) |
michael@0 | 604 | return; |
michael@0 | 605 | |
michael@0 | 606 | Cu.import("resource://gre/modules/ResetProfile.jsm"); |
michael@0 | 607 | if (!ResetProfile.resetSupported()) |
michael@0 | 608 | return; |
michael@0 | 609 | |
michael@0 | 610 | let productName = Services.strings |
michael@0 | 611 | .createBundle("chrome://branding/locale/brand.properties") |
michael@0 | 612 | .GetStringFromName("brandShortName"); |
michael@0 | 613 | let resetBundle = Services.strings |
michael@0 | 614 | .createBundle("chrome://global/locale/resetProfile.properties"); |
michael@0 | 615 | |
michael@0 | 616 | let message = resetBundle.formatStringFromName("resetUnusedProfile.message", [productName], 1); |
michael@0 | 617 | let buttons = [ |
michael@0 | 618 | { |
michael@0 | 619 | label: resetBundle.formatStringFromName("resetProfile.resetButton.label", [productName], 1), |
michael@0 | 620 | accessKey: resetBundle.GetStringFromName("resetProfile.resetButton.accesskey"), |
michael@0 | 621 | callback: function () { |
michael@0 | 622 | ResetProfile.openConfirmationDialog(win); |
michael@0 | 623 | } |
michael@0 | 624 | }, |
michael@0 | 625 | ]; |
michael@0 | 626 | |
michael@0 | 627 | let nb = win.document.getElementById("global-notificationbox"); |
michael@0 | 628 | nb.appendNotification(message, "reset-unused-profile", |
michael@0 | 629 | "chrome://global/skin/icons/question-16.png", |
michael@0 | 630 | nb.PRIORITY_INFO_LOW, buttons); |
michael@0 | 631 | }, |
michael@0 | 632 | |
michael@0 | 633 | // the first browser window has finished initializing |
michael@0 | 634 | _onFirstWindowLoaded: function BG__onFirstWindowLoaded(aWindow) { |
michael@0 | 635 | #ifdef XP_WIN |
michael@0 | 636 | // For windows seven, initialize the jump list module. |
michael@0 | 637 | const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1"; |
michael@0 | 638 | if (WINTASKBAR_CONTRACTID in Cc && |
michael@0 | 639 | Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) { |
michael@0 | 640 | let temp = {}; |
michael@0 | 641 | Cu.import("resource:///modules/WindowsJumpLists.jsm", temp); |
michael@0 | 642 | temp.WinTaskbarJumpList.startup(); |
michael@0 | 643 | } |
michael@0 | 644 | #endif |
michael@0 | 645 | |
michael@0 | 646 | this._trackSlowStartup(); |
michael@0 | 647 | |
michael@0 | 648 | // Offer to reset a user's profile if it hasn't been used for 60 days. |
michael@0 | 649 | const OFFER_PROFILE_RESET_INTERVAL_MS = 60 * 24 * 60 * 60 * 1000; |
michael@0 | 650 | let lastUse = Services.appinfo.replacedLockTime; |
michael@0 | 651 | let disableResetPrompt = false; |
michael@0 | 652 | try { |
michael@0 | 653 | disableResetPrompt = Services.prefs.getBoolPref("browser.disableResetPrompt"); |
michael@0 | 654 | } catch(e) {} |
michael@0 | 655 | if (!disableResetPrompt && lastUse && |
michael@0 | 656 | Date.now() - lastUse >= OFFER_PROFILE_RESET_INTERVAL_MS) { |
michael@0 | 657 | this._resetUnusedProfileNotification(); |
michael@0 | 658 | } |
michael@0 | 659 | |
michael@0 | 660 | this._checkForOldBuildUpdates(); |
michael@0 | 661 | }, |
michael@0 | 662 | |
michael@0 | 663 | /** |
michael@0 | 664 | * Application shutdown handler. |
michael@0 | 665 | */ |
michael@0 | 666 | _onQuitApplicationGranted: function () { |
michael@0 | 667 | // This pref must be set here because SessionStore will use its value |
michael@0 | 668 | // on quit-application. |
michael@0 | 669 | this._setPrefToSaveSession(); |
michael@0 | 670 | |
michael@0 | 671 | // Call trackStartupCrashEnd here in case the delayed call on startup hasn't |
michael@0 | 672 | // yet occurred (see trackStartupCrashEnd caller in browser.js). |
michael@0 | 673 | try { |
michael@0 | 674 | let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"] |
michael@0 | 675 | .getService(Ci.nsIAppStartup); |
michael@0 | 676 | appStartup.trackStartupCrashEnd(); |
michael@0 | 677 | } catch (e) { |
michael@0 | 678 | Cu.reportError("Could not end startup crash tracking in quit-application-granted: " + e); |
michael@0 | 679 | } |
michael@0 | 680 | |
michael@0 | 681 | BrowserNewTabPreloader.uninit(); |
michael@0 | 682 | CustomizationTabPreloader.uninit(); |
michael@0 | 683 | WebappManager.uninit(); |
michael@0 | 684 | #ifdef NIGHTLY_BUILD |
michael@0 | 685 | if (Services.prefs.getBoolPref("dom.identity.enabled")) { |
michael@0 | 686 | SignInToWebsiteUX.uninit(); |
michael@0 | 687 | } |
michael@0 | 688 | #endif |
michael@0 | 689 | webrtcUI.uninit(); |
michael@0 | 690 | }, |
michael@0 | 691 | |
michael@0 | 692 | // All initial windows have opened. |
michael@0 | 693 | _onWindowsRestored: function BG__onWindowsRestored() { |
michael@0 | 694 | // Show update notification, if needed. |
michael@0 | 695 | if (Services.prefs.prefHasUserValue("app.update.postupdate")) |
michael@0 | 696 | this._showUpdateNotification(); |
michael@0 | 697 | |
michael@0 | 698 | // Load the "more info" page for a locked places.sqlite |
michael@0 | 699 | // This property is set earlier by places-database-locked topic. |
michael@0 | 700 | if (this._isPlacesDatabaseLocked) { |
michael@0 | 701 | this._showPlacesLockedNotificationBox(); |
michael@0 | 702 | } |
michael@0 | 703 | |
michael@0 | 704 | // If there are plugins installed that are outdated, and the user hasn't |
michael@0 | 705 | // been warned about them yet, open the plugins update page. |
michael@0 | 706 | if (Services.prefs.getBoolPref(PREF_PLUGINS_NOTIFYUSER)) |
michael@0 | 707 | this._showPluginUpdatePage(); |
michael@0 | 708 | |
michael@0 | 709 | // For any add-ons that were installed disabled and can be enabled offer |
michael@0 | 710 | // them to the user. |
michael@0 | 711 | let changedIDs = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED); |
michael@0 | 712 | if (changedIDs.length > 0) { |
michael@0 | 713 | let win = this.getMostRecentBrowserWindow(); |
michael@0 | 714 | AddonManager.getAddonsByIDs(changedIDs, function(aAddons) { |
michael@0 | 715 | aAddons.forEach(function(aAddon) { |
michael@0 | 716 | // If the add-on isn't user disabled or can't be enabled then skip it. |
michael@0 | 717 | if (!aAddon.userDisabled || !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE)) |
michael@0 | 718 | return; |
michael@0 | 719 | |
michael@0 | 720 | win.openUILinkIn("about:newaddon?id=" + aAddon.id, "tab"); |
michael@0 | 721 | }) |
michael@0 | 722 | }); |
michael@0 | 723 | } |
michael@0 | 724 | |
michael@0 | 725 | // Perform default browser checking. |
michael@0 | 726 | var shell; |
michael@0 | 727 | try { |
michael@0 | 728 | shell = Components.classes["@mozilla.org/browser/shell-service;1"] |
michael@0 | 729 | .getService(Components.interfaces.nsIShellService); |
michael@0 | 730 | } catch (e) { } |
michael@0 | 731 | if (shell) { |
michael@0 | 732 | #ifdef DEBUG |
michael@0 | 733 | let shouldCheck = false; |
michael@0 | 734 | #else |
michael@0 | 735 | let shouldCheck = shell.shouldCheckDefaultBrowser; |
michael@0 | 736 | #endif |
michael@0 | 737 | let willRecoverSession = false; |
michael@0 | 738 | try { |
michael@0 | 739 | let ss = Cc["@mozilla.org/browser/sessionstartup;1"]. |
michael@0 | 740 | getService(Ci.nsISessionStartup); |
michael@0 | 741 | willRecoverSession = |
michael@0 | 742 | (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION); |
michael@0 | 743 | } |
michael@0 | 744 | catch (ex) { /* never mind; suppose SessionStore is broken */ } |
michael@0 | 745 | |
michael@0 | 746 | let isDefault = shell.isDefaultBrowser(true, false); // startup check, check all assoc |
michael@0 | 747 | try { |
michael@0 | 748 | // Report default browser status on startup to telemetry |
michael@0 | 749 | // so we can track whether we are the default. |
michael@0 | 750 | Services.telemetry.getHistogramById("BROWSER_IS_USER_DEFAULT") |
michael@0 | 751 | .add(isDefault); |
michael@0 | 752 | } |
michael@0 | 753 | catch (ex) { /* Don't break the default prompt if telemetry is broken. */ } |
michael@0 | 754 | |
michael@0 | 755 | if (shouldCheck && !isDefault && !willRecoverSession) { |
michael@0 | 756 | Services.tm.mainThread.dispatch(function() { |
michael@0 | 757 | var win = this.getMostRecentBrowserWindow(); |
michael@0 | 758 | var brandBundle = win.document.getElementById("bundle_brand"); |
michael@0 | 759 | var shellBundle = win.document.getElementById("bundle_shell"); |
michael@0 | 760 | |
michael@0 | 761 | var brandShortName = brandBundle.getString("brandShortName"); |
michael@0 | 762 | var promptTitle = shellBundle.getString("setDefaultBrowserTitle"); |
michael@0 | 763 | var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage", |
michael@0 | 764 | [brandShortName]); |
michael@0 | 765 | var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk", |
michael@0 | 766 | [brandShortName]); |
michael@0 | 767 | var checkEveryTime = { value: shouldCheck }; |
michael@0 | 768 | var ps = Services.prompt; |
michael@0 | 769 | var rv = ps.confirmEx(win, promptTitle, promptMessage, |
michael@0 | 770 | ps.STD_YES_NO_BUTTONS, |
michael@0 | 771 | null, null, null, checkboxLabel, checkEveryTime); |
michael@0 | 772 | if (rv == 0) { |
michael@0 | 773 | var claimAllTypes = true; |
michael@0 | 774 | #ifdef XP_WIN |
michael@0 | 775 | try { |
michael@0 | 776 | // In Windows 8, the UI for selecting default protocol is much |
michael@0 | 777 | // nicer than the UI for setting file type associations. So we |
michael@0 | 778 | // only show the protocol association screen on Windows 8. |
michael@0 | 779 | // Windows 8 is version 6.2. |
michael@0 | 780 | let version = Cc["@mozilla.org/system-info;1"] |
michael@0 | 781 | .getService(Ci.nsIPropertyBag2) |
michael@0 | 782 | .getProperty("version"); |
michael@0 | 783 | claimAllTypes = (parseFloat(version) < 6.2); |
michael@0 | 784 | } catch (ex) { } |
michael@0 | 785 | #endif |
michael@0 | 786 | shell.setDefaultBrowser(claimAllTypes, false); |
michael@0 | 787 | } |
michael@0 | 788 | shell.shouldCheckDefaultBrowser = checkEveryTime.value; |
michael@0 | 789 | }.bind(this), Ci.nsIThread.DISPATCH_NORMAL); |
michael@0 | 790 | } |
michael@0 | 791 | } |
michael@0 | 792 | }, |
michael@0 | 793 | |
michael@0 | 794 | _onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) { |
michael@0 | 795 | // If user has already dismissed quit request, then do nothing |
michael@0 | 796 | if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data) |
michael@0 | 797 | return; |
michael@0 | 798 | |
michael@0 | 799 | // There are several cases where we won't show a dialog here: |
michael@0 | 800 | // 1. There is only 1 tab open in 1 window |
michael@0 | 801 | // 2. The session will be restored at startup, indicated by |
michael@0 | 802 | // browser.startup.page == 3 or browser.sessionstore.resume_session_once == true |
michael@0 | 803 | // 3. browser.warnOnQuit == false |
michael@0 | 804 | // 4. The browser is currently in Private Browsing mode |
michael@0 | 805 | // 5. The browser will be restarted. |
michael@0 | 806 | // |
michael@0 | 807 | // Otherwise these are the conditions and the associated dialogs that will be shown: |
michael@0 | 808 | // 1. aQuitType == "lastwindow" or "quit" and browser.showQuitWarning == true |
michael@0 | 809 | // - The quit dialog will be shown |
michael@0 | 810 | // 2. aQuitType == "lastwindow" && browser.tabs.warnOnClose == true |
michael@0 | 811 | // - The "closing multiple tabs" dialog will be shown |
michael@0 | 812 | // |
michael@0 | 813 | // aQuitType == "lastwindow" is overloaded. "lastwindow" is used to indicate |
michael@0 | 814 | // "the last window is closing but we're not quitting (a non-browser window is open)" |
michael@0 | 815 | // and also "we're quitting by closing the last window". |
michael@0 | 816 | |
michael@0 | 817 | if (aQuitType == "restart") |
michael@0 | 818 | return; |
michael@0 | 819 | |
michael@0 | 820 | var windowcount = 0; |
michael@0 | 821 | var pagecount = 0; |
michael@0 | 822 | var browserEnum = Services.wm.getEnumerator("navigator:browser"); |
michael@0 | 823 | let allWindowsPrivate = true; |
michael@0 | 824 | while (browserEnum.hasMoreElements()) { |
michael@0 | 825 | // XXXbz should we skip closed windows here? |
michael@0 | 826 | windowcount++; |
michael@0 | 827 | |
michael@0 | 828 | var browser = browserEnum.getNext(); |
michael@0 | 829 | if (!PrivateBrowsingUtils.isWindowPrivate(browser)) |
michael@0 | 830 | allWindowsPrivate = false; |
michael@0 | 831 | var tabbrowser = browser.document.getElementById("content"); |
michael@0 | 832 | if (tabbrowser) |
michael@0 | 833 | pagecount += tabbrowser.browsers.length - tabbrowser._numPinnedTabs; |
michael@0 | 834 | } |
michael@0 | 835 | |
michael@0 | 836 | this._saveSession = false; |
michael@0 | 837 | if (pagecount < 2) |
michael@0 | 838 | return; |
michael@0 | 839 | |
michael@0 | 840 | if (!aQuitType) |
michael@0 | 841 | aQuitType = "quit"; |
michael@0 | 842 | |
michael@0 | 843 | var mostRecentBrowserWindow; |
michael@0 | 844 | |
michael@0 | 845 | // browser.warnOnQuit is a hidden global boolean to override all quit prompts |
michael@0 | 846 | // browser.showQuitWarning specifically covers quitting |
michael@0 | 847 | // browser.tabs.warnOnClose is the global "warn when closing multiple tabs" pref |
michael@0 | 848 | |
michael@0 | 849 | var sessionWillBeRestored = Services.prefs.getIntPref("browser.startup.page") == 3 || |
michael@0 | 850 | Services.prefs.getBoolPref("browser.sessionstore.resume_session_once"); |
michael@0 | 851 | if (sessionWillBeRestored || !Services.prefs.getBoolPref("browser.warnOnQuit")) |
michael@0 | 852 | return; |
michael@0 | 853 | |
michael@0 | 854 | // On last window close or quit && showQuitWarning, we want to show the |
michael@0 | 855 | // quit warning. |
michael@0 | 856 | if (!Services.prefs.getBoolPref("browser.showQuitWarning")) { |
michael@0 | 857 | if (aQuitType == "lastwindow") { |
michael@0 | 858 | // If aQuitType is "lastwindow" and we aren't showing the quit warning, |
michael@0 | 859 | // we should show the window closing warning instead. warnAboutClosing |
michael@0 | 860 | // tabs checks browser.tabs.warnOnClose and returns if it's ok to close |
michael@0 | 861 | // the window. It doesn't actually close the window. |
michael@0 | 862 | mostRecentBrowserWindow = Services.wm.getMostRecentWindow("navigator:browser"); |
michael@0 | 863 | let allTabs = mostRecentBrowserWindow.gBrowser.closingTabsEnum.ALL; |
michael@0 | 864 | aCancelQuit.data = !mostRecentBrowserWindow.gBrowser.warnAboutClosingTabs(allTabs) |
michael@0 | 865 | } |
michael@0 | 866 | return; |
michael@0 | 867 | } |
michael@0 | 868 | |
michael@0 | 869 | // Never show a prompt inside private browsing mode |
michael@0 | 870 | if (allWindowsPrivate) |
michael@0 | 871 | return; |
michael@0 | 872 | |
michael@0 | 873 | var quitBundle = Services.strings.createBundle("chrome://browser/locale/quitDialog.properties"); |
michael@0 | 874 | var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties"); |
michael@0 | 875 | |
michael@0 | 876 | var appName = brandBundle.GetStringFromName("brandShortName"); |
michael@0 | 877 | var quitTitleString = "quitDialogTitle"; |
michael@0 | 878 | var quitDialogTitle = quitBundle.formatStringFromName(quitTitleString, [appName], 1); |
michael@0 | 879 | |
michael@0 | 880 | var message; |
michael@0 | 881 | if (windowcount == 1) |
michael@0 | 882 | message = quitBundle.formatStringFromName("messageNoWindows", |
michael@0 | 883 | [appName], 1); |
michael@0 | 884 | else |
michael@0 | 885 | message = quitBundle.formatStringFromName("message", |
michael@0 | 886 | [appName], 1); |
michael@0 | 887 | |
michael@0 | 888 | var promptService = Services.prompt; |
michael@0 | 889 | |
michael@0 | 890 | var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0 + |
michael@0 | 891 | promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 + |
michael@0 | 892 | promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_2 + |
michael@0 | 893 | promptService.BUTTON_POS_0_DEFAULT; |
michael@0 | 894 | |
michael@0 | 895 | var neverAsk = {value:false}; |
michael@0 | 896 | var button0Title = quitBundle.GetStringFromName("saveTitle"); |
michael@0 | 897 | var button1Title = quitBundle.GetStringFromName("cancelTitle"); |
michael@0 | 898 | var button2Title = quitBundle.GetStringFromName("quitTitle"); |
michael@0 | 899 | var neverAskText = quitBundle.GetStringFromName("neverAsk2"); |
michael@0 | 900 | |
michael@0 | 901 | // This wouldn't have been set above since we shouldn't be here for |
michael@0 | 902 | // aQuitType == "lastwindow" |
michael@0 | 903 | mostRecentBrowserWindow = Services.wm.getMostRecentWindow("navigator:browser"); |
michael@0 | 904 | |
michael@0 | 905 | var buttonChoice = |
michael@0 | 906 | promptService.confirmEx(mostRecentBrowserWindow, quitDialogTitle, message, |
michael@0 | 907 | flags, button0Title, button1Title, button2Title, |
michael@0 | 908 | neverAskText, neverAsk); |
michael@0 | 909 | |
michael@0 | 910 | switch (buttonChoice) { |
michael@0 | 911 | case 2: // Quit |
michael@0 | 912 | if (neverAsk.value) |
michael@0 | 913 | Services.prefs.setBoolPref("browser.showQuitWarning", false); |
michael@0 | 914 | break; |
michael@0 | 915 | case 1: // Cancel |
michael@0 | 916 | aCancelQuit.QueryInterface(Ci.nsISupportsPRBool); |
michael@0 | 917 | aCancelQuit.data = true; |
michael@0 | 918 | break; |
michael@0 | 919 | case 0: // Save & Quit |
michael@0 | 920 | this._saveSession = true; |
michael@0 | 921 | if (neverAsk.value) { |
michael@0 | 922 | // always save state when shutting down |
michael@0 | 923 | Services.prefs.setIntPref("browser.startup.page", 3); |
michael@0 | 924 | } |
michael@0 | 925 | break; |
michael@0 | 926 | } |
michael@0 | 927 | }, |
michael@0 | 928 | |
michael@0 | 929 | _showUpdateNotification: function BG__showUpdateNotification() { |
michael@0 | 930 | Services.prefs.clearUserPref("app.update.postupdate"); |
michael@0 | 931 | |
michael@0 | 932 | var um = Cc["@mozilla.org/updates/update-manager;1"]. |
michael@0 | 933 | getService(Ci.nsIUpdateManager); |
michael@0 | 934 | try { |
michael@0 | 935 | // If the updates.xml file is deleted then getUpdateAt will throw. |
michael@0 | 936 | var update = um.getUpdateAt(0).QueryInterface(Ci.nsIPropertyBag); |
michael@0 | 937 | } |
michael@0 | 938 | catch (e) { |
michael@0 | 939 | // This should never happen. |
michael@0 | 940 | Cu.reportError("Unable to find update: " + e); |
michael@0 | 941 | return; |
michael@0 | 942 | } |
michael@0 | 943 | |
michael@0 | 944 | var actions = update.getProperty("actions"); |
michael@0 | 945 | if (!actions || actions.indexOf("silent") != -1) |
michael@0 | 946 | return; |
michael@0 | 947 | |
michael@0 | 948 | var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. |
michael@0 | 949 | getService(Ci.nsIURLFormatter); |
michael@0 | 950 | var browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); |
michael@0 | 951 | var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties"); |
michael@0 | 952 | var appName = brandBundle.GetStringFromName("brandShortName"); |
michael@0 | 953 | |
michael@0 | 954 | function getNotifyString(aPropData) { |
michael@0 | 955 | var propValue = update.getProperty(aPropData.propName); |
michael@0 | 956 | if (!propValue) { |
michael@0 | 957 | if (aPropData.prefName) |
michael@0 | 958 | propValue = formatter.formatURLPref(aPropData.prefName); |
michael@0 | 959 | else if (aPropData.stringParams) |
michael@0 | 960 | propValue = browserBundle.formatStringFromName(aPropData.stringName, |
michael@0 | 961 | aPropData.stringParams, |
michael@0 | 962 | aPropData.stringParams.length); |
michael@0 | 963 | else |
michael@0 | 964 | propValue = browserBundle.GetStringFromName(aPropData.stringName); |
michael@0 | 965 | } |
michael@0 | 966 | return propValue; |
michael@0 | 967 | } |
michael@0 | 968 | |
michael@0 | 969 | if (actions.indexOf("showNotification") != -1) { |
michael@0 | 970 | let text = getNotifyString({propName: "notificationText", |
michael@0 | 971 | stringName: "puNotifyText", |
michael@0 | 972 | stringParams: [appName]}); |
michael@0 | 973 | let url = getNotifyString({propName: "notificationURL", |
michael@0 | 974 | prefName: "startup.homepage_override_url"}); |
michael@0 | 975 | let label = getNotifyString({propName: "notificationButtonLabel", |
michael@0 | 976 | stringName: "pu.notifyButton.label"}); |
michael@0 | 977 | let key = getNotifyString({propName: "notificationButtonAccessKey", |
michael@0 | 978 | stringName: "pu.notifyButton.accesskey"}); |
michael@0 | 979 | |
michael@0 | 980 | let win = this.getMostRecentBrowserWindow(); |
michael@0 | 981 | let notifyBox = win.gBrowser.getNotificationBox(); |
michael@0 | 982 | |
michael@0 | 983 | let buttons = [ |
michael@0 | 984 | { |
michael@0 | 985 | label: label, |
michael@0 | 986 | accessKey: key, |
michael@0 | 987 | popup: null, |
michael@0 | 988 | callback: function(aNotificationBar, aButton) { |
michael@0 | 989 | win.openUILinkIn(url, "tab"); |
michael@0 | 990 | } |
michael@0 | 991 | } |
michael@0 | 992 | ]; |
michael@0 | 993 | |
michael@0 | 994 | let notification = notifyBox.appendNotification(text, "post-update-notification", |
michael@0 | 995 | null, notifyBox.PRIORITY_INFO_LOW, |
michael@0 | 996 | buttons); |
michael@0 | 997 | notification.persistence = -1; // Until user closes it |
michael@0 | 998 | } |
michael@0 | 999 | |
michael@0 | 1000 | if (actions.indexOf("showAlert") == -1) |
michael@0 | 1001 | return; |
michael@0 | 1002 | |
michael@0 | 1003 | let notifier; |
michael@0 | 1004 | try { |
michael@0 | 1005 | notifier = Cc["@mozilla.org/alerts-service;1"]. |
michael@0 | 1006 | getService(Ci.nsIAlertsService); |
michael@0 | 1007 | } |
michael@0 | 1008 | catch (e) { |
michael@0 | 1009 | // nsIAlertsService is not available for this platform |
michael@0 | 1010 | return; |
michael@0 | 1011 | } |
michael@0 | 1012 | |
michael@0 | 1013 | let title = getNotifyString({propName: "alertTitle", |
michael@0 | 1014 | stringName: "puAlertTitle", |
michael@0 | 1015 | stringParams: [appName]}); |
michael@0 | 1016 | let text = getNotifyString({propName: "alertText", |
michael@0 | 1017 | stringName: "puAlertText", |
michael@0 | 1018 | stringParams: [appName]}); |
michael@0 | 1019 | let url = getNotifyString({propName: "alertURL", |
michael@0 | 1020 | prefName: "startup.homepage_override_url"}); |
michael@0 | 1021 | |
michael@0 | 1022 | var self = this; |
michael@0 | 1023 | function clickCallback(subject, topic, data) { |
michael@0 | 1024 | // This callback will be called twice but only once with this topic |
michael@0 | 1025 | if (topic != "alertclickcallback") |
michael@0 | 1026 | return; |
michael@0 | 1027 | let win = self.getMostRecentBrowserWindow(); |
michael@0 | 1028 | win.openUILinkIn(data, "tab"); |
michael@0 | 1029 | } |
michael@0 | 1030 | |
michael@0 | 1031 | try { |
michael@0 | 1032 | // This will throw NS_ERROR_NOT_AVAILABLE if the notification cannot |
michael@0 | 1033 | // be displayed per the idl. |
michael@0 | 1034 | notifier.showAlertNotification(null, title, text, |
michael@0 | 1035 | true, url, clickCallback); |
michael@0 | 1036 | } |
michael@0 | 1037 | catch (e) { |
michael@0 | 1038 | } |
michael@0 | 1039 | }, |
michael@0 | 1040 | |
michael@0 | 1041 | _showPluginUpdatePage: function BG__showPluginUpdatePage() { |
michael@0 | 1042 | Services.prefs.setBoolPref(PREF_PLUGINS_NOTIFYUSER, false); |
michael@0 | 1043 | |
michael@0 | 1044 | var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. |
michael@0 | 1045 | getService(Ci.nsIURLFormatter); |
michael@0 | 1046 | var updateUrl = formatter.formatURLPref(PREF_PLUGINS_UPDATEURL); |
michael@0 | 1047 | |
michael@0 | 1048 | var win = this.getMostRecentBrowserWindow(); |
michael@0 | 1049 | win.openUILinkIn(updateUrl, "tab"); |
michael@0 | 1050 | }, |
michael@0 | 1051 | |
michael@0 | 1052 | /** |
michael@0 | 1053 | * Initialize Places |
michael@0 | 1054 | * - imports the bookmarks html file if bookmarks database is empty, try to |
michael@0 | 1055 | * restore bookmarks from a JSON backup if the backend indicates that the |
michael@0 | 1056 | * database was corrupt. |
michael@0 | 1057 | * |
michael@0 | 1058 | * These prefs can be set up by the frontend: |
michael@0 | 1059 | * |
michael@0 | 1060 | * WARNING: setting these preferences to true will overwite existing bookmarks |
michael@0 | 1061 | * |
michael@0 | 1062 | * - browser.places.importBookmarksHTML |
michael@0 | 1063 | * Set to true will import the bookmarks.html file from the profile folder. |
michael@0 | 1064 | * - browser.places.smartBookmarksVersion |
michael@0 | 1065 | * Set during HTML import to indicate that Smart Bookmarks were created. |
michael@0 | 1066 | * Set to -1 to disable Smart Bookmarks creation. |
michael@0 | 1067 | * Set to 0 to restore current Smart Bookmarks. |
michael@0 | 1068 | * - browser.bookmarks.restore_default_bookmarks |
michael@0 | 1069 | * Set to true by safe-mode dialog to indicate we must restore default |
michael@0 | 1070 | * bookmarks. |
michael@0 | 1071 | */ |
michael@0 | 1072 | _initPlaces: function BG__initPlaces(aInitialMigrationPerformed) { |
michael@0 | 1073 | // We must instantiate the history service since it will tell us if we |
michael@0 | 1074 | // need to import or restore bookmarks due to first-run, corruption or |
michael@0 | 1075 | // forced migration (due to a major schema change). |
michael@0 | 1076 | // If the database is corrupt or has been newly created we should |
michael@0 | 1077 | // import bookmarks. |
michael@0 | 1078 | let dbStatus = PlacesUtils.history.databaseStatus; |
michael@0 | 1079 | let importBookmarks = !aInitialMigrationPerformed && |
michael@0 | 1080 | (dbStatus == PlacesUtils.history.DATABASE_STATUS_CREATE || |
michael@0 | 1081 | dbStatus == PlacesUtils.history.DATABASE_STATUS_CORRUPT); |
michael@0 | 1082 | |
michael@0 | 1083 | // Check if user or an extension has required to import bookmarks.html |
michael@0 | 1084 | let importBookmarksHTML = false; |
michael@0 | 1085 | try { |
michael@0 | 1086 | importBookmarksHTML = |
michael@0 | 1087 | Services.prefs.getBoolPref("browser.places.importBookmarksHTML"); |
michael@0 | 1088 | if (importBookmarksHTML) |
michael@0 | 1089 | importBookmarks = true; |
michael@0 | 1090 | } catch(ex) {} |
michael@0 | 1091 | |
michael@0 | 1092 | // Support legacy bookmarks.html format for apps that depend on that format. |
michael@0 | 1093 | let autoExportHTML = false; |
michael@0 | 1094 | try { |
michael@0 | 1095 | autoExportHTML = Services.prefs.getBoolPref("browser.bookmarks.autoExportHTML"); |
michael@0 | 1096 | } catch (ex) {} // Do not export. |
michael@0 | 1097 | if (autoExportHTML) { |
michael@0 | 1098 | // Sqlite.jsm and Places shutdown happen at profile-before-change, thus, |
michael@0 | 1099 | // to be on the safe side, this should run earlier. |
michael@0 | 1100 | AsyncShutdown.profileChangeTeardown.addBlocker( |
michael@0 | 1101 | "Places: export bookmarks.html", |
michael@0 | 1102 | () => BookmarkHTMLUtils.exportToFile(BookmarkHTMLUtils.defaultPath)); |
michael@0 | 1103 | } |
michael@0 | 1104 | |
michael@0 | 1105 | Task.spawn(function() { |
michael@0 | 1106 | // Check if Safe Mode or the user has required to restore bookmarks from |
michael@0 | 1107 | // default profile's bookmarks.html |
michael@0 | 1108 | let restoreDefaultBookmarks = false; |
michael@0 | 1109 | try { |
michael@0 | 1110 | restoreDefaultBookmarks = |
michael@0 | 1111 | Services.prefs.getBoolPref("browser.bookmarks.restore_default_bookmarks"); |
michael@0 | 1112 | if (restoreDefaultBookmarks) { |
michael@0 | 1113 | // Ensure that we already have a bookmarks backup for today. |
michael@0 | 1114 | yield this._backupBookmarks(); |
michael@0 | 1115 | importBookmarks = true; |
michael@0 | 1116 | } |
michael@0 | 1117 | } catch(ex) {} |
michael@0 | 1118 | |
michael@0 | 1119 | // This may be reused later, check for "=== undefined" to see if it has |
michael@0 | 1120 | // been populated already. |
michael@0 | 1121 | let lastBackupFile; |
michael@0 | 1122 | |
michael@0 | 1123 | // If the user did not require to restore default bookmarks, or import |
michael@0 | 1124 | // from bookmarks.html, we will try to restore from JSON |
michael@0 | 1125 | if (importBookmarks && !restoreDefaultBookmarks && !importBookmarksHTML) { |
michael@0 | 1126 | // get latest JSON backup |
michael@0 | 1127 | lastBackupFile = yield PlacesBackups.getMostRecentBackup("json"); |
michael@0 | 1128 | if (lastBackupFile) { |
michael@0 | 1129 | // restore from JSON backup |
michael@0 | 1130 | yield BookmarkJSONUtils.importFromFile(lastBackupFile, true); |
michael@0 | 1131 | importBookmarks = false; |
michael@0 | 1132 | } |
michael@0 | 1133 | else { |
michael@0 | 1134 | // We have created a new database but we don't have any backup available |
michael@0 | 1135 | importBookmarks = true; |
michael@0 | 1136 | if (yield OS.File.exists(BookmarkHTMLUtils.defaultPath)) { |
michael@0 | 1137 | // If bookmarks.html is available in current profile import it... |
michael@0 | 1138 | importBookmarksHTML = true; |
michael@0 | 1139 | } |
michael@0 | 1140 | else { |
michael@0 | 1141 | // ...otherwise we will restore defaults |
michael@0 | 1142 | restoreDefaultBookmarks = true; |
michael@0 | 1143 | } |
michael@0 | 1144 | } |
michael@0 | 1145 | } |
michael@0 | 1146 | |
michael@0 | 1147 | // If bookmarks are not imported, then initialize smart bookmarks. This |
michael@0 | 1148 | // happens during a common startup. |
michael@0 | 1149 | // Otherwise, if any kind of import runs, smart bookmarks creation should be |
michael@0 | 1150 | // delayed till the import operations has finished. Not doing so would |
michael@0 | 1151 | // cause them to be overwritten by the newly imported bookmarks. |
michael@0 | 1152 | if (!importBookmarks) { |
michael@0 | 1153 | // Now apply distribution customized bookmarks. |
michael@0 | 1154 | // This should always run after Places initialization. |
michael@0 | 1155 | this._distributionCustomizer.applyBookmarks(); |
michael@0 | 1156 | this.ensurePlacesDefaultQueriesInitialized(); |
michael@0 | 1157 | } |
michael@0 | 1158 | else { |
michael@0 | 1159 | // An import operation is about to run. |
michael@0 | 1160 | // Don't try to recreate smart bookmarks if autoExportHTML is true or |
michael@0 | 1161 | // smart bookmarks are disabled. |
michael@0 | 1162 | let smartBookmarksVersion = 0; |
michael@0 | 1163 | try { |
michael@0 | 1164 | smartBookmarksVersion = Services.prefs.getIntPref("browser.places.smartBookmarksVersion"); |
michael@0 | 1165 | } catch(ex) {} |
michael@0 | 1166 | if (!autoExportHTML && smartBookmarksVersion != -1) |
michael@0 | 1167 | Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0); |
michael@0 | 1168 | |
michael@0 | 1169 | let bookmarksUrl = null; |
michael@0 | 1170 | if (restoreDefaultBookmarks) { |
michael@0 | 1171 | // User wants to restore bookmarks.html file from default profile folder |
michael@0 | 1172 | bookmarksUrl = "resource:///defaults/profile/bookmarks.html"; |
michael@0 | 1173 | } |
michael@0 | 1174 | else if (yield OS.File.exists(BookmarkHTMLUtils.defaultPath)) { |
michael@0 | 1175 | bookmarksUrl = OS.Path.toFileURI(BookmarkHTMLUtils.defaultPath); |
michael@0 | 1176 | } |
michael@0 | 1177 | |
michael@0 | 1178 | if (bookmarksUrl) { |
michael@0 | 1179 | // Import from bookmarks.html file. |
michael@0 | 1180 | try { |
michael@0 | 1181 | BookmarkHTMLUtils.importFromURL(bookmarksUrl, true).then(null, |
michael@0 | 1182 | function onFailure() { |
michael@0 | 1183 | Cu.reportError("Bookmarks.html file could be corrupt."); |
michael@0 | 1184 | } |
michael@0 | 1185 | ).then( |
michael@0 | 1186 | function onComplete() { |
michael@0 | 1187 | // Now apply distribution customized bookmarks. |
michael@0 | 1188 | // This should always run after Places initialization. |
michael@0 | 1189 | this._distributionCustomizer.applyBookmarks(); |
michael@0 | 1190 | // Ensure that smart bookmarks are created once the operation is |
michael@0 | 1191 | // complete. |
michael@0 | 1192 | this.ensurePlacesDefaultQueriesInitialized(); |
michael@0 | 1193 | }.bind(this) |
michael@0 | 1194 | ); |
michael@0 | 1195 | } catch (err) { |
michael@0 | 1196 | Cu.reportError("Bookmarks.html file could be corrupt. " + err); |
michael@0 | 1197 | } |
michael@0 | 1198 | } |
michael@0 | 1199 | else { |
michael@0 | 1200 | Cu.reportError("Unable to find bookmarks.html file."); |
michael@0 | 1201 | } |
michael@0 | 1202 | |
michael@0 | 1203 | // Reset preferences, so we won't try to import again at next run |
michael@0 | 1204 | if (importBookmarksHTML) |
michael@0 | 1205 | Services.prefs.setBoolPref("browser.places.importBookmarksHTML", false); |
michael@0 | 1206 | if (restoreDefaultBookmarks) |
michael@0 | 1207 | Services.prefs.setBoolPref("browser.bookmarks.restore_default_bookmarks", |
michael@0 | 1208 | false); |
michael@0 | 1209 | } |
michael@0 | 1210 | |
michael@0 | 1211 | // Initialize bookmark archiving on idle. |
michael@0 | 1212 | if (!this._bookmarksBackupIdleTime) { |
michael@0 | 1213 | this._bookmarksBackupIdleTime = BOOKMARKS_BACKUP_IDLE_TIME_SEC; |
michael@0 | 1214 | |
michael@0 | 1215 | // If there is no backup, or the last bookmarks backup is too old, use |
michael@0 | 1216 | // a more aggressive idle observer. |
michael@0 | 1217 | if (lastBackupFile === undefined) |
michael@0 | 1218 | lastBackupFile = yield PlacesBackups.getMostRecentBackup(); |
michael@0 | 1219 | if (!lastBackupFile) { |
michael@0 | 1220 | this._bookmarksBackupIdleTime /= 2; |
michael@0 | 1221 | } |
michael@0 | 1222 | else { |
michael@0 | 1223 | let lastBackupTime = PlacesBackups.getDateForFile(lastBackupFile); |
michael@0 | 1224 | let profileLastUse = Services.appinfo.replacedLockTime || Date.now(); |
michael@0 | 1225 | |
michael@0 | 1226 | // If there is a backup after the last profile usage date it's fine, |
michael@0 | 1227 | // regardless its age. Otherwise check how old is the last |
michael@0 | 1228 | // available backup compared to that session. |
michael@0 | 1229 | if (profileLastUse > lastBackupTime) { |
michael@0 | 1230 | let backupAge = Math.round((profileLastUse - lastBackupTime) / 86400000); |
michael@0 | 1231 | // Report the age of the last available backup. |
michael@0 | 1232 | try { |
michael@0 | 1233 | Services.telemetry |
michael@0 | 1234 | .getHistogramById("PLACES_BACKUPS_DAYSFROMLAST") |
michael@0 | 1235 | .add(backupAge); |
michael@0 | 1236 | } catch (ex) { |
michael@0 | 1237 | Components.utils.reportError("Unable to report telemetry."); |
michael@0 | 1238 | } |
michael@0 | 1239 | |
michael@0 | 1240 | if (backupAge > BOOKMARKS_BACKUP_MAX_INTERVAL_DAYS) |
michael@0 | 1241 | this._bookmarksBackupIdleTime /= 2; |
michael@0 | 1242 | } |
michael@0 | 1243 | } |
michael@0 | 1244 | this._idleService.addIdleObserver(this, this._bookmarksBackupIdleTime); |
michael@0 | 1245 | } |
michael@0 | 1246 | |
michael@0 | 1247 | Services.obs.notifyObservers(null, "places-browser-init-complete", ""); |
michael@0 | 1248 | }.bind(this)); |
michael@0 | 1249 | }, |
michael@0 | 1250 | |
michael@0 | 1251 | /** |
michael@0 | 1252 | * Places shut-down tasks |
michael@0 | 1253 | * - finalize components depending on Places. |
michael@0 | 1254 | * - export bookmarks as HTML, if so configured. |
michael@0 | 1255 | */ |
michael@0 | 1256 | _onPlacesShutdown: function BG__onPlacesShutdown() { |
michael@0 | 1257 | this._sanitizer.onShutdown(); |
michael@0 | 1258 | PageThumbs.uninit(); |
michael@0 | 1259 | |
michael@0 | 1260 | if (this._bookmarksBackupIdleTime) { |
michael@0 | 1261 | this._idleService.removeIdleObserver(this, this._bookmarksBackupIdleTime); |
michael@0 | 1262 | delete this._bookmarksBackupIdleTime; |
michael@0 | 1263 | } |
michael@0 | 1264 | }, |
michael@0 | 1265 | |
michael@0 | 1266 | /** |
michael@0 | 1267 | * If a backup for today doesn't exist, this creates one. |
michael@0 | 1268 | */ |
michael@0 | 1269 | _backupBookmarks: function BG__backupBookmarks() { |
michael@0 | 1270 | return Task.spawn(function() { |
michael@0 | 1271 | let lastBackupFile = yield PlacesBackups.getMostRecentBackup(); |
michael@0 | 1272 | // Should backup bookmarks if there are no backups or the maximum |
michael@0 | 1273 | // interval between backups elapsed. |
michael@0 | 1274 | if (!lastBackupFile || |
michael@0 | 1275 | new Date() - PlacesBackups.getDateForFile(lastBackupFile) > BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS * 86400000) { |
michael@0 | 1276 | let maxBackups = Services.prefs.getIntPref("browser.bookmarks.max_backups"); |
michael@0 | 1277 | yield PlacesBackups.create(maxBackups); |
michael@0 | 1278 | } |
michael@0 | 1279 | }); |
michael@0 | 1280 | }, |
michael@0 | 1281 | |
michael@0 | 1282 | /** |
michael@0 | 1283 | * Show the notificationBox for a locked places database. |
michael@0 | 1284 | */ |
michael@0 | 1285 | _showPlacesLockedNotificationBox: function BG__showPlacesLockedNotificationBox() { |
michael@0 | 1286 | var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties"); |
michael@0 | 1287 | var applicationName = brandBundle.GetStringFromName("brandShortName"); |
michael@0 | 1288 | var placesBundle = Services.strings.createBundle("chrome://browser/locale/places/places.properties"); |
michael@0 | 1289 | var title = placesBundle.GetStringFromName("lockPrompt.title"); |
michael@0 | 1290 | var text = placesBundle.formatStringFromName("lockPrompt.text", [applicationName], 1); |
michael@0 | 1291 | var buttonText = placesBundle.GetStringFromName("lockPromptInfoButton.label"); |
michael@0 | 1292 | var accessKey = placesBundle.GetStringFromName("lockPromptInfoButton.accessKey"); |
michael@0 | 1293 | |
michael@0 | 1294 | var helpTopic = "places-locked"; |
michael@0 | 1295 | var url = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. |
michael@0 | 1296 | getService(Components.interfaces.nsIURLFormatter). |
michael@0 | 1297 | formatURLPref("app.support.baseURL"); |
michael@0 | 1298 | url += helpTopic; |
michael@0 | 1299 | |
michael@0 | 1300 | var win = this.getMostRecentBrowserWindow(); |
michael@0 | 1301 | |
michael@0 | 1302 | var buttons = [ |
michael@0 | 1303 | { |
michael@0 | 1304 | label: buttonText, |
michael@0 | 1305 | accessKey: accessKey, |
michael@0 | 1306 | popup: null, |
michael@0 | 1307 | callback: function(aNotificationBar, aButton) { |
michael@0 | 1308 | win.openUILinkIn(url, "tab"); |
michael@0 | 1309 | } |
michael@0 | 1310 | } |
michael@0 | 1311 | ]; |
michael@0 | 1312 | |
michael@0 | 1313 | var notifyBox = win.gBrowser.getNotificationBox(); |
michael@0 | 1314 | var notification = notifyBox.appendNotification(text, title, null, |
michael@0 | 1315 | notifyBox.PRIORITY_CRITICAL_MEDIUM, |
michael@0 | 1316 | buttons); |
michael@0 | 1317 | notification.persistence = -1; // Until user closes it |
michael@0 | 1318 | }, |
michael@0 | 1319 | |
michael@0 | 1320 | _migrateUI: function BG__migrateUI() { |
michael@0 | 1321 | const UI_VERSION = 22; |
michael@0 | 1322 | const BROWSER_DOCURL = "chrome://browser/content/browser.xul#"; |
michael@0 | 1323 | let currentUIVersion = 0; |
michael@0 | 1324 | try { |
michael@0 | 1325 | currentUIVersion = Services.prefs.getIntPref("browser.migration.version"); |
michael@0 | 1326 | } catch(ex) {} |
michael@0 | 1327 | if (currentUIVersion >= UI_VERSION) |
michael@0 | 1328 | return; |
michael@0 | 1329 | |
michael@0 | 1330 | this._rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService); |
michael@0 | 1331 | this._dataSource = this._rdf.GetDataSource("rdf:local-store"); |
michael@0 | 1332 | this._dirty = false; |
michael@0 | 1333 | |
michael@0 | 1334 | if (currentUIVersion < 2) { |
michael@0 | 1335 | // This code adds the customizable bookmarks button. |
michael@0 | 1336 | let currentsetResource = this._rdf.GetResource("currentset"); |
michael@0 | 1337 | let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); |
michael@0 | 1338 | let currentset = this._getPersist(toolbarResource, currentsetResource); |
michael@0 | 1339 | // Need to migrate only if toolbar is customized and the element is not found. |
michael@0 | 1340 | if (currentset && |
michael@0 | 1341 | currentset.indexOf("bookmarks-menu-button-container") == -1) { |
michael@0 | 1342 | currentset += ",bookmarks-menu-button-container"; |
michael@0 | 1343 | this._setPersist(toolbarResource, currentsetResource, currentset); |
michael@0 | 1344 | } |
michael@0 | 1345 | } |
michael@0 | 1346 | |
michael@0 | 1347 | if (currentUIVersion < 3) { |
michael@0 | 1348 | // This code merges the reload/stop/go button into the url bar. |
michael@0 | 1349 | let currentsetResource = this._rdf.GetResource("currentset"); |
michael@0 | 1350 | let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); |
michael@0 | 1351 | let currentset = this._getPersist(toolbarResource, currentsetResource); |
michael@0 | 1352 | // Need to migrate only if toolbar is customized and all 3 elements are found. |
michael@0 | 1353 | if (currentset && |
michael@0 | 1354 | currentset.indexOf("reload-button") != -1 && |
michael@0 | 1355 | currentset.indexOf("stop-button") != -1 && |
michael@0 | 1356 | currentset.indexOf("urlbar-container") != -1 && |
michael@0 | 1357 | currentset.indexOf("urlbar-container,reload-button,stop-button") == -1) { |
michael@0 | 1358 | currentset = currentset.replace(/(^|,)reload-button($|,)/, "$1$2") |
michael@0 | 1359 | .replace(/(^|,)stop-button($|,)/, "$1$2") |
michael@0 | 1360 | .replace(/(^|,)urlbar-container($|,)/, |
michael@0 | 1361 | "$1urlbar-container,reload-button,stop-button$2"); |
michael@0 | 1362 | this._setPersist(toolbarResource, currentsetResource, currentset); |
michael@0 | 1363 | } |
michael@0 | 1364 | } |
michael@0 | 1365 | |
michael@0 | 1366 | if (currentUIVersion < 4) { |
michael@0 | 1367 | // This code moves the home button to the immediate left of the bookmarks menu button. |
michael@0 | 1368 | let currentsetResource = this._rdf.GetResource("currentset"); |
michael@0 | 1369 | let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); |
michael@0 | 1370 | let currentset = this._getPersist(toolbarResource, currentsetResource); |
michael@0 | 1371 | // Need to migrate only if toolbar is customized and the elements are found. |
michael@0 | 1372 | if (currentset && |
michael@0 | 1373 | currentset.indexOf("home-button") != -1 && |
michael@0 | 1374 | currentset.indexOf("bookmarks-menu-button-container") != -1) { |
michael@0 | 1375 | currentset = currentset.replace(/(^|,)home-button($|,)/, "$1$2") |
michael@0 | 1376 | .replace(/(^|,)bookmarks-menu-button-container($|,)/, |
michael@0 | 1377 | "$1home-button,bookmarks-menu-button-container$2"); |
michael@0 | 1378 | this._setPersist(toolbarResource, currentsetResource, currentset); |
michael@0 | 1379 | } |
michael@0 | 1380 | } |
michael@0 | 1381 | |
michael@0 | 1382 | if (currentUIVersion < 5) { |
michael@0 | 1383 | // This code uncollapses PersonalToolbar if its collapsed status is not |
michael@0 | 1384 | // persisted, and user customized it or changed default bookmarks. |
michael@0 | 1385 | let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "PersonalToolbar"); |
michael@0 | 1386 | let collapsedResource = this._rdf.GetResource("collapsed"); |
michael@0 | 1387 | let collapsed = this._getPersist(toolbarResource, collapsedResource); |
michael@0 | 1388 | // If the user does not have a persisted value for the toolbar's |
michael@0 | 1389 | // "collapsed" attribute, try to determine whether it's customized. |
michael@0 | 1390 | if (collapsed === null) { |
michael@0 | 1391 | // We consider the toolbar customized if it has more than |
michael@0 | 1392 | // 3 children, or if it has a persisted currentset value. |
michael@0 | 1393 | let currentsetResource = this._rdf.GetResource("currentset"); |
michael@0 | 1394 | let toolbarIsCustomized = !!this._getPersist(toolbarResource, |
michael@0 | 1395 | currentsetResource); |
michael@0 | 1396 | function getToolbarFolderCount() { |
michael@0 | 1397 | let toolbarFolder = |
michael@0 | 1398 | PlacesUtils.getFolderContents(PlacesUtils.toolbarFolderId).root; |
michael@0 | 1399 | let toolbarChildCount = toolbarFolder.childCount; |
michael@0 | 1400 | toolbarFolder.containerOpen = false; |
michael@0 | 1401 | return toolbarChildCount; |
michael@0 | 1402 | } |
michael@0 | 1403 | |
michael@0 | 1404 | if (toolbarIsCustomized || getToolbarFolderCount() > 3) { |
michael@0 | 1405 | this._setPersist(toolbarResource, collapsedResource, "false"); |
michael@0 | 1406 | } |
michael@0 | 1407 | } |
michael@0 | 1408 | } |
michael@0 | 1409 | |
michael@0 | 1410 | if (currentUIVersion < 6) { |
michael@0 | 1411 | // convert tabsontop attribute to pref |
michael@0 | 1412 | let toolboxResource = this._rdf.GetResource(BROWSER_DOCURL + "navigator-toolbox"); |
michael@0 | 1413 | let tabsOnTopResource = this._rdf.GetResource("tabsontop"); |
michael@0 | 1414 | let tabsOnTopAttribute = this._getPersist(toolboxResource, tabsOnTopResource); |
michael@0 | 1415 | if (tabsOnTopAttribute) |
michael@0 | 1416 | Services.prefs.setBoolPref("browser.tabs.onTop", tabsOnTopAttribute == "true"); |
michael@0 | 1417 | } |
michael@0 | 1418 | |
michael@0 | 1419 | // Migration at version 7 only occurred for users who wanted to try the new |
michael@0 | 1420 | // Downloads Panel feature before its release. Since migration at version |
michael@0 | 1421 | // 9 adds the button by default, this step has been removed. |
michael@0 | 1422 | |
michael@0 | 1423 | if (currentUIVersion < 8) { |
michael@0 | 1424 | // Reset homepage pref for users who have it set to google.com/firefox |
michael@0 | 1425 | let uri = Services.prefs.getComplexValue("browser.startup.homepage", |
michael@0 | 1426 | Ci.nsIPrefLocalizedString).data; |
michael@0 | 1427 | if (uri && /^https?:\/\/(www\.)?google(\.\w{2,3}){1,2}\/firefox\/?$/.test(uri)) { |
michael@0 | 1428 | Services.prefs.clearUserPref("browser.startup.homepage"); |
michael@0 | 1429 | } |
michael@0 | 1430 | } |
michael@0 | 1431 | |
michael@0 | 1432 | if (currentUIVersion < 9) { |
michael@0 | 1433 | // This code adds the customizable downloads buttons. |
michael@0 | 1434 | let currentsetResource = this._rdf.GetResource("currentset"); |
michael@0 | 1435 | let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); |
michael@0 | 1436 | let currentset = this._getPersist(toolbarResource, currentsetResource); |
michael@0 | 1437 | |
michael@0 | 1438 | // Since the Downloads button is located in the navigation bar by default, |
michael@0 | 1439 | // migration needs to happen only if the toolbar was customized using a |
michael@0 | 1440 | // previous UI version, and the button was not already placed on the |
michael@0 | 1441 | // toolbar manually. |
michael@0 | 1442 | if (currentset && |
michael@0 | 1443 | currentset.indexOf("downloads-button") == -1) { |
michael@0 | 1444 | // The element is added either after the search bar or before the home |
michael@0 | 1445 | // button. As a last resort, the element is added just before the |
michael@0 | 1446 | // non-customizable window controls. |
michael@0 | 1447 | if (currentset.indexOf("search-container") != -1) { |
michael@0 | 1448 | currentset = currentset.replace(/(^|,)search-container($|,)/, |
michael@0 | 1449 | "$1search-container,downloads-button$2") |
michael@0 | 1450 | } else if (currentset.indexOf("home-button") != -1) { |
michael@0 | 1451 | currentset = currentset.replace(/(^|,)home-button($|,)/, |
michael@0 | 1452 | "$1downloads-button,home-button$2") |
michael@0 | 1453 | } else { |
michael@0 | 1454 | currentset = currentset.replace(/(^|,)window-controls($|,)/, |
michael@0 | 1455 | "$1downloads-button,window-controls$2") |
michael@0 | 1456 | } |
michael@0 | 1457 | this._setPersist(toolbarResource, currentsetResource, currentset); |
michael@0 | 1458 | } |
michael@0 | 1459 | } |
michael@0 | 1460 | |
michael@0 | 1461 | #ifdef XP_WIN |
michael@0 | 1462 | if (currentUIVersion < 10) { |
michael@0 | 1463 | // For Windows systems with display set to > 96dpi (i.e. systemDefaultScale |
michael@0 | 1464 | // will return a value > 1.0), we want to discard any saved full-zoom settings, |
michael@0 | 1465 | // as we'll now be scaling the content according to the system resolution |
michael@0 | 1466 | // scale factor (Windows "logical DPI" setting) |
michael@0 | 1467 | let sm = Cc["@mozilla.org/gfx/screenmanager;1"].getService(Ci.nsIScreenManager); |
michael@0 | 1468 | if (sm.systemDefaultScale > 1.0) { |
michael@0 | 1469 | let cps2 = Cc["@mozilla.org/content-pref/service;1"]. |
michael@0 | 1470 | getService(Ci.nsIContentPrefService2); |
michael@0 | 1471 | cps2.removeByName("browser.content.full-zoom", null); |
michael@0 | 1472 | } |
michael@0 | 1473 | } |
michael@0 | 1474 | #endif |
michael@0 | 1475 | |
michael@0 | 1476 | if (currentUIVersion < 11) { |
michael@0 | 1477 | Services.prefs.clearUserPref("dom.disable_window_move_resize"); |
michael@0 | 1478 | Services.prefs.clearUserPref("dom.disable_window_flip"); |
michael@0 | 1479 | Services.prefs.clearUserPref("dom.event.contextmenu.enabled"); |
michael@0 | 1480 | Services.prefs.clearUserPref("javascript.enabled"); |
michael@0 | 1481 | Services.prefs.clearUserPref("permissions.default.image"); |
michael@0 | 1482 | } |
michael@0 | 1483 | |
michael@0 | 1484 | if (currentUIVersion < 12) { |
michael@0 | 1485 | // Remove bookmarks-menu-button-container, then place |
michael@0 | 1486 | // bookmarks-menu-button into its position. |
michael@0 | 1487 | let currentsetResource = this._rdf.GetResource("currentset"); |
michael@0 | 1488 | let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); |
michael@0 | 1489 | let currentset = this._getPersist(toolbarResource, currentsetResource); |
michael@0 | 1490 | // Need to migrate only if toolbar is customized. |
michael@0 | 1491 | if (currentset) { |
michael@0 | 1492 | if (currentset.contains("bookmarks-menu-button-container")) { |
michael@0 | 1493 | currentset = currentset.replace(/(^|,)bookmarks-menu-button-container($|,)/, |
michael@0 | 1494 | "$1bookmarks-menu-button$2"); |
michael@0 | 1495 | this._setPersist(toolbarResource, currentsetResource, currentset); |
michael@0 | 1496 | } |
michael@0 | 1497 | } |
michael@0 | 1498 | } |
michael@0 | 1499 | |
michael@0 | 1500 | if (currentUIVersion < 13) { |
michael@0 | 1501 | try { |
michael@0 | 1502 | if (Services.prefs.getBoolPref("plugins.hide_infobar_for_missing_plugin")) |
michael@0 | 1503 | Services.prefs.setBoolPref("plugins.notifyMissingFlash", false); |
michael@0 | 1504 | } |
michael@0 | 1505 | catch (ex) {} |
michael@0 | 1506 | } |
michael@0 | 1507 | |
michael@0 | 1508 | if (currentUIVersion < 14) { |
michael@0 | 1509 | // DOM Storage doesn't specially handle about: pages anymore. |
michael@0 | 1510 | let path = OS.Path.join(OS.Constants.Path.profileDir, |
michael@0 | 1511 | "chromeappsstore.sqlite"); |
michael@0 | 1512 | OS.File.remove(path); |
michael@0 | 1513 | } |
michael@0 | 1514 | |
michael@0 | 1515 | // Version 15 was obsoleted in favour of 18. |
michael@0 | 1516 | |
michael@0 | 1517 | if (currentUIVersion < 16) { |
michael@0 | 1518 | let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); |
michael@0 | 1519 | let collapsedResource = this._rdf.GetResource("collapsed"); |
michael@0 | 1520 | let isCollapsed = this._getPersist(toolbarResource, collapsedResource); |
michael@0 | 1521 | if (isCollapsed == "true") { |
michael@0 | 1522 | this._setPersist(toolbarResource, collapsedResource, "false"); |
michael@0 | 1523 | } |
michael@0 | 1524 | } |
michael@0 | 1525 | |
michael@0 | 1526 | // Insert the bookmarks-menu-button into the nav-bar if it isn't already |
michael@0 | 1527 | // there. |
michael@0 | 1528 | if (currentUIVersion < 17) { |
michael@0 | 1529 | let currentsetResource = this._rdf.GetResource("currentset"); |
michael@0 | 1530 | let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); |
michael@0 | 1531 | let currentset = this._getPersist(toolbarResource, currentsetResource); |
michael@0 | 1532 | // Need to migrate only if toolbar is customized. |
michael@0 | 1533 | if (currentset) { |
michael@0 | 1534 | if (!currentset.contains("bookmarks-menu-button")) { |
michael@0 | 1535 | // The button isn't in the nav-bar, so let's look for an appropriate |
michael@0 | 1536 | // place to put it. |
michael@0 | 1537 | if (currentset.contains("downloads-button")) { |
michael@0 | 1538 | currentset = currentset.replace(/(^|,)downloads-button($|,)/, |
michael@0 | 1539 | "$1bookmarks-menu-button,downloads-button$2"); |
michael@0 | 1540 | } else if (currentset.contains("home-button")) { |
michael@0 | 1541 | currentset = currentset.replace(/(^|,)home-button($|,)/, |
michael@0 | 1542 | "$1bookmarks-menu-button,home-button$2"); |
michael@0 | 1543 | } else { |
michael@0 | 1544 | // Just append. |
michael@0 | 1545 | currentset = currentset.replace(/(^|,)window-controls($|,)/, |
michael@0 | 1546 | "$1bookmarks-menu-button,window-controls$2") |
michael@0 | 1547 | } |
michael@0 | 1548 | this._setPersist(toolbarResource, currentsetResource, currentset); |
michael@0 | 1549 | } |
michael@0 | 1550 | } |
michael@0 | 1551 | } |
michael@0 | 1552 | |
michael@0 | 1553 | if (currentUIVersion < 18) { |
michael@0 | 1554 | // Remove iconsize and mode from all the toolbars |
michael@0 | 1555 | let toolbars = ["navigator-toolbox", "nav-bar", "PersonalToolbar", |
michael@0 | 1556 | "addon-bar", "TabsToolbar", "toolbar-menubar"]; |
michael@0 | 1557 | for (let resourceName of ["mode", "iconsize"]) { |
michael@0 | 1558 | let resource = this._rdf.GetResource(resourceName); |
michael@0 | 1559 | for (let toolbarId of toolbars) { |
michael@0 | 1560 | let toolbar = this._rdf.GetResource(BROWSER_DOCURL + toolbarId); |
michael@0 | 1561 | if (this._getPersist(toolbar, resource)) { |
michael@0 | 1562 | this._setPersist(toolbar, resource); |
michael@0 | 1563 | } |
michael@0 | 1564 | } |
michael@0 | 1565 | } |
michael@0 | 1566 | } |
michael@0 | 1567 | |
michael@0 | 1568 | if (currentUIVersion < 19) { |
michael@0 | 1569 | let detector = null; |
michael@0 | 1570 | try { |
michael@0 | 1571 | detector = Services.prefs.getComplexValue("intl.charset.detector", |
michael@0 | 1572 | Ci.nsIPrefLocalizedString).data; |
michael@0 | 1573 | } catch (ex) {} |
michael@0 | 1574 | if (!(detector == "" || |
michael@0 | 1575 | detector == "ja_parallel_state_machine" || |
michael@0 | 1576 | detector == "ruprob" || |
michael@0 | 1577 | detector == "ukprob")) { |
michael@0 | 1578 | // If the encoding detector pref value is not reachable from the UI, |
michael@0 | 1579 | // reset to default (varies by localization). |
michael@0 | 1580 | Services.prefs.clearUserPref("intl.charset.detector"); |
michael@0 | 1581 | } |
michael@0 | 1582 | } |
michael@0 | 1583 | |
michael@0 | 1584 | if (currentUIVersion < 20) { |
michael@0 | 1585 | // Remove persisted collapsed state from TabsToolbar. |
michael@0 | 1586 | let resource = this._rdf.GetResource("collapsed"); |
michael@0 | 1587 | let toolbar = this._rdf.GetResource(BROWSER_DOCURL + "TabsToolbar"); |
michael@0 | 1588 | if (this._getPersist(toolbar, resource)) { |
michael@0 | 1589 | this._setPersist(toolbar, resource); |
michael@0 | 1590 | } |
michael@0 | 1591 | } |
michael@0 | 1592 | |
michael@0 | 1593 | if (currentUIVersion < 21) { |
michael@0 | 1594 | // Make sure the 'toolbarbutton-1' class will always be present from here |
michael@0 | 1595 | // on out. |
michael@0 | 1596 | let button = this._rdf.GetResource(BROWSER_DOCURL + "bookmarks-menu-button"); |
michael@0 | 1597 | let classResource = this._rdf.GetResource("class"); |
michael@0 | 1598 | if (this._getPersist(button, classResource)) { |
michael@0 | 1599 | this._setPersist(button, classResource); |
michael@0 | 1600 | } |
michael@0 | 1601 | } |
michael@0 | 1602 | |
michael@0 | 1603 | if (currentUIVersion < 22) { |
michael@0 | 1604 | // Reset the Sync promobox count to promote the new FxAccount-based Sync. |
michael@0 | 1605 | Services.prefs.clearUserPref("browser.syncPromoViewsLeft"); |
michael@0 | 1606 | Services.prefs.clearUserPref("browser.syncPromoViewsLeftMap"); |
michael@0 | 1607 | } |
michael@0 | 1608 | |
michael@0 | 1609 | if (this._dirty) |
michael@0 | 1610 | this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush(); |
michael@0 | 1611 | |
michael@0 | 1612 | delete this._rdf; |
michael@0 | 1613 | delete this._dataSource; |
michael@0 | 1614 | |
michael@0 | 1615 | // Update the migration version. |
michael@0 | 1616 | Services.prefs.setIntPref("browser.migration.version", UI_VERSION); |
michael@0 | 1617 | }, |
michael@0 | 1618 | |
michael@0 | 1619 | _getPersist: function BG__getPersist(aSource, aProperty) { |
michael@0 | 1620 | var target = this._dataSource.GetTarget(aSource, aProperty, true); |
michael@0 | 1621 | if (target instanceof Ci.nsIRDFLiteral) |
michael@0 | 1622 | return target.Value; |
michael@0 | 1623 | return null; |
michael@0 | 1624 | }, |
michael@0 | 1625 | |
michael@0 | 1626 | _setPersist: function BG__setPersist(aSource, aProperty, aTarget) { |
michael@0 | 1627 | this._dirty = true; |
michael@0 | 1628 | try { |
michael@0 | 1629 | var oldTarget = this._dataSource.GetTarget(aSource, aProperty, true); |
michael@0 | 1630 | if (oldTarget) { |
michael@0 | 1631 | if (aTarget) |
michael@0 | 1632 | this._dataSource.Change(aSource, aProperty, oldTarget, this._rdf.GetLiteral(aTarget)); |
michael@0 | 1633 | else |
michael@0 | 1634 | this._dataSource.Unassert(aSource, aProperty, oldTarget); |
michael@0 | 1635 | } |
michael@0 | 1636 | else { |
michael@0 | 1637 | this._dataSource.Assert(aSource, aProperty, this._rdf.GetLiteral(aTarget), true); |
michael@0 | 1638 | } |
michael@0 | 1639 | |
michael@0 | 1640 | // Add the entry to the persisted set for this document if it's not there. |
michael@0 | 1641 | // This code is mostly borrowed from XULDocument::Persist. |
michael@0 | 1642 | let docURL = aSource.ValueUTF8.split("#")[0]; |
michael@0 | 1643 | let docResource = this._rdf.GetResource(docURL); |
michael@0 | 1644 | let persistResource = this._rdf.GetResource("http://home.netscape.com/NC-rdf#persist"); |
michael@0 | 1645 | if (!this._dataSource.HasAssertion(docResource, persistResource, aSource, true)) { |
michael@0 | 1646 | this._dataSource.Assert(docResource, persistResource, aSource, true); |
michael@0 | 1647 | } |
michael@0 | 1648 | } |
michael@0 | 1649 | catch(ex) {} |
michael@0 | 1650 | }, |
michael@0 | 1651 | |
michael@0 | 1652 | // ------------------------------ |
michael@0 | 1653 | // public nsIBrowserGlue members |
michael@0 | 1654 | // ------------------------------ |
michael@0 | 1655 | |
michael@0 | 1656 | sanitize: function BG_sanitize(aParentWindow) { |
michael@0 | 1657 | this._sanitizer.sanitize(aParentWindow); |
michael@0 | 1658 | }, |
michael@0 | 1659 | |
michael@0 | 1660 | ensurePlacesDefaultQueriesInitialized: |
michael@0 | 1661 | function BG_ensurePlacesDefaultQueriesInitialized() { |
michael@0 | 1662 | // This is actual version of the smart bookmarks, must be increased every |
michael@0 | 1663 | // time smart bookmarks change. |
michael@0 | 1664 | // When adding a new smart bookmark below, its newInVersion property must |
michael@0 | 1665 | // be set to the version it has been added in, we will compare its value |
michael@0 | 1666 | // to users' smartBookmarksVersion and add new smart bookmarks without |
michael@0 | 1667 | // recreating old deleted ones. |
michael@0 | 1668 | const SMART_BOOKMARKS_VERSION = 7; |
michael@0 | 1669 | const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark"; |
michael@0 | 1670 | const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion"; |
michael@0 | 1671 | |
michael@0 | 1672 | // TODO bug 399268: should this be a pref? |
michael@0 | 1673 | const MAX_RESULTS = 10; |
michael@0 | 1674 | |
michael@0 | 1675 | // Get current smart bookmarks version. If not set, create them. |
michael@0 | 1676 | let smartBookmarksCurrentVersion = 0; |
michael@0 | 1677 | try { |
michael@0 | 1678 | smartBookmarksCurrentVersion = Services.prefs.getIntPref(SMART_BOOKMARKS_PREF); |
michael@0 | 1679 | } catch(ex) {} |
michael@0 | 1680 | |
michael@0 | 1681 | // If version is current or smart bookmarks are disabled, just bail out. |
michael@0 | 1682 | if (smartBookmarksCurrentVersion == -1 || |
michael@0 | 1683 | smartBookmarksCurrentVersion >= SMART_BOOKMARKS_VERSION) { |
michael@0 | 1684 | return; |
michael@0 | 1685 | } |
michael@0 | 1686 | |
michael@0 | 1687 | let batch = { |
michael@0 | 1688 | runBatched: function BG_EPDQI_runBatched() { |
michael@0 | 1689 | let menuIndex = 0; |
michael@0 | 1690 | let toolbarIndex = 0; |
michael@0 | 1691 | let bundle = Services.strings.createBundle("chrome://browser/locale/places/places.properties"); |
michael@0 | 1692 | |
michael@0 | 1693 | let smartBookmarks = { |
michael@0 | 1694 | MostVisited: { |
michael@0 | 1695 | title: bundle.GetStringFromName("mostVisitedTitle"), |
michael@0 | 1696 | uri: NetUtil.newURI("place:sort=" + |
michael@0 | 1697 | Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING + |
michael@0 | 1698 | "&maxResults=" + MAX_RESULTS), |
michael@0 | 1699 | parent: PlacesUtils.toolbarFolderId, |
michael@0 | 1700 | get position() { return toolbarIndex++; }, |
michael@0 | 1701 | newInVersion: 1 |
michael@0 | 1702 | }, |
michael@0 | 1703 | RecentlyBookmarked: { |
michael@0 | 1704 | title: bundle.GetStringFromName("recentlyBookmarkedTitle"), |
michael@0 | 1705 | uri: NetUtil.newURI("place:folder=BOOKMARKS_MENU" + |
michael@0 | 1706 | "&folder=UNFILED_BOOKMARKS" + |
michael@0 | 1707 | "&folder=TOOLBAR" + |
michael@0 | 1708 | "&queryType=" + |
michael@0 | 1709 | Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS + |
michael@0 | 1710 | "&sort=" + |
michael@0 | 1711 | Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING + |
michael@0 | 1712 | "&maxResults=" + MAX_RESULTS + |
michael@0 | 1713 | "&excludeQueries=1"), |
michael@0 | 1714 | parent: PlacesUtils.bookmarksMenuFolderId, |
michael@0 | 1715 | get position() { return menuIndex++; }, |
michael@0 | 1716 | newInVersion: 1 |
michael@0 | 1717 | }, |
michael@0 | 1718 | RecentTags: { |
michael@0 | 1719 | title: bundle.GetStringFromName("recentTagsTitle"), |
michael@0 | 1720 | uri: NetUtil.newURI("place:"+ |
michael@0 | 1721 | "type=" + |
michael@0 | 1722 | Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY + |
michael@0 | 1723 | "&sort=" + |
michael@0 | 1724 | Ci.nsINavHistoryQueryOptions.SORT_BY_LASTMODIFIED_DESCENDING + |
michael@0 | 1725 | "&maxResults=" + MAX_RESULTS), |
michael@0 | 1726 | parent: PlacesUtils.bookmarksMenuFolderId, |
michael@0 | 1727 | get position() { return menuIndex++; }, |
michael@0 | 1728 | newInVersion: 1 |
michael@0 | 1729 | }, |
michael@0 | 1730 | }; |
michael@0 | 1731 | |
michael@0 | 1732 | if (Services.metro && Services.metro.supported) { |
michael@0 | 1733 | smartBookmarks.Windows8Touch = { |
michael@0 | 1734 | title: PlacesUtils.getString("windows8TouchTitle"), |
michael@0 | 1735 | get uri() { |
michael@0 | 1736 | let metroBookmarksRoot = PlacesUtils.annotations.getItemsWithAnnotation('metro/bookmarksRoot', {}); |
michael@0 | 1737 | if (metroBookmarksRoot.length > 0) { |
michael@0 | 1738 | return NetUtil.newURI("place:folder=" + |
michael@0 | 1739 | metroBookmarksRoot[0] + |
michael@0 | 1740 | "&queryType=" + |
michael@0 | 1741 | Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS + |
michael@0 | 1742 | "&sort=" + |
michael@0 | 1743 | Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING + |
michael@0 | 1744 | "&maxResults=" + MAX_RESULTS + |
michael@0 | 1745 | "&excludeQueries=1") |
michael@0 | 1746 | } |
michael@0 | 1747 | return null; |
michael@0 | 1748 | }, |
michael@0 | 1749 | parent: PlacesUtils.bookmarksMenuFolderId, |
michael@0 | 1750 | get position() { return menuIndex++; }, |
michael@0 | 1751 | newInVersion: 7 |
michael@0 | 1752 | }; |
michael@0 | 1753 | } |
michael@0 | 1754 | |
michael@0 | 1755 | // Set current itemId, parent and position if Smart Bookmark exists, |
michael@0 | 1756 | // we will use these informations to create the new version at the same |
michael@0 | 1757 | // position. |
michael@0 | 1758 | let smartBookmarkItemIds = PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO); |
michael@0 | 1759 | smartBookmarkItemIds.forEach(function (itemId) { |
michael@0 | 1760 | let queryId = PlacesUtils.annotations.getItemAnnotation(itemId, SMART_BOOKMARKS_ANNO); |
michael@0 | 1761 | if (queryId in smartBookmarks) { |
michael@0 | 1762 | let smartBookmark = smartBookmarks[queryId]; |
michael@0 | 1763 | if (!smartBookmark.uri) { |
michael@0 | 1764 | PlacesUtils.bookmarks.removeItem(itemId); |
michael@0 | 1765 | return; |
michael@0 | 1766 | } |
michael@0 | 1767 | smartBookmark.itemId = itemId; |
michael@0 | 1768 | smartBookmark.parent = PlacesUtils.bookmarks.getFolderIdForItem(itemId); |
michael@0 | 1769 | smartBookmark.updatedPosition = PlacesUtils.bookmarks.getItemIndex(itemId); |
michael@0 | 1770 | } |
michael@0 | 1771 | else { |
michael@0 | 1772 | // We don't remove old Smart Bookmarks because user could still |
michael@0 | 1773 | // find them useful, or could have personalized them. |
michael@0 | 1774 | // Instead we remove the Smart Bookmark annotation. |
michael@0 | 1775 | PlacesUtils.annotations.removeItemAnnotation(itemId, SMART_BOOKMARKS_ANNO); |
michael@0 | 1776 | } |
michael@0 | 1777 | }); |
michael@0 | 1778 | |
michael@0 | 1779 | for (let queryId in smartBookmarks) { |
michael@0 | 1780 | let smartBookmark = smartBookmarks[queryId]; |
michael@0 | 1781 | |
michael@0 | 1782 | // We update or create only changed or new smart bookmarks. |
michael@0 | 1783 | // Also we respect user choices, so we won't try to create a smart |
michael@0 | 1784 | // bookmark if it has been removed. |
michael@0 | 1785 | if (smartBookmarksCurrentVersion > 0 && |
michael@0 | 1786 | smartBookmark.newInVersion <= smartBookmarksCurrentVersion && |
michael@0 | 1787 | !smartBookmark.itemId || !smartBookmark.uri) |
michael@0 | 1788 | continue; |
michael@0 | 1789 | |
michael@0 | 1790 | // Remove old version of the smart bookmark if it exists, since it |
michael@0 | 1791 | // will be replaced in place. |
michael@0 | 1792 | if (smartBookmark.itemId) { |
michael@0 | 1793 | PlacesUtils.bookmarks.removeItem(smartBookmark.itemId); |
michael@0 | 1794 | } |
michael@0 | 1795 | |
michael@0 | 1796 | // Create the new smart bookmark and store its updated itemId. |
michael@0 | 1797 | smartBookmark.itemId = |
michael@0 | 1798 | PlacesUtils.bookmarks.insertBookmark(smartBookmark.parent, |
michael@0 | 1799 | smartBookmark.uri, |
michael@0 | 1800 | smartBookmark.updatedPosition || smartBookmark.position, |
michael@0 | 1801 | smartBookmark.title); |
michael@0 | 1802 | PlacesUtils.annotations.setItemAnnotation(smartBookmark.itemId, |
michael@0 | 1803 | SMART_BOOKMARKS_ANNO, |
michael@0 | 1804 | queryId, 0, |
michael@0 | 1805 | PlacesUtils.annotations.EXPIRE_NEVER); |
michael@0 | 1806 | } |
michael@0 | 1807 | |
michael@0 | 1808 | // If we are creating all Smart Bookmarks from ground up, add a |
michael@0 | 1809 | // separator below them in the bookmarks menu. |
michael@0 | 1810 | if (smartBookmarksCurrentVersion == 0 && |
michael@0 | 1811 | smartBookmarkItemIds.length == 0) { |
michael@0 | 1812 | let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, |
michael@0 | 1813 | menuIndex); |
michael@0 | 1814 | // Don't add a separator if the menu was empty or there is one already. |
michael@0 | 1815 | if (id != -1 && |
michael@0 | 1816 | PlacesUtils.bookmarks.getItemType(id) != PlacesUtils.bookmarks.TYPE_SEPARATOR) { |
michael@0 | 1817 | PlacesUtils.bookmarks.insertSeparator(PlacesUtils.bookmarksMenuFolderId, |
michael@0 | 1818 | menuIndex); |
michael@0 | 1819 | } |
michael@0 | 1820 | } |
michael@0 | 1821 | } |
michael@0 | 1822 | }; |
michael@0 | 1823 | |
michael@0 | 1824 | try { |
michael@0 | 1825 | PlacesUtils.bookmarks.runInBatchMode(batch, null); |
michael@0 | 1826 | } |
michael@0 | 1827 | catch(ex) { |
michael@0 | 1828 | Components.utils.reportError(ex); |
michael@0 | 1829 | } |
michael@0 | 1830 | finally { |
michael@0 | 1831 | Services.prefs.setIntPref(SMART_BOOKMARKS_PREF, SMART_BOOKMARKS_VERSION); |
michael@0 | 1832 | Services.prefs.savePrefFile(null); |
michael@0 | 1833 | } |
michael@0 | 1834 | }, |
michael@0 | 1835 | |
michael@0 | 1836 | // this returns the most recent non-popup browser window |
michael@0 | 1837 | getMostRecentBrowserWindow: function BG_getMostRecentBrowserWindow() { |
michael@0 | 1838 | return RecentWindow.getMostRecentBrowserWindow(); |
michael@0 | 1839 | }, |
michael@0 | 1840 | |
michael@0 | 1841 | #ifdef MOZ_SERVICES_SYNC |
michael@0 | 1842 | /** |
michael@0 | 1843 | * Called as an observer when Sync's "display URI" notification is fired. |
michael@0 | 1844 | * |
michael@0 | 1845 | * We open the received URI in a background tab. |
michael@0 | 1846 | * |
michael@0 | 1847 | * Eventually, this will likely be replaced by a more robust tab syncing |
michael@0 | 1848 | * feature. This functionality is considered somewhat evil by UX because it |
michael@0 | 1849 | * opens a new tab automatically without any prompting. However, it is a |
michael@0 | 1850 | * lesser evil than sending a tab to a specific device (from e.g. Fennec) |
michael@0 | 1851 | * and having nothing happen on the receiving end. |
michael@0 | 1852 | */ |
michael@0 | 1853 | _onDisplaySyncURI: function _onDisplaySyncURI(data) { |
michael@0 | 1854 | try { |
michael@0 | 1855 | let tabbrowser = RecentWindow.getMostRecentBrowserWindow({private: false}).gBrowser; |
michael@0 | 1856 | |
michael@0 | 1857 | // The payload is wrapped weirdly because of how Sync does notifications. |
michael@0 | 1858 | tabbrowser.addTab(data.wrappedJSObject.object.uri); |
michael@0 | 1859 | } catch (ex) { |
michael@0 | 1860 | Cu.reportError("Error displaying tab received by Sync: " + ex); |
michael@0 | 1861 | } |
michael@0 | 1862 | }, |
michael@0 | 1863 | #endif |
michael@0 | 1864 | |
michael@0 | 1865 | // for XPCOM |
michael@0 | 1866 | classID: Components.ID("{eab9012e-5f74-4cbc-b2b5-a590235513cc}"), |
michael@0 | 1867 | |
michael@0 | 1868 | QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, |
michael@0 | 1869 | Ci.nsISupportsWeakReference, |
michael@0 | 1870 | Ci.nsIBrowserGlue]), |
michael@0 | 1871 | |
michael@0 | 1872 | // redefine the default factory for XPCOMUtils |
michael@0 | 1873 | _xpcom_factory: BrowserGlueServiceFactory, |
michael@0 | 1874 | } |
michael@0 | 1875 | |
michael@0 | 1876 | function ContentPermissionPrompt() {} |
michael@0 | 1877 | |
michael@0 | 1878 | ContentPermissionPrompt.prototype = { |
michael@0 | 1879 | classID: Components.ID("{d8903bf6-68d5-4e97-bcd1-e4d3012f721a}"), |
michael@0 | 1880 | |
michael@0 | 1881 | QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]), |
michael@0 | 1882 | |
michael@0 | 1883 | _getBrowserForRequest: function (aRequest) { |
michael@0 | 1884 | // "element" is only defined in e10s mode. |
michael@0 | 1885 | let browser = aRequest.element; |
michael@0 | 1886 | if (!browser) { |
michael@0 | 1887 | // Find the requesting browser. |
michael@0 | 1888 | browser = aRequest.window.QueryInterface(Ci.nsIInterfaceRequestor) |
michael@0 | 1889 | .getInterface(Ci.nsIWebNavigation) |
michael@0 | 1890 | .QueryInterface(Ci.nsIDocShell) |
michael@0 | 1891 | .chromeEventHandler; |
michael@0 | 1892 | } |
michael@0 | 1893 | return browser; |
michael@0 | 1894 | }, |
michael@0 | 1895 | |
michael@0 | 1896 | /** |
michael@0 | 1897 | * Show a permission prompt. |
michael@0 | 1898 | * |
michael@0 | 1899 | * @param aRequest The permission request. |
michael@0 | 1900 | * @param aMessage The message to display on the prompt. |
michael@0 | 1901 | * @param aPermission The type of permission to prompt. |
michael@0 | 1902 | * @param aActions An array of actions of the form: |
michael@0 | 1903 | * [main action, secondary actions, ...] |
michael@0 | 1904 | * Actions are of the form { stringId, action, expireType, callback } |
michael@0 | 1905 | * Permission is granted if action is null or ALLOW_ACTION. |
michael@0 | 1906 | * @param aNotificationId The id of the PopupNotification. |
michael@0 | 1907 | * @param aAnchorId The id for the PopupNotification anchor. |
michael@0 | 1908 | * @param aOptions Options for the PopupNotification |
michael@0 | 1909 | */ |
michael@0 | 1910 | _showPrompt: function CPP_showPrompt(aRequest, aMessage, aPermission, aActions, |
michael@0 | 1911 | aNotificationId, aAnchorId, aOptions) { |
michael@0 | 1912 | function onFullScreen() { |
michael@0 | 1913 | popup.remove(); |
michael@0 | 1914 | } |
michael@0 | 1915 | |
michael@0 | 1916 | var browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); |
michael@0 | 1917 | |
michael@0 | 1918 | var browser = this._getBrowserForRequest(aRequest); |
michael@0 | 1919 | var chromeWin = browser.ownerDocument.defaultView; |
michael@0 | 1920 | var requestPrincipal = aRequest.principal; |
michael@0 | 1921 | |
michael@0 | 1922 | // Transform the prompt actions into PopupNotification actions. |
michael@0 | 1923 | var popupNotificationActions = []; |
michael@0 | 1924 | for (var i = 0; i < aActions.length; i++) { |
michael@0 | 1925 | let promptAction = aActions[i]; |
michael@0 | 1926 | |
michael@0 | 1927 | // Don't offer action in PB mode if the action remembers permission for more than a session. |
michael@0 | 1928 | if (PrivateBrowsingUtils.isWindowPrivate(chromeWin) && |
michael@0 | 1929 | promptAction.expireType != Ci.nsIPermissionManager.EXPIRE_SESSION && |
michael@0 | 1930 | promptAction.action) { |
michael@0 | 1931 | continue; |
michael@0 | 1932 | } |
michael@0 | 1933 | |
michael@0 | 1934 | var action = { |
michael@0 | 1935 | label: browserBundle.GetStringFromName(promptAction.stringId), |
michael@0 | 1936 | accessKey: browserBundle.GetStringFromName(promptAction.stringId + ".accesskey"), |
michael@0 | 1937 | callback: function() { |
michael@0 | 1938 | if (promptAction.callback) { |
michael@0 | 1939 | promptAction.callback(); |
michael@0 | 1940 | } |
michael@0 | 1941 | |
michael@0 | 1942 | // Remember permissions. |
michael@0 | 1943 | if (promptAction.action) { |
michael@0 | 1944 | Services.perms.addFromPrincipal(requestPrincipal, aPermission, |
michael@0 | 1945 | promptAction.action, promptAction.expireType); |
michael@0 | 1946 | } |
michael@0 | 1947 | |
michael@0 | 1948 | // Grant permission if action is null or ALLOW_ACTION. |
michael@0 | 1949 | if (!promptAction.action || promptAction.action == Ci.nsIPermissionManager.ALLOW_ACTION) { |
michael@0 | 1950 | aRequest.allow(); |
michael@0 | 1951 | } else { |
michael@0 | 1952 | aRequest.cancel(); |
michael@0 | 1953 | } |
michael@0 | 1954 | }, |
michael@0 | 1955 | }; |
michael@0 | 1956 | |
michael@0 | 1957 | popupNotificationActions.push(action); |
michael@0 | 1958 | } |
michael@0 | 1959 | |
michael@0 | 1960 | var mainAction = popupNotificationActions.length ? |
michael@0 | 1961 | popupNotificationActions[0] : null; |
michael@0 | 1962 | var secondaryActions = popupNotificationActions.splice(1); |
michael@0 | 1963 | |
michael@0 | 1964 | // Only allow exactly one permission rquest here. |
michael@0 | 1965 | let types = aRequest.types.QueryInterface(Ci.nsIArray); |
michael@0 | 1966 | if (types.length != 1) { |
michael@0 | 1967 | aRequest.cancel(); |
michael@0 | 1968 | return; |
michael@0 | 1969 | } |
michael@0 | 1970 | |
michael@0 | 1971 | let perm = types.queryElementAt(0, Ci.nsIContentPermissionType); |
michael@0 | 1972 | |
michael@0 | 1973 | if (perm.type == "pointerLock") { |
michael@0 | 1974 | // If there's no mainAction, this is the autoAllow warning prompt. |
michael@0 | 1975 | let autoAllow = !mainAction; |
michael@0 | 1976 | |
michael@0 | 1977 | if (!aOptions) |
michael@0 | 1978 | aOptions = {}; |
michael@0 | 1979 | |
michael@0 | 1980 | aOptions.removeOnDismissal = autoAllow; |
michael@0 | 1981 | aOptions.eventCallback = type => { |
michael@0 | 1982 | if (type == "removed") { |
michael@0 | 1983 | browser.removeEventListener("mozfullscreenchange", onFullScreen, true); |
michael@0 | 1984 | if (autoAllow) { |
michael@0 | 1985 | aRequest.allow(); |
michael@0 | 1986 | } |
michael@0 | 1987 | } |
michael@0 | 1988 | } |
michael@0 | 1989 | |
michael@0 | 1990 | } |
michael@0 | 1991 | |
michael@0 | 1992 | var popup = chromeWin.PopupNotifications.show(browser, aNotificationId, aMessage, aAnchorId, |
michael@0 | 1993 | mainAction, secondaryActions, aOptions); |
michael@0 | 1994 | if (perm.type == "pointerLock") { |
michael@0 | 1995 | // pointerLock is automatically allowed in fullscreen mode (and revoked |
michael@0 | 1996 | // upon exit), so if the page enters fullscreen mode after requesting |
michael@0 | 1997 | // pointerLock (but before the user has granted permission), we should |
michael@0 | 1998 | // remove the now-impotent notification. |
michael@0 | 1999 | browser.addEventListener("mozfullscreenchange", onFullScreen, true); |
michael@0 | 2000 | } |
michael@0 | 2001 | }, |
michael@0 | 2002 | |
michael@0 | 2003 | _promptGeo : function(aRequest) { |
michael@0 | 2004 | var secHistogram = Services.telemetry.getHistogramById("SECURITY_UI"); |
michael@0 | 2005 | var browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); |
michael@0 | 2006 | var requestingURI = aRequest.principal.URI; |
michael@0 | 2007 | |
michael@0 | 2008 | var message; |
michael@0 | 2009 | |
michael@0 | 2010 | // Share location action. |
michael@0 | 2011 | var actions = [{ |
michael@0 | 2012 | stringId: "geolocation.shareLocation", |
michael@0 | 2013 | action: null, |
michael@0 | 2014 | expireType: null, |
michael@0 | 2015 | callback: function() { |
michael@0 | 2016 | secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_SHARE_LOCATION); |
michael@0 | 2017 | }, |
michael@0 | 2018 | }]; |
michael@0 | 2019 | |
michael@0 | 2020 | if (requestingURI.schemeIs("file")) { |
michael@0 | 2021 | message = browserBundle.formatStringFromName("geolocation.shareWithFile", |
michael@0 | 2022 | [requestingURI.path], 1); |
michael@0 | 2023 | } else { |
michael@0 | 2024 | message = browserBundle.formatStringFromName("geolocation.shareWithSite", |
michael@0 | 2025 | [requestingURI.host], 1); |
michael@0 | 2026 | // Always share location action. |
michael@0 | 2027 | actions.push({ |
michael@0 | 2028 | stringId: "geolocation.alwaysShareLocation", |
michael@0 | 2029 | action: Ci.nsIPermissionManager.ALLOW_ACTION, |
michael@0 | 2030 | expireType: null, |
michael@0 | 2031 | callback: function() { |
michael@0 | 2032 | secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_ALWAYS_SHARE); |
michael@0 | 2033 | }, |
michael@0 | 2034 | }); |
michael@0 | 2035 | |
michael@0 | 2036 | // Never share location action. |
michael@0 | 2037 | actions.push({ |
michael@0 | 2038 | stringId: "geolocation.neverShareLocation", |
michael@0 | 2039 | action: Ci.nsIPermissionManager.DENY_ACTION, |
michael@0 | 2040 | expireType: null, |
michael@0 | 2041 | callback: function() { |
michael@0 | 2042 | secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_NEVER_SHARE); |
michael@0 | 2043 | }, |
michael@0 | 2044 | }); |
michael@0 | 2045 | } |
michael@0 | 2046 | |
michael@0 | 2047 | var options = { |
michael@0 | 2048 | learnMoreURL: Services.urlFormatter.formatURLPref("browser.geolocation.warning.infoURL"), |
michael@0 | 2049 | }; |
michael@0 | 2050 | |
michael@0 | 2051 | secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST); |
michael@0 | 2052 | |
michael@0 | 2053 | this._showPrompt(aRequest, message, "geo", actions, "geolocation", |
michael@0 | 2054 | "geo-notification-icon", options); |
michael@0 | 2055 | }, |
michael@0 | 2056 | |
michael@0 | 2057 | _promptWebNotifications : function(aRequest) { |
michael@0 | 2058 | var browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); |
michael@0 | 2059 | var requestingURI = aRequest.principal.URI; |
michael@0 | 2060 | |
michael@0 | 2061 | var message = browserBundle.formatStringFromName("webNotifications.showFromSite", |
michael@0 | 2062 | [requestingURI.host], 1); |
michael@0 | 2063 | |
michael@0 | 2064 | var actions = [ |
michael@0 | 2065 | { |
michael@0 | 2066 | stringId: "webNotifications.showForSession", |
michael@0 | 2067 | action: Ci.nsIPermissionManager.ALLOW_ACTION, |
michael@0 | 2068 | expireType: Ci.nsIPermissionManager.EXPIRE_SESSION, |
michael@0 | 2069 | callback: function() {}, |
michael@0 | 2070 | }, |
michael@0 | 2071 | { |
michael@0 | 2072 | stringId: "webNotifications.alwaysShow", |
michael@0 | 2073 | action: Ci.nsIPermissionManager.ALLOW_ACTION, |
michael@0 | 2074 | expireType: null, |
michael@0 | 2075 | callback: function() {}, |
michael@0 | 2076 | }, |
michael@0 | 2077 | { |
michael@0 | 2078 | stringId: "webNotifications.neverShow", |
michael@0 | 2079 | action: Ci.nsIPermissionManager.DENY_ACTION, |
michael@0 | 2080 | expireType: null, |
michael@0 | 2081 | callback: function() {}, |
michael@0 | 2082 | }, |
michael@0 | 2083 | ]; |
michael@0 | 2084 | |
michael@0 | 2085 | this._showPrompt(aRequest, message, "desktop-notification", actions, |
michael@0 | 2086 | "web-notifications", |
michael@0 | 2087 | "web-notifications-notification-icon", null); |
michael@0 | 2088 | }, |
michael@0 | 2089 | |
michael@0 | 2090 | _promptPointerLock: function CPP_promtPointerLock(aRequest, autoAllow) { |
michael@0 | 2091 | |
michael@0 | 2092 | let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); |
michael@0 | 2093 | let requestingURI = aRequest.principal.URI; |
michael@0 | 2094 | |
michael@0 | 2095 | let originString = requestingURI.schemeIs("file") ? requestingURI.path : requestingURI.host; |
michael@0 | 2096 | let message = browserBundle.formatStringFromName(autoAllow ? |
michael@0 | 2097 | "pointerLock.autoLock.title2" : "pointerLock.title2", |
michael@0 | 2098 | [originString], 1); |
michael@0 | 2099 | // If this is an autoAllow info prompt, offer no actions. |
michael@0 | 2100 | // _showPrompt() will allow the request when it's dismissed. |
michael@0 | 2101 | let actions = []; |
michael@0 | 2102 | if (!autoAllow) { |
michael@0 | 2103 | actions = [ |
michael@0 | 2104 | { |
michael@0 | 2105 | stringId: "pointerLock.allow2", |
michael@0 | 2106 | action: null, |
michael@0 | 2107 | expireType: null, |
michael@0 | 2108 | callback: function() {}, |
michael@0 | 2109 | }, |
michael@0 | 2110 | { |
michael@0 | 2111 | stringId: "pointerLock.alwaysAllow", |
michael@0 | 2112 | action: Ci.nsIPermissionManager.ALLOW_ACTION, |
michael@0 | 2113 | expireType: null, |
michael@0 | 2114 | callback: function() {}, |
michael@0 | 2115 | }, |
michael@0 | 2116 | { |
michael@0 | 2117 | stringId: "pointerLock.neverAllow", |
michael@0 | 2118 | action: Ci.nsIPermissionManager.DENY_ACTION, |
michael@0 | 2119 | expireType: null, |
michael@0 | 2120 | callback: function() {}, |
michael@0 | 2121 | }, |
michael@0 | 2122 | ]; |
michael@0 | 2123 | } |
michael@0 | 2124 | |
michael@0 | 2125 | this._showPrompt(aRequest, message, "pointerLock", actions, "pointerLock", |
michael@0 | 2126 | "pointerLock-notification-icon", null); |
michael@0 | 2127 | }, |
michael@0 | 2128 | |
michael@0 | 2129 | prompt: function CPP_prompt(request) { |
michael@0 | 2130 | |
michael@0 | 2131 | // Only allow exactly one permission rquest here. |
michael@0 | 2132 | let types = request.types.QueryInterface(Ci.nsIArray); |
michael@0 | 2133 | if (types.length != 1) { |
michael@0 | 2134 | request.cancel(); |
michael@0 | 2135 | return; |
michael@0 | 2136 | } |
michael@0 | 2137 | let perm = types.queryElementAt(0, Ci.nsIContentPermissionType); |
michael@0 | 2138 | |
michael@0 | 2139 | const kFeatureKeys = { "geolocation" : "geo", |
michael@0 | 2140 | "desktop-notification" : "desktop-notification", |
michael@0 | 2141 | "pointerLock" : "pointerLock", |
michael@0 | 2142 | }; |
michael@0 | 2143 | |
michael@0 | 2144 | // Make sure that we support the request. |
michael@0 | 2145 | if (!(perm.type in kFeatureKeys)) { |
michael@0 | 2146 | return; |
michael@0 | 2147 | } |
michael@0 | 2148 | |
michael@0 | 2149 | var requestingPrincipal = request.principal; |
michael@0 | 2150 | var requestingURI = requestingPrincipal.URI; |
michael@0 | 2151 | |
michael@0 | 2152 | // Ignore requests from non-nsIStandardURLs |
michael@0 | 2153 | if (!(requestingURI instanceof Ci.nsIStandardURL)) |
michael@0 | 2154 | return; |
michael@0 | 2155 | |
michael@0 | 2156 | var autoAllow = false; |
michael@0 | 2157 | var permissionKey = kFeatureKeys[perm.type]; |
michael@0 | 2158 | var result = Services.perms.testExactPermissionFromPrincipal(requestingPrincipal, permissionKey); |
michael@0 | 2159 | |
michael@0 | 2160 | if (result == Ci.nsIPermissionManager.DENY_ACTION) { |
michael@0 | 2161 | request.cancel(); |
michael@0 | 2162 | return; |
michael@0 | 2163 | } |
michael@0 | 2164 | |
michael@0 | 2165 | if (result == Ci.nsIPermissionManager.ALLOW_ACTION) { |
michael@0 | 2166 | autoAllow = true; |
michael@0 | 2167 | // For pointerLock, we still want to show a warning prompt. |
michael@0 | 2168 | if (perm.type != "pointerLock") { |
michael@0 | 2169 | request.allow(); |
michael@0 | 2170 | return; |
michael@0 | 2171 | } |
michael@0 | 2172 | } |
michael@0 | 2173 | |
michael@0 | 2174 | var browser = this._getBrowserForRequest(request); |
michael@0 | 2175 | var chromeWin = browser.ownerDocument.defaultView; |
michael@0 | 2176 | if (!chromeWin.PopupNotifications) |
michael@0 | 2177 | // Ignore requests from browsers hosted in windows that don't support |
michael@0 | 2178 | // PopupNotifications. |
michael@0 | 2179 | return; |
michael@0 | 2180 | |
michael@0 | 2181 | // Show the prompt. |
michael@0 | 2182 | switch (perm.type) { |
michael@0 | 2183 | case "geolocation": |
michael@0 | 2184 | this._promptGeo(request); |
michael@0 | 2185 | break; |
michael@0 | 2186 | case "desktop-notification": |
michael@0 | 2187 | this._promptWebNotifications(request); |
michael@0 | 2188 | break; |
michael@0 | 2189 | case "pointerLock": |
michael@0 | 2190 | this._promptPointerLock(request, autoAllow); |
michael@0 | 2191 | break; |
michael@0 | 2192 | } |
michael@0 | 2193 | }, |
michael@0 | 2194 | |
michael@0 | 2195 | }; |
michael@0 | 2196 | |
michael@0 | 2197 | var components = [BrowserGlue, ContentPermissionPrompt]; |
michael@0 | 2198 | this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components); |