diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 511178b5e7de..fc58acb41e35 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -8,6 +8,7 @@ var Cu = Components.utils; var Cc = Components.classes; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/ContextualIdentityService.jsm"); Cu.import("resource://gre/modules/NotificationDB.jsm"); Cu.import("resource:///modules/RecentWindow.jsm"); @@ -56,8 +57,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils", "@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils"); XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager", "resource://gre/modules/LightweightThemeManager.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService", - "resource://gre/modules/ContextualIdentityService.jsm"); XPCOMUtils.defineLazyServiceGetter(this, "gAboutNewTabService", "@mozilla.org/browser/aboutnewtab-service;1", "nsIAboutNewTabService"); diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index 124847b00beb..263b13737dcb 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -4,6 +4,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +Components.utils.import("resource://gre/modules/ContextualIdentityService.jsm"); Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm"); Components.utils.import("resource://gre/modules/LoginManagerContextMenu.jsm"); @@ -11,8 +12,6 @@ Components.utils.import("resource://gre/modules/BrowserUtils.jsm"); Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService", - "resource://gre/modules/ContextualIdentityService.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper", "resource://gre/modules/LoginHelper.jsm"); diff --git a/browser/base/content/utilityOverlay.js b/browser/base/content/utilityOverlay.js index dbddd6de62a3..458a3563ed3b 100644 --- a/browser/base/content/utilityOverlay.js +++ b/browser/base/content/utilityOverlay.js @@ -5,6 +5,7 @@ // Services = object with smart getters for common XPCOM services Components.utils.import("resource://gre/modules/AppConstants.jsm"); +Components.utils.import("resource://gre/modules/ContextualIdentityService.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); @@ -13,9 +14,6 @@ Components.utils.import("resource:///modules/RecentWindow.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ShellService", "resource:///modules/ShellService.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService", - "resource://gre/modules/ContextualIdentityService.jsm"); - XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService", "@mozilla.org/browser/aboutnewtab-service;1", "nsIAboutNewTabService"); diff --git a/toolkit/components/contextualidentity/ContextualIdentityService.jsm b/toolkit/components/contextualidentity/ContextualIdentityService.jsm index 31a4aabea8c5..59b8c5acd414 100644 --- a/toolkit/components/contextualidentity/ContextualIdentityService.jsm +++ b/toolkit/components/contextualidentity/ContextualIdentityService.jsm @@ -7,16 +7,34 @@ this.EXPORTED_SYMBOLS = ["ContextualIdentityService"]; const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm") +Cu.import("resource://gre/modules/Services.jsm"); -const DEFAULT_TAB_COLOR = "#909090" +const DEFAULT_TAB_COLOR = "#909090"; +const SAVE_DELAY_MS = 1500; XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() { return Services.strings.createBundle("chrome://browser/locale/browser.properties"); }); +XPCOMUtils.defineLazyGetter(this, "gTextDecoder", function () { + return new TextDecoder(); +}); + +XPCOMUtils.defineLazyGetter(this, "gTextEncoder", function () { + return new TextEncoder(); +}); + +XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown", + "resource://gre/modules/AsyncShutdown.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "OS", + "resource://gre/modules/osfile.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask", + "resource://gre/modules/DeferredTask.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", + "resource://gre/modules/FileUtils.jsm"); + this.ContextualIdentityService = { - _identities: [ + _defaultIdentities: [ { userContextId: 1, public: true, icon: "chrome://browser/skin/usercontext/personal.svg", @@ -62,15 +80,107 @@ this.ContextualIdentityService = { alreadyOpened: false }, ], + _identities: null, + + _path: null, + _dataReady: false, + + _saver: null, + + init() { + this._path = OS.Path.join(OS.Constants.Path.profileDir, "containers.json"); + + this._saver = new DeferredTask(() => this.save(), SAVE_DELAY_MS); + AsyncShutdown.profileBeforeChange.addBlocker("ContextualIdentityService: writing data", + () => this._saver.finalize()); + + this.load(); + }, + + load() { + OS.File.read(this._path).then(bytes => { + // If synchronous loading happened in the meantime, exit now. + if (this._dataReady) { + return; + } + + try { + this._identities = JSON.parse(gTextDecoder.decode(bytes)); + this._dataReady = true; + } catch(error) { + this.loadError(error); + } + }, (error) => { + this.loadError(error); + }); + }, + + loadError(error) { + if (!(error instanceof OS.File.Error && error.becauseNoSuchFile) && + !(error instanceof Components.Exception && + error.result == Cr.NS_ERROR_FILE_NOT_FOUND)) { + // Let's report the error. + Cu.reportError(error); + } + + // If synchronous loading happened in the meantime, exit now. + if (this._dataReady) { + return; + } + + this._identities = this._defaultIdentities; + this._dataReady = true; + + this.saveSoon(); + }, + + saveSoon() { + this._saver.arm(); + }, + + save() { + let bytes = gTextEncoder.encode(JSON.stringify(this._identities)); + return OS.File.writeAtomic(this._path, bytes, + { tmpPath: this._path + ".tmp" }); + }, + + ensureDataReady() { + if (this._dataReady) { + return; + } + + try { + // This reads the file and automatically detects the UTF-8 encoding. + let inputStream = Cc["@mozilla.org/network/file-input-stream;1"] + .createInstance(Ci.nsIFileInputStream); + inputStream.init(new FileUtils.File(this._path), + FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0); + try { + let json = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON); + this._identities = json.decodeFromStream(inputStream, + inputStream.available()); + this._dataReady = true; + } finally { + inputStream.close(); + } + } catch (error) { + this.loadError(error); + return; + } + }, + getIdentities() { + this.ensureDataReady(); return this._identities.filter(info => info.public); }, getPrivateIdentity(label) { + this.ensureDataReady(); return this._identities.find(info => !info.public && info.label == label); }, getIdentityFromId(userContextId) { + this.ensureDataReady(); return this._identities.find(info => info.userContextId == userContextId); }, @@ -122,3 +232,5 @@ this.ContextualIdentityService = { } }, } + +ContextualIdentityService.init();