forked from mirrors/gecko-dev
CLOSED TREE Backed out changeset 6503123e95dd (bug 1139860) Backed out changeset b83bc163064d (bug 1203331) Backed out changeset 2f501bd57cd2 (bug 1202481) Backed out changeset 37e6ac7beb42 (bug 1202486) Backed out changeset f9b6e99e620e (bug 1202483) Backed out changeset 466af9f9baee (bug 1202482) Backed out changeset 6be690e265a2 (bug 1202479) Backed out changeset 57ff88bfccf4 (bug 1197475) Backed out changeset 7e8c04ff6049 (bug 1202478) Backed out changeset 525227997274 (bug 1202501) Backed out changeset da317cdb79d3 (bug 1199473) Backed out changeset 73b8ddd6dac9 (bug 1190662) --HG-- rename : browser/components/extensions/test/browser/browser_ext_simple.js => browser/components/extensions/test/browser/browser_extensions_simple.js rename : toolkit/components/extensions/test/mochitest/file_sample.html => toolkit/components/extensions/test/mochitest/file_contentscript_page1.html
327 lines
10 KiB
JavaScript
327 lines
10 KiB
JavaScript
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
|
|
"resource:///modules/CustomizableUI.jsm");
|
|
|
|
Cu.import("resource://gre/modules/devtools/shared/event-emitter.js");
|
|
|
|
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
|
var {
|
|
EventManager,
|
|
DefaultWeakMap,
|
|
ignoreEvent,
|
|
runSafe,
|
|
} = ExtensionUtils;
|
|
|
|
// WeakMap[Extension -> BrowserAction]
|
|
var browserActionMap = new WeakMap();
|
|
|
|
function browserActionOf(extension)
|
|
{
|
|
return browserActionMap.get(extension);
|
|
}
|
|
|
|
function makeWidgetId(id)
|
|
{
|
|
id = id.toLowerCase();
|
|
return id.replace(/[^a-z0-9_-]/g, "_");
|
|
}
|
|
|
|
var nextActionId = 0;
|
|
|
|
// Responsible for the browser_action section of the manifest as well
|
|
// as the associated popup.
|
|
function BrowserAction(options, extension)
|
|
{
|
|
this.extension = extension;
|
|
this.id = makeWidgetId(extension.id) + "-browser-action";
|
|
this.widget = null;
|
|
|
|
this.title = new DefaultWeakMap(extension.localize(options.default_title));
|
|
this.badgeText = new DefaultWeakMap();
|
|
this.badgeBackgroundColor = new DefaultWeakMap();
|
|
this.icon = new DefaultWeakMap(options.default_icon);
|
|
this.popup = new DefaultWeakMap(options.default_popup);
|
|
|
|
// Make the default something that won't compare equal to anything.
|
|
this.prevPopups = new DefaultWeakMap({});
|
|
|
|
this.context = null;
|
|
}
|
|
|
|
BrowserAction.prototype = {
|
|
build() {
|
|
let widget = CustomizableUI.createWidget({
|
|
id: this.id,
|
|
type: "custom",
|
|
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");
|
|
|
|
this.updateTab(null, node);
|
|
|
|
let tabbrowser = document.defaultView.gBrowser;
|
|
tabbrowser.ownerDocument.addEventListener("TabSelect", () => {
|
|
this.updateTab(tabbrowser.selectedTab, node);
|
|
});
|
|
|
|
node.addEventListener("command", event => {
|
|
if (node.getAttribute("type") != "panel") {
|
|
this.emit("click");
|
|
}
|
|
});
|
|
|
|
return node;
|
|
},
|
|
});
|
|
this.widget = widget;
|
|
},
|
|
|
|
// Initialize the toolbar icon and popup given that |tab| is the
|
|
// current tab and |node| is the CustomizableUI node. Note: |tab|
|
|
// will be null if we don't know the current tab yet (during
|
|
// initialization).
|
|
updateTab(tab, node) {
|
|
let window = node.ownerDocument.defaultView;
|
|
|
|
let title = this.getProperty(tab, "title");
|
|
if (title) {
|
|
node.setAttribute("tooltiptext", title);
|
|
node.setAttribute("label", title);
|
|
} else {
|
|
node.removeAttribute("tooltiptext");
|
|
node.removeAttribute("label");
|
|
}
|
|
|
|
let badgeText = this.badgeText.get(tab);
|
|
if (badgeText) {
|
|
node.setAttribute("badge", badgeText);
|
|
} else {
|
|
node.removeAttribute("badge");
|
|
}
|
|
|
|
function toHex(n) {
|
|
return Math.floor(n / 16).toString(16) + (n % 16).toString(16);
|
|
}
|
|
|
|
let badgeNode = node.ownerDocument.getAnonymousElementByAttribute(node,
|
|
'class', 'toolbarbutton-badge');
|
|
if (badgeNode) {
|
|
let color = this.badgeBackgroundColor.get(tab);
|
|
if (Array.isArray(color)) {
|
|
color = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
|
|
}
|
|
badgeNode.style.backgroundColor = color;
|
|
}
|
|
|
|
let iconURL = this.getIcon(tab, node);
|
|
node.setAttribute("image", iconURL);
|
|
|
|
let popup = this.getProperty(tab, "popup");
|
|
|
|
if (popup != this.prevPopups.get(window)) {
|
|
this.prevPopups.set(window, popup);
|
|
|
|
let panel = node.querySelector("panel");
|
|
if (panel) {
|
|
panel.remove();
|
|
}
|
|
|
|
if (popup) {
|
|
let popupURL = this.extension.baseURI.resolve(popup);
|
|
node.setAttribute("type", "panel");
|
|
|
|
let document = node.ownerDocument;
|
|
let panel = document.createElement("panel");
|
|
panel.setAttribute("class", "browser-action-panel");
|
|
panel.setAttribute("type", "arrow");
|
|
panel.setAttribute("flip", "slide");
|
|
node.appendChild(panel);
|
|
|
|
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");
|
|
browser.setAttribute("width", "500");
|
|
browser.setAttribute("height", "500");
|
|
panel.appendChild(browser);
|
|
|
|
let loadListener = () => {
|
|
panel.removeEventListener("load", loadListener);
|
|
|
|
if (this.context) {
|
|
this.context.unload();
|
|
}
|
|
|
|
this.context = new ExtensionPage(this.extension, {
|
|
type: "popup",
|
|
contentWindow: browser.contentWindow,
|
|
uri: Services.io.newURI(popupURL, null, null),
|
|
docShell: browser.docShell,
|
|
});
|
|
GlobalManager.injectInDocShell(browser.docShell, this.extension, this.context);
|
|
browser.setAttribute("src", popupURL);
|
|
};
|
|
panel.addEventListener("load", loadListener);
|
|
} else {
|
|
node.removeAttribute("type");
|
|
}
|
|
}
|
|
},
|
|
|
|
// Note: tab is allowed to be null here.
|
|
getIcon(tab, node) {
|
|
let icon = this.icon.get(tab);
|
|
|
|
let url;
|
|
if (typeof(icon) != "object") {
|
|
url = icon;
|
|
} else {
|
|
let window = node.ownerDocument.defaultView;
|
|
let utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
|
let res = {value: 1}
|
|
utils.getResolution(res);
|
|
|
|
let size = res.value == 1 ? 19 : 38;
|
|
url = icon[size];
|
|
}
|
|
|
|
if (url) {
|
|
return this.extension.baseURI.resolve(url);
|
|
} else {
|
|
return "chrome://browser/content/extension.svg";
|
|
}
|
|
},
|
|
|
|
// Update the toolbar button for a given window.
|
|
updateWindow(window) {
|
|
let tab = window.gBrowser ? window.gBrowser.selectedTab : null;
|
|
let node = CustomizableUI.getWidget(this.id).forWindow(window).node;
|
|
this.updateTab(tab, node);
|
|
},
|
|
|
|
// Update the toolbar button when the extension changes the icon,
|
|
// title, badge, etc. If it only changes a parameter for a single
|
|
// tab, |tab| will be that tab. Otherwise it will be null.
|
|
updateOnChange(tab) {
|
|
if (tab) {
|
|
if (tab.selected) {
|
|
this.updateWindow(tab.ownerDocument.defaultView);
|
|
}
|
|
} else {
|
|
let e = Services.wm.getEnumerator("navigator:browser");
|
|
while (e.hasMoreElements()) {
|
|
let window = e.getNext();
|
|
if (window.gBrowser) {
|
|
this.updateWindow(window);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
// tab is allowed to be null.
|
|
// prop should be one of "icon", "title", "badgeText", "popup", or "badgeBackgroundColor".
|
|
setProperty(tab, prop, value) {
|
|
this[prop].set(tab, value);
|
|
this.updateOnChange(tab);
|
|
},
|
|
|
|
// tab is allowed to be null.
|
|
// prop should be one of "title", "badgeText", "popup", or "badgeBackgroundColor".
|
|
getProperty(tab, prop) {
|
|
return this[prop].get(tab);
|
|
},
|
|
|
|
shutdown() {
|
|
CustomizableUI.destroyWidget(this.id);
|
|
},
|
|
};
|
|
|
|
EventEmitter.decorate(BrowserAction.prototype);
|
|
|
|
extensions.on("manifest_browser_action", (type, directive, extension, manifest) => {
|
|
let browserAction = new BrowserAction(manifest.browser_action, extension);
|
|
browserAction.build();
|
|
browserActionMap.set(extension, browserAction);
|
|
});
|
|
|
|
extensions.on("shutdown", (type, extension) => {
|
|
if (browserActionMap.has(extension)) {
|
|
browserActionMap.get(extension).shutdown();
|
|
browserActionMap.delete(extension);
|
|
}
|
|
});
|
|
|
|
extensions.registerAPI((extension, context) => {
|
|
return {
|
|
browserAction: {
|
|
onClicked: new EventManager(context, "browserAction.onClicked", fire => {
|
|
let listener = () => {
|
|
let tab = TabManager.activeTab;
|
|
fire(TabManager.convert(extension, tab));
|
|
};
|
|
browserActionOf(extension).on("click", listener);
|
|
return () => {
|
|
browserActionOf(extension).off("click", listener);
|
|
};
|
|
}).api(),
|
|
|
|
setTitle: function(details) {
|
|
let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
|
|
browserActionOf(extension).setProperty(tab, "title", details.title);
|
|
},
|
|
|
|
getTitle: function(details, callback) {
|
|
let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
|
|
let title = browserActionOf(extension).getProperty(tab, "title");
|
|
runSafe(context, callback, title);
|
|
},
|
|
|
|
setIcon: function(details, callback) {
|
|
let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
|
|
if (details.imageData) {
|
|
// FIXME: Support the imageData attribute.
|
|
return;
|
|
}
|
|
browserActionOf(extension).setProperty(tab, "icon", details.path);
|
|
},
|
|
|
|
setBadgeText: function(details) {
|
|
let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
|
|
browserActionOf(extension).setProperty(tab, "badgeText", details.text);
|
|
},
|
|
|
|
getBadgeText: function(details, callback) {
|
|
let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
|
|
let text = browserActionOf(extension).getProperty(tab, "badgeText");
|
|
runSafe(context, callback, text);
|
|
},
|
|
|
|
setPopup: function(details) {
|
|
let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
|
|
browserActionOf(extension).setProperty(tab, "popup", details.popup);
|
|
},
|
|
|
|
getPopup: function(details, callback) {
|
|
let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
|
|
let popup = browserActionOf(extension).getProperty(tab, "popup");
|
|
runSafe(context, callback, popup);
|
|
},
|
|
|
|
setBadgeBackgroundColor: function(details) {
|
|
let color = details.color;
|
|
let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
|
|
browserActionOf(extension).setProperty(tab, "badgeBackgroundColor", details.color);
|
|
},
|
|
|
|
getBadgeBackgroundColor: function(details, callback) {
|
|
let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
|
|
let color = browserActionOf(extension).getProperty(tab, "badgeBackgroundColor");
|
|
runSafe(context, callback, color);
|
|
},
|
|
}
|
|
};
|
|
});
|