nsBrowserGlue.js 84 KB

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