Bug 1330369 - Part 2 - Add browser_style support to ext-sidebar.js r?mixedpuppy,kmag
MozReview-Commit-ID: AIGuVv98HGR
--- a/browser/base/content/webext-panels.js
+++ b/browser/base/content/webext-panels.js
@@ -7,22 +7,22 @@
/* import-globals-from browser.js */
/* import-globals-from nsContextMenu.js */
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionParent",
"resource://gre/modules/ExtensionParent.jsm");
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
+ extensionStylesheets,
promiseEvent,
} = ExtensionUtils;
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
function getBrowser(sidebar) {
let browser = document.getElementById("webext-panels-browser");
if (browser) {
return Promise.resolve(browser);
}
browser = document.createElementNS(XUL_NS, "browser");
browser.setAttribute("id", "webext-panels-browser");
@@ -49,25 +49,35 @@ function getBrowser(sidebar) {
} else {
readyPromise = Promise.resolve();
}
document.documentElement.appendChild(browser);
return readyPromise.then(() => {
browser.messageManager.loadFrameScript("chrome://browser/content/content.js", false);
ExtensionParent.apiManager.emit("extension-browser-inserted", browser);
+
+ if (sidebar.browserStyle) {
+ browser.messageManager.loadFrameScript(
+ "chrome://extensions/content/ext-browser-content.js", false);
+
+ browser.messageManager.sendAsyncMessage("Extension:InitBrowser", {
+ stylesheets: extensionStylesheets,
+ });
+ }
return browser;
});
}
function loadWebPanel() {
let sidebarURI = new URL(location);
let sidebar = {
uri: sidebarURI.searchParams.get("panel"),
remote: sidebarURI.searchParams.get("remote"),
+ browserStyle: sidebarURI.searchParams.get("browser-style"),
};
getBrowser(sidebar).then(browser => {
browser.loadURI(sidebar.uri);
});
}
function load() {
this.loadWebPanel();
--- a/browser/components/extensions/ext-sidebarAction.js
+++ b/browser/components/extensions/ext-sidebarAction.js
@@ -38,16 +38,20 @@ this.sidebarAction = class extends Exten
let options = extension.manifest.sidebar_action;
// Add the extension to the sidebar menu. The sidebar widget will copy
// from that when it is viewed, so we shouldn't need to update that.
let widgetId = makeWidgetId(extension.id);
this.id = `${widgetId}-sidebar-action`;
this.menuId = `menu_${this.id}`;
+ // We default browser_style to true because this is a new API and
+ // we therefore don't need to worry about breaking existing add-ons.
+ this.browserStyle = options.browser_style || options.browser_style === null;
+
this.defaults = {
enabled: true,
title: options.default_title || extension.name,
icon: IconDetails.normalize({path: options.default_icon}, extension),
panel: options.default_panel || "",
};
this.tabContext = new TabContext(tab => Object.create(this.defaults),
@@ -115,34 +119,42 @@ this.sidebarAction = class extends Exten
let widget = CustomizableUI.getWidget("sidebar-button");
if (!widget.areaType) {
CustomizableUI.addWidgetToArea("sidebar-button", CustomizableUI.AREA_NAVBAR, 0);
}
}
}
sidebarUrl(panel) {
+ let url = `${sidebarURL}?panel=${encodeURIComponent(panel)}`;
+
if (this.extension.remote) {
- return `${sidebarURL}?remote=1&panel=${encodeURIComponent(panel)}`;
+ url += "&remote=1";
}
- return `${sidebarURL}?&panel=${encodeURIComponent(panel)}`;
+
+ if (this.browserStyle) {
+ url += "&browser-style=1";
+ }
+
+ return url;
}
createMenuItem(window, details) {
let {document} = window;
// Use of the broadcaster allows browser-sidebar.js to properly manage the
// checkmarks in the menus.
let broadcaster = document.createElementNS(XUL_NS, "broadcaster");
broadcaster.setAttribute("id", this.id);
broadcaster.setAttribute("autoCheck", "false");
broadcaster.setAttribute("type", "checkbox");
broadcaster.setAttribute("group", "sidebar");
broadcaster.setAttribute("label", details.title);
broadcaster.setAttribute("sidebarurl", this.sidebarUrl(details.panel));
+
// oncommand gets attached to menuitem, so we use the observes attribute to
// get the command id we pass to SidebarUI.
broadcaster.setAttribute("oncommand", "SidebarUI.toggle(this.getAttribute('observes'))");
let menuitem = document.createElementNS(XUL_NS, "menuitem");
menuitem.setAttribute("id", this.menuId);
menuitem.setAttribute("observes", this.id);
menuitem.setAttribute("class", "menuitem-iconic webextension-menuitem");
--- a/browser/components/extensions/schemas/sidebar_action.json
+++ b/browser/components/extensions/schemas/sidebar_action.json
@@ -17,16 +17,20 @@
"type": "string",
"optional": true,
"preprocess": "localize"
},
"default_icon": {
"$ref": "IconPath",
"optional": true
},
+ "browser_style": {
+ "type": "boolean",
+ "optional": true
+ },
"default_panel": {
"type": "string",
"format": "strictRelativeUrl",
"preprocess": "localize"
}
},
"optional": true
}
--- a/toolkit/components/extensions/ext-browser-content.js
+++ b/toolkit/components/extensions/ext-browser-content.js
@@ -66,46 +66,52 @@ const BrowserListener = {
init({allowScriptsToClose, blockParser, fixedWidth, maxHeight, maxWidth, stylesheets, isInline}) {
this.fixedWidth = fixedWidth;
this.stylesheets = stylesheets || [];
this.isInline = isInline;
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
+ this.blockParser = blockParser;
+ this.needsResize = fixedWidth || maxHeight || maxWidth;
+
this.oldBackground = null;
if (allowScriptsToClose) {
getWinUtils(content).allowScriptsToClose();
}
// Force external links to open in tabs.
docShell.isAppTab = true;
- if (blockParser) {
+ if (this.blockParser) {
this.blockingPromise = new Promise(resolve => {
this.unblockParser = resolve;
});
+ addEventListener("DOMDocElementInserted", this, true);
}
+ addEventListener("load", this, true);
addEventListener("DOMWindowCreated", this, true);
- addEventListener("load", this, true);
addEventListener("DOMContentLoaded", this, true);
addEventListener("DOMWindowClose", this, true);
addEventListener("MozScrolledAreaChanged", this, true);
- addEventListener("DOMDocElementInserted", this, true);
},
destroy() {
+ if (this.blockParser) {
+ removeEventListener("DOMDocElementInserted", this, true);
+ }
+
+ removeEventListener("load", this, true);
removeEventListener("DOMWindowCreated", this, true);
- removeEventListener("load", this, true);
removeEventListener("DOMContentLoaded", this, true);
removeEventListener("DOMWindowClose", this, true);
removeEventListener("MozScrolledAreaChanged", this, true);
- removeEventListener("DOMDocElementInserted", this, true);
},
receiveMessage({name, data}) {
if (name === "Extension:InitBrowser") {
this.init(data);
} else if (name === "Extension:UnblockParser") {
if (this.unblockParser) {
this.unblockParser();
@@ -142,17 +148,20 @@ const BrowserListener = {
sendAsyncMessage("Extension:DOMWindowClose");
}
break;
case "DOMContentLoaded":
if (event.target === content.document) {
sendAsyncMessage("Extension:BrowserContentLoaded", {url: content.location.href});
- this.handleDOMChange(true);
+
+ if (this.needsResize) {
+ this.handleDOMChange(true);
+ }
}
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.
@@ -162,16 +171,20 @@ const BrowserListener = {
if (this.isInline) {
this.loadStylesheets();
}
sendAsyncMessage("Extension:BrowserContentLoaded", {url: content.location.href});
} else if (event.target !== content.document) {
break;
}
+ if (!this.needsResize) {
+ 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
// (unless someone spins the event loop, anyway), and hopefully after
// the content has made any modifications.
Promise.resolve().then(() => {
this.handleDOMChange(true);
});
@@ -182,17 +195,19 @@ const BrowserListener = {
attributes: true,
characterData: true,
childList: true,
subtree: true,
});
break;
case "MozScrolledAreaChanged":
- this.handleDOMChange();
+ if (this.needsResize) {
+ this.handleDOMChange();
+ }
break;
}
},
// Resizes the browser to match the preferred size of the content (debounced).
handleDOMChange(ignoreThrottling = false) {
if (ignoreThrottling && this.resizeTimeout) {
clearTimeout(this.resizeTimeout);