Bug 1275287 - Use the same browser_style styling for options that we use for popups r?mixedpuppy
MozReview-Commit-ID: HtbkC9Nf6S8
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -60,16 +60,17 @@ support-files =
[browser_ext_devtools_network.js]
[browser_ext_devtools_page.js]
[browser_ext_devtools_panel.js]
[browser_ext_getViews.js]
[browser_ext_incognito_views.js]
[browser_ext_incognito_popup.js]
[browser_ext_lastError.js]
[browser_ext_omnibox.js]
+[browser_ext_optionsPage_browser_style.js]
[browser_ext_optionsPage_privileges.js]
[browser_ext_pageAction_context.js]
[browser_ext_pageAction_popup.js]
[browser_ext_pageAction_popup_resize.js]
[browser_ext_pageAction_simple.js]
[browser_ext_pageAction_title.js]
[browser_ext_popup_api_injection.js]
[browser_ext_popup_background.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_optionsPage_browser_style.js
@@ -0,0 +1,78 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+function* testOptionsBrowserStyle(optionsUI, assertMessage) {
+ function optionsScript() {
+ browser.test.onMessage.addListener((msgName, optionsUI, assertMessage) => {
+ if (msgName !== "check-style") {
+ browser.test.notifyFail("options-ui-browser_style");
+ }
+
+ let style = window.getComputedStyle(document.getElementById("button"));
+ let buttonBackgroundColor = style.backgroundColor;
+ let browserStyleBackgroundColor = "rgb(9, 150, 248)";
+ if (!("browser_style" in optionsUI) || optionsUI.browser_style) {
+ browser.test.assertEq(browserStyleBackgroundColor, buttonBackgroundColor, assertMessage);
+ } else {
+ browser.test.assertTrue(browserStyleBackgroundColor !== buttonBackgroundColor, assertMessage);
+ }
+
+ browser.test.notifyPass("options-ui-browser_style");
+ });
+ browser.test.sendMessage("options-ui-ready");
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ useAddonManager: "temporary",
+
+ manifest: {
+ "permissions": ["tabs"],
+ "options_ui": optionsUI,
+ },
+ files: {
+ "options.html": `
+ <!DOCTYPE html>
+ <html>
+ <button id="button" name="button" class="default">Default</button>
+ <script src="options.js" type="text/javascript"></script>
+ </html>`,
+ "options.js": optionsScript,
+ },
+ background() {
+ browser.runtime.openOptionsPage();
+ },
+ });
+
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser);
+
+ yield extension.startup();
+ yield extension.awaitMessage("options-ui-ready");
+
+ extension.sendMessage("check-style", optionsUI, assertMessage);
+ yield extension.awaitFinish("options-ui-browser_style");
+
+ yield extension.unload();
+
+ yield BrowserTestUtils.removeTab(tab);
+}
+
+add_task(function* test_options_without_setting_browser_style() {
+ yield testOptionsBrowserStyle({
+ "page": "options.html",
+ }, "Expected correct style when browser_style is excluded");
+});
+
+add_task(function* test_options_with_browser_style_set_to_true() {
+ yield testOptionsBrowserStyle({
+ "page": "options.html",
+ "browser_style": true,
+ }, "Expected correct style when browser_style is set to `true`");
+});
+
+add_task(function* test_options_with_browser_style_set_to_false() {
+ yield testOptionsBrowserStyle({
+ "page": "options.html",
+ "browser_style": false,
+ }, "Expected no style when browser_style is set to `false`");
+});
--- a/toolkit/components/extensions/ext-browser-content.js
+++ b/toolkit/components/extensions/ext-browser-content.js
@@ -27,20 +27,21 @@ const {
} = ExtensionUtils;
/* globals addMessageListener, addEventListener, content, docShell, removeEventListener, sendAsyncMessage */
// Minimum time between two resizes.
const RESIZE_TIMEOUT = 100;
const BrowserListener = {
- init({allowScriptsToClose, fixedWidth, maxHeight, maxWidth, stylesheets}) {
+ init({allowScriptsToClose, fixedWidth, maxHeight, maxWidth, stylesheets, isInline}) {
this.fixedWidth = fixedWidth;
this.stylesheets = stylesheets || [];
+ this.isInline = isInline;
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
this.oldBackground = null;
if (allowScriptsToClose) {
content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
@@ -63,26 +64,30 @@ const BrowserListener = {
},
receiveMessage({name, data}) {
if (name === "Extension:InitBrowser") {
this.init(data);
}
},
+ loadStylesheets() {
+ let winUtils = content.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+
+ for (let url of this.stylesheets) {
+ winUtils.addSheet(stylesheetMap.get(url), winUtils.AGENT_SHEET);
+ }
+ },
+
handleEvent(event) {
switch (event.type) {
case "DOMWindowCreated":
if (event.target === content.document) {
- let winUtils = content.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils);
-
- for (let url of this.stylesheets) {
- winUtils.addSheet(stylesheetMap.get(url), winUtils.AGENT_SHEET);
- }
+ this.loadStylesheets();
}
break;
case "DOMWindowClose":
if (event.target === content) {
event.preventDefault();
sendAsyncMessage("Extension:DOMWindowClose");
@@ -96,16 +101,22 @@ const BrowserListener = {
}
break;
case "load":
if (event.target.contentWindow === content) {
// For about:addons inline <browser>s, we currently receive a load
// event on the <browser> element, but no load or DOMContentLoaded
// events from the content window.
+
+ // Inline browsers don't receive the "DOMWindowCreated" event, so this
+ // is a workaround to load the stylesheets.
+ if (this.isInline) {
+ this.loadStylesheets();
+ }
sendAsyncMessage("Extension:BrowserContentLoaded", {url: content.location.href});
} else if (event.target !== content.document) {
break;
}
// We use a capturing listener, so we get this event earlier than any
// load listeners in the content page. Resizing after a timeout ensures
// that we calculate the size after the entire event cycle has completed
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -13,16 +13,17 @@ var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/DownloadUtils.jsm");
Cu.import("resource://gre/modules/AddonManager.jsm");
+Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/addons/AddonRepository.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils", "resource:///modules/E10SUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Extension",
"resource://gre/modules/Extension.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionParent",
"resource://gre/modules/ExtensionParent.jsm");
@@ -86,16 +87,24 @@ XPCOMUtils.defineLazyGetter(gStrings, "d
XPCOMUtils.defineLazyGetter(gStrings, "brandShortName", function() {
return this.brand.GetStringFromName("brandShortName");
});
XPCOMUtils.defineLazyGetter(gStrings, "appVersion", function() {
return Services.appinfo.version;
});
+XPCOMUtils.defineLazyGetter(this, "gInlineOptionsStylesheets", () => {
+ let stylesheets = ["chrome://browser/content/extension.css"];
+ if (AppConstants.platform === "macosx") {
+ stylesheets.push("chrome://browser/content/extension-mac.css");
+ }
+ return stylesheets;
+});
+
document.addEventListener("load", initialize, true);
window.addEventListener("unload", shutdown);
class MessageDispatcher {
constructor(target) {
this.listeners = new Map();
this.target = target;
}
@@ -3644,17 +3653,27 @@ var gDetailView = {
},
};
let mm = browser.messageManager || new FakeFrameMessageManager(browser);
mm.loadFrameScript("chrome://extensions/content/ext-browser-content.js",
false);
mm.addMessageListener("Extension:BrowserContentLoaded", messageListener);
mm.addMessageListener("Extension:BrowserResized", messageListener);
- mm.sendAsyncMessage("Extension:InitBrowser", {fixedWidth: true});
+
+ let browserOptions = {
+ fixedWidth: true,
+ isInline: true,
+ };
+
+ if (this._addon.optionsBrowserStyle) {
+ browserOptions.stylesheets = gInlineOptionsStylesheets;
+ }
+
+ mm.sendAsyncMessage("Extension:InitBrowser", browserOptions);
browser.loadURI(optionsURL);
});
}),
getSelectedAddon() {
return this._addon;
},
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -992,29 +992,36 @@ var loadManifestFromWebManifest = Task.a
addon.unpack = false;
addon.strictCompatibility = true;
addon.bootstrap = true;
addon.hasBinaryComponents = false;
addon.multiprocessCompatible = true;
addon.internalName = null;
addon.updateURL = bss.update_url;
addon.updateKey = null;
+ addon.optionsBrowserStyle = true;
addon.optionsURL = null;
addon.optionsType = null;
addon.aboutURL = null;
addon.dependencies = Object.freeze(Array.from(extension.dependencies));
if (manifest.options_ui) {
// Store just the relative path here, the AddonWrapper getURL
// wrapper maps this to a full URL.
addon.optionsURL = manifest.options_ui.page;
if (manifest.options_ui.open_in_tab)
addon.optionsType = AddonManager.OPTIONS_TYPE_TAB;
else
addon.optionsType = AddonManager.OPTIONS_TYPE_INLINE_BROWSER;
+
+ if (manifest.options_ui.browser_style === null)
+ logger.warn("Please specify whether you want browser_style " +
+ "or not in your options_ui options.");
+ else
+ addon.optionsBrowserStyle = manifest.options_ui.browser_style;
}
// WebExtensions don't use iconURLs
addon.iconURL = null;
addon.icon64URL = null;
addon.icons = manifest.icons || {};
addon.userPermissions = extension.userPermissions();
@@ -1242,21 +1249,22 @@ let loadManifestFromRDF = Task.async(fun
}
}
} else {
// Some add-on types are always restartless.
if (RESTARTLESS_TYPES.has(addon.type)) {
addon.bootstrap = true;
}
- // Only extensions are allowed to provide an optionsURL, optionsType or aboutURL. For
- // all other types they are silently ignored
+ // Only extensions are allowed to provide an optionsURL, optionsType,
+ // optionsBrowserStyle, or aboutURL. For all other types they are silently ignored
+ addon.aboutURL = null;
+ addon.optionsBrowserStyle = null;
+ addon.optionsType = null;
addon.optionsURL = null;
- addon.optionsType = null;
- addon.aboutURL = null;
if (addon.type == "theme") {
if (!addon.internalName)
throw new Error("Themes must include an internalName property");
addon.skinnable = getRDFProperty(ds, root, "skinnable") == "true";
}
}
@@ -7329,16 +7337,21 @@ AddonWrapper.prototype = {
return AddonManager.OPTIONS_TYPE_INLINE;
if (hasOptionsURL)
return AddonManager.OPTIONS_TYPE_DIALOG;
return null;
},
+ get optionsBrowserStyle() {
+ let addon = addonFor(this);
+ return addon.optionsBrowserStyle;
+ },
+
get iconURL() {
return AddonManager.getPreferredIconURL(this, 48);
},
get icon64URL() {
return AddonManager.getPreferredIconURL(this, 64);
},
--- a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
+++ b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
@@ -76,26 +76,26 @@ const DB_METADATA = ["syncGUID",
const DB_BOOL_METADATA = ["visible", "active", "userDisabled", "appDisabled",
"pendingUninstall", "bootstrap", "skinnable",
"softDisabled", "isForeignInstall",
"hasBinaryComponents", "strictCompatibility"];
// Properties to save in JSON file
const PROP_JSON_FIELDS = ["id", "syncGUID", "location", "version", "type",
"internalName", "updateURL", "updateKey", "optionsURL",
- "optionsType", "aboutURL", "icons", "iconURL", "icon64URL",
+ "optionsType", "optionsBrowserStyle", "aboutURL",
"defaultLocale", "visible", "active", "userDisabled",
"appDisabled", "pendingUninstall", "descriptor", "installDate",
"updateDate", "applyBackgroundUpdates", "bootstrap",
"skinnable", "size", "sourceURI", "releaseNotesURI",
"softDisabled", "foreignInstall", "hasBinaryComponents",
"strictCompatibility", "locales", "targetApplications",
"targetPlatforms", "multiprocessCompatible", "signedState",
"seen", "dependencies", "hasEmbeddedWebExtension", "mpcOptedOut",
- "userPermissions"];
+ "userPermissions", "icons", "iconURL", "icon64URL"];
// Properties that should be migrated where possible from an old database. These
// shouldn't include properties that can be read directly from install.rdf files
// or calculated
const DB_MIGRATE_METADATA = ["installDate", "userDisabled", "softDisabled",
"sourceURI", "applyBackgroundUpdates",
"releaseNotesURI", "foreignInstall", "syncGUID"];