Bug 511771 - Provide a way for Web content to preview and install lightweight themes. r=mconnor, sr=vlad
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -170,18 +170,18 @@ pref("extensions.getMoreThemesURL", "htt
pref("extensions.getMorePluginsURL", "https://%LOCALE%.add-ons.mozilla.com/%LOCALE%/%APP%/%VERSION%/plugins/");
pref("extensions.dss.enabled", false); // Dynamic Skin Switching
pref("extensions.dss.switchPending", false); // Non-dynamic switch pending after next
// restart.
pref("extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.name", "chrome://browser/locale/browser.properties");
pref("extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.description", "chrome://browser/locale/browser.properties");
-pref("xpinstall.whitelist.add", "update.mozilla.org");
-pref("xpinstall.whitelist.add.103", "addons.mozilla.org");
+pref("xpinstall.whitelist.add", "addons.mozilla.org");
+pref("xpinstall.whitelist.add.36", "getpersonas.com");
pref("keyword.enabled", true);
pref("keyword.URL", "chrome://browser-region/locale/region.properties");
pref("general.useragent.locale", "@AB_CD@");
pref("general.skins.selectedSkin", "classic/1.0");
pref("general.useragent.extra.firefox", "@APP_UA_NAME@/@APP_VERSION@");
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1351,16 +1351,20 @@ function delayedStartup(isLoadingBlank,
updateEditUIVisibility();
let placesContext = document.getElementById("placesContext");
placesContext.addEventListener("popupshowing", updateEditUIVisibility, false);
placesContext.addEventListener("popuphiding", updateEditUIVisibility, false);
#endif
// initialize the private browsing UI
gPrivateBrowsingUI.init();
+
+ gBrowser.mPanelContainer.addEventListener("InstallBrowserTheme", LightWeightThemeWebInstaller, false, true);
+ gBrowser.mPanelContainer.addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstaller, false, true);
+ gBrowser.mPanelContainer.addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstaller, false, true);
}
function BrowserShutdown()
{
gPrefService.removeObserver(ctrlTab.prefName, ctrlTab);
gPrefService.removeObserver(allTabs.prefName, allTabs);
tabPreviews.uninit();
ctrlTab.uninit();
@@ -7051,8 +7055,146 @@ let gURLBarEmptyText = {
case 2:
type = "bookmark";
break;
}
}
return gURLBar.getAttribute(type + "emptytext");
}
};
+
+var LightWeightThemeWebInstaller = {
+ handleEvent: function (event) {
+ switch (event.type) {
+ case "InstallBrowserTheme":
+ this._install(event);
+ break;
+ case "PreviewBrowserTheme":
+ this._preview(event);
+ break;
+ case "ResetBrowserThemePreview":
+ this._resetPreview(event);
+ break;
+ }
+ },
+
+ get _manager () {
+ var temp = {};
+ Cu.import("resource://gre/modules/LightweightThemeManager.jsm", temp);
+ delete this._manager;
+ return this._manager = temp.LightweightThemeManager;
+ },
+
+ _install: function (event) {
+ var node = event.target;
+ var data = this._getThemeFromNode(node);
+ if (!data)
+ return;
+
+ if (this._isAllowed(node)) {
+ this._manager.currentTheme = data;
+ return;
+ }
+
+ var allowButtonText =
+ gNavigatorBundle.getString("lwthemeInstallRequest.allowButton");
+ var allowButtonAccesskey =
+ gNavigatorBundle.getString("lwthemeInstallRequest.allowButton.accesskey");
+ var message =
+ gNavigatorBundle.getFormattedString("lwthemeInstallRequest.message",
+ [node.ownerDocument.location.host]);
+ var buttons = [{
+ label: allowButtonText,
+ accessKey: allowButtonAccesskey,
+ callback: function () {
+ LightWeightThemeWebInstaller._manager.currentTheme = data;
+ }
+ }];
+ var notificationBox = gBrowser.getNotificationBox();
+ notificationBox.appendNotification(message, "lwtheme-install-request", "",
+ notificationBox.PRIORITY_INFO_MEDIUM,
+ buttons);
+ },
+
+ _preview: function (event) {
+ if (!this._isAllowed(event.target))
+ return;
+
+ var data = this._getThemeFromNode(event.target);
+ if (!data)
+ return;
+
+ this._manager.previewTheme(data);
+ },
+
+ _resetPreview: function (event) {
+ if (!this._isAllowed(event.target))
+ return;
+
+ this._manager.resetPreview();
+ },
+
+ _isAllowed: function (node) {
+ var pm = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
+
+ var prefs = [["xpinstall.whitelist.add", pm.ALLOW_ACTION],
+ ["xpinstall.whitelist.add.36", pm.ALLOW_ACTION],
+ ["xpinstall.blacklist.add", pm.DENY_ACTION]];
+ prefs.forEach(function ([pref, permission]) {
+ try {
+ var hosts = gPrefService.getCharPref(pref);
+ } catch (e) {}
+
+ if (hosts) {
+ hosts.split(",").forEach(function (host) {
+ pm.add(makeURI("http://" + host), "install", permission);
+ });
+
+ gPrefService.setCharPref(pref, "");
+ }
+ });
+
+ var uri = node.ownerDocument.documentURIObject;
+ return pm.testPermission(uri, "install") == pm.ALLOW_ACTION;
+ },
+
+ _getThemeFromNode: function (node) {
+ const MANDATORY = ["id", "name", "headerURL"];
+ const OPTIONAL = ["footerURL", "textcolor", "accentcolor", "iconURL",
+ "previewURL", "author", "description", "homepageURL"];
+
+ try {
+ var data = JSON.parse(node.getAttribute("data-browsertheme"));
+ } catch (e) {
+ return null;
+ }
+
+ if (!data || typeof data != "object")
+ return null;
+
+ for (let prop in data) {
+ if (!data[prop] ||
+ typeof data[prop] != "string" ||
+ MANDATORY.indexOf(prop) == -1 && OPTIONAL.indexOf(prop) == -1) {
+ delete data[prop];
+ continue;
+ }
+
+ if (/URL$/.test(prop)) {
+ try {
+ data[prop] = makeURLAbsolute(node.baseURI, data[prop]);
+
+ if (/^https?:/.test(data[prop]))
+ continue;
+ } catch (e) {}
+
+ delete data[prop];
+ }
+ }
+
+ for (let i = 0; i < MANDATORY.length; i++) {
+ if (!(MANDATORY[i] in data))
+ return null;
+ }
+
+ return data;
+ }
+}
--- a/browser/components/preferences/permissions.js
+++ b/browser/components/preferences/permissions.js
@@ -366,17 +366,17 @@ var gPermissionManager = {
_updatePermissions: function ()
{
try {
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var pbi = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch2);
var prefList = [["xpinstall.whitelist.add", nsIPermissionManager.ALLOW_ACTION],
- ["xpinstall.whitelist.add.103", nsIPermissionManager.ALLOW_ACTION],
+ ["xpinstall.whitelist.add.36", nsIPermissionManager.ALLOW_ACTION],
["xpinstall.blacklist.add", nsIPermissionManager.DENY_ACTION]];
for (var i = 0; i < prefList.length; ++i) {
try {
// this pref is a comma-delimited list of hosts
var hosts = pbi.getCharPref(prefList[i][0]);
} catch(ex) {
continue;
--- a/toolkit/content/LightweightThemeConsumer.jsm
+++ b/toolkit/content/LightweightThemeConsumer.jsm
@@ -62,29 +62,31 @@ LightweightThemeConsumer.prototype = {
.getService(Components.interfaces.nsIObserverService)
.removeObserver(this, "lightweight-theme-changed");
this._doc = null;
},
_update: function (aData) {
if (!aData)
- aData = { headerURL: "", footerURL: "", textColor: "", dominantColor: "" };
+ aData = { headerURL: "", footerURL: "", textcolor: "", accentcolor: "" };
var root = this._doc.documentElement;
var active = !!aData.headerURL;
if (active) {
- root.style.color = aData.textColor || "black";
+ root.style.color = aData.textcolor || "black";
+ root.style.backgroundColor = aData.accentcolor || "white";
let [r, g, b] = _parseRGB(this._doc.defaultView.getComputedStyle(root, "").color);
let brightness = (r + g + b) / 3;
root.setAttribute("lwthemetextcolor", brightness <= 127 ? "dark" : "bright");
root.setAttribute("lwtheme", "true");
} else {
root.style.color = "";
+ root.style.backgroundColor = "";
root.removeAttribute("lwthemetextcolor");
root.removeAttribute("lwtheme");
}
_setImage(root, active, aData.headerURL);
if (this._footerId) {
let footer = this._doc.getElementById(this._footerId);
_setImage(footer, active, aData.footerURL);
@@ -96,19 +98,19 @@ LightweightThemeConsumer.prototype = {
if (root.hasAttribute("activetitlebarcolor")) {
if (!root.hasAttribute("originalactivetitlebarcolor")) {
root.setAttribute("originalactivetitlebarcolor",
root.getAttribute("activetitlebarcolor"));
root.setAttribute("originalinactivetitlebarcolor",
root.getAttribute("inactivetitlebarcolor"));
}
- root.setAttribute("activetitlebarcolor", aData.dominantColor
+ root.setAttribute("activetitlebarcolor", (active && aData.accentcolor)
|| root.getAttribute("originalactivetitlebarcolor"));
- root.setAttribute("inactivetitlebarcolor", aData.dominantColor
+ root.setAttribute("inactivetitlebarcolor", (active && aData.accentcolor)
|| root.getAttribute("originalinactivetitlebarcolor"));
}
}
}
function _setImage(aElement, aActive, aURL) {
aElement.style.backgroundImage =
(aActive && aURL) ? 'url("' + aURL.replace('"', '\\"', "g") + '")' : "";
--- a/toolkit/mozapps/extensions/test/unit/test_LightweightThemeManager.js
+++ b/toolkit/mozapps/extensions/test/unit/test_LightweightThemeManager.js
@@ -1,16 +1,16 @@
function dummy(id) {
return {
id: id,
name: Math.random(),
headerURL: Math.random(),
footerURL: Math.random(),
- textColor: Math.random(),
- dominantColor: Math.random()
+ textcolor: Math.random(),
+ accentcolor: Math.random()
};
}
function run_test() {
var temp = {};
Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", temp);
do_check_eq(typeof temp.LightweightThemeManager, "object");
--- a/xpinstall/src/nsInstallTrigger.cpp
+++ b/xpinstall/src/nsInstallTrigger.cpp
@@ -345,17 +345,17 @@ nsInstallTrigger::AllowInstall(nsIURI* a
// file: and chrome: don't need whitelisted hosts
if ( !isChrome && !isFile )
{
// check prefs for permission updates before testing URI
updatePermissions( XPINSTALL_WHITELIST_ADD,
nsIPermissionManager::ALLOW_ACTION,
permissionMgr, prefBranch );
- updatePermissions( XPINSTALL_WHITELIST_ADD_103,
+ updatePermissions( XPINSTALL_WHITELIST_ADD_36,
nsIPermissionManager::ALLOW_ACTION,
permissionMgr, prefBranch );
updatePermissions( XPINSTALL_BLACKLIST_ADD,
nsIPermissionManager::DENY_ACTION,
permissionMgr, prefBranch );
PRBool requireWhitelist = PR_TRUE;
prefBranch->GetBoolPref( XPINSTALL_WHITELIST_REQUIRED, &requireWhitelist );
--- a/xpinstall/src/nsInstallTrigger.h
+++ b/xpinstall/src/nsInstallTrigger.h
@@ -27,17 +27,17 @@
#define XPI_PERMISSION "install"
#define XPI_WHITELIST PR_TRUE
#define XPI_GLOBAL PR_FALSE
#define XPINSTALL_ENABLE_PREF "xpinstall.enabled"
#define XPINSTALL_WHITELIST_ADD "xpinstall.whitelist.add"
-#define XPINSTALL_WHITELIST_ADD_103 "xpinstall.whitelist.add.103"
+#define XPINSTALL_WHITELIST_ADD_36 "xpinstall.whitelist.add.36"
#define XPINSTALL_WHITELIST_REQUIRED "xpinstall.whitelist.required"
#define XPINSTALL_BLACKLIST_ADD "xpinstall.blacklist.add"
class nsInstallTrigger: public nsIScriptObjectOwner,
public nsIDOMInstallTriggerGlobal,
public nsIContentHandler
{
public: