[Pkg-mozext-commits] [personasplus] 33/42: Changes from 1.7.2
David Prévot
taffit at moszumanska.debian.org
Wed Feb 3 16:15:41 UTC 2016
This is an automated email from the git hooks/post-receive script.
taffit pushed a commit to branch master
in repository personasplus.
commit 62b6669b01a5c775f64cd09564500a6f8260f620
Author: Andreas Wagner <mail at andreaswagner.org>
Date: Wed Jan 27 19:50:00 2016 +0100
Changes from 1.7.2
---
extension/content/personas.js | 2080 ++++++++---------
extension/locale/bg-BG/personas.properties | 5 +-
extension/locale/cs-CZ/personas.properties | 1 +
extension/locale/da/personas.properties | 3 +-
extension/locale/de/personas.properties | 1 +
extension/locale/el/personas.properties | 3 +-
extension/locale/en-US/personas.properties | 1 +
extension/locale/es-AR/personas.properties | 1 +
extension/locale/es-CL/personas.properties | 1 +
extension/locale/es-ES/personas.properties | 1 +
extension/locale/es-MX/personas.properties | 1 +
extension/locale/eu/personas.properties | 1 +
extension/locale/fi/personas.properties | 1 +
extension/locale/fr/personas.properties | 1 +
extension/locale/fy-NL/personas.properties | 1 +
extension/locale/ga-IE/personas.properties | 1 +
extension/locale/gl-ES/personas.properties | 1 +
extension/locale/he-IL/personas.properties | 1 +
extension/locale/hu-HU/personas.properties | 1 +
extension/locale/it/personas.properties | 1 +
extension/locale/ja-JP-mac/personas.properties | 1 +
extension/locale/ja-JP/personas.properties | 1 +
extension/locale/ja/personas.properties | 1 +
extension/locale/ko-KR/personas.properties | 1 +
extension/locale/lt-LT/personas.properties | 1 +
extension/locale/lt/personas.properties | 1 +
extension/locale/mk-MK/personas.properties | 1 +
extension/locale/nl/personas.properties | 1 +
extension/locale/pl-PL/personas.properties | 3 +-
extension/locale/pl/personas.properties | 1 +
extension/locale/pt-BR/personas.properties | 1 +
extension/locale/ro/personas.properties | 1 +
extension/locale/ru-RU/personas.properties | 1 +
extension/locale/si-LK/personas.properties | 1 +
extension/locale/sk-SK/personas.properties | 1 +
extension/locale/sr/personas.properties | 1 +
extension/locale/sv-SE/personas.properties | 1 +
extension/locale/tr/personas.properties | 1 +
extension/locale/uk-UA/personas.properties | 1 +
extension/locale/vi/personas.properties | 1 +
extension/locale/zh-CN/personas.properties | 1 +
extension/locale/zh-TW/personas.properties | 1 +
extension/modules/service.js | 2828 ++++++++++++------------
43 files changed, 2530 insertions(+), 2429 deletions(-)
diff --git a/extension/content/personas.js b/extension/content/personas.js
index 8d8c529..ffaf70d 100644
--- a/extension/content/personas.js
+++ b/extension/content/personas.js
@@ -44,13 +44,13 @@
// conflict with modules with the same names imported by other extensions.
// Define Components as var here as they are already defined for Firefox. See Bug 484062 for details.
- if (typeof Cc == "undefined")
+if (typeof Cc == "undefined")
var Cc = Components.classes;
- if (typeof Ci == "undefined")
+if (typeof Ci == "undefined")
var Ci = Components.interfaces;
- if (typeof Cr == "undefined")
+if (typeof Cr == "undefined")
var Cr = Components.results;
- if (typeof Cu == "undefined")
+if (typeof Cu == "undefined")
var Cu = Components.utils;
// It's OK to import the service module into the global namespace because its
@@ -58,1100 +58,1114 @@
Cu.import("resource://personas/modules/service.js");
let PersonaController = {
- _previewTimeoutID: null,
- _resetTimeoutID: null,
-
- //**************************************************************************//
- // Shortcuts
-
- // Generic modules get imported into these properties rather than
- // the global namespace so they don't conflict with modules with the same
- // names imported by other extensions.
- Observers: null,
- Preferences: null,
- StringBundle: null,
- URI: null,
- LightweightThemeManager: null,
-
- // Access to extensions.personas.* preferences. To access other preferences,
- // call the Preferences module directly.
- get _prefs() {
- delete this._prefs;
- return this._prefs = new this.Preferences("extensions.personas.");
- },
-
- get _strings() {
- delete this._strings;
- return this._strings = new this.StringBundle("chrome://personas/locale/personas.properties");
- },
-
- get _brandStrings() {
- delete this._brandStrings;
- return this._brandStrings =
- new this.StringBundle("chrome://branding/locale/brand.properties");
- },
-
- get _menu() {
- delete this._menu;
- return this._menu = document.getElementById("personas-menu");
- },
-
- get _menuButton() {
- delete this._menuButton;
- return this._menuButton = document.getElementById("personas-selector-button");
- },
-
- get _menuPopup() {
- delete this._menuPopup;
- return this._menuPopup = document.getElementById("personas-selector-menu");
- },
-
- get _toolbarButton() {
- delete this._toolbarButton;
- return this._toolbarButton = document.getElementById("personas-toolbar-button");
- },
-
- get _sessionStore() {
- delete this._sessionStore;
- return this._sessionStore = Cc["@mozilla.org/browser/sessionstore;1"]
- .getService(Ci.nsISessionStore);
- },
-
- get _header() {
- delete this._header;
- switch (PersonaService.appInfo.ID) {
- case PersonaService.THUNDERBIRD_ID:
- return this._header = document.getElementById("messengerWindow");
- case PersonaService.FIREFOX_ID:
- return this._header = document.getElementById("main-window");
- default:
- throw "unknown application ID " + PersonaService.appInfo.ID;
- }
- },
-
- get _footer() {
- delete this._footer;
- switch (PersonaService.appInfo.ID) {
- case PersonaService.THUNDERBIRD_ID:
- return this._footer = document.getElementById("status-bar");
- case PersonaService.FIREFOX_ID:
- return this._footer = document.getElementById("browser-bottombox");
- default:
- throw "unknown application ID " + PersonaService.appInfo.ID;
- }
- },
-
- get _thunderbirdRegExp() {
- delete this._thunderbirdRegExp;
- return this._thunderbirdRegExp = new RegExp("^" + this._siteURL.replace(/\./g, "\\."));
- },
-
- get _siteURL() {
- return "https://" + this._prefs.get("addons-host") + "/";
- },
-
- get _previewTimeout() {
- return this._prefs.get("previewTimeout");
- },
-
- // XXX We used to use this to direct users to locale-specific directories
- // on the personas server, but we're not using it anymore, as we no longer
- // have locale-specific pages on the server. And once we get them back,
- // it'll probably make more sense for the browser and server to do locale
- // negotiation using the standard mechanisms anyway, so this is no longer
- // needed.
- get _locale() {
- switch (this.Preferences.get("general.useragent.locale", "en-US")) {
- case 'ja':
- case 'ja-JP-mac':
- return "ja";
- }
- return "en-US";
- },
-
- /**
- * Escape CSS special characters in unquoted URLs,
- * per http://www.w3.org/TR/CSS21/syndata.html#uri.
- */
- _escapeURLForCSS: function(url) url.replace(/[(),\s'"]/g, "\$&"),
-
- openURLInTab: function(url) {
- switch (PersonaService.appInfo.ID) {
- case PersonaService.THUNDERBIRD_ID:
- // Thunderbird's "openTab" implementation for the "contentTab" mode
- // automatically switches to an existing tab containing the URL we are
- // opening, so we don't have to check for one here.
- Cc['@mozilla.org/appshell/window-mediator;1'].
- getService(Ci.nsIWindowMediator).
- getMostRecentWindow("mail:3pane").
- document.getElementById("tabmail").
- openTab("contentTab", { contentPage: url,
- clickHandler: "specialTabs.siteClickHandler(event, PersonaController._thunderbirdRegExp);" });
- break;
-
- case PersonaService.FIREFOX_ID:
- default: {
- // Firefox's "openUILinkIn" implementation doesn't check if there is
- // already an existing tab containing the URL we are opening, so we have
- // to check for one here.
- let found = false;
- let tabBrowser = window.getBrowser();
- // Check each tab of this browser for the editor XUL file
- let numTabs = tabBrowser.browsers.length;
- for (let index = 0; index < numTabs; index++) {
- let currentBrowser = tabBrowser.getBrowserAtIndex(index);
- if (url == currentBrowser.currentURI.spec) {
- tabBrowser.selectedTab = tabBrowser.mTabs[index];
- found = true;
- break;
- }
+ _previewTimeoutID: null,
+ _resetTimeoutID: null,
+
+ //**************************************************************************//
+ // Shortcuts
+
+ // Generic modules get imported into these properties rather than
+ // the global namespace so they don't conflict with modules with the same
+ // names imported by other extensions.
+ Observers: null,
+ Preferences: null,
+ StringBundle: null,
+ URI: null,
+ LightweightThemeManager: null,
+
+ // Access to extensions.personas.* preferences. To access other preferences,
+ // call the Preferences module directly.
+ get _prefs() {
+ delete this._prefs;
+ return this._prefs = new this.Preferences("extensions.personas.");
+ },
+
+ get _strings() {
+ delete this._strings;
+ return this._strings = new this.StringBundle("chrome://personas/locale/personas.properties");
+ },
+
+ get _brandStrings() {
+ delete this._brandStrings;
+ return this._brandStrings =
+ new this.StringBundle("chrome://branding/locale/brand.properties");
+ },
+
+ get _menu() {
+ delete this._menu;
+ return this._menu = document.getElementById("personas-menu");
+ },
+
+ get _menuButton() {
+ delete this._menuButton;
+ return this._menuButton = document.getElementById("personas-selector-button");
+ },
+
+ get _menuPopup() {
+ delete this._menuPopup;
+ return this._menuPopup = document.getElementById("personas-selector-menu");
+ },
+
+ get _toolbarButton() {
+ delete this._toolbarButton;
+ return this._toolbarButton = document.getElementById("personas-toolbar-button");
+ },
+
+ get _sessionStore() {
+ delete this._sessionStore;
+ return this._sessionStore = Cc["@mozilla.org/browser/sessionstore;1"]
+ .getService(Ci.nsISessionStore);
+ },
+
+ get _header() {
+ delete this._header;
+ switch (PersonaService.appInfo.ID) {
+ case PersonaService.THUNDERBIRD_ID:
+ return this._header = document.getElementById("messengerWindow");
+ case PersonaService.FIREFOX_ID:
+ return this._header = document.getElementById("main-window");
+ default:
+ throw "unknown application ID " + PersonaService.appInfo.ID;
}
- if (!found)
- window.openUILinkIn(url, "tab");
- break;
- }
- }
- },
-
-
- //**************************************************************************//
- // XPCOM Interface Implementations
-
- // nsISupports
- QueryInterface: function(aIID) {
- if (aIID.equals(Ci.nsIObserver) ||
- aIID.equals(Ci.nsIDOMEventListener) ||
- aIID.equals(Ci.nsISupports))
- return this;
-
- throw Cr.NS_ERROR_NO_INTERFACE;
- },
-
- // nsIObserver
- observe: function(subject, topic, data) {
- switch (topic) {
- case "domwindowopened":
- // Since there's no explicit notification for windows restored from
- // session store, we use this to apply the per-window persona
- // (but only if one exists).
- //
- // Bug 534669 has been filed for adding SSWindowRestored support.
- if (this._prefs.get("perwindow") &&
- (window.document.documentElement.getAttribute("windowtype")
- == "navigator:browser") &&
- this._sessionStore.getWindowValue(window, "persona")) {
- this._applyPersona(JSON.parse(
- this._sessionStore.getWindowValue(window, "persona")
- ));
+ },
+
+ get _footer() {
+ delete this._footer;
+ switch (PersonaService.appInfo.ID) {
+ case PersonaService.THUNDERBIRD_ID:
+ return this._footer = document.getElementById("status-bar");
+ case PersonaService.FIREFOX_ID:
+ return this._footer = document.getElementById("browser-bottombox");
+ default:
+ throw "unknown application ID " + PersonaService.appInfo.ID;
}
- break;
- case "personas:persona:changed":
- // Per-window personas are enabled
- if (this._prefs.get("perwindow")) {
- if (this._sessionStore.getWindowValue(window, "persona")) {
- this._applyPersona(JSON.parse(
- this._sessionStore.getWindowValue(window, "persona")
- ));
- } else {
- this._applyDefault();
- }
- // Pan-window personas are enabled
- } else {
- if (PersonaService.previewingPersona) {
- this._applyPersona(PersonaService.previewingPersona);
- } else if (PersonaService.selected == "default") {
- this._applyDefault();
- } else {
- this._applyPersona(PersonaService.currentPersona);
- }
- break;
+ },
+
+ get _thunderbirdRegExp() {
+ delete this._thunderbirdRegExp;
+ return this._thunderbirdRegExp = new RegExp("^" + this._siteURL.replace(/\./g, "\\."));
+ },
+
+ get _siteURL() {
+ return "https://" + this._prefs.get("addons-host") + "/";
+ },
+
+ get _previewTimeout() {
+ return this._prefs.get("previewTimeout");
+ },
+
+ // XXX We used to use this to direct users to locale-specific directories
+ // on the personas server, but we're not using it anymore, as we no longer
+ // have locale-specific pages on the server. And once we get them back,
+ // it'll probably make more sense for the browser and server to do locale
+ // negotiation using the standard mechanisms anyway, so this is no longer
+ // needed.
+ get _locale() {
+ switch (this.Preferences.get("general.useragent.locale", "en-US")) {
+ case 'ja':
+ case 'ja-JP-mac':
+ return "ja";
}
- }
- },
-
- // nsIDOMEventListener
- handleEvent: function(aEvent) {
- switch (aEvent.type) {
- case "SelectPersona":
- case "PreviewPersona":
- case "ResetPersona":
- let doc = aEvent.originalTarget.ownerDocument;
- if (Services.perms.testPermission(doc.documentURIObject, "install")
- == Services.perms.ALLOW_ACTION)
- this.checkPrivateBrowsing();
- break;
- case "CheckPersonas":
- this.onCheckPersonasFromContent(aEvent);
- break;
- case "AddFavoritePersona":
- this.onAddFavoritePersonaFromContent(aEvent);
- break;
- case "RemoveFavoritePersona":
- this.onRemoveFavoritePersonaFromContent(aEvent);
- break;
- }
- },
-
- // Tab Monitor methods (Thunderbird)
- onTabTitleChanged : function(aTab) { /* ignored */ },
- onTabSwitched : function(aTab, aOldTab) {
- },
-
- //**************************************************************************//
- // Initialization & Destruction
-
- startUp: function() {
- // Set the label for the tooltip that informs users when personas data
- // is unavailable.
- // FIXME: make this a DTD entity rather than a properties string.
- document.getElementById("personasDataUnavailableTooltip").label =
- this._strings.get("dataUnavailable",
- [this._brandStrings.get("brandShortName")]);
-
- document.addEventListener("SelectPersona", this, false, true);
- document.addEventListener("PreviewPersona", this, false, true);
- document.addEventListener("ResetPersona", this, false, true);
- // Listen for various persona-related events that can bubble up from content,
- // not handled by the LightweightThemeManager.
- document.addEventListener("CheckPersonas", this, false, true);
- document.addEventListener("AddFavoritePersona", this, false, true);
- document.addEventListener("RemoveFavoritePersona", this, false, true);
-
-
- Cu.import("resource://gre/modules/AddonManager.jsm", this);
- this.AddonManager.getAddonByID(PERSONAS_EXTENSION_ID,
- function(aAddon) {
- this._prefs.set("lastversion", aAddon.version);
- }.bind(this));
-
- // Perform special operations for Firefox 4 compatibility:
- // * Hide the status bar button
- // * Install the toolbar button in the Add-on bar (first time only).
- if (PersonaService.appInfo.ID == PersonaService.FIREFOX_ID) {
- let addonBar = window.document.getElementById("addon-bar");
- if (addonBar) {
- this._menuButton.setAttribute("hidden", true);
-
- if (!this._prefs.get("toolbarButtonInstalled")) {
- this._installToolbarButton(addonBar);
- this._prefs.set("toolbarButtonInstalled", true);
+ return "en-US";
+ },
+
+ /**
+ * Escape CSS special characters in unquoted URLs,
+ * per http://www.w3.org/TR/CSS21/syndata.html#uri.
+ */
+ _escapeURLForCSS: function(url) url.replace(/[(),\s'"]/g, "\$&"),
+
+ openURLInTab: function(url) {
+ switch (PersonaService.appInfo.ID) {
+ case PersonaService.THUNDERBIRD_ID:
+ // Thunderbird's "openTab" implementation for the "contentTab" mode
+ // automatically switches to an existing tab containing the URL we are
+ // opening, so we don't have to check for one here.
+ Cc['@mozilla.org/appshell/window-mediator;1'].
+ getService(Ci.nsIWindowMediator).
+ getMostRecentWindow("mail:3pane").
+ document.getElementById("tabmail").
+ openTab("contentTab", {
+ contentPage: url,
+ clickHandler: "specialTabs.siteClickHandler(event, PersonaController._thunderbirdRegExp);"
+ });
+ break;
+
+ case PersonaService.FIREFOX_ID:
+ default:
+ {
+ // Firefox's "openUILinkIn" implementation doesn't check if there is
+ // already an existing tab containing the URL we are opening, so we have
+ // to check for one here.
+ let found = false;
+ let tabBrowser = window.getBrowser();
+ // Check each tab of this browser for the editor XUL file
+ let numTabs = tabBrowser.browsers.length;
+ for (let index = 0; index < numTabs; index++) {
+ let currentBrowser = tabBrowser.getBrowserAtIndex(index);
+ if (url == currentBrowser.currentURI.spec) {
+ tabBrowser.selectedTab = tabBrowser.mTabs[index];
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ window.openUILinkIn(url, "tab");
+ break;
+ }
}
- }
- }
- },
+ },
+
+
+ //**************************************************************************//
+ // XPCOM Interface Implementations
+
+ // nsISupports
+ QueryInterface: function(aIID) {
+ if (aIID.equals(Ci.nsIObserver) ||
+ aIID.equals(Ci.nsIDOMEventListener) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+ // nsIObserver
+ observe: function(subject, topic, data) {
+ switch (topic) {
+ case "domwindowopened":
+ // Since there's no explicit notification for windows restored from
+ // session store, we use this to apply the per-window persona
+ // (but only if one exists).
+ //
+ // Bug 534669 has been filed for adding SSWindowRestored support.
+ if (this._prefs.get("perwindow") &&
+ (window.document.documentElement.getAttribute("windowtype") == "navigator:browser") &&
+ this._sessionStore.getWindowValue(window, "persona")) {
+ this._applyPersona(JSON.parse(
+ this._sessionStore.getWindowValue(window, "persona")
+ ));
+ }
+ break;
+ case "personas:persona:changed":
+ // Per-window personas are enabled
+ if (this._prefs.get("perwindow")) {
+ if (this._sessionStore.getWindowValue(window, "persona")) {
+ this._applyPersona(JSON.parse(
+ this._sessionStore.getWindowValue(window, "persona")
+ ));
+ } else {
+ this._applyDefault();
+ }
+ // Pan-window personas are enabled
+ } else {
+ if (PersonaService.previewingPersona) {
+ this._applyPersona(PersonaService.previewingPersona);
+ } else if (PersonaService.selected == "default") {
+ this._applyDefault();
+ } else {
+ this._applyPersona(PersonaService.currentPersona);
+ }
+ break;
+ }
+ }
+ },
+
+ // nsIDOMEventListener
+ handleEvent: PersonaService.wrap(function(aEvent) {
+ switch (aEvent.type) {
+ case "SelectPersona":
+ case "PreviewPersona":
+ case "ResetPersona":
+ let doc = aEvent.originalTarget.ownerDocument;
+ if (Services.perms.testPermission(doc.documentURIObject, "install") == Services.perms.ALLOW_ACTION)
+ this.checkPrivateBrowsing();
+ break;
+ case "CheckPersonas":
+ this.onCheckPersonasFromContent(aEvent);
+ break;
+ case "AddFavoritePersona":
+ this.onAddFavoritePersonaFromContent(aEvent);
+ break;
+ case "RemoveFavoritePersona":
+ this.onRemoveFavoritePersonaFromContent(aEvent);
+ break;
+ }
+ }),
+
+ // Tab Monitor methods (Thunderbird)
+ onTabTitleChanged: function(aTab) { /* ignored */ },
+ onTabSwitched: function(aTab, aOldTab) {},
+
+ //**************************************************************************//
+ // Initialization & Destruction
+
+ startUp: function() {
+ // Set the label for the tooltip that informs users when personas data
+ // is unavailable.
+ // FIXME: make this a DTD entity rather than a properties string.
+ document.getElementById("personasDataUnavailableTooltip").label =
+ this._strings.get("dataUnavailable", [this._brandStrings.get("brandShortName")]);
+
+ document.addEventListener("SelectPersona", this, false, true);
+ document.addEventListener("PreviewPersona", this, false, true);
+ document.addEventListener("ResetPersona", this, false, true);
+ // Listen for various persona-related events that can bubble up from content,
+ // not handled by the LightweightThemeManager.
+ document.addEventListener("CheckPersonas", this, false, true);
+ document.addEventListener("AddFavoritePersona", this, false, true);
+ document.addEventListener("RemoveFavoritePersona", this, false, true);
+
+
+ Cu.import("resource://gre/modules/AddonManager.jsm", this);
+ this.AddonManager.getAddonByID(PERSONAS_EXTENSION_ID,
+ function(aAddon) {
+ this._prefs.set("lastversion", aAddon.version);
+ }.bind(this));
+
+ // Perform special operations for Firefox 4 compatibility:
+ // * Hide the status bar button
+ // * Install the toolbar button in the Add-on bar (first time only).
+ if (PersonaService.appInfo.ID == PersonaService.FIREFOX_ID) {
+ let addonBar = window.document.getElementById("addon-bar");
+ if (addonBar) {
+ this._menuButton.setAttribute("hidden", true);
+
+ if (!this._prefs.get("toolbarButtonInstalled")) {
+ this._installToolbarButton(addonBar);
+ this._prefs.set("toolbarButtonInstalled", true);
+ }
+ }
+ }
+ },
- shutDown: function() {
- document.removeEventListener("SelectPersona", this, false);
- document.removeEventListener("PreviewPersona", this, false);
- document.removeEventListener("ResetPersona", this, false);
- document.removeEventListener("CheckPersonas", this, false);
- document.removeEventListener("AddFavoritePersona", this, false);
- document.removeEventListener("RemoveFavoritePersona", this, false);
- },
+ shutDown: function() {
+ document.removeEventListener("SelectPersona", this, false);
+ document.removeEventListener("PreviewPersona", this, false);
+ document.removeEventListener("ResetPersona", this, false);
+ document.removeEventListener("CheckPersonas", this, false);
+ document.removeEventListener("AddFavoritePersona", this, false);
+ document.removeEventListener("RemoveFavoritePersona", this, false);
+ },
- _installToolbarButton : function(aToolbar) {
- const PERSONAS_BUTTON_ID = "personas-toolbar-button";
+ _installToolbarButton: function(aToolbar) {
+ const PERSONAS_BUTTON_ID = "personas-toolbar-button";
- let curSet = aToolbar.currentSet;
+ let curSet = aToolbar.currentSet;
- // Add the button if it's not in the toolbar's current set
- if (-1 == curSet.indexOf(PERSONAS_BUTTON_ID)) {
+ // Add the button if it's not in the toolbar's current set
+ if (-1 == curSet.indexOf(PERSONAS_BUTTON_ID)) {
- // Insert the button at the end.
- let newSet = curSet + "," + PERSONAS_BUTTON_ID;
+ // Insert the button at the end.
+ let newSet = curSet + "," + PERSONAS_BUTTON_ID;
- aToolbar.currentSet = newSet;
- aToolbar.setAttribute("currentset", newSet);
- document.persist(aToolbar.id, "currentset");
+ aToolbar.currentSet = newSet;
+ aToolbar.setAttribute("currentset", newSet);
+ document.persist(aToolbar.id, "currentset");
- try {
- BrowserToolboxCustomizeDone(true);
- }
- catch(e){}
+ try {
+ BrowserToolboxCustomizeDone(true);
+ } catch (e) {}
- // Make sure the toolbar is visible
- if (aToolbar.getAttribute("collapsed") == "true")
- aToolbar.setAttribute("collapsed", "false");
- document.persist(aToolbar.id, "collapsed");
- }
- },
+ // Make sure the toolbar is visible
+ if (aToolbar.getAttribute("collapsed") == "true")
+ aToolbar.setAttribute("collapsed", "false");
+ document.persist(aToolbar.id, "collapsed");
+ }
+ },
- //**************************************************************************//
- // Appearance Updates
+ //**************************************************************************//
+ // Appearance Updates
- _applyPersona: function(persona) {
+ _applyPersona: function(persona) {
- // Style header and footer
- this._header.setAttribute("persona", persona.id);
- this._footer.setAttribute("persona", persona.id);
+ // Style header and footer
+ this._header.setAttribute("persona", persona.id);
+ this._footer.setAttribute("persona", persona.id);
- // First try to obtain the images from the cache
- let images = PersonaService.getCachedPersonaImages(persona);
- if (images && images.header && images.footer) {
- this._header.style.backgroundImage = "url(" + images.header + ")";
- this._footer.style.backgroundImage = "url(" + images.footer + ")";
- }
- // Else set them from their original source
- else {
- // Use the URI module to resolve the possibly relative URI to an absolute one.
- let headerURI = this.URI.get(persona.headerURL || persona.header,
- null, null);
- this._header.style.backgroundImage = "url(" + this._escapeURLForCSS(headerURI.spec) + ")";
- // Use the URI module to resolve the possibly relative URI to an absolute one.
- let footerURI = this.URI.get(persona.footerURL || persona.footer,
- null, null);
- this._footer.style.backgroundImage = "url(" + this._escapeURLForCSS(footerURI.spec) + ")";
- }
+ // First try to obtain the images from the cache
+ let images = PersonaService.getCachedPersonaImages(persona);
+ if (images && images.header && images.footer) {
+ this._header.style.backgroundImage = "url(" + images.header + ")";
+ this._footer.style.backgroundImage = "url(" + images.footer + ")";
+ }
+ // Else set them from their original source
+ else {
+ // Use the URI module to resolve the possibly relative URI to an absolute one.
+ let headerURI = this.URI.get(persona.headerURL || persona.header,
+ null, null);
+ this._header.style.backgroundImage = "url(" + this._escapeURLForCSS(headerURI.spec) + ")";
+ // Use the URI module to resolve the possibly relative URI to an absolute one.
+ let footerURI = this.URI.get(persona.footerURL || persona.footer,
+ null, null);
+ this._footer.style.backgroundImage = "url(" + this._escapeURLForCSS(footerURI.spec) + ")";
+ }
- // Style the text color.
- if (this._prefs.get("useTextColor")) {
- // FIXME: fall back on the default text color instead of "black".
- let textColor = persona.textcolor || "black";
- this._header.style.color = textColor;
- for (let i = 0; i < document.styleSheets.length; i++) {
- let styleSheet = document.styleSheets[i];
- if (styleSheet.href == "chrome://personas/content/overlay.css") {
- while (styleSheet.cssRules.length > 0)
- styleSheet.deleteRule(0);
-
- // On Mac we do several things differently:
- // 1. make text be regular weight, not bold (not sure why);
- // 2. explicitly style the Find toolbar label ("Find:" or
- // "Quick Find:" in en-US) and status message ("Phrase not found"),
- // which otherwise would be custom colors specified in findBar.css
- // (note: we only do this in Firefox);
- // 3. style the tab color (Mac tabs are transparent).
- // In order to style the Find toolbar text, we have to both explicitly
- // reference it (.findbar-find-fast, .findbar-find-status) and make
- // the declaration !important to override an !important declaration
- // for the status text in findBar.css.
- // XXX Isn't |#main-window[persona]| unnecessary in this rule,
- // given that the rule is only inserted into the stylesheet when
- // a persona is active?
- if (PersonaService.appInfo.OS == "Darwin") {
- switch (PersonaService.appInfo.ID) {
- case PersonaService.FIREFOX_ID:
- styleSheet.insertRule(
- "#main-window[persona] .tabbrowser-tab, " +
- "#navigator-toolbox menubar > menu, " +
- "#navigator-toolbox toolbarbutton, " +
- "#browser-bottombox, " +
- ".findbar-find-fast, " +
- ".findbar-find-status, " +
- "#browser-bottombox toolbarbutton " +
- "{ color: inherit; " +
- "font-weight: normal; }",
- 0
- );
- break;
- case PersonaService.THUNDERBIRD_ID:
- styleSheet.insertRule(
- ".tabmail-tab, " +
- "#mail-toolbox menubar > menu, " +
- "#mail-toolbox toolbarbutton, " +
- "#mail-toolbox toolbaritem > label, " +
- "#status-bar " +
- "{ color: " + textColor + " !important; " +
- "font-weight: normal; }",
- 0
- );
- break;
- default:
- break;
+ // Style the text color.
+ if (this._prefs.get("useTextColor")) {
+ // FIXME: fall back on the default text color instead of "black".
+ let textColor = persona.textcolor || "black";
+ this._header.style.color = textColor;
+ for (let i = 0; i < document.styleSheets.length; i++) {
+ let styleSheet = document.styleSheets[i];
+ if (styleSheet.href == "chrome://personas/content/overlay.css") {
+ while (styleSheet.cssRules.length > 0)
+ styleSheet.deleteRule(0);
+
+ // On Mac we do several things differently:
+ // 1. make text be regular weight, not bold (not sure why);
+ // 2. explicitly style the Find toolbar label ("Find:" or
+ // "Quick Find:" in en-US) and status message ("Phrase not found"),
+ // which otherwise would be custom colors specified in findBar.css
+ // (note: we only do this in Firefox);
+ // 3. style the tab color (Mac tabs are transparent).
+ // In order to style the Find toolbar text, we have to both explicitly
+ // reference it (.findbar-find-fast, .findbar-find-status) and make
+ // the declaration !important to override an !important declaration
+ // for the status text in findBar.css.
+ // XXX Isn't |#main-window[persona]| unnecessary in this rule,
+ // given that the rule is only inserted into the stylesheet when
+ // a persona is active?
+ if (PersonaService.appInfo.OS == "Darwin") {
+ switch (PersonaService.appInfo.ID) {
+ case PersonaService.FIREFOX_ID:
+ styleSheet.insertRule(
+ "#main-window[persona] .tabbrowser-tab, " +
+ "#navigator-toolbox menubar > menu, " +
+ "#navigator-toolbox toolbarbutton, " +
+ "#browser-bottombox, " +
+ ".findbar-find-fast, " +
+ ".findbar-find-status, " +
+ "#browser-bottombox toolbarbutton " +
+ "{ color: inherit; " +
+ "font-weight: normal; }",
+ 0
+ );
+ break;
+ case PersonaService.THUNDERBIRD_ID:
+ styleSheet.insertRule(
+ ".tabmail-tab, " +
+ "#mail-toolbox menubar > menu, " +
+ "#mail-toolbox toolbarbutton, " +
+ "#mail-toolbox toolbaritem > label, " +
+ "#status-bar " +
+ "{ color: " + textColor + " !important; " +
+ "font-weight: normal; }",
+ 0
+ );
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (PersonaService.appInfo.ID) {
+ case PersonaService.FIREFOX_ID:
+ styleSheet.insertRule(
+ "#navigator-toolbox menubar > menu, " +
+ "#navigator-toolbox toolbarbutton, " +
+ "#browser-bottombox, " +
+ "#browser-bottombox toolbarbutton " +
+ "{ color: inherit; }",
+ 0
+ );
+ break;
+ case PersonaService.THUNDERBIRD_ID:
+ styleSheet.insertRule(
+ "#mail-toolbox menubar > menu, " +
+ "#mail-toolbox toolbarbutton, " +
+ "#mail-toolbox toolbaritem > label, " +
+ "#status-bar " +
+ "{ color: " + textColor + "}",
+ 0
+ );
+ break;
+ default:
+ break;
+ }
+ }
+
+ // FIXME: figure out what to do about the disabled color. Maybe we
+ // should let personas specify it independently and then apply it via
+ // a rule like this:
+ // #navigator-toolbox toolbarbutton[disabled="true"],
+ // #browser-toolbox toolbarbutton[disabled="true"],
+ // #browser-bottombox toolbarbutton[disabled="true"]
+ // { color: #cccccc !important; }
+
+ // Stop iterating through stylesheets.
+ break;
+ }
}
- }
- else {
- switch (PersonaService.appInfo.ID) {
- case PersonaService.FIREFOX_ID:
- styleSheet.insertRule(
- "#navigator-toolbox menubar > menu, " +
- "#navigator-toolbox toolbarbutton, " +
- "#browser-bottombox, " +
- "#browser-bottombox toolbarbutton " +
- "{ color: inherit; }",
- 0
- );
- break;
- case PersonaService.THUNDERBIRD_ID:
- styleSheet.insertRule(
- "#mail-toolbox menubar > menu, " +
- "#mail-toolbox toolbarbutton, " +
- "#mail-toolbox toolbaritem > label, " +
- "#status-bar " +
- "{ color: " + textColor + "}",
- 0
- );
- break;
- default:
+ }
+
+ // Style the titlebar with the accent color.
+ if (this._prefs.get("useAccentColor")) {
+ let general, active, inactive;
+ if (persona.accentcolor) {
+ general = persona.accentcolor;
+ active = persona.accentcolor;
+ inactive = persona.accentcolor;
+ } else {
+ general = "";
+ active = "";
+ inactive = "";
+ }
+ this._setTitlebarColors(general, active, inactive);
+ }
+
+ // Opacity overrides (firefox only)
+ if (PersonaService.appInfo.ID == PersonaService.FIREFOX_ID) {
+ let overrideOpacity = this._prefs.get("override.opacity");
+ let overrideActiveOpacity = this._prefs.get("override.activeOpacity");
+ for (let i = 0; i < document.styleSheets.length; i++) {
+ let styleSheet = document.styleSheets[i];
+ if (styleSheet.href == "chrome://personas/content/overlay.css") {
+ if (typeof(overrideOpacity) != "undefined") {
+ styleSheet.insertRule(
+ "#main-window[persona] .tabbrowser-tab " +
+ "{ opacity: " + overrideOpacity + " !important; }",
+ 0
+ );
+ }
+ if (typeof(overrideActiveOpacity) != "undefined") {
+ styleSheet.insertRule(
+ "#main-window[persona] .tabbrowser-tab[selected=\"true\"] " +
+ "{ opacity: " + overrideActiveOpacity + " !important; }",
+ 0
+ );
+ styleSheet.insertRule(
+ "#main-window[persona] #urlbar, " +
+ "#main-window[persona] #searchbar " +
+ "{ opacity: " + overrideActiveOpacity + " !important; }",
+ 0
+ );
+ }
+ break;
+ }
+ }
+ }
+
+ },
+
+ _applyDefault: function() {
+ // Reset the header.
+ this._header.removeAttribute("persona");
+ this._header.style.backgroundImage = "";
+
+ // Reset the footer.
+ this._footer.removeAttribute("persona");
+ this._footer.style.backgroundImage = "";
+
+ // Reset the text color.
+ for (let i = 0; i < document.styleSheets.length; i++) {
+ let styleSheet = document.styleSheets[i];
+ if (styleSheet.href == "chrome://personas/content/overlay.css") {
+ while (styleSheet.cssRules.length > 0)
+ styleSheet.deleteRule(0);
break;
}
- }
-
- // FIXME: figure out what to do about the disabled color. Maybe we
- // should let personas specify it independently and then apply it via
- // a rule like this:
- // #navigator-toolbox toolbarbutton[disabled="true"],
- // #browser-toolbox toolbarbutton[disabled="true"],
- // #browser-bottombox toolbarbutton[disabled="true"]
- // { color: #cccccc !important; }
-
- // Stop iterating through stylesheets.
- break;
}
- }
- }
+ this._header.style.color = "";
- // Style the titlebar with the accent color.
- if (this._prefs.get("useAccentColor")) {
- let general, active, inactive;
- if (persona.accentcolor) {
- general = persona.accentcolor;
- active = persona.accentcolor;
- inactive = persona.accentcolor;
- }
- else {
- general = "";
- active = "";
- inactive = "";
- }
- this._setTitlebarColors(general, active, inactive);
- }
+ // Reset the titlebar color.
+ if (this._prefs.get("useAccentColor")) {
+ this._setTitlebarColors("", "", "");
+ }
+ },
+
+ _setTitlebarColors: function(general, active, inactive) {
+ // Titlebar colors only have an effect on Mac.
+ if (PersonaService.appInfo.OS != "Darwin")
+ return;
- // Opacity overrides (firefox only)
- if (PersonaService.appInfo.ID == PersonaService.FIREFOX_ID) {
- let overrideOpacity = this._prefs.get("override.opacity");
- let overrideActiveOpacity = this._prefs.get("override.activeOpacity");
- for (let i = 0; i < document.styleSheets.length; i++) {
- let styleSheet = document.styleSheets[i];
- if (styleSheet.href == "chrome://personas/content/overlay.css") {
- if (typeof(overrideOpacity) != "undefined") {
- styleSheet.insertRule(
- "#main-window[persona] .tabbrowser-tab " +
- "{ opacity: " + overrideOpacity + " !important; }",
- 0
- );
- }
- if (typeof(overrideActiveOpacity) != "undefined") {
- styleSheet.insertRule(
- "#main-window[persona] .tabbrowser-tab[selected=\"true\"] " +
- "{ opacity: " + overrideActiveOpacity + " !important; }",
- 0
- );
- styleSheet.insertRule(
- "#main-window[persona] #urlbar, " +
- "#main-window[persona] #searchbar " +
- "{ opacity: " + overrideActiveOpacity + " !important; }",
- 0
- );
- }
- break;
+ let changed = false;
+
+ if (general != this._header.getAttribute("titlebarcolor")) {
+ document.documentElement.setAttribute("titlebarcolor", general);
+ changed = true;
+ }
+ if (active != this._header.getAttribute("activetitlebarcolor")) {
+ document.documentElement.setAttribute("activetitlebarcolor", active);
+ changed = true;
+ }
+ if (inactive != this._header.getAttribute("inactivetitlebarcolor")) {
+ document.documentElement.setAttribute("inactivetitlebarcolor", inactive);
+ changed = true;
}
- }
- }
- },
-
- _applyDefault: function() {
- // Reset the header.
- this._header.removeAttribute("persona");
- this._header.style.backgroundImage = "";
-
- // Reset the footer.
- this._footer.removeAttribute("persona");
- this._footer.style.backgroundImage = "";
-
- // Reset the text color.
- for (let i = 0; i < document.styleSheets.length; i++) {
- let styleSheet = document.styleSheets[i];
- if (styleSheet.href == "chrome://personas/content/overlay.css") {
- while (styleSheet.cssRules.length > 0)
- styleSheet.deleteRule(0);
- break;
- }
- }
- this._header.style.color = "";
+ if (changed && PersonaService.appInfo.platformVersion.indexOf("1.9.0") == 0) {
+ // FIXME: Incredibly gross hack in order to force a window redraw event
+ // that ensures that the titlebar color change is applied. We only have to
+ // do this for Firefox 3.0 (Gecko 1.9.0) because bug 485451 on the problem
+ // has been fixed for Firefox 3.5 (Gecko 1.9.1).
+ //
+ // This will unmaximize a maximized window on Windows and Linux,
+ // but we only do this on Mac (which is the only place
+ // the "titlebarcolor" attribute has any effect anyway at the moment),
+ // so that's ok for now.
+ //
+ // This will unminimize a minimized window on Mac, so we can't do it
+ // if the window is minimized.
+ if (window.windowState != Ci.nsIDOMChromeWindow.STATE_MINIMIZED) {
+ window.resizeTo(parseInt(window.outerWidth) + 1, window.outerHeight);
+ window.resizeTo(parseInt(window.outerWidth) - 1, window.outerHeight);
+ }
+ }
+ },
+
+ /**
+ * Checks if the current window is in private browsing mode, and shows
+ * an alert warning that personas will not work, if so.
+ */
+ checkPrivateBrowsing: function() {
+ if (typeof PrivateBrowsingUtils !== "undefined" &&
+ PrivateBrowsingUtils.isWindowPrivate(window)) {
+
+ const ID = "personas-plus-private-browsing-warning";
+
+ var notifications = gBrowser.getNotificationBox();
+ if (!notifications.getNotificationWithValue(ID)) {
+ let _ = this._strings.get.bind(this._strings);
+
+ notifications.appendNotification(
+ _("pbm.message"), ID,
+ "chrome://personas/content/personas_16x16.png",
+ notifications.PRIORITY_WARNING_HIGH, [{
+ label: _("pbm.button.label"),
+ accessKey: _("pbm.button.accesskey"),
+ callback: function(n) {
+ n.close()
+ }
+ }]);
+ }
+ }
+ },
- // Reset the titlebar color.
- if (this._prefs.get("useAccentColor")) {
- this._setTitlebarColors("", "", "");
- }
- },
- _setTitlebarColors: function(general, active, inactive) {
- // Titlebar colors only have an effect on Mac.
- if (PersonaService.appInfo.OS != "Darwin")
- return;
+ //**************************************************************************//
+ // Persona Selection, Preview, and Reset
- let changed = false;
+ /**
+ * Select the persona specified by the DOM node target of the given event.
+ *
+ * @param event {Event}
+ * the SelectPersona DOM event
+ */
+ onSelectPersona: PersonaService.wrap(function(event) {
+ let node = event.target;
- if (general != this._header.getAttribute("titlebarcolor")) {
- document.documentElement.setAttribute("titlebarcolor", general);
- changed = true;
- }
- if (active != this._header.getAttribute("activetitlebarcolor")) {
- document.documentElement.setAttribute("activetitlebarcolor", active);
- changed = true;
- }
- if (inactive != this._header.getAttribute("inactivetitlebarcolor")) {
- document.documentElement.setAttribute("inactivetitlebarcolor", inactive);
- changed = true;
- }
+ if (!node.hasAttribute("persona"))
+ throw "node does not have 'persona' attribute";
- if (changed && PersonaService.appInfo.platformVersion.indexOf("1.9.0") == 0) {
- // FIXME: Incredibly gross hack in order to force a window redraw event
- // that ensures that the titlebar color change is applied. We only have to
- // do this for Firefox 3.0 (Gecko 1.9.0) because bug 485451 on the problem
- // has been fixed for Firefox 3.5 (Gecko 1.9.1).
- //
- // This will unmaximize a maximized window on Windows and Linux,
- // but we only do this on Mac (which is the only place
- // the "titlebarcolor" attribute has any effect anyway at the moment),
- // so that's ok for now.
- //
- // This will unminimize a minimized window on Mac, so we can't do it
- // if the window is minimized.
- if (window.windowState != Ci.nsIDOMChromeWindow.STATE_MINIMIZED) {
- window.resizeTo(parseInt(window.outerWidth)+1, window.outerHeight);
- window.resizeTo(parseInt(window.outerWidth)-1, window.outerHeight);
- }
- }
- },
-
- /**
- * Checks if the current window is in private browsing mode, and shows
- * an alert warning that personas will not work, if so.
- */
- checkPrivateBrowsing: function() {
- if (typeof PrivateBrowsingUtils !== "undefined" &&
- PrivateBrowsingUtils.isWindowPrivate(window)) {
-
- const ID = "personas-plus-private-browsing-warning";
-
- var notifications = gBrowser.getNotificationBox();
- if(!notifications.getNotificationWithValue(ID)) {
- let _ = this._strings.get.bind(this._strings);
-
- notifications.appendNotification(
- _("pbm.message"), ID,
- "chrome://personas/content/personas_16x16.png",
- notifications.PRIORITY_WARNING_HIGH,
- [{ label: _("pbm.button.label"),
- accessKey: _("pbm.button.accesskey"),
- callback: function (n) { n.close() } }]);
- }
- }
- },
-
-
- //**************************************************************************//
- // Persona Selection, Preview, and Reset
-
- /**
- * Select the persona specified by the DOM node target of the given event.
- *
- * @param event {Event}
- * the SelectPersona DOM event
- */
- onSelectPersona: PersonaService.wrap(function(event) {
- let node = event.target;
-
- if (!node.hasAttribute("persona"))
- throw "node does not have 'persona' attribute";
-
- let persona = node.getAttribute("persona");
-
- // We check if the user wants per-window personas
- if (this._prefs.get("perwindow")) {
- // Since per-window personas are window-specific, we persist and
- // set them from here instead instead of going through PersonaService.
- switch (persona) {
- // We store the persona in the "persona" window property, and do not
- // have a seperate type as is the case with pan-window personas. This
- // is because we currently support only a single type of persona in
- // per-window mode. We'll add a new type property when we support
- // other types of selectable personas, such as "random".
- case "default":
- this._applyDefault();
- this._sessionStore.setWindowValue(window, "persona", "default");
- break;
- default:
- this._applyPersona(JSON.parse(persona));
- this._sessionStore.setWindowValue(window, "persona", persona);
- break;
- }
- // Usual, pan-window persona mode
- } else {
- // The persona attribute is either a JSON string specifying the persona
- // to apply or a string identifying a special persona (default, random).
- switch (persona) {
- case "default":
- PersonaService.changeToDefaultPersona();
- break;
- case "random":
- PersonaService.changeToRandomPersona(node.getAttribute("category"));
- break;
- case "custom":
- PersonaService.changeToPersona(PersonaService.customPersona);
- break;
- default:
- PersonaService.changeToPersona(JSON.parse(persona));
- break;
- }
- }
- }),
-
- onPreviewPersona: PersonaService.wrap(function(event) {
- if (!this._prefs.get("previewEnabled"))
- return;
-
- if (!event.target.hasAttribute("persona"))
- throw "node does not have 'persona' attribute";
-
- //this._previewPersona(event.target.getAttribute("persona"));
- let persona = JSON.parse(event.target.getAttribute("persona"));
-
- // We check if the user wants per-window personas
- if (this._prefs.get("perwindow")) {
- // We temporarily set the window specific persona here and let
- // onResetPersona reset it.
- switch (persona) {
- case "default":
- this._applyDefault();
- break;
- default:
- this._applyPersona(persona);
- break;
- }
- } else {
- if (this._resetTimeoutID) {
- window.clearTimeout(this._resetTimeoutID);
- this._resetTimeoutID = null;
- }
-
- let t = this;
- let persona = JSON.parse(event.target.getAttribute("persona"));
- let callback = function() { t._previewPersona(persona) };
- this._previewTimeoutID =
- window.setTimeout(callback, this._previewTimeout);
- }
- }),
-
- _previewPersona: function(persona) {
- PersonaService.previewPersona(persona);
- },
-
- onResetPersona: PersonaService.wrap(function(event) {
- if (!this._prefs.get("previewEnabled"))
- return;
-
- //this._resetPersona();
- // If per-window personas are enabled and there's a valid persona
- // value set for this window, don't reset.
- if (this._prefs.get("perwindow") &&
- this._sessionStore.getWindowValue(window, "persona")) {
- return;
- }
+ let persona = node.getAttribute("persona");
- if (this._previewTimeoutID) {
- window.clearTimeout(this._previewTimeoutID);
- this._previewTimeoutID = null;
- }
+ // We check if the user wants per-window personas
+ if (this._prefs.get("perwindow")) {
+ // Since per-window personas are window-specific, we persist and
+ // set them from here instead instead of going through PersonaService.
+ switch (persona) {
+ // We store the persona in the "persona" window property, and do not
+ // have a seperate type as is the case with pan-window personas. This
+ // is because we currently support only a single type of persona in
+ // per-window mode. We'll add a new type property when we support
+ // other types of selectable personas, such as "random".
+ case "default":
+ this._applyDefault();
+ this._sessionStore.setWindowValue(window, "persona", "default");
+ break;
+ default:
+ this._applyPersona(JSON.parse(persona));
+ this._sessionStore.setWindowValue(window, "persona", persona);
+ break;
+ }
+ // Usual, pan-window persona mode
+ } else {
+ // The persona attribute is either a JSON string specifying the persona
+ // to apply or a string identifying a special persona (default, random).
+ switch (persona) {
+ case "default":
+ PersonaService.changeToDefaultPersona();
+ break;
+ case "random":
+ PersonaService.changeToRandomPersona(node.getAttribute("category"));
+ break;
+ case "custom":
+ PersonaService.changeToPersona(PersonaService.customPersona);
+ break;
+ default:
+ PersonaService.changeToPersona(JSON.parse(persona));
+ break;
+ }
+ }
+ }),
- let t = this;
- let callback = function() { t._resetPersona() };
- this._resetTimeoutID = window.setTimeout(callback, this._previewTimeout);
- }),
-
- _resetPersona: function() {
- PersonaService.resetPersona();
- },
-
- /**
- * Confirm that Firefox has this Personas extension when requested by
- * a web page via a CheckPersonas event. Checks to ensure the page is hosted
- * on a host in the whitelist before responding to the event, so only
- * whitelisted pages can find out if Personas is installed.
- *
- * @param event {Event}
- * the CheckPersonas DOM event
- */
- onCheckPersonasFromContent: PersonaService.wrap(function(event) {
- this._authorizeHost(event);
- event.target.setAttribute("personas", "true");
- }),
-
- onSelectPreferences: PersonaService.wrap(function() {
- window.openDialog('chrome://personas/content/preferences.xul', '',
- 'chrome,titlebar,toolbar,centerscreen');
- }),
-
- onEditCustomPersona: PersonaService.wrap(function() {
- this.openURLInTab("chrome://personas/content/customPersonaEditor.xul");
- }),
-
- /**
- * Adds the favorite persona specified by a web page via a AddFavoritePersona event.
- * Checks to ensure the page is hosted on a server authorized to select personas.
- *
- * @param event {Event}
- * the AddFavoritePersona DOM event
- */
- onAddFavoritePersonaFromContent: PersonaService.wrap(function(event) {
- this._authorizeHost(event);
- this.onAddFavoritePersona(event);
- }),
-
- /**
- * Adds the persona specified by the DOM node target of the given event to
- * the favorites list.
- *
- * @param event {Event}
- * the AddFavoritePersona DOM event
- */
- onAddFavoritePersona: PersonaService.wrap(function(event) {
- let node = event.target;
-
- if (!node.hasAttribute("persona"))
- throw "node does not have 'persona' attribute";
-
- let persona = node.getAttribute("persona");
- PersonaService.addFavoritePersona(JSON.parse(persona));
- }),
-
- /**
- * Removes the favorite persona specified by a web page via a
- * RemoveFavoritePersona event.
- * Checks to ensure the page is hosted on a server authorized to select personas.
- *
- * @param event {Event}
- * the RemoveFavoritePersona DOM event
- */
- onRemoveFavoritePersonaFromContent: PersonaService.wrap(function(event) {
- this._authorizeHost(event);
- this.onRemoveFavoritePersona(event);
- }),
-
- /**
- * Removes the persona specified by the DOM node target of the given event
- * from the favorites list.
- *
- * @param event {Event}
- * the RemoveFavoritePersona DOM event
- */
- onRemoveFavoritePersonaFromContent: PersonaService.wrap(function(event) {
- let node = event.target;
-
- if (!node.hasAttribute("persona"))
- throw "node does not have 'persona' attribute";
-
- let persona = node.getAttribute("persona");
- PersonaService.removeFavoritePersona(JSON.parse(persona));
- }),
-
- /**
- * Ensure the host that loaded the document from which the given DOM event
- * came matches an entry in the personas whitelist. The host matches if it
- * equals one of the entries in the whitelist. For example, if
- * www.mozilla.com is an entry in the whitelist, then www.mozilla.com matches,
- * but labs.mozilla.com, mozilla.com, and evil.com do not.
- *
- * @param aEvent {Event} the DOM event
- */
- _authorizeHost: function(aEvent) {
- let host = aEvent.target.ownerDocument.location.hostname;
- let authorizedHosts = this._prefs.get("authorizedHosts").split(/[, ]+/);
- if (!authorizedHosts.some(function(v) v == host))
- throw host + " not authorized to modify personas";
- },
-
-
- //**************************************************************************//
- // Popup Construction
-
- onMenuButtonMouseDown: PersonaService.wrap(function(event) {
- // If the menu popup isn't on the menu button, then move the popup
- // onto the button so the popup appears when the user clicks it.
- // We'll move the popup back onto the Personas menu in the Tools menu
- // when the popup hides.
- // FIXME: remove this workaround once bug 461899 is fixed.
- if (this._menuPopup.parentNode != this._menuButton)
- this._menuButton.appendChild(this._menuPopup);
- }),
-
- onToolbarButtonMouseDown: PersonaService.wrap(function(event) {
- // If the menu popup isn't on the toolbar button, then move the popup
- // onto the button so the popup appears when the user clicks it.
- // We'll move the popup back onto the Personas menu in the Tools menu
- // when the popup hides.
- // FIXME: remove this workaround once bug 461899 is fixed.
- if (this._menuPopup.parentNode != this._toolbarButton)
- this._toolbarButton.appendChild(this._menuPopup);
- }),
-
- onPopupShowing: PersonaService.wrap(function(event) {
- if (event.target == this._menuPopup)
- this._rebuildMenu();
-
- return true;
- }),
-
- onPopupHiding: PersonaService.wrap(function(event) {
- if (event.target == this._menuPopup) {
- // If the menu popup isn't on the Personas menu in the Tools menu,
- // then move the popup back onto that menu so the popup appears when
- // the user selects it. We'll move the popup back onto the menu button
- // in onMenuButtonMouseDown when the user clicks on the menu button.
- if (this._menuPopup.parentNode != this._menu) {
- this._menuPopup.parentNode.removeAttribute("open");
- this._menu.appendChild(this._menuPopup);
- }
- }
- }),
-
- _rebuildMenu: function() {
- // If we don't have personas data, we won't be able to fully build the menu,
- // and we'll display a message to that effect in tooltips over the parts
- // of the menu that are data-dependent (the Most Popular, New, and
- // By Category submenus). The message also suggests that the user try again
- // in a few minutes, so here we immediately try to refresh data so it will
- // be available when the user tries again.
- if (!PersonaService.personas)
- PersonaService.refreshData();
-
- let openingSeparator = document.getElementById("personasOpeningSeparator");
- let closingSeparator = document.getElementById("personasClosingSeparator");
-
- // Remove everything between the two separators.
- while (openingSeparator.nextSibling && openingSeparator.nextSibling != closingSeparator)
- this._menuPopup.removeChild(openingSeparator.nextSibling);
-
- // Update the item that identifies the current persona.
- let personaStatus = document.getElementById("persona-current");
- let name = PersonaService.currentPersona ? PersonaService.currentPersona.name
- : this._strings.get("unnamedPersona");
-
- personaStatus.setAttribute("class", "menuitem-iconic");
-
- if (PersonaService.selected == "random") {
- personaStatus.setAttribute("label", this._strings.get("randomPersona", [PersonaService.category, name]));
- personaStatus.setAttribute("image", PersonaService.currentPersona.dataurl ? PersonaService.currentPersona.dataurl
- : "chrome://personas/content/personas_16x16.png");
-
- } if (PersonaService.selected == "default") {
- personaStatus.setAttribute("label", this._strings.get("Default"));
- personaStatus.removeAttribute("image");
- personaStatus.removeAttribute("menuitem-iconic");
- } else {
- personaStatus.setAttribute("label", name);
- personaStatus.setAttribute("image", PersonaService.currentPersona.dataurl ? PersonaService.currentPersona.dataurl
- : "chrome://personas/content/personas_16x16.png");
- }
+ onPreviewPersona: PersonaService.wrap(function(event) {
+ if (!this._prefs.get("previewEnabled"))
+ return;
- let personaStatusDetail = document.getElementById("persona-current-view-detail");
- personaStatusDetail.setAttribute("disabled", PersonaService.currentPersona.detailURL ? "false" : "true");
- personaStatusDetail.setAttribute("label", this._strings.get("viewDetail"));
- personaStatusDetail.setAttribute("oncommand", "PersonaController.openURLInTab(this.getAttribute('href'))");
- personaStatusDetail.setAttribute("href", PersonaService.currentPersona.detailURL);
-
- let personaStatusDesigner = document.getElementById("persona-current-view-designer");
- // collapse the "More From User" menu item for custom personas or personas
- // with null username. In this case we only check the username is not null
- // because it is used to generate the url to go to the personas designer page
- // (bug 526788).
- let persona = PersonaService.currentPersona;
- if (!persona.authorURL) {
- personaStatusDesigner.setAttribute("collapsed", true);
- } else {
- personaStatusDesigner.removeAttribute("collapsed");
- let designerLabel = persona.author || persona.username;
- personaStatusDesigner.setAttribute("label", this._strings.get("viewDesigner", [designerLabel]));
- personaStatusDesigner.setAttribute("oncommand", "PersonaController.openURLInTab(this.getAttribute('href'))");
- personaStatusDesigner.setAttribute("href", persona.authorURL);
- }
+ if (!event.target.hasAttribute("persona"))
+ throw "node does not have 'persona' attribute";
+
+ //this._previewPersona(event.target.getAttribute("persona"));
+ let persona = JSON.parse(event.target.getAttribute("persona"));
- // Update the checkmark on the Default menu item.
- document.getElementById("defaultPersona").setAttribute("checked", (PersonaService.selected == "default" ? "true" : "false"));
+ // We check if the user wants per-window personas
+ if (this._prefs.get("perwindow")) {
+ // We temporarily set the window specific persona here and let
+ // onResetPersona reset it.
+ switch (persona) {
+ case "default":
+ this._applyDefault();
+ break;
+ default:
+ this._applyPersona(persona);
+ break;
+ }
+ } else {
+ if (this._resetTimeoutID) {
+ window.clearTimeout(this._resetTimeoutID);
+ this._resetTimeoutID = null;
+ }
- // FIXME: factor out the duplicate code below.
+ let t = this;
+ let persona = JSON.parse(event.target.getAttribute("persona"));
+ let callback = function() {
+ t._previewPersona(persona)
+ };
+ this._previewTimeoutID =
+ window.setTimeout(callback, this._previewTimeout);
+ }
+ }),
- // Create the Favorites menu.
- {
- let menu = document.createElement("menu");
- menu.setAttribute("label", this._strings.get("favorites"));
+ _previewPersona: function(persona) {
+ PersonaService.previewPersona(persona);
+ },
- let popupmenu = menu.appendChild(document.createElement("menupopup"));
+ onResetPersona: PersonaService.wrap(function(event) {
+ if (!this._prefs.get("previewEnabled"))
+ return;
- if (!PersonaService.favorites) {
- let item = popupmenu.appendChild(document.createElement("menuitem"));
- item.setAttribute("label", this._strings.get("favoritesSignIn"));
- item.setAttribute("oncommand", "PersonaController.openURLInTab(this.getAttribute('href'))");
- item.setAttribute("href", PersonaService.getURL("favorites-browse"));
- } else {
- let favorites = PersonaService.favorites;
- if (favorites.length) {
- for each (let persona in favorites)
- popupmenu.appendChild(this._createPersonaItem(persona));
- popupmenu.appendChild(document.createElement("menuseparator"));
-
- // Disable random from favorites menu item if per-window
- // personas are enabled
- if (!this._prefs.get("perwindow")) {
- let item = popupmenu.appendChild(document.createElement("menuitem"));
- item.setAttribute("label", this._strings.get("useRandomPersona", [this._strings.get("favorites")]));
- // item.setAttribute("type", "checkbox");
- item.setAttribute("checked", (PersonaService.selected == "randomFavorite"));
- item.setAttribute("autocheck", "false");
- item.setAttribute("oncommand", "PersonaController.toggleFavoritesRotation()");
- }
+ //this._resetPersona();
+ // If per-window personas are enabled and there's a valid persona
+ // value set for this window, don't reset.
+ if (this._prefs.get("perwindow") &&
+ this._sessionStore.getWindowValue(window, "persona")) {
+ return;
}
- // go to my favorites menu item
- let item = popupmenu.appendChild(document.createElement("menuitem"));
- item.setAttribute("label", this._strings.get("favoritesGoTo"));
- item.setAttribute("oncommand", "PersonaController.openURLInTab(this.getAttribute('href'))");
- item.setAttribute("href", PersonaService.getURL("favorites-browse"));
- }
+ if (this._previewTimeoutID) {
+ window.clearTimeout(this._previewTimeoutID);
+ this._previewTimeoutID = null;
+ }
- this._menuPopup.insertBefore(menu, closingSeparator);
- }
+ let t = this;
+ let callback = function() {
+ t._resetPersona()
+ };
+ this._resetTimeoutID = window.setTimeout(callback, this._previewTimeout);
+ }),
+
+ _resetPersona: function() {
+ PersonaService.resetPersona();
+ },
+
+ /**
+ * Confirm that Firefox has this Personas extension when requested by
+ * a web page via a CheckPersonas event. Checks to ensure the page is hosted
+ * on a host in the whitelist before responding to the event, so only
+ * whitelisted pages can find out if Personas is installed.
+ *
+ * @param event {Event}
+ * the CheckPersonas DOM event
+ */
+ onCheckPersonasFromContent: PersonaService.wrap(function(event) {
+ this._authorizeHost(event);
+ event.target.setAttribute("personas", "true");
+ }),
+
+ onSelectPreferences: PersonaService.wrap(function() {
+ window.openDialog('chrome://personas/content/preferences.xul', '',
+ 'chrome,titlebar,toolbar,centerscreen');
+ }),
+
+ onEditCustomPersona: PersonaService.wrap(function() {
+ this.openURLInTab("chrome://personas/content/customPersonaEditor.xul");
+ }),
+
+ /**
+ * Adds the favorite persona specified by a web page via a AddFavoritePersona event.
+ * Checks to ensure the page is hosted on a server authorized to select personas.
+ *
+ * @param event {Event}
+ * the AddFavoritePersona DOM event
+ */
+ onAddFavoritePersonaFromContent: PersonaService.wrap(function(event) {
+ this._authorizeHost(event);
+ this.onAddFavoritePersona(event);
+ }),
+
+ /**
+ * Adds the persona specified by the DOM node target of the given event to
+ * the favorites list.
+ *
+ * @param event {Event}
+ * the AddFavoritePersona DOM event
+ */
+ onAddFavoritePersona: PersonaService.wrap(function(event) {
+ let node = event.target;
+
+ if (!node.hasAttribute("persona"))
+ throw "node does not have 'persona' attribute";
+
+ let persona = node.getAttribute("persona");
+ PersonaService.addFavoritePersona(JSON.parse(persona));
+ }),
+
+ /**
+ * Removes the favorite persona specified by a web page via a
+ * RemoveFavoritePersona event.
+ * Checks to ensure the page is hosted on a server authorized to select personas.
+ *
+ * @param event {Event}
+ * the RemoveFavoritePersona DOM event
+ */
+ onRemoveFavoritePersonaFromContent: PersonaService.wrap(function(event) {
+ this._authorizeHost(event);
+ this.onRemoveFavoritePersona(event);
+ }),
+
+ /**
+ * Removes the persona specified by the DOM node target of the given event
+ * from the favorites list.
+ *
+ * @param event {Event}
+ * the RemoveFavoritePersona DOM event
+ */
+ onRemoveFavoritePersonaFromContent: PersonaService.wrap(function(event) {
+ let node = event.target;
+
+ if (!node.hasAttribute("persona"))
+ throw "node does not have 'persona' attribute";
+
+ let persona = node.getAttribute("persona");
+ PersonaService.removeFavoritePersona(JSON.parse(persona));
+ }),
+
+ /**
+ * Ensure the host that loaded the document from which the given DOM event
+ * came matches an entry in the personas whitelist. The host matches if it
+ * equals one of the entries in the whitelist. For example, if
+ * www.mozilla.com is an entry in the whitelist, then www.mozilla.com matches,
+ * but labs.mozilla.com, mozilla.com, and evil.com do not.
+ *
+ * @param aEvent {Event} the DOM event
+ */
+ _authorizeHost: function(aEvent) {
+ let host = aEvent.target.ownerDocument.location.hostname;
+ let authorizedHosts = this._prefs.get("authorizedHosts").split(/[, ]+/);
+ if (!authorizedHosts.some(function(v) v == host))
+ throw host + " not authorized to modify personas";
+ },
+
+
+ //**************************************************************************//
+ // Popup Construction
+
+ onMenuButtonMouseDown: PersonaService.wrap(function(event) {
+ // If the menu popup isn't on the menu button, then move the popup
+ // onto the button so the popup appears when the user clicks it.
+ // We'll move the popup back onto the Personas menu in the Tools menu
+ // when the popup hides.
+ // FIXME: remove this workaround once bug 461899 is fixed.
+ if (this._menuPopup.parentNode != this._menuButton)
+ this._menuButton.appendChild(this._menuPopup);
+ }),
+
+ onToolbarButtonMouseDown: PersonaService.wrap(function(event) {
+ // If the menu popup isn't on the toolbar button, then move the popup
+ // onto the button so the popup appears when the user clicks it.
+ // We'll move the popup back onto the Personas menu in the Tools menu
+ // when the popup hides.
+ // FIXME: remove this workaround once bug 461899 is fixed.
+ if (this._menuPopup.parentNode != this._toolbarButton)
+ this._toolbarButton.appendChild(this._menuPopup);
+ }),
+
+ onPopupShowing: PersonaService.wrap(function(event) {
+ if (event.target == this._menuPopup)
+ this._rebuildMenu();
+
+ return true;
+ }),
+
+ onPopupHiding: PersonaService.wrap(function(event) {
+ if (event.target == this._menuPopup) {
+ // If the menu popup isn't on the Personas menu in the Tools menu,
+ // then move the popup back onto that menu so the popup appears when
+ // the user selects it. We'll move the popup back onto the menu button
+ // in onMenuButtonMouseDown when the user clicks on the menu button.
+ if (this._menuPopup.parentNode != this._menu) {
+ this._menuPopup.parentNode.removeAttribute("open");
+ this._menu.appendChild(this._menuPopup);
+ }
+ }
+ }),
- // Create the "Recently Selected" menu.
- {
- let menu = document.createElement("menu");
- menu.setAttribute("label", this._strings.get("recent"));
- let popupmenu = document.createElement("menupopup");
+ _rebuildMenu: function() {
+ // If we don't have personas data, we won't be able to fully build the menu,
+ // and we'll display a message to that effect in tooltips over the parts
+ // of the menu that are data-dependent (the Most Popular, New, and
+ // By Category submenus). The message also suggests that the user try again
+ // in a few minutes, so here we immediately try to refresh data so it will
+ // be available when the user tries again.
+ if (!PersonaService.personas)
+ PersonaService.refreshData();
- let recentPersonas = PersonaService.getRecentPersonas();
- for each (let persona in recentPersonas) {
- popupmenu.appendChild(this._createPersonaItem(persona));
- }
+ let openingSeparator = document.getElementById("personasOpeningSeparator");
+ let closingSeparator = document.getElementById("personasClosingSeparator");
- menu.appendChild(popupmenu);
- this._menuPopup.insertBefore(menu, closingSeparator);
- this._menuPopup.insertBefore(document.createElement("menuseparator"), closingSeparator);
- }
+ // Remove everything between the two separators.
+ while (openingSeparator.nextSibling && openingSeparator.nextSibling != closingSeparator)
+ this._menuPopup.removeChild(openingSeparator.nextSibling);
+
+ // Update the item that identifies the current persona.
+ let personaStatus = document.getElementById("persona-current");
+ let name = PersonaService.currentPersona ? PersonaService.currentPersona.name : this._strings.get("unnamedPersona");
- // Create the Featured menu.
- {
- let menu = document.createElement("menu");
- menu.setAttribute("label", this._strings.get("featured"));
-
- if (PersonaService.personas) {
- let popupmenu = document.createElement("menupopup");
- for each (let persona in PersonaService.personas.featured)
- popupmenu.appendChild(this._createPersonaItem(persona));
-
- // Create an item that picks a random persona from the category.
- // Disable random from category menu item if per-window
- // personas are enabled.
- if (!this._prefs.get("perwindow")) {
- popupmenu.appendChild(document.createElement("menuseparator"));
- popupmenu.appendChild(this._createRandomItem(this._strings.get("featured"), "featured"));
+ personaStatus.setAttribute("class", "menuitem-iconic");
+
+ if (PersonaService.selected == "random") {
+ personaStatus.setAttribute("label", this._strings.get("randomPersona", [PersonaService.category, name]));
+ personaStatus.setAttribute("image", PersonaService.currentPersona.dataurl ? PersonaService.currentPersona.dataurl : "chrome://personas/content/personas_16x16.png");
+
+ }
+ if (PersonaService.selected == "default") {
+ personaStatus.setAttribute("label", this._strings.get("Default"));
+ personaStatus.removeAttribute("image");
+ personaStatus.removeAttribute("menuitem-iconic");
+ } else {
+ personaStatus.setAttribute("label", name);
+ personaStatus.setAttribute("image", PersonaService.currentPersona.dataurl ? PersonaService.currentPersona.dataurl : "chrome://personas/content/personas_16x16.png");
}
- // Create an item that links to the gallery for this category.
- popupmenu.appendChild(
- this._createViewMoreItem(this._strings.get("featured"),
- "featured"));
+ let personaStatusDetail = document.getElementById("persona-current-view-detail");
+ personaStatusDetail.setAttribute("disabled", PersonaService.currentPersona.detailURL ? "false" : "true");
+ personaStatusDetail.setAttribute("label", this._strings.get("viewDetail"));
+ personaStatusDetail.setAttribute("oncommand", "PersonaController.openURLInTab(this.getAttribute('href'))");
+ personaStatusDetail.setAttribute("href", PersonaService.currentPersona.detailURL);
+
+ let personaStatusDesigner = document.getElementById("persona-current-view-designer");
+ // collapse the "More From User" menu item for custom personas or personas
+ // with null username. In this case we only check the username is not null
+ // because it is used to generate the url to go to the personas designer page
+ // (bug 526788).
+ let persona = PersonaService.currentPersona;
+ if (!persona.authorURL) {
+ personaStatusDesigner.setAttribute("collapsed", true);
+ } else {
+ personaStatusDesigner.removeAttribute("collapsed");
+ let designerLabel = persona.author || persona.username;
+ personaStatusDesigner.setAttribute("label", this._strings.get("viewDesigner", [designerLabel]));
+ personaStatusDesigner.setAttribute("oncommand", "PersonaController.openURLInTab(this.getAttribute('href'))");
+ personaStatusDesigner.setAttribute("href", persona.authorURL);
+ }
- menu.appendChild(popupmenu);
- }
- else {
- menu.setAttribute("disabled", "true");
- menu.setAttribute("tooltip", "personasDataUnavailableTooltip");
- }
+ // Update the checkmark on the Default menu item.
+ document.getElementById("defaultPersona").setAttribute("checked", (PersonaService.selected == "default" ? "true" : "false"));
+
+ // FIXME: factor out the duplicate code below.
+
+ // Create the Favorites menu.
+ {
+ let menu = document.createElement("menu");
+ menu.setAttribute("label", this._strings.get("favorites"));
+
+ let popupmenu = menu.appendChild(document.createElement("menupopup"));
+
+ if (PersonaService.favoritesError) {
+ let item = popupmenu.appendChild(document.createElement("menuitem"));
+ item.setAttribute("label", this._strings.get("favoritesError"));
+ item.setAttribute("disabled", "true");
+ } else if (!PersonaService.favorites) {
+ let item = popupmenu.appendChild(document.createElement("menuitem"));
+ item.setAttribute("label", this._strings.get("favoritesSignIn"));
+ item.setAttribute("oncommand", "PersonaController.openURLInTab(this.getAttribute('href'))");
+ item.setAttribute("href", PersonaService.getURL("favorites-browse"));
+ } else {
+ let favorites = PersonaService.favorites;
+ if (favorites.length) {
+ for each(let persona in favorites)
+ popupmenu.appendChild(this._createPersonaItem(persona));
+ popupmenu.appendChild(document.createElement("menuseparator"));
+
+ // Disable random from favorites menu item if per-window
+ // personas are enabled
+ if (!this._prefs.get("perwindow")) {
+ let item = popupmenu.appendChild(document.createElement("menuitem"));
+ item.setAttribute("label", this._strings.get("useRandomPersona", [this._strings.get("favorites")]));
+ // item.setAttribute("type", "checkbox");
+ item.setAttribute("checked", (PersonaService.selected == "randomFavorite"));
+ item.setAttribute("autocheck", "false");
+ item.setAttribute("oncommand", "PersonaController.toggleFavoritesRotation()");
+ }
+ }
+
+ // go to my favorites menu item
+ let item = popupmenu.appendChild(document.createElement("menuitem"));
+ item.setAttribute("label", this._strings.get("favoritesGoTo"));
+ item.setAttribute("oncommand", "PersonaController.openURLInTab(this.getAttribute('href'))");
+ item.setAttribute("href", PersonaService.getURL("favorites-browse"));
+ }
- this._menuPopup.insertBefore(menu, closingSeparator);
- }
+ this._menuPopup.insertBefore(menu, closingSeparator);
+ }
- // Update the Custom menu. Custom personas unavailable in per-window
- // personas mode.
- let customMenu = document.getElementById("personas-plus-custom-menu");
- customMenu.hidden = this._prefs.get("perwindow") || !this._prefs.get("showCustomMenu");
- if (!customMenu.hidden) {
- let name = PersonaService.customPersona &&
- PersonaService.customPersona.name ? PersonaService.customPersona.name
- : this._strings.get("customPersona");
- customMenu.setAttribute("label", name);
- }
- },
+ // Create the "Recently Selected" menu.
+ {
+ let menu = document.createElement("menu");
+ menu.setAttribute("label", this._strings.get("recent"));
+ let popupmenu = document.createElement("menupopup");
- _createPersonaItem: function(persona) {
- let item = document.createElement("menuitem");
- let theme = PersonaService.getPersonaJSON(persona);
+ let recentPersonas = PersonaService.getRecentPersonas();
+ for each(let persona in recentPersonas) {
+ popupmenu.appendChild(this._createPersonaItem(persona));
+ }
- let headerURI;
- if (persona.custom) {
- headerURI = theme.headerURL || theme.header;
- } else {
- headerURI = theme.dataurl || theme.iconURL;
- }
+ menu.appendChild(popupmenu);
+ this._menuPopup.insertBefore(menu, closingSeparator);
+ this._menuPopup.insertBefore(document.createElement("menuseparator"), closingSeparator);
+ }
- item.setAttribute("class", "menuitem-iconic");
- item.setAttribute("image", headerURI);
- item.setAttribute("label", persona.name);
-// item.setAttribute("type", "checkbox");
- item.setAttribute("checked", (PersonaService.selected != "default" &&
- PersonaService.currentPersona &&
- PersonaService.currentPersona.id == persona.id));
- item.setAttribute("autocheck", "false");
- item.setAttribute("oncommand", "PersonaController.onSelectPersona(event)");
- item.setAttribute("recent", persona.recent ? "true" : "false");
- item.setAttribute("persona", JSON.stringify(theme));
- item.addEventListener("DOMMenuItemActive", function(evt) { PersonaController.onPreviewPersona(evt) }, false);
- item.addEventListener("DOMMenuItemInactive", function(evt) { PersonaController.onResetPersona(evt) }, false);
-
- return item;
- },
-
- _createViewMoreItem: function(category, categoryId) {
- let item = document.createElement("menuitem");
-
- item.setAttribute("class", "menuitem-iconic");
- item.setAttribute("label", this._strings.get("viewMoreFrom", [category]));
- item.setAttribute("oncommand", "PersonaController.openURLInTab(this.getAttribute('href'))");
-
- if (categoryId == "featured") {
- item.setAttribute("href", PersonaService.getURL("browse", { SORT: "featured" }));
- }
+ // Create the Featured menu.
+ {
+ let menu = document.createElement("menu");
+ menu.setAttribute("label", this._strings.get("featured"));
+
+ if (PersonaService.personas) {
+ let popupmenu = document.createElement("menupopup");
+ for each(let persona in PersonaService.personas.featured)
+ popupmenu.appendChild(this._createPersonaItem(persona));
+
+ // Create an item that picks a random persona from the category.
+ // Disable random from category menu item if per-window
+ // personas are enabled.
+ if (!this._prefs.get("perwindow")) {
+ popupmenu.appendChild(document.createElement("menuseparator"));
+ popupmenu.appendChild(this._createRandomItem(this._strings.get("featured"), "featured"));
+ }
+
+ // Create an item that links to the gallery for this category.
+ popupmenu.appendChild(
+ this._createViewMoreItem(this._strings.get("featured"),
+ "featured"));
+
+ menu.appendChild(popupmenu);
+ } else {
+ menu.setAttribute("disabled", "true");
+ menu.setAttribute("tooltip", "personasDataUnavailableTooltip");
+ }
- return item;
- },
+ this._menuPopup.insertBefore(menu, closingSeparator);
+ }
+
+ // Update the Custom menu. Custom personas unavailable in per-window
+ // personas mode.
+ let customMenu = document.getElementById("personas-plus-custom-menu");
+ customMenu.hidden = this._prefs.get("perwindow") || !this._prefs.get("showCustomMenu");
+ if (!customMenu.hidden) {
+ let name = PersonaService.customPersona &&
+ PersonaService.customPersona.name ? PersonaService.customPersona.name : this._strings.get("customPersona");
+ customMenu.setAttribute("label", name);
+ }
+ },
+
+ _createPersonaItem: function(persona) {
+ let item = document.createElement("menuitem");
+ let theme = PersonaService.getPersonaJSON(persona);
- _createRandomItem: function(aCategoryName, aCategory) {
- let item = document.createElement("menuitem");
+ let headerURI;
+ if (persona.custom) {
+ headerURI = theme.headerURL || theme.header;
+ } else {
+ headerURI = theme.dataurl || theme.iconURL;
+ }
- item.setAttribute("class", "menuitem-iconic");
- item.setAttribute("label", this._strings.get("useRandomPersona", [aCategoryName]));
- item.setAttribute("oncommand", "PersonaController.onSelectPersona(event)");
- item.setAttribute("persona", "random");
- item.setAttribute("category", aCategory || aCategoryName);
+ item.setAttribute("class", "menuitem-iconic");
+ item.setAttribute("image", headerURI);
+ item.setAttribute("label", persona.name);
+ // item.setAttribute("type", "checkbox");
+ item.setAttribute("checked", (PersonaService.selected != "default" &&
+ PersonaService.currentPersona &&
+ PersonaService.currentPersona.id == persona.id));
+ item.setAttribute("autocheck", "false");
+ item.setAttribute("oncommand", "PersonaController.onSelectPersona(event)");
+ item.setAttribute("recent", persona.recent ? "true" : "false");
+ item.setAttribute("persona", JSON.stringify(theme));
+ item.addEventListener("DOMMenuItemActive", function(evt) {
+ PersonaController.onPreviewPersona(evt)
+ }, false);
+ item.addEventListener("DOMMenuItemInactive", function(evt) {
+ PersonaController.onResetPersona(evt)
+ }, false);
+
+ return item;
+ },
+
+ _createViewMoreItem: function(category, categoryId) {
+ let item = document.createElement("menuitem");
+
+ item.setAttribute("class", "menuitem-iconic");
+ item.setAttribute("label", this._strings.get("viewMoreFrom", [category]));
+ item.setAttribute("oncommand", "PersonaController.openURLInTab(this.getAttribute('href'))");
- return item;
- },
+ if (categoryId == "featured") {
+ item.setAttribute("href", PersonaService.getURL("browse", {
+ SORT: "featured"
+ }));
+ }
- toggleFavoritesRotation : function() {
- if (PersonaService.selected != "randomFavorite") {
- PersonaService.changeToRandomFavoritePersona();
- } else {
- PersonaService.selected = "current";
+ return item;
+ },
+
+ _createRandomItem: function(aCategoryName, aCategory) {
+ let item = document.createElement("menuitem");
+
+ item.setAttribute("class", "menuitem-iconic");
+ item.setAttribute("label", this._strings.get("useRandomPersona", [aCategoryName]));
+ item.setAttribute("oncommand", "PersonaController.onSelectPersona(event)");
+ item.setAttribute("persona", "random");
+ item.setAttribute("category", aCategory || aCategoryName);
+
+ return item;
+ },
+
+ toggleFavoritesRotation: function() {
+ if (PersonaService.selected != "randomFavorite") {
+ PersonaService.changeToRandomFavoritePersona();
+ } else {
+ PersonaService.selected = "current";
+ }
}
- }
};
// Import generic modules into the persona controller rather than
// the global namespace so they don't conflict with modules with the same names
// imported by other extensions.
-Cu.import("resource://personas/modules/Observers.js", PersonaController);
-Cu.import("resource://personas/modules/Preferences.js", PersonaController);
-Cu.import("resource://personas/modules/StringBundle.js", PersonaController);
-Cu.import("resource://personas/modules/URI.js", PersonaController);
+Cu.import("resource://personas/modules/Observers.js", PersonaController);
+Cu.import("resource://personas/modules/Preferences.js", PersonaController);
+Cu.import("resource://personas/modules/StringBundle.js", PersonaController);
+Cu.import("resource://personas/modules/URI.js", PersonaController);
// Import modules that come with Firefox into the persona controller rather
// than the global namespace.
// LightweightThemeManager may not be not available (Firefox < 3.6 or Thunderbird)
-try { Cu.import("resource://gre/modules/LightweightThemeManager.jsm", PersonaController); }
-catch (e) {}
-
-window.addEventListener("load", function(e) { PersonaController.startUp(e) }, false);
-window.addEventListener("unload", function(e) { PersonaController.shutDown(e) }, false);
+try {
+ Cu.import("resource://gre/modules/LightweightThemeManager.jsm", PersonaController);
+} catch (e) {}
+
+window.addEventListener("load", PersonaService.wrap(function(e) {
+ PersonaController.startUp(e)
+}), false);
+window.addEventListener("unload", PersonaService.wrap(function(e) {
+ PersonaController.shutDown(e)
+}), false);
\ No newline at end of file
diff --git a/extension/locale/bg-BG/personas.properties b/extension/locale/bg-BG/personas.properties
index 85a30b3..38ff649 100644
--- a/extension/locale/bg-BG/personas.properties
+++ b/extension/locale/bg-BG/personas.properties
@@ -1,9 +1,10 @@
# The labels of various items in the personas menu.
About=Относно
-featured= Featured
+featured=Featured
recent=Скоро избирани
favorites=Любими
favoritesSignIn=Влезте, за да достъпите вашите любими
+favoritesError=При зареждане на вашите любими възникна грешка
viewDetail=Преглед детайли
favoritesGoTo=Към моите любими
# Labels that identify the current persona when the current persona is either
@@ -19,7 +20,7 @@ viewDesigner=Още от %S
# LOCALIZATION NOTE (viewMore): the label of a command that will load the gallery
# page on getpersonas.com for this specific category.
# %1$S = number of additional personas in this category available on the site (e.g. 1,542)
-viewMoreFrom= More from %1$S...
+viewMoreFrom=More from %1$S...
# %2$2 = the name of the category (e.g. Music)
# LOCALIZATION NOTE (notification.personaWasSelected): the text
# of the notification shown when a persona is selected for the first time.
diff --git a/extension/locale/cs-CZ/personas.properties b/extension/locale/cs-CZ/personas.properties
index 4e22243..674ee41 100644
--- a/extension/locale/cs-CZ/personas.properties
+++ b/extension/locale/cs-CZ/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent=Posledně použité
favorites=Oblíbené
favoritesSignIn=Pro práci s oblíbenými položkami se musíte přihlásit
+favoritesError= An error occurred while loading your favorites
viewDetail=Zobrazit podrobnosti
favoritesGoTo=Přejít do oblíbených...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/da/personas.properties b/extension/locale/da/personas.properties
index 63cf430..cf486e0 100644
--- a/extension/locale/da/personas.properties
+++ b/extension/locale/da/personas.properties
@@ -4,6 +4,7 @@ featured=Anbefalede
recent=Mine senest valgte
favorites=Mine favoritter
favoritesSignIn=Log ind for at få adgang til dine favoritter
+favoritesError=Der opstod en fejl under indlæsning af dine favoritter
viewDetail=Vis detaljer…
favoritesGoTo=Gå til mine favoritter…
# Labels that identify the current persona when the current persona is either
@@ -15,7 +16,7 @@ customPersona=Tilpas Persona
# LOCALIZATION NOTE (viewDesigner): a label that indicates the name of the designer
# of the current persona as part of a link to more personas
# $@ = the name of the designer of the current persona
-viewDesigner=Flere fra %S…
+viewDesigner=Flere af %S…
# LOCALIZATION NOTE (viewMore): the label of a command that will load the gallery
# page on getpersonas.com for this specific category.
# %1$S = number of additional personas in this category available on the site (e.g. 1,542)
diff --git a/extension/locale/de/personas.properties b/extension/locale/de/personas.properties
index 5749b7e..f788d34 100644
--- a/extension/locale/de/personas.properties
+++ b/extension/locale/de/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent=Kürzlich ausgewählt
favorites=Meine Favoriten
favoritesSignIn=Melden Sie sich an, um auf Ihre Favoriten zuzugreifen
+favoritesError= An error occurred while loading your favorites
viewDetail=Details ansehen...
favoritesGoTo=Meinen Favoriten aufrufen...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/el/personas.properties b/extension/locale/el/personas.properties
index 032143d..2f2c6c6 100644
--- a/extension/locale/el/personas.properties
+++ b/extension/locale/el/personas.properties
@@ -4,6 +4,7 @@ featured=Προβεβλημένα
recent=Επιλεγμένα πρόσφατα
favorites=Αγαπημένα
favoritesSignIn=Συνδεθείτε για να έχετε πρόσβαση στα αγαπημένα σας
+favoritesError=Παρουσιάστηκε σφάλμα κατά τη φόρτωση των αγαπημένων σας
viewDetail=Εμφάνιση λεπτομερειών...
favoritesGoTo=Μετάβαση στα αγαπημένα μου...
# Labels that identify the current persona when the current persona is either
@@ -19,7 +20,7 @@ viewDesigner=Περισσότερα από %S...
# LOCALIZATION NOTE (viewMore): the label of a command that will load the gallery
# page on getpersonas.com for this specific category.
# %1$S = number of additional personas in this category available on the site (e.g. 1,542)
-viewMoreFrom= More from %1$S...
+viewMoreFrom=More from %1$S...
# %2$2 = the name of the category (e.g. Music)
# LOCALIZATION NOTE (notification.personaWasSelected): the text
# of the notification shown when a persona is selected for the first time.
diff --git a/extension/locale/en-US/personas.properties b/extension/locale/en-US/personas.properties
index be39106..0346770 100644
--- a/extension/locale/en-US/personas.properties
+++ b/extension/locale/en-US/personas.properties
@@ -4,6 +4,7 @@ featured = Featured
recent = My Recently Selected
favorites = My Favorites
favoritesSignIn = Sign In to Access Your Favorites
+favoritesError = An error occurred while loading your favorites
viewDetail = View Details...
favoritesGoTo = Go to My Favorites...
diff --git a/extension/locale/es-AR/personas.properties b/extension/locale/es-AR/personas.properties
index 8a50480..f3de633 100644
--- a/extension/locale/es-AR/personas.properties
+++ b/extension/locale/es-AR/personas.properties
@@ -4,6 +4,7 @@ featured=Destacadas
recent=Mi selección reciente
favorites=Mis favoritas
favoritesSignIn=Ingresá para acceder a tus favoritas
+favoritesError=Ocurrió un error al cargar tus favoritas
viewDetail=Ver detalles…
favoritesGoTo=Ir a mis favoritas…
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/es-CL/personas.properties b/extension/locale/es-CL/personas.properties
index 83545a3..8010649 100644
--- a/extension/locale/es-CL/personas.properties
+++ b/extension/locale/es-CL/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent=Recientemente seleccionado
favorites=Favoritos
favoritesSignIn=Inicie sesión para acceder a sus favoritos
+favoritesError= An error occurred while loading your favorites
viewDetail=Ver detalles...
favoritesGoTo=Ir a Favoritos...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/es-ES/personas.properties b/extension/locale/es-ES/personas.properties
index 309af26..cbf655e 100644
--- a/extension/locale/es-ES/personas.properties
+++ b/extension/locale/es-ES/personas.properties
@@ -4,6 +4,7 @@ featured=Destacados
recent=Seleccionados recientemente
favorites=Mis Favoritos
favoritesSignIn=Iniciar sesión para acceder a tus favoritos
+favoritesError=Se ha producido un error mientras se cargaban sus favoritos
viewDetail=Ver detalles...
favoritesGoTo=Ir a mis favoritos...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/es-MX/personas.properties b/extension/locale/es-MX/personas.properties
index e37b3d8..3acb36e 100644
--- a/extension/locale/es-MX/personas.properties
+++ b/extension/locale/es-MX/personas.properties
@@ -4,6 +4,7 @@ featured=Destacado
recent=Mi selección reciente
favorites=Mis Favoritos
favoritesSignIn=Inicia sesión para acceder a tus favoritos
+favoritesError=Ocurrió un error al cargar tus favoritos
viewDetail=Ver detalles...
favoritesGoTo=Ir a mis favoritos
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/eu/personas.properties b/extension/locale/eu/personas.properties
index e1e142e..a56062b 100644
--- a/extension/locale/eu/personas.properties
+++ b/extension/locale/eu/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent=Azkenez hautatuak
favorites=Gogokoenak
favoritesSignIn=Saioa hasi zure gogokoenak atzitzeko
+favoritesError= An error occurred while loading your favorites
viewDetail=Ikusi xehtasunak...
favoritesGoTo=Joan nire gogokoetara...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/fi/personas.properties b/extension/locale/fi/personas.properties
index ddeb4f1..03da007 100644
--- a/extension/locale/fi/personas.properties
+++ b/extension/locale/fi/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent=Viimeksi valitut
favorites=Suosikit
favoritesSignIn=Kirjaudu sisään nähdäksesi suosikkisi
+favoritesError= An error occurred while loading your favorites
viewDetail=Katso lisätietoja...
favoritesGoTo=Siirry suosikkeihin...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/fr/personas.properties b/extension/locale/fr/personas.properties
index 9ab5f30..41d837d 100644
--- a/extension/locale/fr/personas.properties
+++ b/extension/locale/fr/personas.properties
@@ -4,6 +4,7 @@ featured=Thèmes vedettes
recent=Choisis récemment
favorites=Favoris
favoritesSignIn=Identifiez-vous pour accéder à vos favoris
+favoritesError=Une erreur s'est produite pendant le chargement de vos favoris
viewDetail=Détails…
favoritesGoTo=Accéder à mes favoris…
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/fy-NL/personas.properties b/extension/locale/fy-NL/personas.properties
index dc3c183..635bcb7 100644
--- a/extension/locale/fy-NL/personas.properties
+++ b/extension/locale/fy-NL/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent=Resint selektearre
favorites=Favoriten
favoritesSignIn=Loch yn om jo favoriten te besjen
+favoritesError= An error occurred while loading your favorites
viewDetail=Toan details...
favoritesGoTo=Gean nei myn favoriten...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/ga-IE/personas.properties b/extension/locale/ga-IE/personas.properties
index 0a28841..6677c7c 100644
--- a/extension/locale/ga-IE/personas.properties
+++ b/extension/locale/ga-IE/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent=Roghnaithe agam le déanaí
favorites=Ceanáin
favoritesSignIn=Logáil Isteach chun do chuid Ceanán a fheiceáil
+favoritesError= An error occurred while loading your favorites
viewDetail=Féach ar na mionsonraí...
favoritesGoTo=Féach ar mo chuid Ceanán...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/gl-ES/personas.properties b/extension/locale/gl-ES/personas.properties
index 01b5883..474f14f 100644
--- a/extension/locale/gl-ES/personas.properties
+++ b/extension/locale/gl-ES/personas.properties
@@ -4,6 +4,7 @@ featured=Destacado
recent=Seleccionados recentemente
favorites=Favoritos
favoritesSignIn=Acceda aos seus favoritos
+favoritesError=Produciuse un erro mentres se cargaban os favoritos.
viewDetail=Ver detalles...
favoritesGoTo=Ir aos favoritos...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/he-IL/personas.properties b/extension/locale/he-IL/personas.properties
index 7bf5df4..1acd641 100644
--- a/extension/locale/he-IL/personas.properties
+++ b/extension/locale/he-IL/personas.properties
@@ -4,6 +4,7 @@ featured=נבחרים
recent=נבחר לאחרונה
favorites=המועדפים שלי
favoritesSignIn=התחברות כדי לגשת אל המועדפים שלך
+favoritesError= An error occurred while loading your favorites
viewDetail=הצגת הפרטים...
favoritesGoTo=לך אל המועדפים שלי...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/hu-HU/personas.properties b/extension/locale/hu-HU/personas.properties
index 151d4e2..b0a7308 100644
--- a/extension/locale/hu-HU/personas.properties
+++ b/extension/locale/hu-HU/personas.properties
@@ -4,6 +4,7 @@ featured=Kiemelt
recent=Legutóbb kiválasztott
favorites=Kedvencek
favoritesSignIn=A Kedvencek eléréséhez be kell jelentkezni
+favoritesError=Hiba történt a kedvencek betöltése közben
viewDetail=Részletek...
favoritesGoTo=Kedvencek...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/it/personas.properties b/extension/locale/it/personas.properties
index 8d94063..bc444b6 100644
--- a/extension/locale/it/personas.properties
+++ b/extension/locale/it/personas.properties
@@ -4,6 +4,7 @@ featured=Consigliati
recent=Selezionati recentemente
favorites=Preferiti
favoritesSignIn=Accedi per visualizzare i tuoi Preferiti
+favoritesError= An error occurred while loading your favorites
viewDetail=Visualizza dettagli...
favoritesGoTo=Visualizza i miei Preferiti
# the default persona, a persona that doesn't have a name, or a custom persona
diff --git a/extension/locale/ja-JP-mac/personas.properties b/extension/locale/ja-JP-mac/personas.properties
index 400c517..5d32c50 100644
--- a/extension/locale/ja-JP-mac/personas.properties
+++ b/extension/locale/ja-JP-mac/personas.properties
@@ -4,6 +4,7 @@ featured=注目
recent=最近の選択
favorites=お気に入り
favoritesSignIn=サインインしてお気に入りにアクセス
+favoritesError=お気に入りの読み込み中にエラーが発生しました
viewDetail=詳細を表示...
favoritesGoTo=お気に入りを開く...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/ja-JP/personas.properties b/extension/locale/ja-JP/personas.properties
index 172cd5a..bacd039 100644
--- a/extension/locale/ja-JP/personas.properties
+++ b/extension/locale/ja-JP/personas.properties
@@ -4,6 +4,7 @@ featured=注目
recent=最近の選択
favorites=お気に入り
favoritesSignIn=サインインしてお気に入りにアクセス
+favoritesError=お気に入りを読み込み中にエラーが発生しました
viewDetail=詳細を表示...
favoritesGoTo=お気に入りに行く...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/ja/personas.properties b/extension/locale/ja/personas.properties
index fa374cf..d0d87be 100644
--- a/extension/locale/ja/personas.properties
+++ b/extension/locale/ja/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent=最近の選択
favorites=お気に入り
favoritesSignIn=サインインしてお気に入りにアクセス
+favoritesError= An error occurred while loading your favorites
viewDetail=詳細を表示...
favoritesGoTo=お気に入りに行く...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/ko-KR/personas.properties b/extension/locale/ko-KR/personas.properties
index 48ebce5..6b5980d 100644
--- a/extension/locale/ko-KR/personas.properties
+++ b/extension/locale/ko-KR/personas.properties
@@ -4,6 +4,7 @@ featured=인기
recent=최근 선택
favorites=선호하는 스킨
favoritesSignIn=당신이 선호하는 스킨 접속을 위해 로그인
+favoritesError= An error occurred while loading your favorites
viewDetail=상세 보기
favoritesGoTo=나의 즐겨찾기로 가기...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/lt-LT/personas.properties b/extension/locale/lt-LT/personas.properties
index 1c4be2c..ccac62c 100644
--- a/extension/locale/lt-LT/personas.properties
+++ b/extension/locale/lt-LT/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent=Neseniai naudotos
favorites=Mėgstamiausios
favoritesSignIn=Prisijungti, mėgstamiausių pasiekimui
+favoritesError= An error occurred while loading your favorites
viewDetail=Išsamiau...
favoritesGoTo=Eiti į mėgstamiausius...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/lt/personas.properties b/extension/locale/lt/personas.properties
index eccba4e..ab16499 100644
--- a/extension/locale/lt/personas.properties
+++ b/extension/locale/lt/personas.properties
@@ -4,6 +4,7 @@ featured=Siūlomos
recent=Neseniai naudotos
favorites=Mėgstamiausios
favoritesSignIn=Prisijunkite ir galėsite pasiekti mėgstamiausias
+favoritesError= An error occurred while loading your favorites
viewDetail=Išsamiau...
favoritesGoTo=Eiti į mėgstamiausius...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/mk-MK/personas.properties b/extension/locale/mk-MK/personas.properties
index c9402bc..b561444 100644
--- a/extension/locale/mk-MK/personas.properties
+++ b/extension/locale/mk-MK/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent=Мои скоро одбрани
favorites=Мои омилени
favoritesSignIn=Највете се за да пристапите до Вашите омилени
+favoritesError= An error occurred while loading your favorites
viewDetail=Деталји...
favoritesGoTo=Отвори ги моите омилени
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/nl/personas.properties b/extension/locale/nl/personas.properties
index f357665..2789f76 100644
--- a/extension/locale/nl/personas.properties
+++ b/extension/locale/nl/personas.properties
@@ -4,6 +4,7 @@ featured= Aanbevolen
recent= Onlangs geselecteerd
favorites= Mijn favorieten
favoritesSignIn= Meld u aan om naar uw favorieten te gaan
+favoritesError= An error occurred while loading your favorites
viewDetail= Details bekijken…
favoritesGoTo= Naar mijn favorieten…
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/pl-PL/personas.properties b/extension/locale/pl-PL/personas.properties
index 9f72998..8e5986c 100644
--- a/extension/locale/pl-PL/personas.properties
+++ b/extension/locale/pl-PL/personas.properties
@@ -4,6 +4,7 @@ featured=Polecane
recent=Ostatnio używane
favorites=Ulubione
favoritesSignIn=Zaloguj się, aby uzyskać dostęp do ulubionych motywów
+favoritesError=Podczas wczytywania ulubionych motywów wystąpił błąd
viewDetail=Wyświetl szczegóły…
favoritesGoTo=Przejdź do ulubionych…
# Labels that identify the current persona when the current persona is either
@@ -19,7 +20,7 @@ viewDesigner=Więcej autorstwa %S…
# %2$S = the name of the persona
# f.e. "Random Selection from Scenery > Yosemite"
# LOCALIZATION NOTE (useRandomPersona): the label of a command that selects
-viewMoreFrom= More from %1$S...
+viewMoreFrom=More from %1$S...
# a category from which to pick random personas.
# %S = the category from which to pick random personas
# f.e. "Random Selection from Scenery"
diff --git a/extension/locale/pl/personas.properties b/extension/locale/pl/personas.properties
index 72df175..b744e46 100644
--- a/extension/locale/pl/personas.properties
+++ b/extension/locale/pl/personas.properties
@@ -4,6 +4,7 @@ featured=Polecane
recent=Ostatnio używane
favorites=Ulubione
favoritesSignIn=Zaloguj się, aby uzyskać dostęp do ulubionych motywów
+favoritesError=Podczas wczytywania ulubionych minimotywów wystąpił błąd
viewDetail=Wyświetl szczegóły…
favoritesGoTo=Przejdź do ulubionych…
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/pt-BR/personas.properties b/extension/locale/pt-BR/personas.properties
index b246d97..e37bf20 100644
--- a/extension/locale/pt-BR/personas.properties
+++ b/extension/locale/pt-BR/personas.properties
@@ -4,6 +4,7 @@ featured=Em destaque
recent=Selecionadas recentemente
favorites=Favoritos
favoritesSignIn=Conecte-se para acessar seus favoritos
+favoritesError= An error occurred while loading your favorites
viewDetail=Ver detalhes…
favoritesGoTo=Ir para Favoritas…
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/ro/personas.properties b/extension/locale/ro/personas.properties
index 313dfec..44e49d1 100644
--- a/extension/locale/ro/personas.properties
+++ b/extension/locale/ro/personas.properties
@@ -4,6 +4,7 @@ featured=Recomandat
recent=Recent selectate
favorites=Favorite
favoritesSignIn=Abonare pentru accesul la favorite…
+favoritesError= An error occurred while loading your favorites
viewDetail=Vezi detalii…
favoritesGoTo=Afișează favoritele
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/ru-RU/personas.properties b/extension/locale/ru-RU/personas.properties
index 63def6c..7be6c58 100644
--- a/extension/locale/ru-RU/personas.properties
+++ b/extension/locale/ru-RU/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent=Недавно выбранные
favorites=Избранное
favoritesSignIn=Войдите для просмотра Избранного
+favoritesError= An error occurred while loading your favorites
viewDetail=Данные...
favoritesGoTo=Перейти к Избранному...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/si-LK/personas.properties b/extension/locale/si-LK/personas.properties
index 4f5742f..e287ef7 100644
--- a/extension/locale/si-LK/personas.properties
+++ b/extension/locale/si-LK/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent=මැතකදී තොරාගත්
favorites=මම වඩාත් කැමති
favoritesSignIn=ඔබ කැමති පුද්ගල සැකසුම තොරා ගැනීමට ඔබගේ ගිණුමට පිවිසෙන්න
+favoritesError= An error occurred while loading your favorites
viewDetail=තොරතුරු පෙන්වන්න
favoritesGoTo=මා කැමති පුද්ගල සැකසුම් පෙන්වන්න
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/sk-SK/personas.properties b/extension/locale/sk-SK/personas.properties
index 28a86dd..a2d9f74 100644
--- a/extension/locale/sk-SK/personas.properties
+++ b/extension/locale/sk-SK/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent=Najčastejšie vybrané
favorites=Moje obľúbené
favoritesSignIn=Na zobrazenie obľúbených sa musíte prihlásiť
+favoritesError= An error occurred while loading your favorites
viewDetail=Zobraziť podrobnosti...
favoritesGoTo=Prejsť na Moje obľúbené...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/sr/personas.properties b/extension/locale/sr/personas.properties
index 051b699..4405183 100644
--- a/extension/locale/sr/personas.properties
+++ b/extension/locale/sr/personas.properties
@@ -4,6 +4,7 @@ featured=Истакнута
recent=Моја недавно изабрана
favorites=Моја омиљена
favoritesSignIn=Пријавите се да бисте приступили својим омиљеним оделима
+favoritesError=Дошло је до грешке приликом учитавања омиљених одела
viewDetail=Прикажи детаље...
favoritesGoTo=Иди до мојих омиљених одела...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/sv-SE/personas.properties b/extension/locale/sv-SE/personas.properties
index 78795b5..6bf7dbd 100644
--- a/extension/locale/sv-SE/personas.properties
+++ b/extension/locale/sv-SE/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent=Nyligen valda
favorites=Favoriter
favoritesSignIn=Logga in för att se dina favoriter
+favoritesError= An error occurred while loading your favorites
viewDetail=Se Detaljer
favoritesGoTo=Visa Mina Favoriter
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/tr/personas.properties b/extension/locale/tr/personas.properties
index 10b3dfb..784215c 100644
--- a/extension/locale/tr/personas.properties
+++ b/extension/locale/tr/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent=Son Seçilen
favorites= Favorites
favoritesSignIn= Sign In to Access Your Favorites
+favoritesError= An error occurred while loading your favorites
viewDetail= View Details...
favoritesGoTo= Go to My Favorites...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/uk-UA/personas.properties b/extension/locale/uk-UA/personas.properties
index 1214ef4..fd62b34 100644
--- a/extension/locale/uk-UA/personas.properties
+++ b/extension/locale/uk-UA/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent= Нещодавно вибрані мною
favorites= Мої улюблені
favoritesSignIn= Увійти для доступу в свої улюблені
+favoritesError= An error occurred while loading your favorites
viewDetail= Переглянути типові...
favoritesGoTo= Перейти до моїх улюблених...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/vi/personas.properties b/extension/locale/vi/personas.properties
index 1943134..adc0314 100644
--- a/extension/locale/vi/personas.properties
+++ b/extension/locale/vi/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent=Được chọn Gần đây
favorites=Ưa thích
favoritesSignIn=Đăng nhập để Truy cập Giao diện Ưa thích của Bạn
+favoritesError= An error occurred while loading your favorites
viewDetail=Xem chi tiết...
favoritesGoTo=Vào Danh mục Ưa thích của Tôi...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/zh-CN/personas.properties b/extension/locale/zh-CN/personas.properties
index 2070403..2fe55e2 100644
--- a/extension/locale/zh-CN/personas.properties
+++ b/extension/locale/zh-CN/personas.properties
@@ -4,6 +4,7 @@ featured=精选
recent=最近选择
favorites=收藏
favoritesSignIn=登录访问你的收藏
+favoritesError= An error occurred while loading your favorites
viewDetail=查看详情...
favoritesGoTo=转到我的收藏
# Labels that identify the current persona when the current persona is either
diff --git a/extension/locale/zh-TW/personas.properties b/extension/locale/zh-TW/personas.properties
index e97237f..53c329b 100644
--- a/extension/locale/zh-TW/personas.properties
+++ b/extension/locale/zh-TW/personas.properties
@@ -4,6 +4,7 @@ featured= Featured
recent=最近使用
favorites=我的最愛
favoritesSignIn=登入我的最愛
+favoritesError= An error occurred while loading your favorites
viewDetail=檢視細節...
favoritesGoTo=到我的最愛...
# Labels that identify the current persona when the current persona is either
diff --git a/extension/modules/service.js b/extension/modules/service.js
index 3911f7e..fedaf99 100644
--- a/extension/modules/service.js
+++ b/extension/modules/service.js
@@ -44,12 +44,17 @@ const Cr = Components.results;
const Cu = Components.utils;
// modules that come with Firefox
+Cu.import("resource://gre/modules/ctypes.jsm")
+Cu.import("resource://gre/modules/osfile.jsm")
Cu.import("resource://gre/modules/AddonManager.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
// LightweightThemeManager may not be not available (Firefox < 3.6 or Thunderbird)
-try { Cu.import("resource://gre/modules/LightweightThemeManager.jsm"); }
-catch (e) { LightweightThemeManager = null; }
+try {
+ Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
+} catch (e) {
+ LightweightThemeManager = null;
+}
// modules that are generic
Cu.import("resource://personas/modules/log4moz.js");
@@ -63,1455 +68,1496 @@ const PERSONAS_EXTENSION_ID = "personas at christopher.beard";
const COOKIE_INITIAL_PERSONA = "initial_persona";
function endsWith(str, end) {
- return !end || end.length <= str.length && end == str.slice(-end.length);
+ return !end || end.length <= str.length && end == str.slice(-end.length);
}
let PersonaService = {
- THUNDERBIRD_ID: "{3550f703-e582-4d05-9a08-453d09bdfdc6}",
- FIREFOX_ID: "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
-
- // Wraps event listener functions so errors are not lost.
- wrap: function(fn) {
- return function() {
- try {
- return fn.apply(this, arguments);
- } catch (e) {
- Cu.reportError(e);
- }
- };
- },
-
- //**************************************************************************//
- // Shortcuts
-
- // Access to extensions.personas.* preferences. To access other preferences,
- // call the Preferences module directly.
- get _prefs() {
- delete this._prefs;
- return this._prefs = new Preferences("extensions.personas.");
- },
-
- get _strings() {
- delete this._strings;
- return this._strings = new StringBundle("chrome://personas/locale/personas.properties");
- },
-
- get appInfo() {
- delete this.appInfo;
- return this.appInfo = Cc["@mozilla.org/xre/app-info;1"].
- getService(Ci.nsIXULAppInfo).
- QueryInterface(Ci.nsIXULRuntime);
- },
-
- get _log() {
- delete this._log;
- return this._log = Log4Moz.getConfiguredLogger("PersonaService");
- },
-
-
- //**************************************************************************//
- // Initialization & Destruction
-
- _init: function() {
- let t = this;
- AddonManager.getAddonByID(PERSONAS_EXTENSION_ID, function (addon) {
- t.urlSource = "personas-plus-" + addon.version;
- t.onAddonsHostChanged();
- });
-
- // Observe quit so we can destroy ourselves.
- Observers.add("quit-application", this.onQuitApplication, this);
- // Observe the "cookie-changed" topic to load the favorite personas when
- // the user signs in.
- if (false)
- // Skip this for now, since AMO session cookies are not currently
- // cooperative
- Observers.add("cookie-changed", this.onCookieChanged, this);
- // Observe the "lightweight-theme-changed" to sync the add-on with the
- // lightweight theme manager.
- Observers.add("lightweight-theme-changed",
- this.onLightweightThemeChanged, this);
- // Observe HTTP responses to detect login and logout
- Observers.add("http-on-examine-response",
- this.onHTTPResponse, this);
-
- this._prefs.observe("useTextColor", this.onUseColorChanged, this);
- this._prefs.observe("useAccentColor", this.onUseColorChanged, this);
- this._prefs.observe("selected", this.onSelectedModeChanged, this);
- this._prefs.observe("addons-host", this.onAddonsHostChanged, this);
-
- this._ltmSyncTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-
- // Change to the initial persona if preferences indicate that a persona
- // should be active but they don't specify the persona that is active.
- // This normally happens only on first run, although it could theoretically
- // happen at other times.
- //
- // The initial persona is either the persona specified by a cookie (set by
- // the gallery), the one specified by the extensions.personas.initial
- // preference (set by a distribution.ini file for a BYOB/bundle/distributon
- // build), or the Groovy Blue persona which is hardcoded into this code.
- //
- // NOTE: the logic here is carefully designed to achieve the correct outcome
- // under a variety of circumstances and should be changed with caution!
- // See bug 513765 and bug 503300 for some details on why it works this way.
- //
- if (this._prefs.get("selected") == "current" && !this._prefs.get("current")) {
- if (this._prefs.has("initial"))
- this.changeToPersona(JSON.parse(this._prefs.get("initial")));
- else if (LightweightThemeManager && LightweightThemeManager.currentTheme)
- this.changeToPersona(LightweightThemeManager.currentTheme);
- else {
- let addonSlug = this._prefs.get("initial.slug");
- let url = this.getURL("addon-details", { "ADDON_SLUG": addonSlug });
-
- this._makeRequest(url, function (event) {
- let responseJSON = JSON.parse(event.target.responseText);
- this._log.debug("Fetched persona from " + url + " " +
- JSON.stringify(responseJSON.theme));
- this.changeToPersona(this.getPersonaJSON(responseJSON));
- // There seems to be a bug in the LightweightThemeManager that
- // prevents this from being called automatically at this
- // point.
- LightweightThemeManager.themeChanged(
- LightweightThemeManager.currentTheme);
- }.bind(this));
- }
- }
+ THUNDERBIRD_ID: "{3550f703-e582-4d05-9a08-453d09bdfdc6}",
+ FIREFOX_ID: "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
+
+ // Wraps event listener functions so errors are not lost.
+ wrap: function(fn) {
+ return function() {
+ try {
+ return fn.apply(this, arguments);
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ };
+ },
+
+ //**************************************************************************//
+ // Shortcuts
+
+ // Access to extensions.personas.* preferences. To access other preferences,
+ // call the Preferences module directly.
+ get _prefs() {
+ delete this._prefs;
+ return this._prefs = new Preferences("extensions.personas.");
+ },
+
+ get _strings() {
+ delete this._strings;
+ return this._strings = new StringBundle("chrome://personas/locale/personas.properties");
+ },
+
+ get appInfo() {
+ delete this.appInfo;
+ return this.appInfo = Cc["@mozilla.org/xre/app-info;1"].
+ getService(Ci.nsIXULAppInfo).
+ QueryInterface(Ci.nsIXULRuntime);
+ },
+
+ get _log() {
+ delete this._log;
+ return this._log = Log4Moz.getConfiguredLogger("PersonaService");
+ },
+
+
+ //**************************************************************************//
+ // Initialization & Destruction
+
+ _init: function() {
+ let t = this;
+ AddonManager.getAddonByID(PERSONAS_EXTENSION_ID, function(addon) {
+ t.urlSource = "personas-plus-" + addon.version;
+ t.onAddonsHostChanged();
+ });
+
+ // Observe quit so we can destroy ourselves.
+ Observers.add("quit-application", this.onQuitApplication, this);
+ // Observe the "cookie-changed" topic to load the favorite personas when
+ // the user signs in.
+ if (false)
+ // Skip this for now, since AMO session cookies are not currently
+ // cooperative
+ Observers.add("cookie-changed", this.onCookieChanged, this);
+ // Observe the "lightweight-theme-changed" to sync the add-on with the
+ // lightweight theme manager.
+ Observers.add("lightweight-theme-changed",
+ this.onLightweightThemeChanged, this);
+ // Observe HTTP responses to detect login and logout
+ Observers.add("http-on-examine-response",
+ this.onHTTPResponse, this);
+
+ this._prefs.observe("useTextColor", this.onUseColorChanged, this);
+ this._prefs.observe("useAccentColor", this.onUseColorChanged, this);
+ this._prefs.observe("selected", this.onSelectedModeChanged, this);
+ this._prefs.observe("addons-host", this.onAddonsHostChanged, this);
+
+ this._ltmSyncTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+
+ // Change to the initial persona if preferences indicate that a persona
+ // should be active but they don't specify the persona that is active.
+ // This normally happens only on first run, although it could theoretically
+ // happen at other times.
+ //
+ // The initial persona is either the persona specified by a cookie (set by
+ // the gallery), the one specified by the extensions.personas.initial
+ // preference (set by a distribution.ini file for a BYOB/bundle/distributon
+ // build), or the Groovy Blue persona which is hardcoded into this code.
+ //
+ // NOTE: the logic here is carefully designed to achieve the correct outcome
+ // under a variety of circumstances and should be changed with caution!
+ // See bug 513765 and bug 503300 for some details on why it works this way.
+ //
+ if (this._prefs.get("selected") == "current" && !this._prefs.get("current")) {
+ if (this._prefs.has("initial"))
+ this.changeToPersona(JSON.parse(this._prefs.get("initial")));
+ else if (LightweightThemeManager && LightweightThemeManager.currentTheme)
+ this.changeToPersona(LightweightThemeManager.currentTheme);
+ else {
+ let addonSlug = this._prefs.get("initial.slug");
+ let url = this.getURL("addon-details", {
+ "ADDON_SLUG": addonSlug
+ });
+
+ this._makeRequest(url, function(event) {
+ let responseJSON = JSON.parse(event.target.responseText);
+ this._log.debug("Fetched persona from " + url + " " +
+ JSON.stringify(responseJSON.theme));
+ this.changeToPersona(this.getPersonaJSON(responseJSON));
+ // There seems to be a bug in the LightweightThemeManager that
+ // prevents this from being called automatically at this
+ // point.
+ LightweightThemeManager.themeChanged(
+ LightweightThemeManager.currentTheme);
+ }.bind(this));
+ }
+ }
- let timerManager = Cc["@mozilla.org/updates/timer-manager;1"].
- getService(Ci.nsIUpdateTimerManager);
-
- // Load cached personas data
- this.loadDataFromCache();
-
- // Refresh data, then set a timer to refresh it periodically.
- // This isn't quite right, since we always load data on startup, even if
- // we've recently refreshed it. And the timer that refreshes data ignores
- // the data load on startup, so if it's been more than the timer interval
- // since a user last started her browser, we load the data twice:
- // once because the browser starts and once because the refresh timer fires.
- this.refreshData();
- let dataRefreshCallback = {
- _svc: this,
- notify: function(timer) { this._svc._refreshDataWithMetrics() }
- };
- timerManager.registerTimer("personas-data-refresh-timer",
- dataRefreshCallback,
- 86400 /* in seconds == one day */);
-
- // Refresh the current persona once per day. We only do this for
- // Thunderbird and Firefox < 3.6, since the LightweightThemeManager
- // does this for Firefox 3.6.
- if (!LightweightThemeManager) {
- let personaRefreshCallback = {
- _svc: this,
- notify: function(timer) { this._svc._refreshPersona() }
- };
- timerManager.registerTimer("personas-persona-refresh-timer",
- personaRefreshCallback,
- 86400 /* in seconds == one day */);
- }
+ let timerManager = Cc["@mozilla.org/updates/timer-manager;1"].
+ getService(Ci.nsIUpdateTimerManager);
+
+ // Load cached personas data
+ this.loadDataFromCache();
+
+ // Refresh data, then set a timer to refresh it periodically.
+ // This isn't quite right, since we always load data on startup, even if
+ // we've recently refreshed it. And the timer that refreshes data ignores
+ // the data load on startup, so if it's been more than the timer interval
+ // since a user last started her browser, we load the data twice:
+ // once because the browser starts and once because the refresh timer fires.
+ this.refreshData();
+ let dataRefreshCallback = {
+ _svc: this,
+ notify: function(timer) {
+ this._svc._refreshDataWithMetrics()
+ }
+ };
+ timerManager.registerTimer("personas-data-refresh-timer",
+ dataRefreshCallback,
+ 86400 /* in seconds == one day */ );
+
+ // Refresh the current persona once per day. We only do this for
+ // Thunderbird and Firefox < 3.6, since the LightweightThemeManager
+ // does this for Firefox 3.6.
+ if (!LightweightThemeManager) {
+ let personaRefreshCallback = {
+ _svc: this,
+ notify: function(timer) {
+ this._svc._refreshPersona()
+ }
+ };
+ timerManager.registerTimer("personas-persona-refresh-timer",
+ personaRefreshCallback,
+ 86400 /* in seconds == one day */ );
+ }
- // Load cached favorite personas
- this.loadFavoritesFromCache();
+ // Load cached favorite personas
+ this.loadFavoritesFromCache();
- // Refresh the favorite personas once per day.
- let favoritesRefreshCallback = {
- _svc: this,
- notify: function(timer) { this._svc.refreshFavorites() }
- };
- timerManager.registerTimer("personas-favorites-refresh-timer",
- favoritesRefreshCallback,
- 24 * 60 * 60);
+ // Refresh the favorite personas once per day.
+ let favoritesRefreshCallback = {
+ _svc: this,
+ notify: function(timer) {
+ this._svc.refreshFavorites()
+ }
+ };
+ timerManager.registerTimer("personas-favorites-refresh-timer",
+ favoritesRefreshCallback,
+ 24 * 60 * 60);
- this._rotationTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
- this.onSelectedModeChanged();
- },
+ this._rotationTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ this.onSelectedModeChanged();
+ },
- _destroy: function() {
- Observers.remove("cookie-changed", this.onCookieChanged, this);
- Observers.remove("lightweight-theme-changed",
- this.onLightweightThemeChanged, this);
- Observers.remove("http-on-examine-response", this.onHTTPResponse, this);
+ _destroy: function() {
+ Observers.remove("cookie-changed", this.onCookieChanged, this);
+ Observers.remove("lightweight-theme-changed",
+ this.onLightweightThemeChanged, this);
+ Observers.remove("http-on-examine-response", this.onHTTPResponse, this);
- this._prefs.ignore("useTextColor", this.onUseColorChanged, this);
- this._prefs.ignore("useAccentColor", this.onUseColorChanged, this);
- this._prefs.ignore("selected", this.onSelectedModeChanged, this);
- this._prefs.ignore("addons-host", this.onAddonsHostChanged, this);
- },
+ this._prefs.ignore("useTextColor", this.onUseColorChanged, this);
+ this._prefs.ignore("useAccentColor", this.onUseColorChanged, this);
+ this._prefs.ignore("selected", this.onSelectedModeChanged, this);
+ this._prefs.ignore("addons-host", this.onAddonsHostChanged, this);
+ },
- // The `src` parameter used in AMO requests.
- urlSource: "personas-plus",
+ // The `src` parameter used in AMO requests.
+ urlSource: "personas-plus",
- //**************************************************************************//
- // XPCOM Plumbing
+ //**************************************************************************//
+ // XPCOM Plumbing
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
- Ci.nsIDOMEventListener,
- Ci.nsITimerCallback]),
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+ Ci.nsIDOMEventListener,
+ Ci.nsITimerCallback
+ ]),
- //**************************************************************************//
- // Data Retrieval
+ //**************************************************************************//
+ // Data Retrieval
- _makeRequest: function(url, loadCallback, headers, aIsBinary, wantErrors) {
- let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
+ _makeRequest: function(url, loadCallback, headers, aIsBinary, wantErrors, options) {
+ let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
- request = request.QueryInterface(Ci.nsIDOMEventTarget);
- request.addEventListener("load", this.wrap(loadCallback), false);
- if (wantErrors)
- request.addEventListener("error", this.wrap(loadCallback), false);
+ request.addEventListener("load", this.wrap(loadCallback), false);
+ if (wantErrors)
+ request.addEventListener("error", this.wrap(loadCallback), false);
- request = request.QueryInterface(Ci.nsIXMLHttpRequest);
- request.open("GET", url, true);
+ request.open("GET", url, true);
- // Force the request to include cookies even though this chrome code
- // is seen as a third-party, so the server knows the user for which we are
- // requesting favorites (or anything else user-specific in the future).
- // This only works in Firefox 3.6; in Firefox 3.5 the request will instead
- // fail to send cookies if the user has disabled third-party cookies.
- try {
- request.channel.QueryInterface(Ci.nsIHttpChannelInternal).
- forceAllowThirdPartyCookie = true;
- }
- catch(ex) { /* user is using Firefox 3.5 */ }
-
- if (headers)
- for (let header in headers)
- request.setRequestHeader(header, headers[header]);
-
- if (aIsBinary)
- request.overrideMimeType('text/plain; charset=x-user-defined');
-
- request.send(null);
- },
-
- /**
- * Refresh data. This method gets called on demand (including on startup)
- * and retrieves data without passing any additional information about
- * the selected persona and the application (that information is only included
- * in the daily retrieval so we can get consistent daily statistics from it
- * no matter how many times a user starts the application in a given day).
- */
- refreshData: function() {
- let url = this.getURL("featured-feed");
- this._makeRequest(url, this.onDataLoadComplete.bind(this));
- },
-
- /**
- * Refresh data, providing metrics on persona usage in the process.
- * This method gets called approximately once per day on a cross-session timer
- * (provided Firefox is run every day), updates the version of the data
- * that is currently in memory, and passes information about the selected
- * persona and the host application to the server for statistical analysis
- * (f.e. figuring out which personas are the most popular).
- */
- _refreshDataWithMetrics: function() {
- let appInfo = Cc["@mozilla.org/xre/app-info;1"].
- getService(Ci.nsIXULAppInfo);
- let xulRuntime = Cc["@mozilla.org/xre/app-info;1"].
- getService(Ci.nsIXULRuntime);
-
- // Calculate the amount of time (in hours) since the persona was last changed.
- let duration = "";
- if (this._prefs.has("persona.lastChanged"))
- duration = Math.round((new Date() - new Date(parseInt(this._prefs.get("persona.lastChanged")))) / 1000 / 60 / 60);
-
- // This logic is based on ExtensionManager::_updateLocale.
- let locale;
- try {
- if (Preferences.get("intl.locale.matchOS")) {
- let localeSvc = Cc["@mozilla.org/intl/nslocaleservice;1"].
- getService(Ci.nsILocaleService);
- locale = localeSvc.getLocaleComponentForUserAgent();
- }
- else
- throw "set locale in the catch block";
- }
- catch (ex) {
- locale = Preferences.get("general.useragent.locale");
- }
-
- let params = {
- type: this.selected,
- id: this.currentPersona ? this.currentPersona.id : "",
- duration: duration,
- appID: appInfo.ID,
- appVersion: appInfo.version,
- appLocale: locale,
- appOS: xulRuntime.OS,
- appABI: xulRuntime.XPCOMABI
- };
-
- //dump("params: " + [name + "=" + encodeURIComponent(params[name]) for (name in params)].join("&") + "\n");
-
- let url = this.getURL("featured-feed") +
- "?" + [name + "=" + encodeURIComponent(params[name]) for (name in params)].join("&");
- this._makeRequest(url, this.onDataLoadComplete.bind(this));
- },
-
- onDataLoadComplete: function(aEvent) {
- let request = aEvent.target;
-
- // XXX Try to reload again sooner?
- if (request.status != 200)
- throw("problem loading data: " + request.status + " - " + request.statusText);
-
- let response = JSON.parse(request.responseText);
-
- this.personas = {
- featured: response.addons
- };
-
- // Cache the response
- let cacheDirectory =
- FileUtils.getDirectory(FileUtils.getPersonasDirectory(), "cache");
- FileUtils.writeFile(cacheDirectory, "personas.json", request.responseText);
- },
-
- /**
- * Attempts to load this.personas from the cached file in cache/personas.json
- */
- loadDataFromCache : function() {
- let cacheDirectory =
- FileUtils.getDirectory(FileUtils.getPersonasDirectory(), "cache");
- let data = FileUtils.readFile(cacheDirectory, "personas.json");
-
- try { this.personas = JSON.parse(data); }
- catch (e) {
- // Could not load from cached data, file empty or does not exist perhaps
- return;
- }
+ // Force the request to include cookies even though this chrome code
+ // is seen as a third-party, so the server knows the user for which we are
+ // requesting favorites (or anything else user-specific in the future).
+ // This only works in Firefox 3.6; in Firefox 3.5 the request will instead
+ // fail to send cookies if the user has disabled third-party cookies.
+ try {
+ request.channel.QueryInterface(Ci.nsIHttpChannelInternal).
+ forceAllowThirdPartyCookie = true;
+ } catch (ex) { /* user is using Firefox 3.5 */ }
+
+ if (headers)
+ for (let header in headers)
+ request.setRequestHeader(header, headers[header]);
+
+ if (aIsBinary)
+ request.overrideMimeType('text/plain; charset=x-user-defined');
+ else if (/^file:/.test(url) && !/\.xml$/.test(url))
+ request.overrideMimeType('text/plain');
+
+ if (options)
+ for (let option in options)
+ request[option] = options[option];
+
+ request.send(null);
+ },
+
+ /**
+ * Refresh data. This method gets called on demand (including on startup)
+ * and retrieves data without passing any additional information about
+ * the selected persona and the application (that information is only included
+ * in the daily retrieval so we can get consistent daily statistics from it
+ * no matter how many times a user starts the application in a given day).
+ */
+ refreshData: function() {
+ let url = this.getURL("featured-feed");
+ this._makeRequest(url, this.onDataLoadComplete.bind(this));
+ },
+
+ /**
+ * Refresh data, providing metrics on persona usage in the process.
+ * This method gets called approximately once per day on a cross-session timer
+ * (provided Firefox is run every day), updates the version of the data
+ * that is currently in memory, and passes information about the selected
+ * persona and the host application to the server for statistical analysis
+ * (f.e. figuring out which personas are the most popular).
+ */
+ _refreshDataWithMetrics: function() {
+ let appInfo = Cc["@mozilla.org/xre/app-info;1"].
+ getService(Ci.nsIXULAppInfo);
+ let xulRuntime = Cc["@mozilla.org/xre/app-info;1"].
+ getService(Ci.nsIXULRuntime);
+
+ // Calculate the amount of time (in hours) since the persona was last changed.
+ let duration = "";
+ if (this._prefs.has("persona.lastChanged"))
+ duration = Math.round((new Date() - new Date(parseInt(this._prefs.get("persona.lastChanged")))) / 1000 / 60 / 60);
+
+ // This logic is based on ExtensionManager::_updateLocale.
+ let locale;
+ try {
+ if (Preferences.get("intl.locale.matchOS")) {
+ let localeSvc = Cc["@mozilla.org/intl/nslocaleservice;1"].
+ getService(Ci.nsILocaleService);
+ locale = localeSvc.getLocaleComponentForUserAgent();
+ } else
+ throw "set locale in the catch block";
+ } catch (ex) {
+ locale = Preferences.get("general.useragent.locale");
+ }
- // Now that we have data, pick a new random persona. Currently, this is
- // the only time we pick a random persona besides when the user selects
- // the "Random From [category]" menuitem, which means the user gets a new
- // random persona each time they start the browser.
- if (this.selected == "random") {
- this.currentPersona = this._getRandomPersonaFromCategory(this.category);
- this._prefs.reset("persona.lastRefreshed");
- this._notifyPersonaChanged(this.currentPersona);
- }
- },
-
- /**
- * Makes a request to obtain the favorite personas json. This occurs only if
- * a user is currenly signed in.
- */
- refreshFavorites : function() {
- let url = this.getURL("favorites-feed");
- this._makeRequest(url, this.onFavoritesLoadComplete.bind(this),
- null, null, true);
- },
-
- /**
- * Handles the response from the refreshFavorites method. Loads the favorite
- * personas list.
- * @param aEvent The Http request event object.
- */
- onFavoritesLoadComplete : function(aEvent) {
- let request = aEvent.target;
-
- if (request.status != 200 || request.getResponseHeader("Content-Type") != "application/json") {
- this._log.info("problem loading favorites from " + request.channel.name + ": " + request.status + " - " + request.statusText);
- this.favorites = null;
- return;
- }
+ let params = {
+ type: this.selected,
+ id: this.currentPersona ? this.currentPersona.id : "",
+ duration: duration,
+ appID: appInfo.ID,
+ appVersion: appInfo.version,
+ appLocale: locale,
+ appOS: xulRuntime.OS,
+ appABI: xulRuntime.XPCOMABI
+ };
- try {
- this.favorites = JSON.parse(request.responseText)
- .addons.filter(function (a) a.theme);
- }
- catch(ex) {
- this._log.debug("error parsing favorites data: " + request.responseText.slice(0, 100) + "...");
- return;
- }
+ //dump("params: " + [name + "=" + encodeURIComponent(params[name]) for (name in params)].join("&") + "\n");
- // Cache the response
- let cacheDirectory =
- FileUtils.getDirectory(FileUtils.getPersonasDirectory(), "cache");
- FileUtils.writeFile(cacheDirectory, "favorites.json", request.responseText);
- },
-
- /**
- * Attempts to load this.favorites from the cached file in
- * cache/favorites.json
- */
- loadFavoritesFromCache : function() {
- let cacheDirectory =
- FileUtils.getDirectory(FileUtils.getPersonasDirectory(), "cache");
- let data = FileUtils.readFile(cacheDirectory, "favorites.json");
-
- try { this.favorites = JSON.parse(data); }
- catch (e) {
- // Could not load from cached data, file empty or does not exist perhaps
- }
- },
-
- /**
- * Adds the given persona to the favorites list. If the persona is already
- * in the list then it is replaced.
- * @param aPersona The persona object to be added.
- */
- addFavoritePersona : function(aPersona) {
- // Make sure the favorites list exists.
- if (!this.favorites)
- this.favorites = [];
-
- let i = this._findPersonaInArray(aPersona, this.favorites);
- if (i >= 0)
- this.favorites[i] = aPersona;
- else
- this.favorites.push(aPersona);
- },
-
- /**
- * Removes the given persona from the favorites list, if found.
- * @param aPersona The persona object to be removed.
- */
- removeFavoritePersona : function(aPersona) {
- // Abort if the favorites list hasn't been created.
- if (!this.favorites)
- return;
-
- let i = this._findPersonaInArray(aPersona, this.favorites);
- if (i >= 0)
- this.favorites.splice(i, 1);
- },
-
- _refreshPersona: function() {
- // Only refresh the persona if the user selected a specific persona with an
- // ID and update URL. If the user selected a random persona, we'll change
- // it the next time we refresh the directory; if the user selected
- // the default persona, we don't need to refresh it, as it doesn't change;
- // if the user selected a custom persona (which doesn't have an ID), it's
- // not clear what refreshing it would mean; and if the persona doesn't have
- // an update URL, then we don't have a way to refresh it.
- if (this.selected != "current" ||
- !this.currentPersona ||
- !this.currentPersona.id ||
- !this.currentPersona.updateURL)
- return;
-
- let headers = {};
-
- if (this._prefs.has("persona.lastRefreshed")) {
- let date = new Date(parseInt(this._prefs.get("persona.lastRefreshed")));
- headers["If-Modified-Since"] = DateUtils.toRFC1123(date);
- }
+ let url = this.getURL("featured-feed") +
+ "?" + [name + "=" + encodeURIComponent(params[name]) for (name in params)].join("&");
+ this._makeRequest(url, this.onDataLoadComplete.bind(this));
+ },
- let currentPersona;
- try { currentPersona = JSON.stringify(this.currentPersona) }
- catch(ex) { currentPersona = "error JSON.stringify-ing currentPersona " +
- this.currentPersona + ": " + ex }
- this._log.debug("_refreshPersona: currentPersona = " + currentPersona +
- "; url = " + this.currentPersona.updateURL);
-
- let t = this;
- this._makeRequest(this.currentPersona.updateURL,
- function(evt) { t.onPersonaLoadComplete(evt) },
- headers);
- },
-
- onPersonaLoadComplete: function(event) {
- let request = event.target;
-
- this._log.debug("onPersonaLoadComplete: status = " + request.status +
- "; responseText = " + request.responseText);
-
- // 304 means the file we requested has not been modified since the
- // If-Modified-Since date we specified, so there's nothing to do.
- if (request.status == 304) {
- //dump("304 - the persona has not been modified\n");
- return;
- }
+ onDataLoadComplete: function(aEvent) {
+ let request = aEvent.target;
- if (request.status != 200)
- throw("problem refreshing persona: " + request.status + " - " + request.statusText);
+ // XXX Try to reload again sooner?
+ if (request.status != 200)
+ throw ("problem loading data: " + request.status + " - " + request.statusText);
- let persona = JSON.parse(request.responseText);
+ let response = JSON.parse(request.responseText);
- // If the persona we're refreshing is no longer the selected persona,
- // then cancel the refresh (otherwise we'd undo whatever changes the user
- // has just made).
- if (this.selected != "current" || !this.currentPersona ||
- this.currentPersona.id != persona.id) {
- //dump("persona " + persona.id + "(" + persona.name + ") no longer the current persona; ignoring refresh\n");
- return;
- }
+ this.personas = {
+ featured: response.addons
+ };
- // If the version strings are identical, the persona hasn't changed.
- if ((persona.version || "") == (this.currentPersona.version || ""))
- return;
-
- // Set the current persona to the updated version we got from the server,
- // and notify observers about the change.
- this.currentPersona = persona;
- this._notifyPersonaChanged(this.currentPersona);
-
- // Record when this refresh took place so the next refresh only looks
- // for changes since this refresh.
- // Note: we set the preference to a string value because preferences
- // can't hold large enough integer values.
- this._prefs.set("persona.lastRefreshed", new Date().getTime().toString());
- },
-
-
- //**************************************************************************//
- // Implementation
-
- // The JSON feed of personas retrieved from the server.
- // Loaded upon service initialization and reloaded periodically thereafter.
- personas: null,
-
- // The JSON feed of favorite personas retrieved from the server.
- favorites: null,
-
- /**
- * extensions.personas.selected: the type of persona that the user selected;
- * possible values are default (the default Firefox theme), random (a random
- * persona from a category), current (the value of this.currentPersona), and
- * randomFavorite (a random persona from the favorite list).
- */
- get selected() { return this._prefs.get("selected") },
- set selected(newVal) { this._prefs.set("selected", newVal); },
-
- /**
- * extensions.personas.current: the current persona
- */
- get currentPersona() {
- let current = this._prefs.get("current");
- if (current) {
- try { return JSON.parse(current) }
- catch(ex) { Cu.reportError("error getting current persona: " + ex) }
- }
- return null;
- },
- set currentPersona(newVal) {
- try {
- this._prefs.set("current", JSON.stringify(newVal));
- this._cachePersonaImages(newVal);
- }
- catch(ex) { Cu.reportError("error setting current persona: " + ex) }
- },
-
- /**
- * extensions.personas.category: the category from which to pick a random
- * persona.
- */
- get category() { return this._prefs.get("category") },
- set category(newVal) { this._prefs.set("category", newVal) },
-
- /**
- * Returns a formatted URL from preferences.
- */
- getURL: function(pref, replacements) {
- let defaults = {
- SRC: this.urlSource
- };
-
- let t = this;
- return this._prefs.get(pref + ".url")
- .replace(/%([A-Z_]+)%/g, function(m0, m1) {
- if (replacements && m1 in replacements)
- return encodeURIComponent(replacements[m1]);
-
- if (m1 in defaults)
- return encodeURIComponent(defaults[m1]);
-
- let pref = m1.toLowerCase().replace(/_/g, "-");
- return encodeURIComponent(t._prefs.get(pref));
- });
- },
-
- /**
- * extensions.personas.custom: the custom persona.
- */
- get customPersona() {
- let custom = this._prefs.get("custom");
- if (custom) {
- try { return JSON.parse(custom) }
- catch(ex) { Cu.reportError("error getting custom persona: " + ex) }
- }
- return null;
- },
- set customPersona(newVal) {
- try { this._prefs.set("custom", JSON.stringify(newVal)) }
- catch(ex) { Cu.reportError("error setting custom persona: " + ex) }
- },
-
- /**
- * Notifies the persona changes or uses the lightweight theme manager
- * functionality for this purpose (if available)
- * @param aPersona the persona to be set as current if the lightweight theme
- * manager is available
- */
- _notifyPersonaChanged : function(aPersona) {
- this._log.debug("_notifyPersonaChanged:\n" + Log4Moz.getStackTrace());
- if (LightweightThemeManager) {
- if (aPersona && aPersona.custom && LightweightThemeManager.setLocalTheme)
- LightweightThemeManager.setLocalTheme(aPersona);
- else
- LightweightThemeManager.currentTheme = aPersona;
- }
- else
- Observers.notify("personas:persona:changed");
- },
-
- changeToDefaultPersona: function() {
- this.selected = "default";
- this._prefs.set("persona.lastChanged", new Date().getTime().toString());
- this._notifyPersonaChanged(null);
- },
-
- changeToRandomPersona: function(category) {
- this.category = category;
- this.currentPersona = this._getRandomPersonaFromCategory(category);
- this.selected = "random";
- this._prefs.set("persona.lastChanged", new Date().getTime().toString());
- this._notifyPersonaChanged(this.currentPersona);
- },
-
- changeToRandomFavoritePersona : function() {
- if (this.favorites && this.favorites.length > 0) {
- this.currentPersona = this._getRandomPersonaFromArray(this.favorites);
- this.selected = "randomFavorite";
- this._prefs.set("persona.lastChanged", new Date().getTime().toString());
- this._notifyPersonaChanged(this.currentPersona);
- }
- },
-
- changeToPersona: function(persona) {
- // The id must be a string and not an integer in order for the
- // LightweightThemeManager to accept the persona. Older versions used to
- // set a zero to the id of custom personas; if so, it needs to be changed
- // to a "1". See: https://bugzilla.mozilla.org/show_bug.cgi?id=554220
- if (persona.custom && persona.id === 0)
- persona.id = "1";
-
- // Check whether the persona is in the favorites or the recent lists,
- // in which case the change-notification should not be shown.
- let recent = this.getRecentPersonas();
- let favorites = this.favorites;
- let inRecent =
- (recent && recent.some(function(v) v.id == persona.id));
- let inFavorites =
- (favorites && favorites.some(function(v) v.id == persona.id));
-
- this.currentPersona = persona;
- this._addPersonaToRecent(persona);
- this.selected = "current";
- this._prefs.reset("persona.lastRefreshed");
- this._prefs.set("persona.lastChanged", new Date().getTime().toString());
- this._notifyPersonaChanged(this.currentPersona);
-
- // Show the notification if the selected persona is not in the favorite or
- // recent lists, is not a custom persona and its author or username is not null.
- // In this case we make sure at least one of these two fields is not null
- // to prevent bug 526788.
- if (!inRecent && !inFavorites && !persona.custom && (persona.author || persona.username))
- this._showPersonaChangeNotification();
- },
-
- getPersonaJSON: function(data) {
- if (data.theme) {
- if (data.learnmore && !data.theme.detailURL)
- data.theme.detailURL = this.updateURLSource(data.learnmore);
- let authorURL = data.authors && data.authors[0] && data.authors[0].link;
- if (authorURL)
- data.theme.authorURL = this.updateURLSource(authorURL);
- return data.theme;
- }
- return data;
- },
-
- updateURLSource: function(url) {
- return url.replace(/([?&])src=api($|&)/, "$1src=personas-plus$2");
- },
-
- /**
- * Reverts the current persona to the previously selected persona, if
- * available
- */
- revertToPreviousPersona : function() {
- let undonePersonaId = this.currentPersona.id;
- let previousPersona = this._prefs.get("lastselected1");
- if (previousPersona) {
- this.currentPersona = JSON.parse(previousPersona);
- this._revertRecent();
- this.selected = "current";
-
- if (LightweightThemeManager) {
- // forget the lightweight theme too
- LightweightThemeManager.forgetUsedTheme(undonePersonaId);
-
- if (this.currentPersona && this.currentPersona.custom &&
- LightweightThemeManager.setLocalTheme)
- LightweightThemeManager.setLocalTheme(this.currentPersona);
- else
- LightweightThemeManager.currentTheme = this.currentPersona;
- }
- else
- this.resetPersona();
- }
- },
-
- /**
- * Shows a notification displaying the currently selected persona and button
- * to revert the changes.
- */
- _showPersonaChangeNotification : function() {
- // Obtain most recent window and its notification box
- let wm =
- Cc["@mozilla.org/appshell/window-mediator;1"].
- getService(Ci.nsIWindowMediator);
+ // Cache the response
+ let cacheDirectory =
+ FileUtils.getDirectory(FileUtils.getPersonasDirectory(), "cache");
+ FileUtils.writeFile(cacheDirectory, "personas.json", request.responseText);
+ },
+
+ /**
+ * Attempts to load this.personas from the cached file in cache/personas.json
+ */
+ loadDataFromCache: function() {
+ let path =
+ FileUtils.getDirectory(FileUtils.getPersonasDirectory(), "cache");
+ path.append("personas.json");
+
+ this._makeRequest(Services.io.newFileURI(path).spec, onload.bind(this),
+ null, false, true);
+
+ function onload(event) {
+ try {
+ this.personas = JSON.parse(event.target.responseText);
+ } catch (e) {
+ // Could not load from cached data, file empty or does not exist perhaps
+ return;
+ }
+
+ // Now that we have data, pick a new random persona. Currently, this is
+ // the only time we pick a random persona besides when the user selects
+ // the "Random From [category]" menuitem, which means the user gets a new
+ // random persona each time they start the browser.
+ if (this.selected == "random") {
+ this.currentPersona = this._getRandomPersonaFromCategory(this.category);
+ this._prefs.reset("persona.lastRefreshed");
+ this._notifyPersonaChanged(this.currentPersona);
+ }
+ }
+ },
+
+ /**
+ * Makes a request to obtain the favorite personas json. This occurs only if
+ * a user is currenly signed in.
+ */
+ refreshFavorites: function(aURL) {
+ let url = aURL || this.getURL("favorites-feed");
+ this._makeRequest(url, this.onFavoritesLoadComplete.bind(this, aURL),
+ null, null, true);
+ },
+
+ /**
+ * Handles the response from the refreshFavorites method. Loads the favorite
+ * personas list.
+ * @param aURL The URL passed to refreshFavorites
+ * @param aEvent The Http request event object.
+ */
+ onFavoritesLoadComplete: function(aURL, aEvent) {
+ let request = aEvent.target;
+
+ this.favoritesError = false;
+ if (request.status != 200 || request.getResponseHeader("Content-Type") != "application/json") {
+ this._log.info("problem loading favorites from " + request.channel.name + ": " + request.status + " - " + request.statusText);
+ if (request.status != 200)
+ this.favoritesError = true;
+ this.favorites = null;
+ return;
+ }
- let notificationBox;
- switch (this.appInfo.ID) {
- case this.FIREFOX_ID:
- notificationBox = wm.getMostRecentWindow("navigator:browser").
- getBrowser().getNotificationBox();
- break;
- case this.THUNDERBIRD_ID:
- notificationBox = wm.getMostRecentWindow("mail:3pane").
- document.getElementById("mail-notification-box");
- break;
- default:
- throw "unknown application ID " + this.appInfo.ID;
- }
+ try {
+ var json = JSON.parse(request.responseText)
+ .addons.filter(function(a) a.theme);
+ } catch (ex) {
+ this._log.debug("error parsing favorites data: " + request.responseText.slice(0, 100) + "...");
+ return;
+ }
- // If there is another notification of the same kind already, remove it.
- let oldNotification =
- notificationBox.getNotificationWithValue("lwtheme-install-notification");
- if (oldNotification)
- notificationBox.removeNotification(oldNotification);
-
- let message = this._strings.get("notification.personaWasSelected",
- [this.currentPersona.name,
- (this.currentPersona.author ?
- this.currentPersona.author :
- this.currentPersona.username)]);
-
- let revertButton = {
- label : this._strings.get("notification.revertButton.label"),
- accessKey : this._strings.get("notification.revertButton.accesskey"),
- popup : null,
- callback : function() { PersonaService.revertToPreviousPersona(); }
- };
-
- notificationBox.appendNotification(
- message, "lwtheme-install-notification", null,
- notificationBox.PRIORITY_INFO_MEDIUM, [ revertButton ] );
- },
-
- /**
- * Looks for the given persona in the given array and returns its index.
- * @param aPersona The persona to be found.
- * @param aPersonaArray The array in which to look for the persona.
- * @return The index of the persona in the array; -1 if not found.
- */
- _findPersonaInArray : function(aPersona, aPersonaArray) {
- for (let i = 0; i < aPersonaArray.length; i++) {
- if (aPersonaArray[i].id == aPersona.id)
- return i;
- }
- return -1;
- },
-
- _getRandomPersonaFromArray : function(aPersonaArray) {
- // Get a random item from the list, trying up to five times to get one
- // that is different from the currently-selected item in the category
- // (if any). We use Math.floor instead of Math.round to pick a random
- // number because the JS reference says Math.round returns a non-uniform
- // distribution
- // <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Math:random#Examples>.
- if (aPersonaArray && aPersonaArray.length > 0) {
- let randomIndex, randomItem;
- for (let i = 0; i < 5; i++) {
- randomIndex = Math.floor(Math.random() * aPersonaArray.length);
- randomItem = aPersonaArray[randomIndex];
- if (!this.currentPersona || randomItem.id != this.currentPersona.id)
- break;
- }
-
- return this.getPersonaJSON(randomItem);
- }
- return this.currentPersona;
- },
+ this[aURL ? "_favorites" : "favorites"] = json;
+ },
- _getRandomPersonaFromCategory: function(aCategoryName) {
- // If we have the list of categories, use it to pick a random persona
- // from the selected category.
+ /**
+ * Attempts to load this.favorites from the cached file in
+ * cache/favorites.json
+ */
+ loadFavoritesFromCache: function() {
+ let path = FileUtils.getDirectory(FileUtils.getPersonasDirectory(), "cache");
+ path.append("favorites.json");
- if (this.personas) {
- let personas = null;
+ try {
+ this.refreshFavorites(Services.io.newFileURI(path).spec);
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ },
+
+ /**
+ * Adds the given persona to the favorites list. If the persona is already
+ * in the list then it is replaced.
+ * @param aPersona The persona object to be added.
+ */
+ addFavoritePersona: function(aPersona) {
+ // Make sure the favorites list exists.
+ if (!this.favorites)
+ this.favorites = [];
+
+ let i = this._findPersonaInArray(aPersona, this.favorites);
+ if (i >= 0)
+ this.favorites[i] = aPersona;
+ else
+ this.favorites.push(aPersona);
+ },
+
+ /**
+ * Removes the given persona from the favorites list, if found.
+ * @param aPersona The persona object to be removed.
+ */
+ removeFavoritePersona: function(aPersona) {
+ // Abort if the favorites list hasn't been created.
+ if (!this.favorites)
+ return;
+
+ let i = this._findPersonaInArray(aPersona, this.favorites);
+ if (i >= 0)
+ this.favorites.splice(i, 1);
+ },
+
+ _refreshPersona: function() {
+ // Only refresh the persona if the user selected a specific persona with an
+ // ID and update URL. If the user selected a random persona, we'll change
+ // it the next time we refresh the directory; if the user selected
+ // the default persona, we don't need to refresh it, as it doesn't change;
+ // if the user selected a custom persona (which doesn't have an ID), it's
+ // not clear what refreshing it would mean; and if the persona doesn't have
+ // an update URL, then we don't have a way to refresh it.
+ if (this.selected != "current" ||
+ !this.currentPersona ||
+ !this.currentPersona.id ||
+ !this.currentPersona.updateURL)
+ return;
+
+ let headers = {};
+
+ if (this._prefs.has("persona.lastRefreshed")) {
+ let date = new Date(parseInt(this._prefs.get("persona.lastRefreshed")));
+ headers["If-Modified-Since"] = DateUtils.toRFC1123(date);
+ }
- if (aCategoryName == "featured")
- personas = this.personas.featured;
- if (personas)
- return this._getRandomPersonaFromArray(personas);
- }
- return this.currentPersona;
- },
-
- /**
- * Obtains the list of recently selected personas, parsed from the
- * "lastselected" preferences.
- * @param aHowMany (Optional, default 4) How many personas to obtain.
- * @return The list of recent personas.
- */
- getRecentPersonas : function(aHowMany) {
- if (!aHowMany)
- aHowMany = 4;
-
- // Parse the list from the preferences
- let personas = [];
- for (let i = 0; i < aHowMany; i++) {
- if (this._prefs.has("lastselected" + i)) {
+ let currentPersona;
try {
- personas.push(JSON.parse(this._prefs.get("lastselected" + i)));
+ currentPersona = JSON.stringify(this.currentPersona)
+ } catch (ex) {
+ currentPersona = "error JSON.stringify-ing currentPersona " +
+ this.currentPersona + ": " + ex
+ }
+ this._log.debug("_refreshPersona: currentPersona = " + currentPersona +
+ "; url = " + this.currentPersona.updateURL);
+
+ let t = this;
+ this._makeRequest(this.currentPersona.updateURL,
+ function(evt) {
+ t.onPersonaLoadComplete(evt)
+ },
+ headers);
+ },
+
+ onPersonaLoadComplete: function(event) {
+ let request = event.target;
+
+ this._log.debug("onPersonaLoadComplete: status = " + request.status +
+ "; responseText = " + request.responseText);
+
+ // 304 means the file we requested has not been modified since the
+ // If-Modified-Since date we specified, so there's nothing to do.
+ if (request.status == 304) {
+ //dump("304 - the persona has not been modified\n");
+ return;
}
- catch(ex) {}
- }
- }
- return personas;
- },
-
- _addPersonaToRecent: function(persona) {
- let personas = this.getRecentPersonas();
-
- // Remove personas with the same ID (i.e. don't allow the recent persona
- // to appear twice on the list). Afterwards, we'll add the recent persona
- // to the list in a way that makes it the most recent one.
- if (persona.id)
- personas = personas.filter(function(v) !v.id || v.id != persona.id);
-
- // Make the new persona the most recent one.
- personas.unshift(persona);
-
- // Note: at this point, there might be five personas on the list, four
- // that we parsed from preferences and the one we're now adding. But we
- // only serialize the first four back to preferences, so the oldest one
- // drops off the end of the list.
-
- // We store five in case we need to revert changes in the list alter on,
- // even though only four will be displayed.
- for (let i = 0; i < 5; i++) {
- if (i < personas.length)
- this._prefs.set("lastselected" + i, JSON.stringify(personas[i]));
- else
- this._prefs.reset("lastselected" + i);
- }
- },
-
- /**
- * Removes the most recent persona from the recent list, and leaves the
- * following four personas as the most recent.
- */
- _revertRecent: function() {
- // Create a new list of recent personas, removing the first one, but
- // including the 5th (hidden) one.
- let personas = this.getRecentPersonas(5);
- personas.shift();
-
- // Serialize the list of recent personas.
- for (let i = 0; i < 5; i++) {
- if (i < personas.length)
- this._prefs.set("lastselected" + i, JSON.stringify(personas[i]));
- else
- this._prefs.reset("lastselected" + i);
- }
- },
-
- onUseColorChanged: function() {
- // Notify observers that the persona has changed so the change in whether
- // or not to use the text or accent color will get applied. The persona
- // hasn't really changed, but doing this has the desired effect without any
- // known unwanted side effects.
- Observers.notify("personas:persona:changed");
- },
-
- previewingPersona: null,
-
- /**
- * Display the given persona temporarily. Useful for showing users who are
- * browsing the directory of personas what a given persona will look like
- * when selected, f.e. on mouseover. Consumers who call this method should
- * call resetPersona when the preview ends, f.e. on mouseout.
- */
- previewPersona: function(persona) {
- if (LightweightThemeManager)
- LightweightThemeManager.previewTheme(persona);
- else {
- this.previewingPersona = persona;
- Observers.notify("personas:persona:changed");
- }
- },
-
- /**
- * Stop previewing a persona.
- */
- resetPersona: function() {
- if (LightweightThemeManager)
- LightweightThemeManager.resetPreview();
- else {
- this.previewingPersona = null;
- Observers.notify("personas:persona:changed");
- }
- },
-
- /**
- * Gets the persona specified by the initial_persona cookie.
- */
- _getPersonaFromCookie: function() {
- let authorizedHosts = this._prefs.get("authorizedHosts").split(/[, ]+/);
- let cookieManager =
- Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager);
- let cookieEnu = cookieManager.enumerator;
- let selectedCookie = null;
-
- while (cookieEnu.hasMoreElements()) {
- let cookie = cookieEnu.getNext().QueryInterface(Ci.nsICookie);
-
- // XXX: Remove the initial dot from the host name, if any, before it is
- // compared against the authorized hosts. This fixes the bug reported in
- // bug 492392 that getpersonas.com and www.getpersonas.com cookies
- // imported from IE have the cookie host .getpersonas.com, so they didn't
- // match any of the authorized hosts.
- let cookieHost = cookie.host.replace(/^\./, "");
-
- if (cookie.name == COOKIE_INITIAL_PERSONA &&
- authorizedHosts.some(function(v) v == cookieHost)) {
-
- // There could be more than one "initial_persona" cookie. The cookie
- // with latest expiration time is selected.
- if (null == selectedCookie ||
- cookie.expires > selectedCookie.expires) {
- selectedCookie = cookie;
+ if (request.status != 200)
+ throw ("problem refreshing persona: " + request.status + " - " + request.statusText);
+
+ let persona = JSON.parse(request.responseText);
+
+ // If the persona we're refreshing is no longer the selected persona,
+ // then cancel the refresh (otherwise we'd undo whatever changes the user
+ // has just made).
+ if (this.selected != "current" || !this.currentPersona ||
+ this.currentPersona.id != persona.id) {
+ //dump("persona " + persona.id + "(" + persona.name + ") no longer the current persona; ignoring refresh\n");
+ return;
}
- cookieManager.remove(cookie.host, cookie.name, cookie.path, false);
- }
- }
+ // If the version strings are identical, the persona hasn't changed.
+ if ((persona.version || "") == (this.currentPersona.version || ""))
+ return;
- if (selectedCookie)
- return JSON.parse(decodeURIComponent(selectedCookie.value));
-
- return null;
- },
-
- //**************************************************************************//
- // Lightweight Themes - Personas synchronization
-
- _ltmSyncTimer : null,
-
- /**
- * Updates the add-on to reflect the changes from the Tools - Add-ons - Themes
- * dialog. If a lightweight theme is set, it is also set as the add-on's current
- * persona. If a regular theme is set, the current persona is set to "default".
- */
- onLightweightThemeChanged: function() {
- let currentTheme = LightweightThemeManager.currentTheme;
-
- this._ltmSyncTimer.cancel();
-
- if (currentTheme &&
- (currentTheme.id != this.currentPersona.id || this.selected == "default"))
- this.changeToPersona(currentTheme);
- else if (!currentTheme && this.selected != "default") {
-
- // XXX: In Firefox 4, a persona change using the LightweightThemeManager causes
- // two "lightweight-theme-changed" notifications to be fired instead of one:
- // the first one with a null theme, and the second one with the actual theme.
- // This causes the extension to get confused, as the first null theme causes
- // it go change the persona to the default one, losing the "random" setting,
- // amongst other things. To circumvent this, notifications with a "null" theme
- // are delayed just a bit to allow the second notification to cancel it. If there's
- // no second notification, then the null theme is applied.
- // See: https://bugzilla.mozilla.org/show_bug.cgi?id=631803
- let t = this;
- this._ltmSyncTimer.initWithCallback(
- { notify : function() { t.changeToDefaultPersona(); } },
- 100,
- Ci.nsITimer.TYPE_ONE_SHOT);
- }
- },
+ // Set the current persona to the updated version we got from the server,
+ // and notify observers about the change.
+ this.currentPersona = persona;
+ this._notifyPersonaChanged(this.currentPersona);
- //**************************************************************************//
- // Random Rotation
+ // Record when this refresh took place so the next refresh only looks
+ // for changes since this refresh.
+ // Note: we set the preference to a string value because preferences
+ // can't hold large enough integer values.
+ this._prefs.set("persona.lastRefreshed", new Date().getTime().toString());
+ },
- _rotationTimer : null,
- /**
- * Checks the current value of the "selected" preference and sets the
- * rotation timer accordingly.
- */
- onSelectedModeChanged: function() {
- this._rotationTimer.cancel();
+ //**************************************************************************//
+ // Implementation
- let selectedMode = this.selected;
+ // The JSON feed of personas retrieved from the server.
+ // Loaded upon service initialization and reloaded periodically thereafter.
+ personas: null,
- if (selectedMode == "random" || selectedMode == "randomFavorite") {
- let interval = this._prefs.get("rotationInterval") * 1000;
- let that = this;
+ // The JSON feed of favorite personas retrieved from the server.
+ _favorites: null,
- this._rotationTimer.initWithCallback(
- { notify: function(aTimer) { that._rotatePersona(); } },
- interval, Ci.nsITimer.TYPE_REPEATING_SLACK);
+ get favorites() this._favorites,
+ set favorites(val) {
+ this._favorites = val;
- this._rotatePersona();
- }
- },
-
- /**
- * Ensures that cookies are enabled for our current add-ons host, and
- * prompts the user to enable them if not.
- */
- checkCookiesEnabled: function() {
- const PERM = "cookie";
- const ALLOW = 1, SESSION_ONLY = 8;
-
- try {
- let prefs = new Preferences("network.cookie.");
- let cookiesEnabled = (prefs.get("alwaysAcceptSessionCookies") ||
- prefs.get("cookieBehavior") != 2);
- let uri = Services.io.newURI(this.getURL("favorites-feed"), null, null);
-
- let _ = this._strings.get.bind(this._strings);
- if (cookiesEnabled
- || ~[ALLOW, SESSION_ONLY].indexOf(Services.perms.testPermission(uri, PERM))) {
- this._prefs.reset("naggedAboutCookies");
- } else {
- if (this._prefs.get("naggedAboutCookies") == this.addonsHost)
- return;
-
- let stopNagging = { value: true };
- let enableCookies = Services.prompt.confirmCheck(
- null, _("cookies.confirm.title"),
- _("cookies.confirm.message", [this.addonsHost]),
- _("cookies.confirm.checkbox"),
- stopNagging);
-
- if (enableCookies) {
- Services.perms.remove(uri.host, PERM);
- Services.perms.add(uri, PERM, ALLOW);
+ let path =
+ FileUtils.getDirectory(FileUtils.getPersonasDirectory(), "cache");
+
+ // Cache the response
+ if (val && typeof val == "object")
+ // This FileUtils nonsense has to go...
+ FileUtils.writeFile(path, "favorites.json", JSON.stringify(val));
+ else {
+ path.append("favorites.json");
+ OS.File.remove(path.spec);
}
- else if (stopNagging.value) {
- this._prefs.set("naggedAboutCookies", this.addonsHost);
+ },
+
+ /**
+ * extensions.personas.selected: the type of persona that the user selected;
+ * possible values are default (the default Firefox theme), random (a random
+ * persona from a category), current (the value of this.currentPersona), and
+ * randomFavorite (a random persona from the favorite list).
+ */
+ get selected() {
+ return this._prefs.get("selected")
+ },
+ set selected(newVal) {
+ this._prefs.set("selected", newVal);
+ },
+
+ /**
+ * extensions.personas.current: the current persona
+ */
+ get currentPersona() {
+ let current = this._prefs.get("current");
+ if (current) {
+ try {
+ return JSON.parse(current)
+ } catch (ex) {
+ Cu.reportError("error getting current persona: " + ex)
+ }
}
- }
- }
- catch (e) {
- Cu.reportError(e);
- }
- },
-
- /**
- * Handles the addons-host preference being changed.
- */
- onAddonsHostChanged: function() {
- this.addonsHost = this._prefs.get("addons-host");
- this.checkCookiesEnabled()
- this.refreshFavorites();
- },
-
- /**
- * Changes the current persona to a random persona of the same category (while
- * in "random" mode) or a random persona from he favorite list (while in
- * "randomFavorite" mode).
- */
- _rotatePersona : function() {
- switch (this.selected) {
- case "random":
- this.changeToRandomPersona(this.category);
- break;
- case "randomFavorite":
- this.changeToRandomFavoritePersona();
- break;
- }
- },
-
- //**************************************************************************//
- // Persona Images Caching
-
- /**
- * Caches the header and footer images of the given persona inside the
- * directory [profile]/personas/cache/[persona.id]. It removes all other
- * existing persona directories before doing so.
- * @param aPersona The persona for which to cache the images.
- */
- _cachePersonaImages : function(aPersona) {
- let cacheDirectory =
- FileUtils.getDirectory(FileUtils.getPersonasDirectory(), "cache");
-
- // Remove all other subdirectories in the cache directory
- // XXX: In the future, if we want to keep more than one persona cached
- // this step would be removed.
- let subdirs = FileUtils.getDirectoryEntries(cacheDirectory);
- for (let i = 0; i < subdirs.length; i++) {
- if (subdirs[i].isDirectory())
- subdirs[i].remove(true);
- }
-
- // Create directory for the given persona
- let personaDir = FileUtils.getDirectory(cacheDirectory, aPersona.id);
-
- // Save header if specified.
- let header = aPersona.headerURL || aPersona.header;
- if (header) {
- // The header can be a base64 string or a malformed URL, in which case
- // the error can be safely ignored.
- try {
- let headerURI = URI.get(header, null, null)
- .QueryInterface(Ci.nsIURL);
-
- let headerCallback = function(aEvent) {
- let request = aEvent.target;
- // Save only if the folder still exists (Could have been deleted already)
- if (request.status == 200 && personaDir.exists()) {
- FileUtils.writeBinaryFile(
- personaDir.clone(),
- "header" + "." + headerURI.fileExtension,
- request.responseText);
- }
+ return null;
+ },
+ set currentPersona(newVal) {
+ try {
+ this._prefs.set("current", JSON.stringify(newVal));
+ this._cachePersonaImages(newVal);
+ } catch (ex) {
+ Cu.reportError("error setting current persona: " + ex)
+ }
+ },
+
+ /**
+ * extensions.personas.category: the category from which to pick a random
+ * persona.
+ */
+ get category() {
+ return this._prefs.get("category")
+ },
+ set category(newVal) {
+ this._prefs.set("category", newVal)
+ },
+
+ /**
+ * Returns a formatted URL from preferences.
+ */
+ getURL: function(pref, replacements) {
+ let defaults = {
+ SRC: this.urlSource
};
- this._makeRequest(headerURI.spec, headerCallback, null, true);
- }
- catch (e) {}
- }
- // Save footer if specified.
- let footer = aPersona.footerURL || aPersona.footer;
- if (footer) {
- // The footer can be a base64 string or a malformed URL, in which case
- // the error can be safely ignored.
- try {
- let footerURI = URI.get(footer, null, null)
- .QueryInterface(Ci.nsIURL);
- let footerCallback = function(aEvent) {
- let request = aEvent.target;
- // Save only if the folder still exists (Could have been deleted already)
- if (request.status == 200 && personaDir.exists()) {
- FileUtils.writeBinaryFile(
- personaDir.clone(),
- "footer" + "." + footerURI.fileExtension,
- request.responseText);
- }
- };
- this._makeRequest(footerURI.spec, footerCallback, null, true);
- }
- catch (e) {}
- }
- },
-
- /**
- * Obtains the cached images of the given persona. This are stored in the
- * _cachePersonaImages method under the directory
- * [profile]/personas/cache/[persona.id].
- * @param aPersona The persona for which to look the cached images.
- * @return An object with "header" and "footer" properties, each containing
- * the file URL of the image. Null otherwise.
- */
- getCachedPersonaImages : function(aPersona) {
- let cacheDirectory =
- FileUtils.getDirectory(FileUtils.getPersonasDirectory(), "cache");
-
- let personaDir = FileUtils.getDirectory(cacheDirectory, aPersona.id, true);
- if (personaDir.exists()) {
-
- let headerFile = personaDir.clone();
- let footerFile = personaDir.clone();
-
- let headerFileExtension =
- URI.get(aPersona.headerURL || aPersona.header, null, null)
- .QueryInterface(Ci.nsIURL).fileExtension;
-
- let footerFileExtension =
- URI.get(aPersona.footerURL || aPersona.footer, null, null)
- .QueryInterface(Ci.nsIURL).fileExtension;
-
- headerFile.append("header" + "." + headerFileExtension);
- footerFile.append("footer" + "." + footerFileExtension);
-
- if (headerFile.exists() && footerFile.exists()) {
- let ios =
- Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
-
- let headerURI = ios.newFileURI(headerFile);
- let footerURI = ios.newFileURI(footerFile);
-
- return {
- header : headerURI.spec,
- footer : footerURI.spec
+ let t = this;
+ return this._prefs.get(pref + ".url")
+ .replace(/%([A-Z_]+)%/g, function(m0, m1) {
+ if (replacements && m1 in replacements)
+ return encodeURIComponent(replacements[m1]);
+
+ if (m1 in defaults)
+ return encodeURIComponent(defaults[m1]);
+
+ let pref = m1.toLowerCase().replace(/_/g, "-");
+ return encodeURIComponent(t._prefs.get(pref));
+ });
+ },
+
+ /**
+ * extensions.personas.custom: the custom persona.
+ */
+ get customPersona() {
+ let custom = this._prefs.get("custom");
+ if (custom) {
+ try {
+ return JSON.parse(custom)
+ } catch (ex) {
+ Cu.reportError("error getting custom persona: " + ex)
+ }
+ }
+ return null;
+ },
+ set customPersona(newVal) {
+ try {
+ this._prefs.set("custom", JSON.stringify(newVal))
+ } catch (ex) {
+ Cu.reportError("error setting custom persona: " + ex)
+ }
+ },
+
+ /**
+ * Notifies the persona changes or uses the lightweight theme manager
+ * functionality for this purpose (if available)
+ * @param aPersona the persona to be set as current if the lightweight theme
+ * manager is available
+ */
+ _notifyPersonaChanged: function(aPersona) {
+ this._log.debug("_notifyPersonaChanged:\n" + Log4Moz.getStackTrace());
+ if (LightweightThemeManager) {
+ if (aPersona && aPersona.custom && LightweightThemeManager.setLocalTheme)
+ LightweightThemeManager.setLocalTheme(aPersona);
+ else
+ LightweightThemeManager.currentTheme = aPersona;
+ } else
+ Observers.notify("personas:persona:changed");
+ },
+
+ changeToDefaultPersona: function() {
+ this.selected = "default";
+ this._prefs.set("persona.lastChanged", new Date().getTime().toString());
+ this._notifyPersonaChanged(null);
+ },
+
+ changeToRandomPersona: function(category) {
+ this.category = category;
+ this.currentPersona = this._getRandomPersonaFromCategory(category);
+ this.selected = "random";
+ this._prefs.set("persona.lastChanged", new Date().getTime().toString());
+ this._notifyPersonaChanged(this.currentPersona);
+ },
+
+ changeToRandomFavoritePersona: function() {
+ if (this.favorites && this.favorites.length > 0) {
+ this.currentPersona = this._getRandomPersonaFromArray(this.favorites);
+ this.selected = "randomFavorite";
+ this._prefs.set("persona.lastChanged", new Date().getTime().toString());
+ this._notifyPersonaChanged(this.currentPersona);
+ }
+ },
+
+ changeToPersona: function(persona) {
+ // The id must be a string and not an integer in order for the
+ // LightweightThemeManager to accept the persona. Older versions used to
+ // set a zero to the id of custom personas; if so, it needs to be changed
+ // to a "1". See: https://bugzilla.mozilla.org/show_bug.cgi?id=554220
+ if (persona.custom && persona.id === 0)
+ persona.id = "1";
+
+ // Check whether the persona is in the favorites or the recent lists,
+ // in which case the change-notification should not be shown.
+ let recent = this.getRecentPersonas();
+ let favorites = this.favorites;
+ let inRecent =
+ (recent && recent.some(function(v) v.id == persona.id));
+ let inFavorites =
+ (favorites && favorites.some(function(v) v.id == persona.id));
+
+ this.currentPersona = persona;
+ this._addPersonaToRecent(persona);
+ this.selected = "current";
+ this._prefs.reset("persona.lastRefreshed");
+ this._prefs.set("persona.lastChanged", new Date().getTime().toString());
+ this._notifyPersonaChanged(this.currentPersona);
+
+ // Show the notification if the selected persona is not in the favorite or
+ // recent lists, is not a custom persona and its author or username is not null.
+ // In this case we make sure at least one of these two fields is not null
+ // to prevent bug 526788.
+ if (!inRecent && !inFavorites && !persona.custom && (persona.author || persona.username))
+ this._showPersonaChangeNotification();
+ },
+
+ getPersonaJSON: function(data) {
+ if (data.theme) {
+ if (data.learnmore && !data.theme.detailURL)
+ data.theme.detailURL = this.updateURLSource(data.learnmore);
+ let authorURL = data.authors && data.authors[0] && data.authors[0].link;
+ if (authorURL)
+ data.theme.authorURL = this.updateURLSource(authorURL);
+ return data.theme;
+ }
+ return data;
+ },
+
+ updateURLSource: function(url) {
+ return url.replace(/([?&])src=api($|&)/, "$1src=personas-plus$2");
+ },
+
+ /**
+ * Reverts the current persona to the previously selected persona, if
+ * available
+ */
+ revertToPreviousPersona: function() {
+ let undonePersonaId = this.currentPersona.id;
+ let previousPersona = this._prefs.get("lastselected1");
+ if (previousPersona) {
+ this.currentPersona = JSON.parse(previousPersona);
+ this._revertRecent();
+ this.selected = "current";
+
+ if (LightweightThemeManager) {
+ // forget the lightweight theme too
+ LightweightThemeManager.forgetUsedTheme(undonePersonaId);
+
+ if (this.currentPersona && this.currentPersona.custom &&
+ LightweightThemeManager.setLocalTheme)
+ LightweightThemeManager.setLocalTheme(this.currentPersona);
+ else
+ LightweightThemeManager.currentTheme = this.currentPersona;
+ } else
+ this.resetPersona();
+ }
+ },
+
+ /**
+ * Shows a notification displaying the currently selected persona and button
+ * to revert the changes.
+ */
+ _showPersonaChangeNotification: function() {
+ // Obtain most recent window and its notification box
+ let wm =
+ Cc["@mozilla.org/appshell/window-mediator;1"].
+ getService(Ci.nsIWindowMediator);
+
+ let notificationBox;
+ switch (this.appInfo.ID) {
+ case this.FIREFOX_ID:
+ notificationBox = wm.getMostRecentWindow("navigator:browser").
+ getBrowser().getNotificationBox();
+ break;
+ case this.THUNDERBIRD_ID:
+ notificationBox = wm.getMostRecentWindow("mail:3pane").
+ document.getElementById("mail-notification-box");
+ break;
+ default:
+ throw "unknown application ID " + this.appInfo.ID;
+ }
+
+ // If there is another notification of the same kind already, remove it.
+ let oldNotification =
+ notificationBox.getNotificationWithValue("lwtheme-install-notification");
+ if (oldNotification)
+ notificationBox.removeNotification(oldNotification);
+
+ let message = this._strings.get("notification.personaWasSelected", [this.currentPersona.name, (this.currentPersona.author ?
+ this.currentPersona.author :
+ this.currentPersona.username)]);
+
+ let revertButton = {
+ label: this._strings.get("notification.revertButton.label"),
+ accessKey: this._strings.get("notification.revertButton.accesskey"),
+ popup: null,
+ callback: function() {
+ PersonaService.revertToPreviousPersona();
+ }
};
- }
- }
- return null;
- },
-
- /**
- * Monitors changes in cookies. If the modified cookie is the Personas session
- * cookie, then the favorites are refreshed (if the user is signed in).
- * @param aCookie The cookie that has been added, changed or removed.
- */
- onCookieChanged : function(aCookie, aChange) {
- if (aCookie instanceof Ci.nsICookie) {
- if (aCookie.name == "sessionid" && (aCookie.host == this.addonsHost ||
- aCookie.host == "." + this.addonsHost)) {
- if (aCookie.value == this._cookieValue)
- return;
- this._cookieValue = aCookie.value;
+
+ notificationBox.appendNotification(
+ message, "lwtheme-install-notification", null,
+ notificationBox.PRIORITY_INFO_MEDIUM, [revertButton]);
+ },
+
+ /**
+ * Looks for the given persona in the given array and returns its index.
+ * @param aPersona The persona to be found.
+ * @param aPersonaArray The array in which to look for the persona.
+ * @return The index of the persona in the array; -1 if not found.
+ */
+ _findPersonaInArray: function(aPersona, aPersonaArray) {
+ for (let i = 0; i < aPersonaArray.length; i++) {
+ if (aPersonaArray[i].id == aPersona.id)
+ return i;
+ }
+ return -1;
+ },
+
+ _getRandomPersonaFromArray: function(aPersonaArray) {
+ // Get a random item from the list, trying up to five times to get one
+ // that is different from the currently-selected item in the category
+ // (if any). We use Math.floor instead of Math.round to pick a random
+ // number because the JS reference says Math.round returns a non-uniform
+ // distribution
+ // <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Math:random#Examples>.
+ if (aPersonaArray && aPersonaArray.length > 0) {
+ let randomIndex, randomItem;
+ for (let i = 0; i < 5; i++) {
+ randomIndex = Math.floor(Math.random() * aPersonaArray.length);
+ randomItem = aPersonaArray[randomIndex];
+ if (!this.currentPersona || randomItem.id != this.currentPersona.id)
+ break;
+ }
+
+ return this.getPersonaJSON(randomItem);
+ }
+ return this.currentPersona;
+ },
+
+ _getRandomPersonaFromCategory: function(aCategoryName) {
+ // If we have the list of categories, use it to pick a random persona
+ // from the selected category.
+
+ if (this.personas) {
+ let personas = null;
+
+ if (aCategoryName == "featured")
+ personas = this.personas.featured;
+ if (personas)
+ return this._getRandomPersonaFromArray(personas);
+ }
+ return this.currentPersona;
+ },
+
+ /**
+ * Obtains the list of recently selected personas, parsed from the
+ * "lastselected" preferences.
+ * @param aHowMany (Optional, default 4) How many personas to obtain.
+ * @return The list of recent personas.
+ */
+ getRecentPersonas: function(aHowMany) {
+ if (!aHowMany)
+ aHowMany = 4;
+
+ // Parse the list from the preferences
+ let personas = [];
+ for (let i = 0; i < aHowMany; i++) {
+ if (this._prefs.has("lastselected" + i)) {
+ try {
+ personas.push(JSON.parse(this._prefs.get("lastselected" + i)));
+ } catch (ex) {}
+ }
+ }
+
+ return personas;
+ },
+
+ _addPersonaToRecent: function(persona) {
+ let personas = this.getRecentPersonas();
+
+ // Remove personas with the same ID (i.e. don't allow the recent persona
+ // to appear twice on the list). Afterwards, we'll add the recent persona
+ // to the list in a way that makes it the most recent one.
+ if (persona.id)
+ personas = personas.filter(function(v) !v.id || v.id != persona.id);
+
+ // Make the new persona the most recent one.
+ personas.unshift(persona);
+
+ // Note: at this point, there might be five personas on the list, four
+ // that we parsed from preferences and the one we're now adding. But we
+ // only serialize the first four back to preferences, so the oldest one
+ // drops off the end of the list.
+
+ // We store five in case we need to revert changes in the list alter on,
+ // even though only four will be displayed.
+ for (let i = 0; i < 5; i++) {
+ if (i < personas.length)
+ this._prefs.set("lastselected" + i, JSON.stringify(personas[i]));
+ else
+ this._prefs.reset("lastselected" + i);
+ }
+ },
+
+ /**
+ * Removes the most recent persona from the recent list, and leaves the
+ * following four personas as the most recent.
+ */
+ _revertRecent: function() {
+ // Create a new list of recent personas, removing the first one, but
+ // including the 5th (hidden) one.
+ let personas = this.getRecentPersonas(5);
+ personas.shift();
+
+ // Serialize the list of recent personas.
+ for (let i = 0; i < 5; i++) {
+ if (i < personas.length)
+ this._prefs.set("lastselected" + i, JSON.stringify(personas[i]));
+ else
+ this._prefs.reset("lastselected" + i);
+ }
+ },
+
+ onUseColorChanged: function() {
+ // Notify observers that the persona has changed so the change in whether
+ // or not to use the text or accent color will get applied. The persona
+ // hasn't really changed, but doing this has the desired effect without any
+ // known unwanted side effects.
+ Observers.notify("personas:persona:changed");
+ },
+
+ previewingPersona: null,
+
+ /**
+ * Display the given persona temporarily. Useful for showing users who are
+ * browsing the directory of personas what a given persona will look like
+ * when selected, f.e. on mouseover. Consumers who call this method should
+ * call resetPersona when the preview ends, f.e. on mouseout.
+ */
+ previewPersona: function(persona) {
+ if (LightweightThemeManager)
+ LightweightThemeManager.previewTheme(persona);
+ else {
+ this.previewingPersona = persona;
+ Observers.notify("personas:persona:changed");
+ }
+ },
+
+ /**
+ * Stop previewing a persona.
+ */
+ resetPersona: function() {
+ if (LightweightThemeManager)
+ LightweightThemeManager.resetPreview();
+ else {
+ this.previewingPersona = null;
+ Observers.notify("personas:persona:changed");
+ }
+ },
+
+ /**
+ * Gets the persona specified by the initial_persona cookie.
+ */
+ _getPersonaFromCookie: function() {
+ let authorizedHosts = this._prefs.get("authorizedHosts").split(/[, ]+/);
+ let cookieManager =
+ Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager);
+ let cookieEnu = cookieManager.enumerator;
+ let selectedCookie = null;
+
+ while (cookieEnu.hasMoreElements()) {
+ let cookie = cookieEnu.getNext().QueryInterface(Ci.nsICookie);
+
+ // XXX: Remove the initial dot from the host name, if any, before it is
+ // compared against the authorized hosts. This fixes the bug reported in
+ // bug 492392 that getpersonas.com and www.getpersonas.com cookies
+ // imported from IE have the cookie host .getpersonas.com, so they didn't
+ // match any of the authorized hosts.
+ let cookieHost = cookie.host.replace(/^\./, "");
+
+ if (cookie.name == COOKIE_INITIAL_PERSONA &&
+ authorizedHosts.some(function(v) v == cookieHost)) {
+
+ // There could be more than one "initial_persona" cookie. The cookie
+ // with latest expiration time is selected.
+ if (null == selectedCookie ||
+ cookie.expires > selectedCookie.expires) {
+ selectedCookie = cookie;
+ }
+
+ cookieManager.remove(cookie.host, cookie.name, cookie.path, false);
+ }
+ }
+
+ if (selectedCookie)
+ return JSON.parse(decodeURIComponent(selectedCookie.value));
+
+ return null;
+ },
+
+ //**************************************************************************//
+ // Lightweight Themes - Personas synchronization
+
+ _ltmSyncTimer: null,
+
+ /**
+ * Updates the add-on to reflect the changes from the Tools - Add-ons - Themes
+ * dialog. If a lightweight theme is set, it is also set as the add-on's current
+ * persona. If a regular theme is set, the current persona is set to "default".
+ */
+ onLightweightThemeChanged: function() {
+ let currentTheme = LightweightThemeManager.currentTheme;
+
+ this._ltmSyncTimer.cancel();
+
+ if (currentTheme &&
+ (currentTheme.id != this.currentPersona.id || this.selected == "default"))
+ this.changeToPersona(currentTheme);
+ else if (!currentTheme && this.selected != "default") {
+
+ // XXX: In Firefox 4, a persona change using the LightweightThemeManager causes
+ // two "lightweight-theme-changed" notifications to be fired instead of one:
+ // the first one with a null theme, and the second one with the actual theme.
+ // This causes the extension to get confused, as the first null theme causes
+ // it go change the persona to the default one, losing the "random" setting,
+ // amongst other things. To circumvent this, notifications with a "null" theme
+ // are delayed just a bit to allow the second notification to cancel it. If there's
+ // no second notification, then the null theme is applied.
+ // See: https://bugzilla.mozilla.org/show_bug.cgi?id=631803
+ let t = this;
+ this._ltmSyncTimer.initWithCallback({
+ notify: function() {
+ t.changeToDefaultPersona();
+ }
+ },
+ 100,
+ Ci.nsITimer.TYPE_ONE_SHOT);
+ }
+ },
+
+ //**************************************************************************//
+ // Random Rotation
+
+ _rotationTimer: null,
+
+ /**
+ * Checks the current value of the "selected" preference and sets the
+ * rotation timer accordingly.
+ */
+ onSelectedModeChanged: function() {
+ this._rotationTimer.cancel();
+
+ let selectedMode = this.selected;
+
+ if (selectedMode == "random" || selectedMode == "randomFavorite") {
+ let interval = this._prefs.get("rotationInterval") * 1000;
+ let that = this;
+
+ this._rotationTimer.initWithCallback({
+ notify: function(aTimer) {
+ that._rotatePersona();
+ }
+ },
+ interval, Ci.nsITimer.TYPE_REPEATING_SLACK);
+
+ this._rotatePersona();
+ }
+ },
+
+ /**
+ * Ensures that cookies are enabled for our current add-ons host, and
+ * prompts the user to enable them if not.
+ */
+ checkCookiesEnabled: function() {
+ const PERM = "cookie";
+ const ALLOW = 1,
+ SESSION_ONLY = 8;
+
+ try {
+ let prefs = new Preferences("network.cookie.");
+ let cookiesEnabled = (prefs.get("alwaysAcceptSessionCookies") ||
+ prefs.get("cookieBehavior") != 2);
+ let uri = Services.io.newURI(this.getURL("favorites-feed"), null, null);
+
+ let _ = this._strings.get.bind(this._strings);
+ if (cookiesEnabled || ~[ALLOW, SESSION_ONLY].indexOf(Services.perms.testPermission(uri, PERM))) {
+ this._prefs.reset("naggedAboutCookies");
+ } else {
+ if (this._prefs.get("naggedAboutCookies") == this.addonsHost)
+ return;
+
+ let stopNagging = {
+ value: true
+ };
+ let enableCookies = Services.prompt.confirmCheck(
+ null, _("cookies.confirm.title"),
+ _("cookies.confirm.message", [this.addonsHost]),
+ _("cookies.confirm.checkbox"),
+ stopNagging);
+
+ if (enableCookies) {
+ Services.perms.remove(uri.host, PERM);
+ Services.perms.add(uri, PERM, ALLOW);
+ } else if (stopNagging.value) {
+ this._prefs.set("naggedAboutCookies", this.addonsHost);
+ }
+ }
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ },
+
+ /**
+ * Handles the addons-host preference being changed.
+ */
+ onAddonsHostChanged: function() {
+ this.addonsHost = this._prefs.get("addons-host");
+ this.checkCookiesEnabled()
this.refreshFavorites();
- }
- }
- else if (aCookie instanceof Ci.nsIArray) {
- for (let enum_ = aCookie.enumerate(); enum_.hasMoreElements();)
- this.onCookieChanged(enum_.getNext());
+ },
+
+ /**
+ * Changes the current persona to a random persona of the same category (while
+ * in "random" mode) or a random persona from he favorite list (while in
+ * "randomFavorite" mode).
+ */
+ _rotatePersona: function() {
+ switch (this.selected) {
+ case "random":
+ this.changeToRandomPersona(this.category);
+ break;
+ case "randomFavorite":
+ this.changeToRandomFavoritePersona();
+ break;
+ }
+ },
+
+ //**************************************************************************//
+ // Persona Images Caching
+
+ /**
+ * Caches the header and footer images of the given persona inside the
+ * directory [profile]/personas/cache/[persona.id]. It removes all other
+ * existing persona directories before doing so.
+ * @param aPersona The persona for which to cache the images.
+ */
+ _cachePersonaImages: function(aPersona) {
+ let cacheDirectory =
+ FileUtils.getDirectory(FileUtils.getPersonasDirectory(), "cache");
+
+ // Remove all other subdirectories in the cache directory
+ // XXX: In the future, if we want to keep more than one persona cached
+ // this step would be removed.
+ let subdirs = FileUtils.getDirectoryEntries(cacheDirectory);
+ for (let i = 0; i < subdirs.length; i++) {
+ if (subdirs[i].isDirectory())
+ subdirs[i].remove(true);
+ }
+
+ // Create directory for the given persona
+ let personaDir = FileUtils.getDirectory(cacheDirectory, aPersona.id);
+
+ // Save header if specified.
+ let header = aPersona.headerURL || aPersona.header;
+ if (header) {
+ // The header can be a base64 string or a malformed URL, in which case
+ // the error can be safely ignored.
+ try {
+ let headerURI = URI.get(header, null, null)
+ .QueryInterface(Ci.nsIURL);
+
+ let headerCallback = function(aEvent) {
+ let request = aEvent.target;
+ // Save only if the folder still exists (Could have been deleted already)
+ if (request.status == 200 && personaDir.exists()) {
+ FileUtils.writeBinaryFile(
+ personaDir.clone(),
+ "header." + headerURI.fileExtension,
+ new Uint8Array(request.response));
+ }
+ };
+ this._makeRequest(headerURI.spec, headerCallback, null, true, true, {
+ responseType: "arraybuffer"
+ });
+ } catch (e) {}
+ }
+
+ // Save footer if specified.
+ let footer = aPersona.footerURL || aPersona.footer;
+ if (footer) {
+ // The footer can be a base64 string or a malformed URL, in which case
+ // the error can be safely ignored.
+ try {
+ let footerURI = URI.get(footer, null, null)
+ .QueryInterface(Ci.nsIURL);
+ let footerCallback = function(aEvent) {
+ let request = aEvent.target;
+ // Save only if the folder still exists (Could have been deleted already)
+ if (request.status == 200 && personaDir.exists()) {
+ FileUtils.writeBinaryFile(
+ personaDir.clone(),
+ "footer." + footerURI.fileExtension,
+ new Uint8Array(request.response));
+ }
+ };
+ this._makeRequest(footerURI.spec, footerCallback, null, true, true, {
+ responseType: "arraybuffer"
+ });
+ } catch (e) {}
+ }
+ },
+
+ /**
+ * Obtains the cached images of the given persona. This are stored in the
+ * _cachePersonaImages method under the directory
+ * [profile]/personas/cache/[persona.id].
+ * @param aPersona The persona for which to look the cached images.
+ * @return An object with "header" and "footer" properties, each containing
+ * the file URL of the image. Null otherwise.
+ */
+ getCachedPersonaImages: function(aPersona) {
+ let cacheDirectory =
+ FileUtils.getDirectory(FileUtils.getPersonasDirectory(), "cache");
+
+ let personaDir = FileUtils.getDirectory(cacheDirectory, aPersona.id, true);
+ if (personaDir.exists()) {
+
+ let headerFile = personaDir.clone();
+ let footerFile = personaDir.clone();
+
+ let headerFileExtension =
+ URI.get(aPersona.headerURL || aPersona.header, null, null)
+ .QueryInterface(Ci.nsIURL).fileExtension;
+
+ let footerFileExtension =
+ URI.get(aPersona.footerURL || aPersona.footer, null, null)
+ .QueryInterface(Ci.nsIURL).fileExtension;
+
+ headerFile.append("header" + "." + headerFileExtension);
+ footerFile.append("footer" + "." + footerFileExtension);
+
+ if (headerFile.exists() && footerFile.exists()) {
+ let ios =
+ Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+
+ let headerURI = ios.newFileURI(headerFile);
+ let footerURI = ios.newFileURI(footerFile);
+
+ return {
+ header: headerURI.spec,
+ footer: footerURI.spec
+ };
+ }
+ }
+ return null;
+ },
+
+ /**
+ * Monitors changes in cookies. If the modified cookie is the Personas session
+ * cookie, then the favorites are refreshed (if the user is signed in).
+ * @param aCookie The cookie that has been added, changed or removed.
+ */
+ onCookieChanged: function(aCookie, aChange) {
+ if (aCookie instanceof Ci.nsICookie) {
+ if (aCookie.name == "sessionid" && (aCookie.host == this.addonsHost ||
+ aCookie.host == "." + this.addonsHost)) {
+ if (aCookie.value == this._cookieValue)
+ return;
+ this._cookieValue = aCookie.value;
+ this.refreshFavorites();
+ }
+ } else if (aCookie instanceof Ci.nsIArray) {
+ for (let enum_ = aCookie.enumerate(); enum_.hasMoreElements();)
+ this.onCookieChanged(enum_.getNext());
+ }
+ },
+ _cookieValue: null,
+
+ /**
+ * Monitors HTTP responses so that we can heuristically detect logins
+ * and logouts.
+ * @param aRequest The HTTP request
+ */
+ onHTTPResponse: function(aRequest) {
+ aRequest.QueryInterface(Ci.nsIHttpChannel);
+
+ let uri = aRequest.URI.QueryInterface(Ci.nsIURL);
+ if (uri.host != this.addonsHost)
+ return;
+
+ if (endsWith(uri.filePath, "/users/logout") || aRequest.requestMethod == "POST" && endsWith(uri.filePath, "/users/login"))
+ this.refreshFavorites();
+ },
+
+ onQuitApplication: function() {
+ Observers.remove("quit-application", this.onQuitApplication, this);
+ this._destroy();
}
- },
- _cookieValue: null,
-
- /**
- * Monitors HTTP responses so that we can heuristically detect logins
- * and logouts.
- * @param aRequest The HTTP request
- */
- onHTTPResponse : function(aRequest) {
- aRequest.QueryInterface(Ci.nsIHttpChannel);
-
- let uri = aRequest.URI.QueryInterface(Ci.nsIURL);
- if (uri.host != this.addonsHost)
- return;
-
- if (endsWith(uri.filePath, "/users/logout")
- || aRequest.requestMethod == "POST" && endsWith(uri.filePath, "/users/login"))
- this.refreshFavorites();
- },
-
- onQuitApplication: function() {
- Observers.remove("quit-application", this.onQuitApplication, this);
- this._destroy();
- }
};
let DateUtils = {
- /**
- * Returns the number as a string with a 0 prepended to it if it contains
- * only one digit, for formats like ISO 8601 that require two digit month,
- * day, hour, minute, and second values (f.e. so midnight on January 1, 2009
- * becomes 2009:01:01T00:00:00Z instead of 2009:1:1T0:0:0Z, which would be
- * invalid).
- */
- _pad: function(number) {
- return (number >= 0 && number <= 9) ? "0" + number : "" + number;
- },
-
- /**
- * Format a date per ISO 8601, in particular the subset described in
- * http://www.w3.org/TR/NOTE-datetime, which is recommended for date
- * interchange on the internet.
- *
- * Example: 1994-11-06T08:49:37Z
- *
- * @param date {Date} the date to format
- * @returns {String} the date formatted per ISO 8601
- */
- toISO8601: function(date) {
- let year = date.getUTCFullYear();
- let month = this._pad(date.getUTCMonth() + 1);
- let day = this._pad(date.getUTCDate());
- let hours = this._pad(date.getUTCHours());
- let minutes = this._pad(date.getUTCMinutes());
- let seconds = this._pad(date.getUTCSeconds());
- return year + "-" + month + "-" + day + "T" +
- hours + ":" + minutes + ":" + seconds + "Z";
- },
-
- /**
- * Format a date per RFC 1123, which is the standard for HTTP headers.
- *
- * Example: Sun, 06 Nov 1994 08:49:37 GMT
- *
- * I'd love to use Datejs here, but its Date::toString formatting method
- * doesn't convert dates to their UTC equivalents before formatting them,
- * resulting in incorrect output (since RFC 1123 requires dates to be
- * in UTC), so instead I roll my own.
- *
- * @param date {Date} the date to format
- * @returns {String} the date formatted per RFC 1123
- */
- toRFC1123: function(date) {
- let dayOfWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][date.getUTCDay()];
- let day = this._pad(date.getUTCDate());
- let month = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][date.getUTCMonth()];
- let year = date.getUTCFullYear();
- let hours = this._pad(date.getUTCHours());
- let minutes = this._pad(date.getUTCMinutes());
- let seconds = this._pad(date.getUTCSeconds());
- return dayOfWeek + ", " + day + " " + month + " " + year + " " +
- hours + ":" + minutes + ":" + seconds + " GMT";
- }
+ /**
+ * Returns the number as a string with a 0 prepended to it if it contains
+ * only one digit, for formats like ISO 8601 that require two digit month,
+ * day, hour, minute, and second values (f.e. so midnight on January 1, 2009
+ * becomes 2009:01:01T00:00:00Z instead of 2009:1:1T0:0:0Z, which would be
+ * invalid).
+ */
+ _pad: function(number) {
+ return (number >= 0 && number <= 9) ? "0" + number : "" + number;
+ },
+
+ /**
+ * Format a date per ISO 8601, in particular the subset described in
+ * http://www.w3.org/TR/NOTE-datetime, which is recommended for date
+ * interchange on the internet.
+ *
+ * Example: 1994-11-06T08:49:37Z
+ *
+ * @param date {Date} the date to format
+ * @returns {String} the date formatted per ISO 8601
+ */
+ toISO8601: function(date) {
+ let year = date.getUTCFullYear();
+ let month = this._pad(date.getUTCMonth() + 1);
+ let day = this._pad(date.getUTCDate());
+ let hours = this._pad(date.getUTCHours());
+ let minutes = this._pad(date.getUTCMinutes());
+ let seconds = this._pad(date.getUTCSeconds());
+ return year + "-" + month + "-" + day + "T" +
+ hours + ":" + minutes + ":" + seconds + "Z";
+ },
+
+ /**
+ * Format a date per RFC 1123, which is the standard for HTTP headers.
+ *
+ * Example: Sun, 06 Nov 1994 08:49:37 GMT
+ *
+ * I'd love to use Datejs here, but its Date::toString formatting method
+ * doesn't convert dates to their UTC equivalents before formatting them,
+ * resulting in incorrect output (since RFC 1123 requires dates to be
+ * in UTC), so instead I roll my own.
+ *
+ * @param date {Date} the date to format
+ * @returns {String} the date formatted per RFC 1123
+ */
+ toRFC1123: function(date) {
+ let dayOfWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][date.getUTCDay()];
+ let day = this._pad(date.getUTCDate());
+ let month = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][date.getUTCMonth()];
+ let year = date.getUTCFullYear();
+ let hours = this._pad(date.getUTCHours());
+ let minutes = this._pad(date.getUTCMinutes());
+ let seconds = this._pad(date.getUTCSeconds());
+ return dayOfWeek + ", " + day + " " + month + " " + year + " " +
+ hours + ":" + minutes + ":" + seconds + " GMT";
+ }
};
let FileUtils = {
- /**
- * Gets the [profile]/personas directory.
- * @return The reference to the personas directory (nsIFile).
- */
- getPersonasDirectory : function() {
- let directoryService =
- Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
- let dir = directoryService.get("ProfD", Ci.nsIFile);
-
- return this.getDirectory(dir, "personas");
- },
-
- /**
- * Gets a reference to a directory (nsIFile) specified by the given name,
- * located inside the given parent directory.
- * @param aParentDirectory The parent directory of the directory to obtain.
- * @param aDirectoryName The name of the directory to obtain.
- * @param aDontCreate (Optional) Whether or not to create the directory if it
- * does not exist.
- * @return The reference to the directory (nsIFile).
- */
- getDirectory : function(aParentDirectory, aDirectoryName, aDontCreate) {
- let dir = aParentDirectory.clone();
- try {
- dir.append(aDirectoryName);
- if (!dir.exists() || !dir.isDirectory()) {
- if (!aDontCreate) {
- // read and write permissions to owner and group, read-only for others.
- dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0774);
+ /**
+ * Gets the [profile]/personas directory.
+ * @return The reference to the personas directory (nsIFile).
+ */
+ getPersonasDirectory: function() {
+ let directoryService =
+ Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
+ let dir = directoryService.get("ProfD", Ci.nsIFile);
+
+ return this.getDirectory(dir, "personas");
+ },
+
+ /**
+ * Gets a reference to a directory (nsIFile) specified by the given name,
+ * located inside the given parent directory.
+ * @param aParentDirectory The parent directory of the directory to obtain.
+ * @param aDirectoryName The name of the directory to obtain.
+ * @param aDontCreate (Optional) Whether or not to create the directory if it
+ * does not exist.
+ * @return The reference to the directory (nsIFile).
+ */
+ getDirectory: function(aParentDirectory, aDirectoryName, aDontCreate) {
+ let dir = aParentDirectory.clone();
+ try {
+ dir.append(aDirectoryName);
+ if (!dir.exists() || !dir.isDirectory()) {
+ if (!aDontCreate) {
+ // read and write permissions to owner and group, read-only for others.
+ dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0774);
+ }
+ }
+ } catch (ex) {
+ Cu.reportError("Could not create '" + aDirectoryName + "' directory");
+ dir = null;
}
- }
- }
- catch (ex) {
- Cu.reportError("Could not create '" + aDirectoryName + "' directory");
- dir = null;
- }
- return dir;
- },
-
- /**
- * Gets an array of the entries (nsIFile) found in the given directory.
- * @param aDirectory The directory from which to obtain the entries.
- * @return The array of entries.
- */
- getDirectoryEntries : function(aDirectory) {
- let entries = [];
- try {
- let enu = aDirectory.directoryEntries;
- while (enu.hasMoreElements()) {
- let entry = enu.getNext().QueryInterface(Ci.nsIFile);
- entries.push(entry);
- }
- }
- catch (ex) {
- Cu.reportError("Could not read entries of directory");
- }
- return entries;
- },
-
- /**
- * Reads the contents of a text file located at the given directory.
- * @param aDirectory The directory in which the file is read from (nsIFile)
- * @param aFileName The name of the file to be read.
- * @return The contents of the file (string), if any.
- */
- readFile : function(aDirectory, aFileName) {
- let data = "";
-
- try {
- let file = aDirectory.clone();
- file.append(aFileName);
-
- if (file.exists()) {
- let fstream =
- Cc["@mozilla.org/network/file-input-stream;1"].
- createInstance(Ci.nsIFileInputStream);
- fstream.init(file, -1, 0, 0);
-
- let cstream =
- Cc["@mozilla.org/intl/converter-input-stream;1"].
- createInstance(Ci.nsIConverterInputStream);
- cstream.init(fstream, "UTF-8", 0, 0);
-
- let str = {};
- // read the whole file
- while (cstream.readString(-1, str))
- data += str.value;
- cstream.close(); // this also closes fstream
- }
- }
- catch (ex) {
- Cu.reportError("Could not read file " + aFileName);
- }
+ return dir;
+ },
+
+ /**
+ * Gets an array of the entries (nsIFile) found in the given directory.
+ * @param aDirectory The directory from which to obtain the entries.
+ * @return The array of entries.
+ */
+ getDirectoryEntries: function(aDirectory) {
+ let entries = [];
+ try {
+ let enu = aDirectory.directoryEntries;
+ while (enu.hasMoreElements()) {
+ let entry = enu.getNext().QueryInterface(Ci.nsIFile);
+ entries.push(entry);
+ }
+ } catch (ex) {
+ Cu.reportError("Could not read entries of directory");
+ }
+ return entries;
+ },
+
+ /**
+ * Reads the contents of a text file located at the given directory.
+ * @param aDirectory The directory in which the file is read from (nsIFile)
+ * @param aFileName The name of the file to be read.
+ * @return The contents of the file (string), if any.
+ */
+ readFile: function(aDirectory, aFileName) {
+ let data = "";
- return data;
- },
-
- /**
- * Writes a text file in the given directory. If the file already exists it is
- * overwritten.
- * @param aDirectory The directory in which the file will be written (nsIFile).
- * @param aFileName The name of the file to be written.
- * @param aData The contents of the file.
- */
- writeFile : function(aDirectory, aFileName, aData) {
- try {
- let file = aDirectory.clone();
- file.append(aFileName);
-
- let foStream =
- Cc["@mozilla.org/network/file-output-stream;1"].
- createInstance(Ci.nsIFileOutputStream);
- // flags are write, create, truncate
- foStream.init(file, 0x02 | 0x08 | 0x20, 0666, 0);
-
- let converter =
- Cc["@mozilla.org/intl/converter-output-stream;1"].
- createInstance(Ci.nsIConverterOutputStream);
- converter.init(foStream, "UTF-8", 0, 0);
- converter.writeString(aData);
- converter.close(); // this also closes foStream
- }
- catch (ex) {
- Cu.reportError("Could not write file " + aFileName);
- }
- },
-
- /**
- * Writes a binary file in the given directory. If the file already exists it
- * is overwritten.
- * @param aDirectory The directory in which the file will be written (nsIFile).
- * @param aFileName The name of the file to be written.
- * @param aData The binary contents of the file.
- */
- writeBinaryFile : function(aDirectory, aFileName, aData) {
- try {
- let file = aDirectory.clone();
- file.append(aFileName);
-
- let stream =
- Cc["@mozilla.org/network/safe-file-output-stream;1"].
- createInstance(Ci.nsIFileOutputStream);
- // Flags are: write, create, truncate
- stream.init(file, 0x04 | 0x08 | 0x20, 0600, 0);
-
- stream.write(aData, aData.length);
- if (stream instanceof Ci.nsISafeOutputStream)
- stream.finish();
- else
- stream.close();
- }
- catch (ex) {
- Cu.reportError("Could not write binary file " + aFileName);
+ try {
+ let file = aDirectory.clone();
+ file.append(aFileName);
+
+ if (file.exists()) {
+ let fstream =
+ Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Ci.nsIFileInputStream);
+ fstream.init(file, -1, 0, 0);
+
+ let cstream =
+ Cc["@mozilla.org/intl/converter-input-stream;1"].
+ createInstance(Ci.nsIConverterInputStream);
+ cstream.init(fstream, "UTF-8", 0, 0);
+
+ let str = {};
+ // read the whole file
+ while (cstream.readString(-1, str))
+ data += str.value;
+ cstream.close(); // this also closes fstream
+ }
+ } catch (ex) {
+ Cu.reportError("Could not read file " + aFileName);
+ }
+
+ return data;
+ },
+
+ /**
+ * Writes a text file in the given directory. If the file already exists it is
+ * overwritten.
+ * @param aDirectory The directory in which the file will be written (nsIFile).
+ * @param aFileName The name of the file to be written.
+ * @param aData The contents of the file.
+ */
+ writeFile: function(aDirectory, aFileName, aData) {
+ try {
+ let file = aDirectory.clone();
+ file.append(aFileName);
+
+ return OS.File.writeAtomic(file.path, aData, {
+ tmpPath: file.path + ".tmp"
+ });
+ } catch (ex) {
+ Cu.reportError("Could not write file " + aFileName);
+ }
+ },
+
+ /**
+ * Writes a binary file in the given directory. If the file already exists it
+ * is overwritten.
+ * @param aDirectory The directory in which the file will be written (nsIFile).
+ * @param aFileName The name of the file to be written.
+ * @param aData The binary contents of the file.
+ */
+ writeBinaryFile: function(aDirectory, aFileName, aData) {
+ try {
+ let file = aDirectory.clone();
+ file.append(aFileName);
+
+ OS.File.writeAtomic(file.path, aData, {
+ tmpPath: file.path + ".tmp"
+ });
+ } catch (ex) {
+ Cu.reportError("Could not write binary file " + aFileName);
+ }
}
- }
};
-PersonaService._init();
+try {
+ PersonaService._init();
+} catch (e) {
+ Cu.reportError(e);
+}
\ No newline at end of file
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mozext/personasplus.git
More information about the Pkg-mozext-commits
mailing list