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