--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -157,16 +157,20 @@
command="Browser:ShowAllBookmarks"
onclick="PanelUI.hide();"/>
</panelview>
<panelview id="PanelUI-socialapi" flex="1"/>
<panelview id="PanelUI-loopapi" flex="1"/>
+ <panelview id="PanelUI-webExtensions-browserAction" flex="1">
+ <browser type="content" disableglobalhistory="true"/>
+ </panelview>
+
<panelview id="PanelUI-feeds" flex="1" oncommand="FeedHandler.subscribeToFeed(null, event);">
<label value="&feedsMenu.label;" class="panel-subview-header"/>
</panelview>
<panelview id="PanelUI-helpView" flex="1" class="PanelUI-subView">
<label value="&helpMenu.label;" class="panel-subview-header"/>
<vbox id="PanelUI-helpItems" class="panel-subview-body"/>
</panelview>
@@ -321,8 +325,16 @@
<description>&panicButton.thankyou.msg1;</description>
<description>&panicButton.thankyou.msg2;</description>
</vbox>
</hbox>
<button label="&panicButton.thankyou.buttonlabel;"
id="panic-button-success-closebutton"
oncommand="PanicButtonNotifier.close()"/>
</panel>
+
+<panel id="panel-webExtensions-pageAction"
+ class="browser-extension-panel"
+ type="arrow"
+ orient="vertical"
+ flex="1">
+ <browser type="content" disableglobalhistory="true"/>
+</panel>
--- a/browser/components/extensions/ext-browserAction.js
+++ b/browser/components/extensions/ext-browserAction.js
@@ -52,53 +52,49 @@ function BrowserAction(options, extensio
EventEmitter.decorate(this);
}
BrowserAction.prototype = {
build() {
let widget = CustomizableUI.createWidget({
id: this.id,
- type: "custom",
+ type: "view",
+ viewId: "PanelUI-webExtensions-browserAction",
removable: true,
defaultArea: CustomizableUI.AREA_NAVBAR,
- onBuild: document => {
- let node = document.createElement("toolbarbutton");
- node.id = this.id;
- node.setAttribute("class", "toolbarbutton-1 chromeclass-toolbar-additional badged-button");
- node.setAttribute("constrain-size", "true");
-
+ onCreated: node => {
+ this.node = node;
+ node.classList.add("badged-button");
this.updateButton(node, this.defaults);
-
+ },
+ onClick: event => {
+ let document = this.node.ownerDocument;
let tabbrowser = document.defaultView.gBrowser;
-
- node.addEventListener("command", event => {
- let tab = tabbrowser.selectedTab;
- let popup = this.getProperty(tab, "popup");
- if (popup) {
- this.togglePopup(node, popup);
- } else {
- this.emit("click");
- }
- });
-
- return node;
- },
+ let tab = tabbrowser.selectedTab;
+ let popup = this.getProperty(tab, "popup");
+ if (popup) {
+ let panel = document.getElementById(this.widget.viewId);
+ loadPanel(panel, popup, this.extension, () => {
+ let browser = panel.querySelector("browser");
+ panel.setAttribute("width", browser.getAttribute("width"));
+ panel.setAttribute("height", browser.getAttribute("height"));
+ });
+ return;
+ }
+ this.emit("click", tab);
+ }
});
this.tabContext.on("tab-select",
(evt, tab) => { this.updateWindow(tab.ownerDocument.defaultView); })
this.widget = widget;
},
- togglePopup(node, popupResource) {
- openPanel(node, popupResource, this.extension);
- },
-
// Update the toolbar button |node| with the tab context data
// in |tabData|.
updateButton(node, tabData) {
if (tabData.title) {
node.setAttribute("tooltiptext", tabData.title);
node.setAttribute("label", tabData.title);
node.setAttribute("aria-label", tabData.title);
} else {
--- a/browser/components/extensions/ext-pageAction.js
+++ b/browser/components/extensions/ext-pageAction.js
@@ -135,17 +135,22 @@ PageAction.prototype = {
// If the page action has a |popup| property, a panel is opened to
// that URL. Otherwise, a "click" event is emitted, and dispatched to
// the any click listeners in the add-on.
handleClick(window) {
let tab = window.gBrowser.selectedTab;
let popup = this.tabContext.get(tab).popup;
if (popup) {
- openPanel(this.getButton(window), popup, this.extension);
+ let node = this.getButton(window);
+ let document = node.ownerDocument;
+ let panel = document.getElementById("panel-webExtensions-pageAction");
+ loadPanel(panel, popup, this.extension, () => {
+ panel.openPopup(node, "bottomcenter topright", 0, 0, false, false);
+ });
} else {
this.emit("click", tab);
}
},
handleLocationChange(eventType, tab, fromBrowse) {
if (fromBrowse) {
this.tabContext.clear(tab);
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -445,21 +445,23 @@ extensions.registerAPI((extension, conte
return true;
}
let result = [];
let e = Services.wm.getEnumerator("navigator:browser");
while (e.hasMoreElements()) {
let window = e.getNext();
+ dump("\n\nwindow = " + window.document.documentURI + "\n");
if (window.document.readyState != "complete") {
continue;
}
let tabs = TabManager.getTabs(extension, window);
for (let tab of tabs) {
+ dump(" tab = " + tab.id + ":" + tab.windowId + " - " + tab.url + "\n");
if (matches(window, tab)) {
result.push(tab);
}
}
}
runSafe(context, callback, result);
},
--- a/browser/components/extensions/ext-utils.js
+++ b/browser/components/extensions/ext-utils.js
@@ -106,108 +106,73 @@ global.IconDetails = {
};
global.makeWidgetId = id => {
id = id.toLowerCase();
// FIXME: This allows for collisions.
return id.replace(/[^a-z0-9_-]/g, "_");
}
-// Open a panel anchored to the given node, containing a browser opened
-// to the given URL, owned by the given extension. If |popupURL| is not
-// an absolute URL, it is resolved relative to the given extension's
-// base URL.
-global.openPanel = (node, popupURL, extension) => {
- let document = node.ownerDocument;
-
+// Toggle the panel anchored to the given node. The panel will contain a
+// browser opened to the given URL, owned by the given extension. If
+// |popupURL| is not an absolute URL, it is resolved relative to the
+// given extension's base URL.
+global.loadPanel = (panel, popupURL, extension, cb) => {
let popupURI = Services.io.newURI(popupURL, null, extension.baseURI);
Services.scriptSecurityManager.checkLoadURIWithPrincipal(
extension.principal, popupURI,
Services.scriptSecurityManager.DISALLOW_SCRIPT);
- let panel = document.createElement("panel");
- panel.setAttribute("id", makeWidgetId(extension.id) + "-panel");
- panel.setAttribute("class", "browser-extension-panel");
- panel.setAttribute("type", "arrow");
- panel.setAttribute("role", "group");
-
- let anchor;
- if (node.localName == "toolbarbutton") {
- // Toolbar buttons are a special case. The panel becomes a child of
- // the button, and is anchored to the button's icon.
- node.appendChild(panel);
- anchor = document.getAnonymousElementByAttribute(node, "class", "toolbarbutton-icon");
- } else {
- // In all other cases, the panel is anchored to the target node
- // itself, and is a child of a popupset node.
- document.getElementById("mainPopupSet").appendChild(panel);
- anchor = node;
- }
-
- const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
- let browser = document.createElementNS(XUL_NS, "browser");
- browser.setAttribute("type", "content");
- browser.setAttribute("disableglobalhistory", "true");
- panel.appendChild(browser);
+ let browser = panel.querySelector("browser");
+ browser.setAttribute("src", "about:blank");
let titleChangedListener = () => {
panel.setAttribute("aria-label", browser.contentTitle);
}
- let context;
- panel.addEventListener("popuphidden", () => {
- browser.removeEventListener("DOMTitleChanged", titleChangedListener, true);
- context.unload();
- panel.remove();
+ let context = new ExtensionPage(extension, {
+ type: "popup",
+ contentWindow: browser.contentWindow,
+ uri: popupURI,
+ docShell: browser.docShell,
});
- let loadListener = () => {
- panel.removeEventListener("load", loadListener);
-
- context = new ExtensionPage(extension, {
- type: "popup",
- contentWindow: browser.contentWindow,
- uri: popupURI,
- docShell: browser.docShell,
- });
- GlobalManager.injectInDocShell(browser.docShell, extension, context);
- browser.setAttribute("src", context.uri.spec);
+ let contentLoadListener = () => {
+ browser.removeEventListener("load", contentLoadListener, true);
- let contentLoadListener = () => {
- browser.removeEventListener("load", contentLoadListener, true);
-
- let contentViewer = browser.docShell.contentViewer;
- let width = {}, height = {};
- try {
- contentViewer.getContentSize(width, height);
- [width, height] = [width.value, height.value];
- } catch (e) {
- // getContentSize can throw
- [width, height] = [400, 400];
- }
-
+ let contentViewer = browser.docShell.contentViewer;
+ let width = {}, height = {};
+ try {
+ contentViewer.getContentSize(width, height);
+ [width, height] = [width.value, height.value];
+ let document = panel.ownerDocument;
let window = document.defaultView;
width /= window.devicePixelRatio;
height /= window.devicePixelRatio;
- width = Math.min(width, 800);
- height = Math.min(height, 800);
+ } catch (e) {
+ // getContentSize can throw
+ [width, height] = [400, 400];
+ }
- browser.setAttribute("width", width);
- browser.setAttribute("height", height);
+ width = Math.min(width, 800);
+ height = Math.min(height, 800);
- panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
- };
- browser.addEventListener("load", contentLoadListener, true);
+ browser.setAttribute("width", width);
+ browser.setAttribute("height", height);
+ if (cb) {
+ cb();
+ }
+ };
+ browser.addEventListener("load", contentLoadListener, true);
- browser.addEventListener("DOMTitleChanged", titleChangedListener, true);
- };
- panel.addEventListener("load", loadListener);
+ browser.addEventListener("DOMTitleChanged", titleChangedListener, true);
- return panel;
+ GlobalManager.injectInDocShell(browser.docShell, extension, context);
+ browser.setAttribute("src", context.uri.spec);
}
// Manages tab-specific context data, and dispatching tab select events
// across all windows.
global.TabContext = function TabContext(getDefaults, extension) {
this.extension = extension;
this.getDefaults = getDefaults;
@@ -419,25 +384,27 @@ global.WindowListManager = {
while (e.hasMoreElements()) {
let window = e.getNext();
if (includeIncomplete || window.document.readyState == "complete") {
yield window;
}
}
},
- addOpenListener(listener) {
+ addOpenListener(listener, fireOnExisting = true) {
if (this._openListeners.length == 0 && this._closeListeners.length == 0) {
Services.ww.registerNotification(this);
}
this._openListeners.add(listener);
for (let window of this.browserWindows(true)) {
if (window.document.readyState != "complete") {
window.addEventListener("load", this);
+ } else if (fireOnExisting) {
+ listener(window);
}
}
},
removeOpenListener(listener) {
this._openListeners.delete(listener);
if (this._openListeners.length == 0 && this._closeListeners.length == 0) {
Services.ww.unregisterNotification(this);
@@ -455,17 +422,17 @@ global.WindowListManager = {
this._closeListeners.delete(listener);
if (this._openListeners.length == 0 && this._closeListeners.length == 0) {
Services.ww.unregisterNotification(this);
}
},
handleEvent(event) {
let window = event.target.defaultView;
- window.removeEventListener("load", this);
+ window.removeEventListener("load", this.loadListener);
if (window.document.documentElement.getAttribute("windowtype") != "navigator:browser") {
return;
}
for (let listener of this._openListeners) {
listener(window);
}
},
@@ -497,27 +464,28 @@ global.AllWindowEvents = {
// a web progress listener that covers all open windows.
addListener(type, listener) {
if (type == "domwindowopened") {
return WindowListManager.addOpenListener(listener);
} else if (type == "domwindowclosed") {
return WindowListManager.addCloseListener(listener);
}
- if (this._listeners.size == 0) {
- WindowListManager.addOpenListener(this.openListener);
- }
+ let needOpenListener = this._listeners.size == 0;
if (!this._listeners.has(type)) {
this._listeners.set(type, new Set());
}
let list = this._listeners.get(type);
list.add(listener);
- // Register listener on all existing windows.
+ if (needOpenListener) {
+ WindowListManager.addOpenListener(this.openListener, false);
+ }
+
for (let window of WindowListManager.browserWindows()) {
this.addWindowListener(window, type, listener);
}
},
removeListener(type, listener) {
if (type == "domwindowopened") {
return WindowListManager.removeOpenListener(listener);
@@ -529,17 +497,16 @@ global.AllWindowEvents = {
listeners.delete(listener);
if (listeners.size == 0) {
this._listeners.delete(type);
if (this._listeners.size == 0) {
WindowListManager.removeOpenListener(this.openListener);
}
}
- // Unregister listener from all existing windows.
for (let window of WindowListManager.browserWindows()) {
if (type == "progress") {
window.gBrowser.removeTabsProgressListener(listener);
} else {
window.removeEventListener(type, listener);
}
}
},
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_simple.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_simple.js
@@ -29,24 +29,18 @@ add_task(function* () {
yield extension.startup();
let widgetId = makeWidgetId(extension.id) + "-browser-action";
let node = CustomizableUI.getWidget(widgetId).forWindow(window).node;
// Do this a few times to make sure the pop-up is reloaded each time.
for (let i = 0; i < 3; i++) {
- let evt = new CustomEvent("command", {
- bubbles: true,
- cancelable: true
- });
+ let evt = new CustomEvent("click", {});
node.dispatchEvent(evt);
yield extension.awaitMessage("popup");
- let panel = node.querySelector("panel");
- if (panel) {
- panel.hidePopup();
- }
+ node.dispatchEvent(evt);
}
yield extension.unload();
});
--- a/browser/components/extensions/test/browser/browser_ext_currentWindow.js
+++ b/browser/components/extensions/test/browser/browser_ext_currentWindow.js
@@ -4,20 +4,25 @@ function genericChecker()
var path = window.location.pathname;
if (path.indexOf("popup") != -1) {
kind = "popup";
} else if (path.indexOf("page") != -1) {
kind = "page";
}
browser.test.onMessage.addListener((msg, ...args) => {
+ dump("\n\nBW2 - " + msg + "\n");
if (msg == kind + "-check-current1") {
+ // if (kind === 'popup') {
+ // debugger;
+ // }
browser.tabs.query({
currentWindow: true
}, function(tabs) {
+ dump("\n\nBW3 - " + kind + ", " + tabs[0].windowId + "\n");
browser.test.sendMessage("result", tabs[0].windowId);
});
} else if (msg == kind + "-check-current2") {
browser.tabs.query({
windowId: browser.windows.WINDOW_ID_CURRENT
}, function(tabs) {
browser.test.sendMessage("result", tabs[0].windowId);
});
@@ -88,16 +93,18 @@ add_task(function* () {
yield Promise.all([extension.startup(), extension.awaitMessage("background-ready")]);
let {TabManager, WindowManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
let winId1 = WindowManager.getId(win1);
let winId2 = WindowManager.getId(win2);
function* checkWindow(kind, winId, name) {
+ dump("\n\nBW1 - " + kind + ", " + winId + ", " + name + "\n");
+ // debugger;
extension.sendMessage(kind + "-check-current1");
is((yield extension.awaitMessage("result")), winId, `${name} is on top (check 1) [${kind}]`);
extension.sendMessage(kind + "-check-current2");
is((yield extension.awaitMessage("result")), winId, `${name} is on top (check 2) [${kind}]`);
extension.sendMessage(kind + "-check-current3");
is((yield extension.awaitMessage("result")), winId, `${name} is on top (check 3) [${kind}]`);
}
@@ -105,30 +112,24 @@ add_task(function* () {
yield checkWindow("background", winId1, "win1");
yield focusWindow(win2);
yield checkWindow("background", winId2, "win2");
function* triggerPopup(win, callback) {
let widgetId = makeWidgetId(extension.id) + "-browser-action";
let node = CustomizableUI.getWidget(widgetId).forWindow(win).node;
- let evt = new CustomEvent("command", {
- bubbles: true,
- cancelable: true
- });
+ let evt = new CustomEvent("click", {});
node.dispatchEvent(evt);
yield extension.awaitMessage("popup-ready");
yield callback();
- let panel = node.querySelector("panel");
- if (panel) {
- panel.hidePopup();
- }
+ node.dispatchEvent(evt);
}
// Set focus to some other window.
yield focusWindow(window);
yield triggerPopup(win1, function*() {
yield checkWindow("popup", winId1, "win1");
});
--- a/browser/components/extensions/test/browser/browser_ext_getViews.js
+++ b/browser/components/extensions/test/browser/browser_ext_getViews.js
@@ -111,30 +111,24 @@ add_task(function* () {
yield openTab(winId2);
yield checkViews("background", 2, 0);
function* triggerPopup(win, callback) {
let widgetId = makeWidgetId(extension.id) + "-browser-action";
let node = CustomizableUI.getWidget(widgetId).forWindow(win).node;
- let evt = new CustomEvent("command", {
- bubbles: true,
- cancelable: true
- });
+ let evt = new CustomEvent("click", {});
node.dispatchEvent(evt);
yield extension.awaitMessage("popup-ready");
yield callback();
- let panel = node.querySelector("panel");
- if (panel) {
- panel.hidePopup();
- }
+ node.dispatchEvent(evt);
}
yield triggerPopup(win1, function*() {
yield checkViews("background", 2, 1);
yield checkViews("popup", 2, 1);
});
yield triggerPopup(win2, function*() {
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
+++ b/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
@@ -131,17 +131,18 @@ add_task(function* testPageActionPopup()
let evt = new MouseEvent("click", {});
image.dispatchEvent(evt);
});
extension.onMessage("next-test", Task.async(function* () {
let panel = document.getElementById(panelId);
if (panel) {
yield promisePopupShown(panel);
- panel.hidePopup();
+ let evt = new MouseEvent("click", {});
+ document.getElementById(pageActionId).dispatchEvent(evt);
panel = document.getElementById(panelId);
is(panel, undefined, "panel successfully removed from document after hiding");
}
extension.sendMessage("next-test");
}));
@@ -193,17 +194,17 @@ add_task(function* testPageActionSecurit
});
yield Promise.all([extension.startup(), extension.awaitMessage("ready")]);
let browserActionId = makeWidgetId(extension.id) + "-browser-action";
let pageActionId = makeWidgetId(extension.id) + "-page-action";
let browserAction = document.getElementById(browserActionId);
- let evt = new CustomEvent("command", {});
+ let evt = new CustomEvent("click", {});
browserAction.dispatchEvent(evt);
let pageAction = document.getElementById(pageActionId);
evt = new MouseEvent("click", {});
pageAction.dispatchEvent(evt);
yield extension.unload();