--- a/browser/components/extensions/ext-bookmarks.js
+++ b/browser/components/extensions/ext-bookmarks.js
@@ -1,21 +1,16 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
-const {
+var {
SingletonEventManager,
} = ExtensionUtils;
-XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
- "resource://devtools/shared/event-emitter.js");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
let listenerCount = 0;
function getTree(rootGuid, onlyChildren) {
@@ -178,199 +173,201 @@ function decrementListeners() {
function incrementListeners() {
listenerCount++;
if (listenerCount == 1) {
PlacesUtils.bookmarks.addObserver(observer, false);
}
}
-extensions.registerSchemaAPI("bookmarks", "addon_parent", context => {
- return {
- bookmarks: {
- get: function(idOrIdList) {
- let list = Array.isArray(idOrIdList) ? idOrIdList : [idOrIdList];
+this.bookmarks = class extends ExtensionAPI {
+ getAPI(context) {
+ return {
+ bookmarks: {
+ get: function(idOrIdList) {
+ let list = Array.isArray(idOrIdList) ? idOrIdList : [idOrIdList];
- return Task.spawn(function* () {
- let bookmarks = [];
- for (let id of list) {
- let bookmark = yield PlacesUtils.bookmarks.fetch({guid: id});
- if (!bookmark) {
- throw new Error("Bookmark not found");
+ return Task.spawn(function* () {
+ let bookmarks = [];
+ for (let id of list) {
+ let bookmark = yield PlacesUtils.bookmarks.fetch({guid: id});
+ if (!bookmark) {
+ throw new Error("Bookmark not found");
+ }
+ bookmarks.push(convert(bookmark));
}
- bookmarks.push(convert(bookmark));
- }
- return bookmarks;
- }).catch(error => Promise.reject({message: error.message}));
- },
+ return bookmarks;
+ }).catch(error => Promise.reject({message: error.message}));
+ },
- getChildren: function(id) {
- // TODO: We should optimize this.
- return getTree(id, true);
- },
+ getChildren: function(id) {
+ // TODO: We should optimize this.
+ return getTree(id, true);
+ },
- getTree: function() {
- return getTree(PlacesUtils.bookmarks.rootGuid, false);
- },
+ getTree: function() {
+ return getTree(PlacesUtils.bookmarks.rootGuid, false);
+ },
- getSubTree: function(id) {
- return getTree(id, false);
- },
+ getSubTree: function(id) {
+ return getTree(id, false);
+ },
- search: function(query) {
- return PlacesUtils.bookmarks.search(query).then(result => result.map(convert));
- },
+ search: function(query) {
+ return PlacesUtils.bookmarks.search(query).then(result => result.map(convert));
+ },
- getRecent: function(numberOfItems) {
- return PlacesUtils.bookmarks.getRecent(numberOfItems).then(result => result.map(convert));
- },
+ getRecent: function(numberOfItems) {
+ return PlacesUtils.bookmarks.getRecent(numberOfItems).then(result => result.map(convert));
+ },
- create: function(bookmark) {
- let info = {
- title: bookmark.title || "",
- };
+ create: function(bookmark) {
+ let info = {
+ title: bookmark.title || "",
+ };
- // If url is NULL or missing, it will be a folder.
- if (bookmark.url !== null) {
- info.type = PlacesUtils.bookmarks.TYPE_BOOKMARK;
- info.url = bookmark.url || "";
- } else {
- info.type = PlacesUtils.bookmarks.TYPE_FOLDER;
- }
+ // If url is NULL or missing, it will be a folder.
+ if (bookmark.url !== null) {
+ info.type = PlacesUtils.bookmarks.TYPE_BOOKMARK;
+ info.url = bookmark.url || "";
+ } else {
+ info.type = PlacesUtils.bookmarks.TYPE_FOLDER;
+ }
- if (bookmark.index !== null) {
- info.index = bookmark.index;
- }
+ if (bookmark.index !== null) {
+ info.index = bookmark.index;
+ }
- if (bookmark.parentId !== null) {
- info.parentGuid = bookmark.parentId;
- } else {
- info.parentGuid = PlacesUtils.bookmarks.unfiledGuid;
- }
+ if (bookmark.parentId !== null) {
+ info.parentGuid = bookmark.parentId;
+ } else {
+ info.parentGuid = PlacesUtils.bookmarks.unfiledGuid;
+ }
- try {
- return PlacesUtils.bookmarks.insert(info).then(convert)
- .catch(error => Promise.reject({message: error.message}));
- } catch (e) {
- return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
- }
- },
+ try {
+ return PlacesUtils.bookmarks.insert(info).then(convert)
+ .catch(error => Promise.reject({message: error.message}));
+ } catch (e) {
+ return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
+ }
+ },
- move: function(id, destination) {
- let info = {
- guid: id,
- };
+ move: function(id, destination) {
+ let info = {
+ guid: id,
+ };
- if (destination.parentId !== null) {
- info.parentGuid = destination.parentId;
- }
- info.index = (destination.index === null) ?
- PlacesUtils.bookmarks.DEFAULT_INDEX : destination.index;
+ if (destination.parentId !== null) {
+ info.parentGuid = destination.parentId;
+ }
+ info.index = (destination.index === null) ?
+ PlacesUtils.bookmarks.DEFAULT_INDEX : destination.index;
- try {
- return PlacesUtils.bookmarks.update(info).then(convert)
- .catch(error => Promise.reject({message: error.message}));
- } catch (e) {
- return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
- }
- },
+ try {
+ return PlacesUtils.bookmarks.update(info).then(convert)
+ .catch(error => Promise.reject({message: error.message}));
+ } catch (e) {
+ return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
+ }
+ },
- update: function(id, changes) {
- let info = {
- guid: id,
- };
+ update: function(id, changes) {
+ let info = {
+ guid: id,
+ };
- if (changes.title !== null) {
- info.title = changes.title;
- }
- if (changes.url !== null) {
- info.url = changes.url;
- }
+ if (changes.title !== null) {
+ info.title = changes.title;
+ }
+ if (changes.url !== null) {
+ info.url = changes.url;
+ }
- try {
- return PlacesUtils.bookmarks.update(info).then(convert)
- .catch(error => Promise.reject({message: error.message}));
- } catch (e) {
- return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
- }
- },
+ try {
+ return PlacesUtils.bookmarks.update(info).then(convert)
+ .catch(error => Promise.reject({message: error.message}));
+ } catch (e) {
+ return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
+ }
+ },
- remove: function(id) {
- let info = {
- guid: id,
- };
+ remove: function(id) {
+ let info = {
+ guid: id,
+ };
- // The API doesn't give you the old bookmark at the moment
- try {
- return PlacesUtils.bookmarks.remove(info, {preventRemovalOfNonEmptyFolders: true}).then(result => {})
- .catch(error => Promise.reject({message: error.message}));
- } catch (e) {
- return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
- }
- },
+ // The API doesn't give you the old bookmark at the moment
+ try {
+ return PlacesUtils.bookmarks.remove(info, {preventRemovalOfNonEmptyFolders: true}).then(result => {})
+ .catch(error => Promise.reject({message: error.message}));
+ } catch (e) {
+ return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
+ }
+ },
- removeTree: function(id) {
- let info = {
- guid: id,
- };
+ removeTree: function(id) {
+ let info = {
+ guid: id,
+ };
- try {
- return PlacesUtils.bookmarks.remove(info).then(result => {})
- .catch(error => Promise.reject({message: error.message}));
- } catch (e) {
- return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
- }
- },
+ try {
+ return PlacesUtils.bookmarks.remove(info).then(result => {})
+ .catch(error => Promise.reject({message: error.message}));
+ } catch (e) {
+ return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
+ }
+ },
- onCreated: new SingletonEventManager(context, "bookmarks.onCreated", fire => {
- let listener = (event, bookmark) => {
- fire.sync(bookmark.id, bookmark);
- };
+ onCreated: new SingletonEventManager(context, "bookmarks.onCreated", fire => {
+ let listener = (event, bookmark) => {
+ fire.sync(bookmark.id, bookmark);
+ };
- observer.on("created", listener);
- incrementListeners();
- return () => {
- observer.off("created", listener);
- decrementListeners();
- };
- }).api(),
+ observer.on("created", listener);
+ incrementListeners();
+ return () => {
+ observer.off("created", listener);
+ decrementListeners();
+ };
+ }).api(),
- onRemoved: new SingletonEventManager(context, "bookmarks.onRemoved", fire => {
- let listener = (event, data) => {
- fire.sync(data.guid, data.info);
- };
+ onRemoved: new SingletonEventManager(context, "bookmarks.onRemoved", fire => {
+ let listener = (event, data) => {
+ fire.sync(data.guid, data.info);
+ };
- observer.on("removed", listener);
- incrementListeners();
- return () => {
- observer.off("removed", listener);
- decrementListeners();
- };
- }).api(),
+ observer.on("removed", listener);
+ incrementListeners();
+ return () => {
+ observer.off("removed", listener);
+ decrementListeners();
+ };
+ }).api(),
- onChanged: new SingletonEventManager(context, "bookmarks.onChanged", fire => {
- let listener = (event, data) => {
- fire.sync(data.guid, data.info);
- };
+ onChanged: new SingletonEventManager(context, "bookmarks.onChanged", fire => {
+ let listener = (event, data) => {
+ fire.sync(data.guid, data.info);
+ };
- observer.on("changed", listener);
- incrementListeners();
- return () => {
- observer.off("changed", listener);
- decrementListeners();
- };
- }).api(),
+ observer.on("changed", listener);
+ incrementListeners();
+ return () => {
+ observer.off("changed", listener);
+ decrementListeners();
+ };
+ }).api(),
- onMoved: new SingletonEventManager(context, "bookmarks.onMoved", fire => {
- let listener = (event, data) => {
- fire.sync(data.guid, data.info);
- };
+ onMoved: new SingletonEventManager(context, "bookmarks.onMoved", fire => {
+ let listener = (event, data) => {
+ fire.sync(data.guid, data.info);
+ };
- observer.on("moved", listener);
- incrementListeners();
- return () => {
- observer.off("moved", listener);
- decrementListeners();
- };
- }).api(),
- },
- };
-});
+ observer.on("moved", listener);
+ incrementListeners();
+ return () => {
+ observer.off("moved", listener);
+ decrementListeners();
+ };
+ }).api(),
+ },
+ };
+ }
+};
rename from browser/components/extensions/ext-desktop-runtime.js
rename to browser/components/extensions/ext-browser.js
--- a/browser/components/extensions/ext-desktop-runtime.js
+++ b/browser/components/extensions/ext-browser.js
@@ -1,17 +1,21 @@
"use strict";
+XPCOMUtils.defineLazyModuleGetter(global, "EventEmitter",
+ "resource://devtools/shared/event-emitter.js");
+
/* eslint-disable mozilla/balanced-listeners */
extensions.on("uninstall", (msg, extension) => {
if (extension.uninstallURL) {
let browser = windowTracker.topWindow.gBrowser;
browser.addTab(extension.uninstallURL, {relatedToCurrent: true});
}
});
+/* eslint-enable mozilla/balanced-listeners */
global.openOptionsPage = (extension) => {
let window = windowTracker.topWindow;
if (!window) {
return Promise.reject({message: "No browser window available"});
}
if (extension.manifest.options_ui.open_in_tab) {
@@ -19,8 +23,159 @@ global.openOptionsPage = (extension) =>
return Promise.resolve();
}
let viewId = `addons://detail/${encodeURIComponent(extension.id)}/preferences`;
return window.BrowserOpenAddonsMgr(viewId);
};
+extensions.registerModules({
+ bookmarks: {
+ url: "chrome://browser/content/ext-bookmarks.js",
+ schema: "chrome://browser/content/schemas/bookmarks.json",
+ scopes: ["addon_parent"],
+ paths: [
+ ["bookmarks"],
+ ],
+ },
+ browserAction: {
+ url: "chrome://browser/content/ext-browserAction.js",
+ schema: "chrome://browser/content/schemas/browser_action.json",
+ scopes: ["addon_parent"],
+ manifest: ["browser_action"],
+ paths: [
+ ["browserAction"],
+ ],
+ },
+ browsingData: {
+ url: "chrome://browser/content/ext-browsingData.js",
+ schema: "chrome://browser/content/schemas/browsing_data.json",
+ scopes: ["addon_parent"],
+ paths: [
+ ["browsingData"],
+ ],
+ },
+ chrome_settings_overrides: {
+ url: "chrome://browser/content/ext-chrome-settings-overrides.js",
+ scopes: [],
+ schema: "chrome://browser/content/schemas/chrome_settings_overrides.json",
+ manifest: ["chrome_settings_overrides"],
+ },
+ commands: {
+ url: "chrome://browser/content/ext-commands.js",
+ schema: "chrome://browser/content/schemas/commands.json",
+ scopes: ["addon_parent"],
+ manifest: ["commands"],
+ paths: [
+ ["commands"],
+ ],
+ },
+ contextMenus: {
+ url: "chrome://browser/content/ext-contextMenus.js",
+ schema: "chrome://browser/content/schemas/context_menus.json",
+ scopes: ["addon_parent"],
+ paths: [
+ ["contextMenus"],
+ ],
+ },
+ devtools: {
+ url: "chrome://browser/content/ext-devtools.js",
+ schema: "chrome://browser/content/schemas/devtools.json",
+ scopes: ["devtools_parent"],
+ manifest: ["devtools_page"],
+ paths: [
+ ["devtools"],
+ ],
+ },
+ devtools_inspectedWindow: {
+ url: "chrome://browser/content/ext-devtools-inspectedWindow.js",
+ schema: "chrome://browser/content/schemas/devtools_inspected_window.json",
+ scopes: ["devtools_parent"],
+ paths: [
+ ["devtools", "inspectedWindow"],
+ ],
+ },
+ devtools_network: {
+ url: "chrome://browser/content/ext-devtools-network.js",
+ schema: "chrome://browser/content/schemas/devtools_network.json",
+ scopes: ["devtools_parent"],
+ paths: [
+ ["devtools", "network"],
+ ],
+ },
+ devtools_panels: {
+ url: "chrome://browser/content/ext-devtools-panels.js",
+ schema: "chrome://browser/content/schemas/devtools_panels.json",
+ scopes: ["devtools_parent"],
+ paths: [
+ ["devtools", "panels"],
+ ],
+ },
+ history: {
+ url: "chrome://browser/content/ext-history.js",
+ schema: "chrome://browser/content/schemas/history.json",
+ scopes: ["addon_parent"],
+ paths: [
+ ["history"],
+ ],
+ },
+ omnibox: {
+ url: "chrome://browser/content/ext-omnibox.js",
+ schema: "chrome://browser/content/schemas/omnibox.json",
+ scopes: ["addon_parent"],
+ manifest: ["omnibox"],
+ paths: [
+ ["omnibox"],
+ ],
+ },
+ pageAction: {
+ url: "chrome://browser/content/ext-pageAction.js",
+ schema: "chrome://browser/content/schemas/page_action.json",
+ scopes: ["addon_parent"],
+ manifest: ["page_action"],
+ paths: [
+ ["pageAction"],
+ ],
+ },
+ sessions: {
+ url: "chrome://browser/content/ext-sessions.js",
+ schema: "chrome://browser/content/schemas/sessions.json",
+ scopes: ["addon_parent"],
+ paths: [
+ ["sessions"],
+ ],
+ },
+ sidebarAction: {
+ url: "chrome://browser/content/ext-sidebarAction.js",
+ schema: "chrome://browser/content/schemas/sidebar_action.json",
+ scopes: ["addon_parent"],
+ manifest: ["sidebar_action"],
+ paths: [
+ ["sidebarAction"],
+ ],
+ },
+ tabs: {
+ url: "chrome://browser/content/ext-tabs.js",
+ schema: "chrome://browser/content/schemas/tabs.json",
+ scopes: ["addon_parent"],
+ paths: [
+ ["tabs"],
+ ],
+ },
+ urlOverrides: {
+ url: "chrome://browser/content/ext-url-overrides.js",
+ schema: "chrome://browser/content/schemas/url_overrides.json",
+ scopes: ["addon_parent"],
+ manifest: ["chrome_url_overrides"],
+ paths: [
+ ["urlOverrides"],
+ ],
+ },
+ windows: {
+ url: "chrome://browser/content/ext-windows.js",
+ schema: "chrome://browser/content/schemas/windows.json",
+ scopes: ["addon_parent"],
+ paths: [
+ ["windows"],
+ ],
+ },
+});
--- a/browser/components/extensions/ext-browserAction.js
+++ b/browser/components/extensions/ext-browserAction.js
@@ -11,27 +11,26 @@ XPCOMUtils.defineLazyModuleGetter(this,
XPCOMUtils.defineLazyModuleGetter(this, "ViewPopup",
"resource:///modules/ExtensionPopups.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "DOMUtils",
"@mozilla.org/inspector/dom-utils;1",
"inIDOMUtils");
Cu.import("resource://devtools/shared/event-emitter.js");
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
Cu.import("resource://gre/modules/Task.jsm");
var {
IconDetails,
SingletonEventManager,
} = ExtensionUtils;
const POPUP_PRELOAD_TIMEOUT_MS = 200;
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
function isAncestorOrSelf(target, node) {
for (; node; node = node.parentNode) {
if (node === target) {
return true;
}
}
return false;
@@ -458,133 +457,138 @@ BrowserAction.prototype = {
};
BrowserAction.for = (extension) => {
return browserActionMap.get(extension);
};
global.browserActionFor = BrowserAction.for;
-/* eslint-disable mozilla/balanced-listeners */
-extensions.on("manifest_browser_action", (type, directive, extension, manifest) => {
- let browserAction = new BrowserAction(manifest.browser_action, extension);
- browserAction.build();
- browserActionMap.set(extension, browserAction);
-});
+this.browserAction = class extends ExtensionAPI {
+ onManifestEntry(entryName) {
+ let {extension} = this;
+ let {manifest} = extension;
-extensions.on("shutdown", (type, extension) => {
- if (browserActionMap.has(extension)) {
- browserActionMap.get(extension).shutdown();
- browserActionMap.delete(extension);
+ let browserAction = new BrowserAction(manifest.browser_action, extension);
+ browserAction.build();
+ browserActionMap.set(extension, browserAction);
}
-});
-/* eslint-enable mozilla/balanced-listeners */
-extensions.registerSchemaAPI("browserAction", "addon_parent", context => {
- let {extension} = context;
+ onShutdown(reason) {
+ let {extension} = this;
- let {tabManager} = extension;
-
- function getTab(tabId) {
- if (tabId !== null) {
- return tabTracker.getTab(tabId);
+ if (browserActionMap.has(extension)) {
+ browserActionMap.get(extension).shutdown();
+ browserActionMap.delete(extension);
}
- return null;
}
- return {
- browserAction: {
- onClicked: new SingletonEventManager(context, "browserAction.onClicked", fire => {
- let listener = () => {
- fire.async(tabManager.convert(tabTracker.activeTab));
- };
- BrowserAction.for(extension).on("click", listener);
- return () => {
- BrowserAction.for(extension).off("click", listener);
- };
- }).api(),
+ getAPI(context) {
+ let {extension} = context;
+
+ let {tabManager} = extension;
+
+ function getTab(tabId) {
+ if (tabId !== null) {
+ return tabTracker.getTab(tabId);
+ }
+ return null;
+ }
- enable: function(tabId) {
- let tab = getTab(tabId);
- BrowserAction.for(extension).setProperty(tab, "enabled", true);
- },
-
- disable: function(tabId) {
- let tab = getTab(tabId);
- BrowserAction.for(extension).setProperty(tab, "enabled", false);
- },
+ return {
+ browserAction: {
+ onClicked: new SingletonEventManager(context, "browserAction.onClicked", fire => {
+ let listener = () => {
+ fire.async(tabManager.convert(tabTracker.activeTab));
+ };
+ BrowserAction.for(extension).on("click", listener);
+ return () => {
+ BrowserAction.for(extension).off("click", listener);
+ };
+ }).api(),
- setTitle: function(details) {
- let tab = getTab(details.tabId);
+ enable: function(tabId) {
+ let tab = getTab(tabId);
+ BrowserAction.for(extension).setProperty(tab, "enabled", true);
+ },
- let title = details.title;
- // Clear the tab-specific title when given a null string.
- if (tab && title == "") {
- title = null;
- }
- BrowserAction.for(extension).setProperty(tab, "title", title);
- },
+ disable: function(tabId) {
+ let tab = getTab(tabId);
+ BrowserAction.for(extension).setProperty(tab, "enabled", false);
+ },
+
+ setTitle: function(details) {
+ let tab = getTab(details.tabId);
- getTitle: function(details) {
- let tab = getTab(details.tabId);
-
- let title = BrowserAction.for(extension).getProperty(tab, "title");
- return Promise.resolve(title);
- },
+ let title = details.title;
+ // Clear the tab-specific title when given a null string.
+ if (tab && title == "") {
+ title = null;
+ }
+ BrowserAction.for(extension).setProperty(tab, "title", title);
+ },
- setIcon: function(details) {
- let tab = getTab(details.tabId);
+ getTitle: function(details) {
+ let tab = getTab(details.tabId);
- let icon = IconDetails.normalize(details, extension, context);
- BrowserAction.for(extension).setProperty(tab, "icon", icon);
- },
+ let title = BrowserAction.for(extension).getProperty(tab, "title");
+ return Promise.resolve(title);
+ },
+
+ setIcon: function(details) {
+ let tab = getTab(details.tabId);
- setBadgeText: function(details) {
- let tab = getTab(details.tabId);
+ let icon = IconDetails.normalize(details, extension, context);
+ BrowserAction.for(extension).setProperty(tab, "icon", icon);
+ },
- BrowserAction.for(extension).setProperty(tab, "badgeText", details.text);
- },
+ setBadgeText: function(details) {
+ let tab = getTab(details.tabId);
- getBadgeText: function(details) {
- let tab = getTab(details.tabId);
+ BrowserAction.for(extension).setProperty(tab, "badgeText", details.text);
+ },
- let text = BrowserAction.for(extension).getProperty(tab, "badgeText");
- return Promise.resolve(text);
- },
-
- setPopup: function(details) {
- let tab = getTab(details.tabId);
+ getBadgeText: function(details) {
+ let tab = getTab(details.tabId);
- // Note: Chrome resolves arguments to setIcon relative to the calling
- // context, but resolves arguments to setPopup relative to the extension
- // root.
- // For internal consistency, we currently resolve both relative to the
- // calling context.
- let url = details.popup && context.uri.resolve(details.popup);
- BrowserAction.for(extension).setProperty(tab, "popup", url);
- },
+ let text = BrowserAction.for(extension).getProperty(tab, "badgeText");
+ return Promise.resolve(text);
+ },
+
+ setPopup: function(details) {
+ let tab = getTab(details.tabId);
- getPopup: function(details) {
- let tab = getTab(details.tabId);
+ // Note: Chrome resolves arguments to setIcon relative to the calling
+ // context, but resolves arguments to setPopup relative to the extension
+ // root.
+ // For internal consistency, we currently resolve both relative to the
+ // calling context.
+ let url = details.popup && context.uri.resolve(details.popup);
+ BrowserAction.for(extension).setProperty(tab, "popup", url);
+ },
- let popup = BrowserAction.for(extension).getProperty(tab, "popup");
- return Promise.resolve(popup);
- },
+ getPopup: function(details) {
+ let tab = getTab(details.tabId);
+
+ let popup = BrowserAction.for(extension).getProperty(tab, "popup");
+ return Promise.resolve(popup);
+ },
- setBadgeBackgroundColor: function(details) {
- let tab = getTab(details.tabId);
- let color = details.color;
- if (!Array.isArray(color)) {
- let col = DOMUtils.colorToRGBA(color);
- color = col && [col.r, col.g, col.b, Math.round(col.a * 255)];
- }
- BrowserAction.for(extension).setProperty(tab, "badgeBackgroundColor", color);
- },
+ setBadgeBackgroundColor: function(details) {
+ let tab = getTab(details.tabId);
+ let color = details.color;
+ if (!Array.isArray(color)) {
+ let col = DOMUtils.colorToRGBA(color);
+ color = col && [col.r, col.g, col.b, Math.round(col.a * 255)];
+ }
+ BrowserAction.for(extension).setProperty(tab, "badgeBackgroundColor", color);
+ },
- getBadgeBackgroundColor: function(details, callback) {
- let tab = getTab(details.tabId);
+ getBadgeBackgroundColor: function(details, callback) {
+ let tab = getTab(details.tabId);
- let color = BrowserAction.for(extension).getProperty(tab, "badgeBackgroundColor");
- return Promise.resolve(color || [0xd9, 0, 0, 255]);
+ let color = BrowserAction.for(extension).getProperty(tab, "badgeBackgroundColor");
+ return Promise.resolve(color || [0xd9, 0, 0, 255]);
+ },
},
- },
- };
-});
+ };
+ }
+};
--- a/browser/components/extensions/ext-browsingData.js
+++ b/browser/components/extensions/ext-browsingData.js
@@ -1,14 +1,12 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
Cu.import("resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
"resource://gre/modules/Preferences.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Sanitizer",
"resource:///modules/Sanitizer.jsm");
@@ -170,68 +168,70 @@ function doRemoval(options, dataToRemove
}
if (extension && invalidDataTypes.length) {
extension.logger.warn(
`Firefox does not support dataTypes: ${invalidDataTypes.toString()}.`);
}
return Promise.all(removalPromises);
}
-extensions.registerSchemaAPI("browsingData", "addon_parent", context => {
- let {extension} = context;
- return {
- browsingData: {
- settings() {
- const PREF_DOMAIN = "privacy.cpd.";
- // The following prefs are the only ones in Firefox that match corresponding
- // values used by Chrome when rerturning settings.
- const PREF_LIST = ["cache", "cookies", "history", "formdata", "downloads"];
+this.browsingData = class extends ExtensionAPI {
+ getAPI(context) {
+ let {extension} = context;
+ return {
+ browsingData: {
+ settings() {
+ const PREF_DOMAIN = "privacy.cpd.";
+ // The following prefs are the only ones in Firefox that match corresponding
+ // values used by Chrome when rerturning settings.
+ const PREF_LIST = ["cache", "cookies", "history", "formdata", "downloads"];
- // since will be the start of what is returned by Sanitizer.getClearRange
- // divided by 1000 to convert to ms.
- // If Sanitizer.getClearRange returns undefined that means the range is
- // currently "Everything", so we should set since to 0.
- let clearRange = Sanitizer.getClearRange();
- let since = clearRange ? clearRange[0] / 1000 : 0;
- let options = {since};
+ // since will be the start of what is returned by Sanitizer.getClearRange
+ // divided by 1000 to convert to ms.
+ // If Sanitizer.getClearRange returns undefined that means the range is
+ // currently "Everything", so we should set since to 0.
+ let clearRange = Sanitizer.getClearRange();
+ let since = clearRange ? clearRange[0] / 1000 : 0;
+ let options = {since};
- let dataToRemove = {};
- let dataRemovalPermitted = {};
+ let dataToRemove = {};
+ let dataRemovalPermitted = {};
- for (let item of PREF_LIST) {
- // The property formData needs a different case than the
- // formdata preference.
- const name = item === "formdata" ? "formData" : item;
- dataToRemove[name] = Preferences.get(`${PREF_DOMAIN}${item}`);
- // Firefox doesn't have the same concept of dataRemovalPermitted
- // as Chrome, so it will always be true.
- dataRemovalPermitted[name] = true;
- }
+ for (let item of PREF_LIST) {
+ // The property formData needs a different case than the
+ // formdata preference.
+ const name = item === "formdata" ? "formData" : item;
+ dataToRemove[name] = Preferences.get(`${PREF_DOMAIN}${item}`);
+ // Firefox doesn't have the same concept of dataRemovalPermitted
+ // as Chrome, so it will always be true.
+ dataRemovalPermitted[name] = true;
+ }
- return Promise.resolve({options, dataToRemove, dataRemovalPermitted});
- },
- remove(options, dataToRemove) {
- return doRemoval(options, dataToRemove, extension);
- },
- removeCache(options) {
- return doRemoval(options, {cache: true});
+ return Promise.resolve({options, dataToRemove, dataRemovalPermitted});
+ },
+ remove(options, dataToRemove) {
+ return doRemoval(options, dataToRemove, extension);
+ },
+ removeCache(options) {
+ return doRemoval(options, {cache: true});
+ },
+ removeCookies(options) {
+ return doRemoval(options, {cookies: true});
+ },
+ removeDownloads(options) {
+ return doRemoval(options, {downloads: true});
+ },
+ removeFormData(options) {
+ return doRemoval(options, {formData: true});
+ },
+ removeHistory(options) {
+ return doRemoval(options, {history: true});
+ },
+ removePasswords(options) {
+ return doRemoval(options, {passwords: true});
+ },
+ removePluginData(options) {
+ return doRemoval(options, {pluginData: true});
+ },
},
- removeCookies(options) {
- return doRemoval(options, {cookies: true});
- },
- removeDownloads(options) {
- return doRemoval(options, {downloads: true});
- },
- removeFormData(options) {
- return doRemoval(options, {formData: true});
- },
- removeHistory(options) {
- return doRemoval(options, {history: true});
- },
- removePasswords(options) {
- return doRemoval(options, {passwords: true});
- },
- removePluginData(options) {
- return doRemoval(options, {pluginData: true});
- },
- },
- };
-});
+ };
+ }
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/ext-c-browser.js
@@ -0,0 +1,46 @@
+"use strict";
+
+extensions.registerModules({
+ devtools: {
+ url: "chrome://browser/content/ext-c-devtools.js",
+ scopes: ["devtools_child"],
+ paths: [
+ ["devtools"],
+ ],
+ },
+ devtools_inspectedWindow: {
+ url: "chrome://browser/content/ext-c-devtools-inspectedWindow.js",
+ scopes: ["devtools_child"],
+ paths: [
+ ["devtools", "inspectedWindow"],
+ ],
+ },
+ devtools_panels: {
+ url: "chrome://browser/content/ext-c-devtools-panels.js",
+ scopes: ["devtools_child"],
+ paths: [
+ ["devtools", "panels"],
+ ],
+ },
+ contextMenus: {
+ url: "chrome://browser/content/ext-c-contextMenus.js",
+ scopes: ["addon_child"],
+ paths: [
+ ["contextMenus"],
+ ],
+ },
+ omnibox: {
+ url: "chrome://browser/content/ext-c-omnibox.js",
+ scopes: ["addon_child"],
+ paths: [
+ ["omnibox"],
+ ],
+ },
+ tabs: {
+ url: "chrome://browser/content/ext-c-tabs.js",
+ scopes: ["addon_child"],
+ paths: [
+ ["tabs"],
+ ],
+ },
+});
--- a/browser/components/extensions/ext-c-contextMenus.js
+++ b/browser/components/extensions/ext-c-contextMenus.js
@@ -96,63 +96,65 @@ class ContextMenusClickPropHandler {
// Removes all `onclick` handlers from this context.
close() {
for (let id of this.onclickMap.keys()) {
this.unsetListener(id);
}
}
}
-extensions.registerSchemaAPI("contextMenus", "addon_child", context => {
- let onClickedProp = new ContextMenusClickPropHandler(context);
+this.contextMenus = class extends ExtensionAPI {
+ getAPI(context) {
+ let onClickedProp = new ContextMenusClickPropHandler(context);
- return {
- contextMenus: {
- create(createProperties, callback) {
- if (createProperties.id === null) {
- createProperties.id = ++gNextMenuItemID;
- }
- let {onclick} = createProperties;
- delete createProperties.onclick;
- context.childManager.callParentAsyncFunction("contextMenus.createInternal", [
- createProperties,
- ]).then(() => {
- if (onclick) {
- onClickedProp.setListener(createProperties.id, onclick);
+ return {
+ contextMenus: {
+ create(createProperties, callback) {
+ if (createProperties.id === null) {
+ createProperties.id = ++gNextMenuItemID;
}
- if (callback) {
- callback();
- }
- });
- return createProperties.id;
- },
+ let {onclick} = createProperties;
+ delete createProperties.onclick;
+ context.childManager.callParentAsyncFunction("contextMenus.createInternal", [
+ createProperties,
+ ]).then(() => {
+ if (onclick) {
+ onClickedProp.setListener(createProperties.id, onclick);
+ }
+ if (callback) {
+ callback();
+ }
+ });
+ return createProperties.id;
+ },
- update(id, updateProperties) {
- let {onclick} = updateProperties;
- delete updateProperties.onclick;
- return context.childManager.callParentAsyncFunction("contextMenus.update", [
- id,
- updateProperties,
- ]).then(() => {
- if (onclick) {
- onClickedProp.setListener(id, onclick);
- } else if (onclick === null) {
- onClickedProp.unsetListenerFromAnyContext(id);
- }
- // else onclick is not set so it should not be changed.
- });
- },
+ update(id, updateProperties) {
+ let {onclick} = updateProperties;
+ delete updateProperties.onclick;
+ return context.childManager.callParentAsyncFunction("contextMenus.update", [
+ id,
+ updateProperties,
+ ]).then(() => {
+ if (onclick) {
+ onClickedProp.setListener(id, onclick);
+ } else if (onclick === null) {
+ onClickedProp.unsetListenerFromAnyContext(id);
+ }
+ // else onclick is not set so it should not be changed.
+ });
+ },
- remove(id) {
- onClickedProp.unsetListenerFromAnyContext(id);
- return context.childManager.callParentAsyncFunction("contextMenus.remove", [
- id,
- ]);
- },
+ remove(id) {
+ onClickedProp.unsetListenerFromAnyContext(id);
+ return context.childManager.callParentAsyncFunction("contextMenus.remove", [
+ id,
+ ]);
+ },
- removeAll() {
- onClickedProp.deleteAllListenersFromExtension();
+ removeAll() {
+ onClickedProp.deleteAllListenersFromExtension();
- return context.childManager.callParentAsyncFunction("contextMenus.removeAll", []);
+ return context.childManager.callParentAsyncFunction("contextMenus.removeAll", []);
+ },
},
- },
- };
-});
+ };
+ }
+};
--- a/browser/components/extensions/ext-c-devtools-inspectedWindow.js
+++ b/browser/components/extensions/ext-c-devtools-inspectedWindow.js
@@ -1,22 +1,24 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-extensions.registerSchemaAPI("devtools.inspectedWindow", "devtools_child", context => {
- // `devtoolsToolboxInfo` is received from the child process when the root devtools view
- // has been created, and every sub-frame of that top level devtools frame will
- // receive the same information when the context has been created from the
- // `ExtensionChild.createExtensionContext` method.
- let tabId = (context.devtoolsToolboxInfo &&
- context.devtoolsToolboxInfo.inspectedWindowTabId);
+this.devtools_inspectedWindow = class extends ExtensionAPI {
+ getAPI(context) {
+ // `devtoolsToolboxInfo` is received from the child process when the root devtools view
+ // has been created, and every sub-frame of that top level devtools frame will
+ // receive the same information when the context has been created from the
+ // `ExtensionChild.createExtensionContext` method.
+ let tabId = (context.devtoolsToolboxInfo &&
+ context.devtoolsToolboxInfo.inspectedWindowTabId);
- return {
- devtools: {
- inspectedWindow: {
- get tabId() {
- return tabId;
+ return {
+ devtools: {
+ inspectedWindow: {
+ get tabId() {
+ return tabId;
+ },
},
},
- },
- };
-});
+ };
+ }
+};
--- a/browser/components/extensions/ext-c-devtools-panels.js
+++ b/browser/components/extensions/ext-c-devtools-panels.js
@@ -1,21 +1,20 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
+ "resource://devtools/shared/event-emitter.js");
-const {
+var {
promiseDocumentLoaded,
SingletonEventManager,
} = ExtensionUtils;
-const {EventEmitter} = Cu.import("resource://devtools/shared/event-emitter.js", {});
-
/**
* Represents an addon devtools panel in the child process.
*
* @param {DevtoolsExtensionContext}
* A devtools extension context running in a child process.
* @param {object} panelOptions
* @param {string} panelOptions.id
* The id of the addon devtools panel registered in the main process.
@@ -127,29 +126,31 @@ class ChildDevToolsPanel extends EventEm
this.mm.removeMessageListener("Extension:DevToolsPanelShown", this);
this.mm.removeMessageListener("Extension:DevToolsPanelHidden", this);
this._panelContext = null;
this.context = null;
}
}
-extensions.registerSchemaAPI("devtools.panels", "devtools_child", context => {
- return {
- devtools: {
- panels: {
- create(title, icon, url) {
- return context.cloneScope.Promise.resolve().then(async () => {
- const panelId = await context.childManager.callParentAsyncFunction(
- "devtools.panels.create", [title, icon, url]);
+this.devtools_panels = class extends ExtensionAPI {
+ getAPI(context) {
+ return {
+ devtools: {
+ panels: {
+ create(title, icon, url) {
+ return context.cloneScope.Promise.resolve().then(async () => {
+ const panelId = await context.childManager.callParentAsyncFunction(
+ "devtools.panels.create", [title, icon, url]);
- const devtoolsPanel = new ChildDevToolsPanel(context, {id: panelId});
+ const devtoolsPanel = new ChildDevToolsPanel(context, {id: panelId});
- const devtoolsPanelAPI = Cu.cloneInto(devtoolsPanel.api(),
- context.cloneScope,
- {cloneFunctions: true});
- return devtoolsPanelAPI;
- });
+ const devtoolsPanelAPI = Cu.cloneInto(devtoolsPanel.api(),
+ context.cloneScope,
+ {cloneFunctions: true});
+ return devtoolsPanelAPI;
+ });
+ },
},
},
- },
- };
-});
+ };
+ }
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/ext-c-devtools.js
@@ -0,0 +1,13 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+
+this.devtools = class extends ExtensionAPI {
+ getAPI(context) {
+ return {
+ devtools: {},
+ };
+ }
+};
+
--- a/browser/components/extensions/ext-c-omnibox.js
+++ b/browser/components/extensions/ext-c-omnibox.js
@@ -1,30 +1,30 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
-
var {
SingletonEventManager,
} = ExtensionUtils;
-extensions.registerSchemaAPI("omnibox", "addon_child", context => {
- return {
- omnibox: {
- onInputChanged: new SingletonEventManager(context, "omnibox.onInputChanged", fire => {
- let listener = (text, id) => {
- fire.asyncWithoutClone(text, suggestions => {
- context.childManager.callParentFunctionNoReturn("omnibox_internal.addSuggestions", [
- id,
- suggestions,
- ]);
- });
- };
- context.childManager.getParentEvent("omnibox_internal.onInputChanged").addListener(listener);
- return () => {
- context.childManager.getParentEvent("omnibox_internal.onInputChanged").removeListener(listener);
- };
- }).api(),
- },
- };
-});
+this.omnibox = class extends ExtensionAPI {
+ getAPI(context) {
+ return {
+ omnibox: {
+ onInputChanged: new SingletonEventManager(context, "omnibox.onInputChanged", fire => {
+ let listener = (text, id) => {
+ fire.asyncWithoutClone(text, suggestions => {
+ context.childManager.callParentFunctionNoReturn("omnibox_internal.addSuggestions", [
+ id,
+ suggestions,
+ ]);
+ });
+ };
+ context.childManager.getParentEvent("omnibox_internal.onInputChanged").addListener(listener);
+ return () => {
+ context.childManager.getParentEvent("omnibox_internal.onInputChanged").removeListener(listener);
+ };
+ }).api(),
+ },
+ };
+ }
+};
--- a/browser/components/extensions/ext-c-tabs.js
+++ b/browser/components/extensions/ext-c-tabs.js
@@ -1,35 +1,37 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-extensions.registerSchemaAPI("tabs", "addon_child", context => {
- return {
- tabs: {
- connect: function(tabId, connectInfo) {
- let name = "";
- if (connectInfo && connectInfo.name !== null) {
- name = connectInfo.name;
- }
- let recipient = {
- extensionId: context.extension.id,
- tabId,
- };
- if (connectInfo && connectInfo.frameId !== null) {
- recipient.frameId = connectInfo.frameId;
- }
- return context.messenger.connect(context.messageManager, name, recipient);
+this.tabs = class extends ExtensionAPI {
+ getAPI(context) {
+ return {
+ tabs: {
+ connect: function(tabId, connectInfo) {
+ let name = "";
+ if (connectInfo && connectInfo.name !== null) {
+ name = connectInfo.name;
+ }
+ let recipient = {
+ extensionId: context.extension.id,
+ tabId,
+ };
+ if (connectInfo && connectInfo.frameId !== null) {
+ recipient.frameId = connectInfo.frameId;
+ }
+ return context.messenger.connect(context.messageManager, name, recipient);
+ },
+
+ sendMessage: function(tabId, message, options, responseCallback) {
+ let recipient = {
+ extensionId: context.extension.id,
+ tabId: tabId,
+ };
+ if (options && options.frameId !== null) {
+ recipient.frameId = options.frameId;
+ }
+ return context.messenger.sendMessage(context.messageManager, message, recipient, responseCallback);
+ },
},
-
- sendMessage: function(tabId, message, options, responseCallback) {
- let recipient = {
- extensionId: context.extension.id,
- tabId: tabId,
- };
- if (options && options.frameId !== null) {
- recipient.frameId = options.frameId;
- }
- return context.messenger.sendMessage(context.messageManager, message, recipient, responseCallback);
- },
- },
- };
-});
+ };
+ }
+};
--- a/browser/components/extensions/ext-chrome-settings-overrides.js
+++ b/browser/components/extensions/ext-chrome-settings-overrides.js
@@ -1,28 +1,28 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionPreferencesManager",
"resource://gre/modules/ExtensionPreferencesManager.jsm");
-/* eslint-disable mozilla/balanced-listeners */
-extensions.on("manifest_chrome_settings_overrides", (type, directive, extension, manifest) => {
- if (manifest.chrome_settings_overrides.homepage) {
- ExtensionPreferencesManager.setSetting(extension, "homepage_override",
- manifest.chrome_settings_overrides.homepage);
+this.chrome_settings_overrides = class extends ExtensionAPI {
+ onManifestEntry(entryName) {
+ let {extension} = this;
+ let {manifest} = extension;
+
+ if (manifest.chrome_settings_overrides.homepage) {
+ ExtensionPreferencesManager.setSetting(extension, "homepage_override",
+ manifest.chrome_settings_overrides.homepage);
+ }
}
-});
-/* eslint-enable mozilla/balanced-listeners */
+};
ExtensionPreferencesManager.addSetting("homepage_override", {
prefNames: [
"browser.startup.homepage",
],
setCallback(value) {
return {
"browser.startup.homepage": value,
--- a/browser/components/extensions/ext-commands.js
+++ b/browser/components/extensions/ext-commands.js
@@ -1,24 +1,21 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-Cu.import("resource://devtools/shared/event-emitter.js");
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
-
var {
SingletonEventManager,
PlatformInfo,
} = ExtensionUtils;
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
// WeakMap[Extension -> CommandList]
-var commandsMap = new WeakMap();
+let commandsMap = new WeakMap();
function CommandList(manifest, extension) {
this.extension = extension;
this.id = makeWidgetId(extension.id);
this.windowOpenListener = null;
// Map[{String} commandName -> {Object} commandProperties]
this.commands = this.loadCommandsFromManifest(manifest);
@@ -218,49 +215,53 @@ CommandList.prototype = {
"Shift": "shift",
};
return Array.from(chromeModifiers, modifier => {
return modifiersMap[modifier];
}).join(" ");
},
};
+this.commands = class extends ExtensionAPI {
+ onManifestEntry(entryName) {
+ let {extension} = this;
+ let {manifest} = extension;
-/* eslint-disable mozilla/balanced-listeners */
-extensions.on("manifest_commands", (type, directive, extension, manifest) => {
- commandsMap.set(extension, new CommandList(manifest, extension));
-});
+ commandsMap.set(extension, new CommandList(manifest, extension));
+ }
-extensions.on("shutdown", (type, extension) => {
- let commandsList = commandsMap.get(extension);
- if (commandsList) {
- commandsList.unregister();
- commandsMap.delete(extension);
+ onShutdown(reason) {
+ let {extension} = this;
+
+ let commandsList = commandsMap.get(extension);
+ if (commandsList) {
+ commandsList.unregister();
+ commandsMap.delete(extension);
+ }
}
-});
-/* eslint-enable mozilla/balanced-listeners */
-extensions.registerSchemaAPI("commands", "addon_parent", context => {
- let {extension} = context;
- return {
- commands: {
- getAll() {
- let commands = commandsMap.get(extension).commands;
- return Promise.resolve(Array.from(commands, ([name, command]) => {
- return ({
- name,
- description: command.description,
- shortcut: command.shortcut,
- });
- }));
+ getAPI(context) {
+ let {extension} = context;
+ return {
+ commands: {
+ getAll() {
+ let commands = commandsMap.get(extension).commands;
+ return Promise.resolve(Array.from(commands, ([name, command]) => {
+ return ({
+ name,
+ description: command.description,
+ shortcut: command.shortcut,
+ });
+ }));
+ },
+ onCommand: new SingletonEventManager(context, "commands.onCommand", fire => {
+ let listener = (eventName, commandName) => {
+ fire.async(commandName);
+ };
+ commandsMap.get(extension).on("command", listener);
+ return () => {
+ commandsMap.get(extension).off("command", listener);
+ };
+ }).api(),
},
- onCommand: new SingletonEventManager(context, "commands.onCommand", fire => {
- let listener = (eventName, commandName) => {
- fire.async(commandName);
- };
- commandsMap.get(extension).on("command", listener);
- return () => {
- commandsMap.get(extension).off("command", listener);
- };
- }).api(),
- },
- };
-});
+ };
+ }
+};
--- a/browser/components/extensions/ext-contextMenus.js
+++ b/browser/components/extensions/ext-contextMenus.js
@@ -1,17 +1,17 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
Cu.import("resource://gre/modules/MatchPattern.jsm");
Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/AppConstants.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+ "resource://gre/modules/NetUtil.jsm");
var {
ExtensionError,
IconDetails,
SingletonEventManager,
} = ExtensionUtils;
const ACTION_MENU_TOP_LEVEL_LIMIT = 6;
@@ -238,19 +238,19 @@ var gMenuBuilder = {
info.modifiers = Object.keys(map).filter(key => event[key]).map(key => map[key]);
if (event.ctrlKey && AppConstants.platform === "macosx") {
info.modifiers.push("MacCtrl");
}
// Allow context menu's to open various actions supported in webext prior
// to notifying onclicked.
let actionFor = {
- _execute_page_action: pageActionFor,
- _execute_browser_action: browserActionFor,
- _execute_sidebar_action: sidebarActionFor,
+ _execute_page_action: global.pageActionFor,
+ _execute_browser_action: global.browserActionFor,
+ _execute_sidebar_action: global.sidebarActionFor,
}[item.command];
if (actionFor) {
let win = event.target.ownerGlobal;
actionFor(item.extension).triggerAction(win);
}
item.extension.emit("webext-contextmenu-menuitem-click", info, tab);
});
@@ -590,71 +590,75 @@ const contextMenuTracker = {
const tab = trigger.localName === "tab" ? trigger : tabTracker.activeTab;
const pageUrl = tab.linkedBrowser.currentURI.spec;
gMenuBuilder.build({menu, tab, pageUrl, onTab: true});
}
},
};
var gExtensionCount = 0;
-/* eslint-disable mozilla/balanced-listeners */
-extensions.on("startup", (type, extension) => {
- gContextMenuMap.set(extension, new Map());
- if (++gExtensionCount == 1) {
- contextMenuTracker.register();
- }
-});
+
+this.contextMenus = class extends ExtensionAPI {
+ onShutdown(reason) {
+ let {extension} = this;
-extensions.on("shutdown", (type, extension) => {
- gContextMenuMap.delete(extension);
- gRootItems.delete(extension);
- if (--gExtensionCount == 0) {
- contextMenuTracker.unregister();
+ if (gContextMenuMap.has(extension)) {
+ gContextMenuMap.delete(extension);
+ gRootItems.delete(extension);
+ if (--gExtensionCount == 0) {
+ contextMenuTracker.unregister();
+ }
+ }
}
-});
-/* eslint-enable mozilla/balanced-listeners */
+
+ getAPI(context) {
+ let {extension} = context;
+
+ gContextMenuMap.set(extension, new Map());
+ if (++gExtensionCount == 1) {
+ contextMenuTracker.register();
+ }
-extensions.registerSchemaAPI("contextMenus", "addon_parent", context => {
- let {extension} = context;
- return {
- contextMenus: {
- createInternal: function(createProperties) {
- // Note that the id is required by the schema. If the addon did not set
- // it, the implementation of contextMenus.create in the child should
- // have added it.
- let menuItem = new MenuItem(extension, createProperties);
- gContextMenuMap.get(extension).set(menuItem.id, menuItem);
- },
+ return {
+ contextMenus: {
+ createInternal: function(createProperties) {
+ // Note that the id is required by the schema. If the addon did not set
+ // it, the implementation of contextMenus.create in the child should
+ // have added it.
+ let menuItem = new MenuItem(extension, createProperties);
+ gContextMenuMap.get(extension).set(menuItem.id, menuItem);
+ },
- update: function(id, updateProperties) {
- let menuItem = gContextMenuMap.get(extension).get(id);
- if (menuItem) {
- menuItem.setProps(updateProperties);
- }
- },
+ update: function(id, updateProperties) {
+ let menuItem = gContextMenuMap.get(extension).get(id);
+ if (menuItem) {
+ menuItem.setProps(updateProperties);
+ }
+ },
- remove: function(id) {
- let menuItem = gContextMenuMap.get(extension).get(id);
- if (menuItem) {
- menuItem.remove();
- }
- },
+ remove: function(id) {
+ let menuItem = gContextMenuMap.get(extension).get(id);
+ if (menuItem) {
+ menuItem.remove();
+ }
+ },
- removeAll: function() {
- let root = gRootItems.get(extension);
- if (root) {
- root.remove();
- }
- },
+ removeAll: function() {
+ let root = gRootItems.get(extension);
+ if (root) {
+ root.remove();
+ }
+ },
- onClicked: new SingletonEventManager(context, "contextMenus.onClicked", fire => {
- let listener = (event, info, tab) => {
- fire.async(info, tab);
- };
+ onClicked: new SingletonEventManager(context, "contextMenus.onClicked", fire => {
+ let listener = (event, info, tab) => {
+ fire.async(info, tab);
+ };
- extension.on("webext-contextmenu-menuitem-click", listener);
- return () => {
- extension.off("webext-contextmenu-menuitem-click", listener);
- };
- }).api(),
- },
- };
-});
+ extension.on("webext-contextmenu-menuitem-click", listener);
+ return () => {
+ extension.off("webext-contextmenu-menuitem-click", listener);
+ };
+ }).api(),
+ },
+ };
+ }
+};
--- a/browser/components/extensions/ext-devtools-inspectedWindow.js
+++ b/browser/components/extensions/ext-devtools-inspectedWindow.js
@@ -1,64 +1,64 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
/* global getDevToolsTargetForContext */
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
-
-const {
+var {
SpreadArgs,
} = ExtensionUtils;
-extensions.registerSchemaAPI("devtools.inspectedWindow", "devtools_parent", context => {
- const {
- WebExtensionInspectedWindowFront,
- } = require("devtools/shared/fronts/webextension-inspected-window");
+this.devtools_inspectedWindow = class extends ExtensionAPI {
+ getAPI(context) {
+ const {
+ WebExtensionInspectedWindowFront,
+ } = require("devtools/shared/fronts/webextension-inspected-window");
- // Lazily retrieve and store an inspectedWindow actor front per child context.
- let waitForInspectedWindowFront;
- async function getInspectedWindowFront() {
- // If there is not yet a front instance, then a lazily cloned target for the context is
- // retrieved using the DevtoolsParentContextsManager helper (which is an asynchronous operation,
- // because the first time that the target has been cloned, it is not ready to be used to create
- // the front instance until it is connected to the remote debugger successfully).
- const clonedTarget = await getDevToolsTargetForContext(context);
- return new WebExtensionInspectedWindowFront(clonedTarget.client, clonedTarget.form);
- }
+ // Lazily retrieve and store an inspectedWindow actor front per child context.
+ let waitForInspectedWindowFront;
+ async function getInspectedWindowFront() {
+ // If there is not yet a front instance, then a lazily cloned target for the context is
+ // retrieved using the DevtoolsParentContextsManager helper (which is an asynchronous operation,
+ // because the first time that the target has been cloned, it is not ready to be used to create
+ // the front instance until it is connected to the remote debugger successfully).
+ const clonedTarget = await getDevToolsTargetForContext(context);
+ return new WebExtensionInspectedWindowFront(clonedTarget.client, clonedTarget.form);
+ }
- // TODO(rpl): retrive a more detailed callerInfo object, like the filename and
- // lineNumber of the actual extension called, in the child process.
- const callerInfo = {
- addonId: context.extension.id,
- url: context.extension.baseURI.spec,
- };
+ // TODO(rpl): retrive a more detailed callerInfo object, like the filename and
+ // lineNumber of the actual extension called, in the child process.
+ const callerInfo = {
+ addonId: context.extension.id,
+ url: context.extension.baseURI.spec,
+ };
- return {
- devtools: {
- inspectedWindow: {
- async eval(expression, options) {
- if (!waitForInspectedWindowFront) {
- waitForInspectedWindowFront = getInspectedWindowFront();
- }
+ return {
+ devtools: {
+ inspectedWindow: {
+ async eval(expression, options) {
+ if (!waitForInspectedWindowFront) {
+ waitForInspectedWindowFront = getInspectedWindowFront();
+ }
- const front = await waitForInspectedWindowFront;
- return front.eval(callerInfo, expression, options || {}).then(evalResult => {
- // TODO(rpl): check for additional undocumented behaviors on chrome
- // (e.g. if we should also print error to the console or set lastError?).
- return new SpreadArgs([evalResult.value, evalResult.exceptionInfo]);
- });
- },
- async reload(options) {
- const {ignoreCache, userAgent, injectedScript} = options || {};
+ const front = await waitForInspectedWindowFront;
+ return front.eval(callerInfo, expression, options || {}).then(evalResult => {
+ // TODO(rpl): check for additional undocumented behaviors on chrome
+ // (e.g. if we should also print error to the console or set lastError?).
+ return new SpreadArgs([evalResult.value, evalResult.exceptionInfo]);
+ });
+ },
+ async reload(options) {
+ const {ignoreCache, userAgent, injectedScript} = options || {};
- if (!waitForInspectedWindowFront) {
- waitForInspectedWindowFront = getInspectedWindowFront();
- }
+ if (!waitForInspectedWindowFront) {
+ waitForInspectedWindowFront = getInspectedWindowFront();
+ }
- const front = await waitForInspectedWindowFront;
- front.reload(callerInfo, {ignoreCache, userAgent, injectedScript});
+ const front = await waitForInspectedWindowFront;
+ front.reload(callerInfo, {ignoreCache, userAgent, injectedScript});
+ },
},
},
- },
- };
-});
+ };
+ }
+};
--- a/browser/components/extensions/ext-devtools-network.js
+++ b/browser/components/extensions/ext-devtools-network.js
@@ -1,35 +1,33 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
-
-const {
+var {
SingletonEventManager,
} = ExtensionUtils;
-extensions.registerSchemaAPI("devtools.network", "devtools_parent", (context) => {
- return {
- devtools: {
- network: {
- onNavigated: new SingletonEventManager(context, "devtools.onNavigated", fire => {
- let listener = (event, data) => {
- fire.async(data.url);
- };
+this.devtools_network = class extends ExtensionAPI {
+ getAPI(context) {
+ return {
+ devtools: {
+ network: {
+ onNavigated: new SingletonEventManager(context, "devtools.onNavigated", fire => {
+ let listener = (event, data) => {
+ fire.async(data.url);
+ };
- let targetPromise = getDevToolsTargetForContext(context);
- targetPromise.then(target => {
- target.on("navigate", listener);
- });
- return () => {
+ let targetPromise = getDevToolsTargetForContext(context);
targetPromise.then(target => {
- target.off("navigate", listener);
+ target.on("navigate", listener);
});
- };
- }).api(),
+ return () => {
+ targetPromise.then(target => {
+ target.off("navigate", listener);
+ });
+ };
+ }).api(),
+ },
},
- },
- };
-});
+ };
+ }
+};
--- a/browser/components/extensions/ext-devtools-panels.js
+++ b/browser/components/extensions/ext-devtools-panels.js
@@ -1,30 +1,27 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/ExtensionParent.jsm");
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
"resource:///modules/E10SUtils.jsm");
-const {
+var {
watchExtensionProxyContextLoad,
} = ExtensionParent;
-const {
+var {
IconDetails,
promiseEvent,
} = ExtensionUtils;
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
/**
* Represents an addon devtools panel in the main process.
*
* @param {ExtensionChildProxyContext} context
* A devtools extension proxy context running in a main process.
* @param {object} options
* @param {string} options.id
@@ -223,40 +220,42 @@ class ParentDevToolsPanel {
this.context = null;
this.toolbox = null;
this.waitTopLevelContext = null;
this._resolveTopLevelContext = null;
}
}
-extensions.registerSchemaAPI("devtools.panels", "devtools_parent", context => {
- // An incremental "per context" id used in the generated devtools panel id.
- let nextPanelId = 0;
+this.devtools_panels = class extends ExtensionAPI {
+ getAPI(context) {
+ // An incremental "per context" id used in the generated devtools panel id.
+ let nextPanelId = 0;
- return {
- devtools: {
- panels: {
- create(title, icon, url) {
- // Get a fallback icon from the manifest data.
- if (icon === "" && context.extension.manifest.icons) {
- const iconInfo = IconDetails.getPreferredIcon(context.extension.manifest.icons,
- context.extension, 128);
- icon = iconInfo ? iconInfo.icon : "";
- }
+ return {
+ devtools: {
+ panels: {
+ create(title, icon, url) {
+ // Get a fallback icon from the manifest data.
+ if (icon === "" && context.extension.manifest.icons) {
+ const iconInfo = IconDetails.getPreferredIcon(context.extension.manifest.icons,
+ context.extension, 128);
+ icon = iconInfo ? iconInfo.icon : "";
+ }
- icon = context.extension.baseURI.resolve(icon);
- url = context.extension.baseURI.resolve(url);
+ icon = context.extension.baseURI.resolve(icon);
+ url = context.extension.baseURI.resolve(url);
- const baseId = `${context.extension.id}-${context.contextId}-${nextPanelId++}`;
- const id = `${makeWidgetId(baseId)}-devtools-panel`;
+ const baseId = `${context.extension.id}-${context.contextId}-${nextPanelId++}`;
+ const id = `${makeWidgetId(baseId)}-devtools-panel`;
- new ParentDevToolsPanel(context, {title, icon, url, id});
+ new ParentDevToolsPanel(context, {title, icon, url, id});
- // Resolved to the devtools panel id into the child addon process,
- // where it will be used to identify the messages related
- // to the panel API onShown/onHidden events.
- return Promise.resolve(id);
+ // Resolved to the devtools panel id into the child addon process,
+ // where it will be used to identify the messages related
+ // to the panel API onShown/onHidden events.
+ return Promise.resolve(id);
+ },
},
},
- },
- };
-});
+ };
+ }
+};
--- a/browser/components/extensions/ext-devtools.js
+++ b/browser/components/extensions/ext-devtools.js
@@ -11,17 +11,17 @@
XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
"resource://devtools/client/framework/gDevTools.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/ExtensionParent.jsm");
-const {
+var {
HiddenExtensionPage,
watchExtensionProxyContextLoad,
} = ExtensionParent;
// Map[extension -> DevToolsPageDefinition]
let devtoolsPageDefinitionMap = new Map();
let initDevTools;
@@ -244,24 +244,24 @@ class DevToolsPageDefinition {
if (this.devtoolsPageForTarget.size > 0) {
throw new Error(
`Leaked ${this.devtoolsPageForTarget.size} DevToolsPage instances in devtoolsPageForTarget Map`
);
}
}
}
-/* eslint-disable mozilla/balanced-listeners */
let devToolsInitialized = false;
initDevTools = function() {
if (devToolsInitialized) {
return;
}
+ /* eslint-disable mozilla/balanced-listeners */
// Create a devtools page context for a new opened toolbox,
// based on the registered devtools_page definitions.
gDevTools.on("toolbox-created", (evt, toolbox) => {
if (!toolbox.target.isLocalTab) {
// Only local tabs are currently supported (See Bug 1304378 for additional details
// related to remote targets support).
let msg = `Ignoring DevTools Toolbox for target "${toolbox.target.toString()}": ` +
`"${toolbox.target.name}" ("${toolbox.target.url}"). ` +
@@ -287,27 +287,40 @@ initDevTools = function() {
// related to remote targets support).
return;
}
for (let devtoolsPageDefinition of devtoolsPageDefinitionMap.values()) {
devtoolsPageDefinition.shutdownForTarget(target);
}
});
+ /* eslint-enable mozilla/balanced-listeners */
devToolsInitialized = true;
};
-// Create and register a new devtools_page definition as specified in the
-// "devtools_page" property in the extension manifest.
-extensions.on("manifest_devtools_page", (type, directive, extension, manifest) => {
- let devtoolsPageDefinition = new DevToolsPageDefinition(extension, manifest[directive]);
- devtoolsPageDefinitionMap.set(extension, devtoolsPageDefinition);
-});
+this.devtools = class extends ExtensionAPI {
+ onManifestEntry(entryName) {
+ let {extension} = this;
+ let {manifest} = extension;
+
+ // Create and register a new devtools_page definition as specified in the
+ // "devtools_page" property in the extension manifest.
+ let devtoolsPageDefinition = new DevToolsPageDefinition(extension, manifest.devtools_page);
+ devtoolsPageDefinitionMap.set(extension, devtoolsPageDefinition);
+ }
+
+ onShutdown(reason) {
+ let {extension} = this;
-// Destroy the registered devtools_page definition on extension shutdown.
-extensions.on("shutdown", (type, extension) => {
- if (devtoolsPageDefinitionMap.has(extension)) {
- devtoolsPageDefinitionMap.get(extension).shutdown();
- devtoolsPageDefinitionMap.delete(extension);
+ // Destroy the registered devtools_page definition on extension shutdown.
+ if (devtoolsPageDefinitionMap.has(extension)) {
+ devtoolsPageDefinitionMap.get(extension).shutdown();
+ devtoolsPageDefinitionMap.delete(extension);
+ }
}
-});
-/* eslint-enable mozilla/balanced-listeners */
+
+ getAPI(context) {
+ return {
+ devtools: {},
+ };
+ }
+};
--- a/browser/components/extensions/ext-history.js
+++ b/browser/components/extensions/ext-history.js
@@ -1,24 +1,18 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
- "resource://devtools/shared/event-emitter.js");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
-const {
+var {
normalizeTime,
SingletonEventManager,
} = ExtensionUtils;
let nsINavHistoryService = Ci.nsINavHistoryService;
const TRANSITION_TO_TRANSITION_TYPES_MAP = new Map([
["link", nsINavHistoryService.TRANSITION_LINK],
["typed", nsINavHistoryService.TRANSITION_TYPED],
@@ -125,122 +119,124 @@ function getObserver() {
},
};
EventEmitter.decorate(_observer);
PlacesUtils.history.addObserver(_observer, false);
}
return _observer;
}
-extensions.registerSchemaAPI("history", "addon_parent", context => {
- return {
- history: {
- addUrl: function(details) {
- let transition, date;
- try {
- transition = getTransitionType(details.transition);
- } catch (error) {
- return Promise.reject({message: error.message});
- }
- if (details.visitTime) {
- date = normalizeTime(details.visitTime);
- }
- let pageInfo = {
- title: details.title,
- url: details.url,
- visits: [
- {
- transition,
- date,
- },
- ],
- };
- try {
- return PlacesUtils.history.insert(pageInfo).then(() => undefined);
- } catch (error) {
- return Promise.reject({message: error.message});
- }
- },
+this.history = class extends ExtensionAPI {
+ getAPI(context) {
+ return {
+ history: {
+ addUrl: function(details) {
+ let transition, date;
+ try {
+ transition = getTransitionType(details.transition);
+ } catch (error) {
+ return Promise.reject({message: error.message});
+ }
+ if (details.visitTime) {
+ date = normalizeTime(details.visitTime);
+ }
+ let pageInfo = {
+ title: details.title,
+ url: details.url,
+ visits: [
+ {
+ transition,
+ date,
+ },
+ ],
+ };
+ try {
+ return PlacesUtils.history.insert(pageInfo).then(() => undefined);
+ } catch (error) {
+ return Promise.reject({message: error.message});
+ }
+ },
- deleteAll: function() {
- return PlacesUtils.history.clear();
- },
+ deleteAll: function() {
+ return PlacesUtils.history.clear();
+ },
- deleteRange: function(filter) {
- let newFilter = {
- beginDate: normalizeTime(filter.startTime),
- endDate: normalizeTime(filter.endTime),
- };
- // History.removeVisitsByFilter returns a boolean, but our API should return nothing
- return PlacesUtils.history.removeVisitsByFilter(newFilter).then(() => undefined);
- },
+ deleteRange: function(filter) {
+ let newFilter = {
+ beginDate: normalizeTime(filter.startTime),
+ endDate: normalizeTime(filter.endTime),
+ };
+ // History.removeVisitsByFilter returns a boolean, but our API should return nothing
+ return PlacesUtils.history.removeVisitsByFilter(newFilter).then(() => undefined);
+ },
- deleteUrl: function(details) {
- let url = details.url;
- // History.remove returns a boolean, but our API should return nothing
- return PlacesUtils.history.remove(url).then(() => undefined);
- },
+ deleteUrl: function(details) {
+ let url = details.url;
+ // History.remove returns a boolean, but our API should return nothing
+ return PlacesUtils.history.remove(url).then(() => undefined);
+ },
- search: function(query) {
- let beginTime = (query.startTime == null) ?
- PlacesUtils.toPRTime(Date.now() - 24 * 60 * 60 * 1000) :
- PlacesUtils.toPRTime(normalizeTime(query.startTime));
- let endTime = (query.endTime == null) ?
- Number.MAX_VALUE :
- PlacesUtils.toPRTime(normalizeTime(query.endTime));
- if (beginTime > endTime) {
- return Promise.reject({message: "The startTime cannot be after the endTime"});
- }
+ search: function(query) {
+ let beginTime = (query.startTime == null) ?
+ PlacesUtils.toPRTime(Date.now() - 24 * 60 * 60 * 1000) :
+ PlacesUtils.toPRTime(normalizeTime(query.startTime));
+ let endTime = (query.endTime == null) ?
+ Number.MAX_VALUE :
+ PlacesUtils.toPRTime(normalizeTime(query.endTime));
+ if (beginTime > endTime) {
+ return Promise.reject({message: "The startTime cannot be after the endTime"});
+ }
- let options = PlacesUtils.history.getNewQueryOptions();
- options.sortingMode = options.SORT_BY_DATE_DESCENDING;
- options.maxResults = query.maxResults || 100;
+ let options = PlacesUtils.history.getNewQueryOptions();
+ options.sortingMode = options.SORT_BY_DATE_DESCENDING;
+ options.maxResults = query.maxResults || 100;
- let historyQuery = PlacesUtils.history.getNewQuery();
- historyQuery.searchTerms = query.text;
- historyQuery.beginTime = beginTime;
- historyQuery.endTime = endTime;
- let queryResult = PlacesUtils.history.executeQuery(historyQuery, options).root;
- let results = convertNavHistoryContainerResultNode(queryResult, convertNodeToHistoryItem);
- return Promise.resolve(results);
- },
+ let historyQuery = PlacesUtils.history.getNewQuery();
+ historyQuery.searchTerms = query.text;
+ historyQuery.beginTime = beginTime;
+ historyQuery.endTime = endTime;
+ let queryResult = PlacesUtils.history.executeQuery(historyQuery, options).root;
+ let results = convertNavHistoryContainerResultNode(queryResult, convertNodeToHistoryItem);
+ return Promise.resolve(results);
+ },
- getVisits: function(details) {
- let url = details.url;
- if (!url) {
- return Promise.reject({message: "A URL must be provided for getVisits"});
- }
+ getVisits: function(details) {
+ let url = details.url;
+ if (!url) {
+ return Promise.reject({message: "A URL must be provided for getVisits"});
+ }
- let options = PlacesUtils.history.getNewQueryOptions();
- options.sortingMode = options.SORT_BY_DATE_DESCENDING;
- options.resultType = options.RESULTS_AS_VISIT;
+ let options = PlacesUtils.history.getNewQueryOptions();
+ options.sortingMode = options.SORT_BY_DATE_DESCENDING;
+ options.resultType = options.RESULTS_AS_VISIT;
- let historyQuery = PlacesUtils.history.getNewQuery();
- historyQuery.uri = NetUtil.newURI(url);
- let queryResult = PlacesUtils.history.executeQuery(historyQuery, options).root;
- let results = convertNavHistoryContainerResultNode(queryResult, convertNodeToVisitItem);
- return Promise.resolve(results);
- },
+ let historyQuery = PlacesUtils.history.getNewQuery();
+ historyQuery.uri = NetUtil.newURI(url);
+ let queryResult = PlacesUtils.history.executeQuery(historyQuery, options).root;
+ let results = convertNavHistoryContainerResultNode(queryResult, convertNodeToVisitItem);
+ return Promise.resolve(results);
+ },
- onVisited: new SingletonEventManager(context, "history.onVisited", fire => {
- let listener = (event, data) => {
- fire.sync(data);
- };
+ onVisited: new SingletonEventManager(context, "history.onVisited", fire => {
+ let listener = (event, data) => {
+ fire.sync(data);
+ };
- getObserver().on("visited", listener);
- return () => {
- getObserver().off("visited", listener);
- };
- }).api(),
+ getObserver().on("visited", listener);
+ return () => {
+ getObserver().off("visited", listener);
+ };
+ }).api(),
- onVisitRemoved: new SingletonEventManager(context, "history.onVisitRemoved", fire => {
- let listener = (event, data) => {
- fire.sync(data);
- };
+ onVisitRemoved: new SingletonEventManager(context, "history.onVisitRemoved", fire => {
+ let listener = (event, data) => {
+ fire.sync(data);
+ };
- getObserver().on("visitRemoved", listener);
- return () => {
- getObserver().off("visitRemoved", listener);
- };
- }).api(),
- },
- };
-});
+ getObserver().on("visitRemoved", listener);
+ return () => {
+ getObserver().off("visitRemoved", listener);
+ };
+ }).api(),
+ },
+ };
+ }
+};
--- a/browser/components/extensions/ext-omnibox.js
+++ b/browser/components/extensions/ext-omnibox.js
@@ -1,104 +1,107 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
-
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionSearchHandler",
"resource://gre/modules/ExtensionSearchHandler.jsm");
var {
SingletonEventManager,
} = ExtensionUtils;
// WeakMap[extension -> keyword]
let gKeywordMap = new WeakMap();
-/* eslint-disable mozilla/balanced-listeners */
-extensions.on("manifest_omnibox", (type, directive, extension, manifest) => {
- let keyword = manifest.omnibox.keyword;
- try {
- // This will throw if the keyword is already registered.
- ExtensionSearchHandler.registerKeyword(keyword, extension);
- gKeywordMap.set(extension, keyword);
- } catch (e) {
- extension.manifestError(e.message);
+this.omnibox = class extends ExtensionAPI {
+ onManifestEntry(entryName) {
+ let {extension} = this;
+ let {manifest} = extension;
+
+ let keyword = manifest.omnibox.keyword;
+ try {
+ // This will throw if the keyword is already registered.
+ ExtensionSearchHandler.registerKeyword(keyword, extension);
+ gKeywordMap.set(extension, keyword);
+ } catch (e) {
+ extension.manifestError(e.message);
+ }
}
-});
+
+ onShutdown(reason) {
+ let {extension} = this;
+
+ let keyword = gKeywordMap.get(extension);
+ if (keyword) {
+ ExtensionSearchHandler.unregisterKeyword(keyword);
+ gKeywordMap.delete(extension);
+ }
+ }
-extensions.on("shutdown", (type, extension) => {
- let keyword = gKeywordMap.get(extension);
- if (keyword) {
- ExtensionSearchHandler.unregisterKeyword(keyword);
- gKeywordMap.delete(extension);
- }
-});
-/* eslint-enable mozilla/balanced-listeners */
+ getAPI(context) {
+ let {extension} = context;
+ return {
+ omnibox: {
+ setDefaultSuggestion(suggestion) {
+ let keyword = gKeywordMap.get(extension);
+ try {
+ // This will throw if the keyword failed to register.
+ ExtensionSearchHandler.setDefaultSuggestion(keyword, suggestion);
+ } catch (e) {
+ return Promise.reject(e.message);
+ }
+ },
-extensions.registerSchemaAPI("omnibox", "addon_parent", context => {
- let {extension} = context;
- return {
- omnibox: {
- setDefaultSuggestion(suggestion) {
- let keyword = gKeywordMap.get(extension);
- try {
- // This will throw if the keyword failed to register.
- ExtensionSearchHandler.setDefaultSuggestion(keyword, suggestion);
- } catch (e) {
- return Promise.reject(e.message);
- }
+ onInputStarted: new SingletonEventManager(context, "omnibox.onInputStarted", fire => {
+ let listener = (eventName) => {
+ fire.sync();
+ };
+ extension.on(ExtensionSearchHandler.MSG_INPUT_STARTED, listener);
+ return () => {
+ extension.off(ExtensionSearchHandler.MSG_INPUT_STARTED, listener);
+ };
+ }).api(),
+
+ onInputCancelled: new SingletonEventManager(context, "omnibox.onInputCancelled", fire => {
+ let listener = (eventName) => {
+ fire.sync();
+ };
+ extension.on(ExtensionSearchHandler.MSG_INPUT_CANCELLED, listener);
+ return () => {
+ extension.off(ExtensionSearchHandler.MSG_INPUT_CANCELLED, listener);
+ };
+ }).api(),
+
+ onInputEntered: new SingletonEventManager(context, "omnibox.onInputEntered", fire => {
+ let listener = (eventName, text, disposition) => {
+ fire.sync(text, disposition);
+ };
+ extension.on(ExtensionSearchHandler.MSG_INPUT_ENTERED, listener);
+ return () => {
+ extension.off(ExtensionSearchHandler.MSG_INPUT_ENTERED, listener);
+ };
+ }).api(),
},
- onInputStarted: new SingletonEventManager(context, "omnibox.onInputStarted", fire => {
- let listener = (eventName) => {
- fire.sync();
- };
- extension.on(ExtensionSearchHandler.MSG_INPUT_STARTED, listener);
- return () => {
- extension.off(ExtensionSearchHandler.MSG_INPUT_STARTED, listener);
- };
- }).api(),
-
- onInputCancelled: new SingletonEventManager(context, "omnibox.onInputCancelled", fire => {
- let listener = (eventName) => {
- fire.sync();
- };
- extension.on(ExtensionSearchHandler.MSG_INPUT_CANCELLED, listener);
- return () => {
- extension.off(ExtensionSearchHandler.MSG_INPUT_CANCELLED, listener);
- };
- }).api(),
+ omnibox_internal: {
+ addSuggestions(id, suggestions) {
+ let keyword = gKeywordMap.get(extension);
+ try {
+ ExtensionSearchHandler.addSuggestions(keyword, id, suggestions);
+ } catch (e) {
+ // Silently fail because the extension developer can not know for sure if the user
+ // has already invalidated the callback when asynchronously providing suggestions.
+ }
+ },
- onInputEntered: new SingletonEventManager(context, "omnibox.onInputEntered", fire => {
- let listener = (eventName, text, disposition) => {
- fire.sync(text, disposition);
- };
- extension.on(ExtensionSearchHandler.MSG_INPUT_ENTERED, listener);
- return () => {
- extension.off(ExtensionSearchHandler.MSG_INPUT_ENTERED, listener);
- };
- }).api(),
- },
-
- omnibox_internal: {
- addSuggestions(id, suggestions) {
- let keyword = gKeywordMap.get(extension);
- try {
- ExtensionSearchHandler.addSuggestions(keyword, id, suggestions);
- } catch (e) {
- // Silently fail because the extension developer can not know for sure if the user
- // has already invalidated the callback when asynchronously providing suggestions.
- }
+ onInputChanged: new SingletonEventManager(context, "omnibox_internal.onInputChanged", fire => {
+ let listener = (eventName, text, id) => {
+ fire.sync(text, id);
+ };
+ extension.on(ExtensionSearchHandler.MSG_INPUT_CHANGED, listener);
+ return () => {
+ extension.off(ExtensionSearchHandler.MSG_INPUT_CHANGED, listener);
+ };
+ }).api(),
},
-
- onInputChanged: new SingletonEventManager(context, "omnibox_internal.onInputChanged", fire => {
- let listener = (eventName, text, id) => {
- fire.sync(text, id);
- };
- extension.on(ExtensionSearchHandler.MSG_INPUT_CHANGED, listener);
- return () => {
- extension.off(ExtensionSearchHandler.MSG_INPUT_CHANGED, listener);
- };
- }).api(),
- },
- };
-});
+ };
+ }
+};
--- a/browser/components/extensions/ext-pageAction.js
+++ b/browser/components/extensions/ext-pageAction.js
@@ -1,24 +1,24 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "PanelPopup",
"resource:///modules/ExtensionPopups.jsm");
Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
+
var {
SingletonEventManager,
IconDetails,
} = ExtensionUtils;
// WeakMap[Extension -> PageAction]
-var pageActionMap = new WeakMap();
+let pageActionMap = new WeakMap();
// Handles URL bar icons, including the |page_action| manifest entry
// and associated API.
function PageAction(options, extension) {
this.extension = extension;
this.id = makeWidgetId(extension.id) + "-page-action";
this.tabManager = extension.tabManager;
@@ -218,99 +218,104 @@ PageAction.prototype = {
if (this.buttons.has(window)) {
this.buttons.get(window).remove();
window.removeEventListener("popupshowing", this);
}
}
},
};
-/* eslint-disable mozilla/balanced-listeners */
-extensions.on("manifest_page_action", (type, directive, extension, manifest) => {
- let pageAction = new PageAction(manifest.page_action, extension);
- pageActionMap.set(extension, pageAction);
-});
-
-extensions.on("shutdown", (type, extension) => {
- if (pageActionMap.has(extension)) {
- pageActionMap.get(extension).shutdown();
- pageActionMap.delete(extension);
- }
-});
-/* eslint-enable mozilla/balanced-listeners */
-
PageAction.for = extension => {
return pageActionMap.get(extension);
};
global.pageActionFor = PageAction.for;
-extensions.registerSchemaAPI("pageAction", "addon_parent", context => {
- let {extension} = context;
+this.pageAction = class extends ExtensionAPI {
+ onManifestEntry(entryName) {
+ let {extension} = this;
+ let {manifest} = extension;
- const {tabManager} = extension;
+ let pageAction = new PageAction(manifest.page_action, extension);
+ pageActionMap.set(extension, pageAction);
+ }
- return {
- pageAction: {
- onClicked: new SingletonEventManager(context, "pageAction.onClicked", fire => {
- let listener = (evt, tab) => {
- fire.async(tabManager.convert(tab));
- };
- let pageAction = PageAction.for(extension);
+ onShutdown(reason) {
+ let {extension} = this;
+
+ if (pageActionMap.has(extension)) {
+ pageActionMap.get(extension).shutdown();
+ pageActionMap.delete(extension);
+ }
+ }
+
+ getAPI(context) {
+ let {extension} = context;
- pageAction.on("click", listener);
- return () => {
- pageAction.off("click", listener);
- };
- }).api(),
+ const {tabManager} = extension;
- show(tabId) {
- let tab = tabTracker.getTab(tabId);
- PageAction.for(extension).setProperty(tab, "show", true);
- },
+ return {
+ pageAction: {
+ onClicked: new SingletonEventManager(context, "pageAction.onClicked", fire => {
+ let listener = (evt, tab) => {
+ fire.async(tabManager.convert(tab));
+ };
+ let pageAction = PageAction.for(extension);
- hide(tabId) {
- let tab = tabTracker.getTab(tabId);
- PageAction.for(extension).setProperty(tab, "show", false);
- },
+ pageAction.on("click", listener);
+ return () => {
+ pageAction.off("click", listener);
+ };
+ }).api(),
- setTitle(details) {
- let tab = tabTracker.getTab(details.tabId);
-
- // Clear the tab-specific title when given a null string.
- PageAction.for(extension).setProperty(tab, "title", details.title || null);
- },
+ show(tabId) {
+ let tab = tabTracker.getTab(tabId);
+ PageAction.for(extension).setProperty(tab, "show", true);
+ },
- getTitle(details) {
- let tab = tabTracker.getTab(details.tabId);
+ hide(tabId) {
+ let tab = tabTracker.getTab(tabId);
+ PageAction.for(extension).setProperty(tab, "show", false);
+ },
- let title = PageAction.for(extension).getProperty(tab, "title");
- return Promise.resolve(title);
- },
+ setTitle(details) {
+ let tab = tabTracker.getTab(details.tabId);
- setIcon(details) {
- let tab = tabTracker.getTab(details.tabId);
+ // Clear the tab-specific title when given a null string.
+ PageAction.for(extension).setProperty(tab, "title", details.title || null);
+ },
+
+ getTitle(details) {
+ let tab = tabTracker.getTab(details.tabId);
- let icon = IconDetails.normalize(details, extension, context);
- PageAction.for(extension).setProperty(tab, "icon", icon);
- },
+ let title = PageAction.for(extension).getProperty(tab, "title");
+ return Promise.resolve(title);
+ },
- setPopup(details) {
- let tab = tabTracker.getTab(details.tabId);
+ setIcon(details) {
+ let tab = tabTracker.getTab(details.tabId);
- // Note: Chrome resolves arguments to setIcon relative to the calling
- // context, but resolves arguments to setPopup relative to the extension
- // root.
- // For internal consistency, we currently resolve both relative to the
- // calling context.
- let url = details.popup && context.uri.resolve(details.popup);
- PageAction.for(extension).setProperty(tab, "popup", url);
- },
+ let icon = IconDetails.normalize(details, extension, context);
+ PageAction.for(extension).setProperty(tab, "icon", icon);
+ },
+
+ setPopup(details) {
+ let tab = tabTracker.getTab(details.tabId);
- getPopup(details) {
- let tab = tabTracker.getTab(details.tabId);
+ // Note: Chrome resolves arguments to setIcon relative to the calling
+ // context, but resolves arguments to setPopup relative to the extension
+ // root.
+ // For internal consistency, we currently resolve both relative to the
+ // calling context.
+ let url = details.popup && context.uri.resolve(details.popup);
+ PageAction.for(extension).setProperty(tab, "popup", url);
+ },
- let popup = PageAction.for(extension).getProperty(tab, "popup");
- return Promise.resolve(popup);
+ getPopup(details) {
+ let tab = tabTracker.getTab(details.tabId);
+
+ let popup = PageAction.for(extension).getProperty(tab, "popup");
+ return Promise.resolve(popup);
+ },
},
- },
- };
-});
+ };
+ }
+};
--- a/browser/components/extensions/ext-sessions.js
+++ b/browser/components/extensions/ext-sessions.js
@@ -1,13 +1,12 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
promiseObserved,
SingletonEventManager,
} = ExtensionUtils;
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
"resource:///modules/sessionstore/SessionStore.jsm");
@@ -49,59 +48,61 @@ function createSession(restored, extensi
sessionObj.window = extension.windowManager.convert(restored, {populate: true});
return Promise.resolve([sessionObj]);
});
}
sessionObj.tab = extension.tabManager.convert(restored);
return Promise.resolve([sessionObj]);
}
-extensions.registerSchemaAPI("sessions", "addon_parent", context => {
- let {extension} = context;
- return {
- sessions: {
- getRecentlyClosed: function(filter) {
- let maxResults = filter.maxResults == undefined ? this.MAX_SESSION_RESULTS : filter.maxResults;
- return Promise.resolve(getRecentlyClosed(maxResults, extension));
- },
+this.sessions = class extends ExtensionAPI {
+ getAPI(context) {
+ let {extension} = context;
+ return {
+ sessions: {
+ getRecentlyClosed: function(filter) {
+ let maxResults = filter.maxResults == undefined ? this.MAX_SESSION_RESULTS : filter.maxResults;
+ return Promise.resolve(getRecentlyClosed(maxResults, extension));
+ },
- restore: function(sessionId) {
- let session, closedId;
- if (sessionId) {
- closedId = sessionId;
- session = SessionStore.undoCloseById(closedId);
- } else if (SessionStore.lastClosedObjectType == "window") {
- // If the most recently closed object is a window, just undo closing the most recent window.
- session = SessionStore.undoCloseWindow(0);
- } else {
- // It is a tab, and we cannot call SessionStore.undoCloseTab without a window,
- // so we must find the tab in which case we can just use its closedId.
- let recentlyClosedTabs = [];
- for (let window of windowTracker.browserWindows()) {
- let closedTabData = SessionStore.getClosedTabData(window, false);
- for (let tab of closedTabData) {
- recentlyClosedTabs.push(tab);
+ restore: function(sessionId) {
+ let session, closedId;
+ if (sessionId) {
+ closedId = sessionId;
+ session = SessionStore.undoCloseById(closedId);
+ } else if (SessionStore.lastClosedObjectType == "window") {
+ // If the most recently closed object is a window, just undo closing the most recent window.
+ session = SessionStore.undoCloseWindow(0);
+ } else {
+ // It is a tab, and we cannot call SessionStore.undoCloseTab without a window,
+ // so we must find the tab in which case we can just use its closedId.
+ let recentlyClosedTabs = [];
+ for (let window of windowTracker.browserWindows()) {
+ let closedTabData = SessionStore.getClosedTabData(window, false);
+ for (let tab of closedTabData) {
+ recentlyClosedTabs.push(tab);
+ }
}
- }
- // Sort the tabs.
- recentlyClosedTabs.sort((a, b) => b.closedAt - a.closedAt);
+ // Sort the tabs.
+ recentlyClosedTabs.sort((a, b) => b.closedAt - a.closedAt);
- // Use the closedId of the most recently closed tab to restore it.
- closedId = recentlyClosedTabs[0].closedId;
- session = SessionStore.undoCloseById(closedId);
- }
- return createSession(session, extension, closedId);
- },
+ // Use the closedId of the most recently closed tab to restore it.
+ closedId = recentlyClosedTabs[0].closedId;
+ session = SessionStore.undoCloseById(closedId);
+ }
+ return createSession(session, extension, closedId);
+ },
- onChanged: new SingletonEventManager(context, "sessions.onChanged", fire => {
- let observer = () => {
- fire.async();
- };
+ onChanged: new SingletonEventManager(context, "sessions.onChanged", fire => {
+ let observer = () => {
+ fire.async();
+ };
- Services.obs.addObserver(observer, SS_ON_CLOSED_OBJECTS_CHANGED, false);
- return () => {
- Services.obs.removeObserver(observer, SS_ON_CLOSED_OBJECTS_CHANGED);
- };
- }).api(),
- },
- };
-});
+ Services.obs.addObserver(observer, SS_ON_CLOSED_OBJECTS_CHANGED, false);
+ return () => {
+ Services.obs.removeObserver(observer, SS_ON_CLOSED_OBJECTS_CHANGED);
+ };
+ }).api(),
+ },
+ };
+ }
+};
--- a/browser/components/extensions/ext-sidebarAction.js
+++ b/browser/components/extensions/ext-sidebarAction.js
@@ -1,29 +1,27 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
-
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
"resource:///modules/CustomizableUI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
-let {
+var {
ExtensionError,
IconDetails,
} = ExtensionUtils;
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
// WeakMap[Extension -> SidebarAction]
let sidebarActionMap = new WeakMap();
const sidebarURL = "chrome://browser/content/webext-panels.xul";
/**
* Responsible for the sidebar_action section of the manifest as well
@@ -275,94 +273,101 @@ class SidebarAction {
SidebarAction.for = (extension) => {
return sidebarActionMap.get(extension);
};
global.sidebarActionFor = SidebarAction.for;
/* eslint-disable mozilla/balanced-listeners */
-extensions.on("manifest_sidebar_action", (type, directive, extension, manifest) => {
- let sidebarAction = new SidebarAction(manifest.sidebar_action, extension);
- sidebarActionMap.set(extension, sidebarAction);
-});
-
extensions.on("ready", (type, extension) => {
// We build sidebars during ready to ensure the background scripts are ready.
if (sidebarActionMap.has(extension)) {
sidebarActionMap.get(extension).build();
}
});
-
-extensions.on("shutdown", (type, extension) => {
- if (sidebarActionMap.has(extension)) {
- // Don't remove everything on app shutdown so session restore can handle
- // restoring open sidebars.
- if (extension.shutdownReason !== "APP_SHUTDOWN") {
- sidebarActionMap.get(extension).shutdown();
- }
- sidebarActionMap.delete(extension);
- }
-});
/* eslint-enable mozilla/balanced-listeners */
-extensions.registerSchemaAPI("sidebarAction", "addon_parent", context => {
- let {extension} = context;
+this.sidebarAction = class extends ExtensionAPI {
+ onManifestEntry(entryName) {
+ let {extension} = this;
+ let {manifest} = extension;
+
+ let sidebarAction = new SidebarAction(manifest.sidebar_action, extension);
+ sidebarActionMap.set(extension, sidebarAction);
+ }
- function getTab(tabId) {
- if (tabId !== null) {
- return tabTracker.getTab(tabId);
+ onShutdown(reason) {
+ let {extension} = this;
+
+ if (sidebarActionMap.has(extension)) {
+ // Don't remove everything on app shutdown so session restore can handle
+ // restoring open sidebars.
+ if (extension.shutdownReason !== "APP_SHUTDOWN") {
+ sidebarActionMap.get(extension).shutdown();
+ }
+ sidebarActionMap.delete(extension);
}
- return null;
}
- return {
- sidebarAction: {
- async setTitle(details) {
- let nativeTab = getTab(details.tabId);
+ getAPI(context) {
+ let {extension} = context;
+
+ function getTab(tabId) {
+ if (tabId !== null) {
+ return tabTracker.getTab(tabId);
+ }
+ return null;
+ }
+
+ return {
+ sidebarAction: {
+ async setTitle(details) {
+ let nativeTab = getTab(details.tabId);
- let title = details.title;
- // Clear the tab-specific title when given a null string.
- if (nativeTab && title === "") {
- title = null;
- }
- SidebarAction.for(extension).setProperty(nativeTab, "title", title);
- },
+ let title = details.title;
+ // Clear the tab-specific title when given a null string.
+ if (nativeTab && title === "") {
+ title = null;
+ }
+ SidebarAction.for(extension).setProperty(nativeTab, "title", title);
+ },
+
+ getTitle(details) {
+ let nativeTab = getTab(details.tabId);
+
+ let title = SidebarAction.for(extension).getProperty(nativeTab, "title");
+ return Promise.resolve(title);
+ },
- getTitle(details) {
- let nativeTab = getTab(details.tabId);
+ async setIcon(details) {
+ let nativeTab = getTab(details.tabId);
+
+ let icon = IconDetails.normalize(details, extension, context);
+ SidebarAction.for(extension).setProperty(nativeTab, "icon", icon);
+ },
+
+ async setPanel(details) {
+ let nativeTab = getTab(details.tabId);
- let title = SidebarAction.for(extension).getProperty(nativeTab, "title");
- return Promise.resolve(title);
- },
+ let url;
+ // Clear the tab-specific url when given a null string.
+ if (nativeTab && details.panel === "") {
+ url = null;
+ } else if (details.panel !== "") {
+ url = context.uri.resolve(details.panel);
+ } else {
+ throw new ExtensionError("Invalid url for sidebar panel.");
+ }
- async setIcon(details) {
- let nativeTab = getTab(details.tabId);
+ SidebarAction.for(extension).setProperty(nativeTab, "panel", url);
+ },
- let icon = IconDetails.normalize(details, extension, context);
- SidebarAction.for(extension).setProperty(nativeTab, "icon", icon);
+ getPanel(details) {
+ let nativeTab = getTab(details.tabId);
+
+ let panel = SidebarAction.for(extension).getProperty(nativeTab, "panel");
+ return Promise.resolve(panel);
+ },
},
-
- async setPanel(details) {
- let nativeTab = getTab(details.tabId);
-
- let url;
- // Clear the tab-specific url when given a null string.
- if (nativeTab && details.panel === "") {
- url = null;
- } else if (details.panel !== "") {
- url = context.uri.resolve(details.panel);
- } else {
- throw new ExtensionError("Invalid url for sidebar panel.");
- }
-
- SidebarAction.for(extension).setProperty(nativeTab, "panel", url);
- },
-
- getPanel(details) {
- let nativeTab = getTab(details.tabId);
-
- let panel = SidebarAction.for(extension).getProperty(nativeTab, "panel");
- return Promise.resolve(panel);
- },
- },
- };
-});
+ };
+ }
+};
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -10,18 +10,16 @@ XPCOMUtils.defineLazyModuleGetter(this,
"resource://gre/modules/MatchPattern.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
"resource://gre/modules/PromiseUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
-
var {
SingletonEventManager,
} = ExtensionUtils;
// This function is pretty tightly tied to Extension.jsm.
// Its job is to fill in the |tab| property of the sender.
function getSender(extension, target, sender) {
let tabId;
@@ -117,671 +115,673 @@ let tabListener = {
this.initTabReady();
this.tabReadyPromises.set(nativeTab, deferred);
}
}
return deferred.promise;
},
};
-extensions.registerSchemaAPI("tabs", "addon_parent", context => {
- let {extension} = context;
+this.tabs = class extends ExtensionAPI {
+ getAPI(context) {
+ let {extension} = context;
+
+ let {tabManager} = extension;
- let {tabManager} = extension;
-
- function getTabOrActive(tabId) {
- if (tabId !== null) {
- return tabTracker.getTab(tabId);
+ function getTabOrActive(tabId) {
+ if (tabId !== null) {
+ return tabTracker.getTab(tabId);
+ }
+ return tabTracker.activeTab;
}
- return tabTracker.activeTab;
- }
- async function promiseTabWhenReady(tabId) {
- let tab;
- if (tabId !== null) {
- tab = tabManager.get(tabId);
- } else {
- tab = tabManager.getWrapper(tabTracker.activeTab);
+ async function promiseTabWhenReady(tabId) {
+ let tab;
+ if (tabId !== null) {
+ tab = tabManager.get(tabId);
+ } else {
+ tab = tabManager.getWrapper(tabTracker.activeTab);
+ }
+
+ await tabListener.awaitTabReady(tab.nativeTab);
+
+ return tab;
}
- await tabListener.awaitTabReady(tab.nativeTab);
-
- return tab;
- }
+ let self = {
+ tabs: {
+ onActivated: new WindowEventManager(context, "tabs.onActivated", "TabSelect", (fire, event) => {
+ let nativeTab = event.originalTarget;
+ let tabId = tabTracker.getId(nativeTab);
+ let windowId = windowTracker.getId(nativeTab.ownerGlobal);
+ fire.async({tabId, windowId});
+ }).api(),
- let self = {
- tabs: {
- onActivated: new WindowEventManager(context, "tabs.onActivated", "TabSelect", (fire, event) => {
- let nativeTab = event.originalTarget;
- let tabId = tabTracker.getId(nativeTab);
- let windowId = windowTracker.getId(nativeTab.ownerGlobal);
- fire.async({tabId, windowId});
- }).api(),
+ onCreated: new SingletonEventManager(context, "tabs.onCreated", fire => {
+ let listener = (eventName, event) => {
+ fire.async(tabManager.convert(event.nativeTab));
+ };
- onCreated: new SingletonEventManager(context, "tabs.onCreated", fire => {
- let listener = (eventName, event) => {
- fire.async(tabManager.convert(event.nativeTab));
- };
+ tabTracker.on("tab-created", listener);
+ return () => {
+ tabTracker.off("tab-created", listener);
+ };
+ }).api(),
- tabTracker.on("tab-created", listener);
- return () => {
- tabTracker.off("tab-created", listener);
- };
- }).api(),
+ /**
+ * Since multiple tabs currently can't be highlighted, onHighlighted
+ * essentially acts an alias for self.tabs.onActivated but returns
+ * the tabId in an array to match the API.
+ * @see https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/Tabs/onHighlighted
+ */
+ onHighlighted: new WindowEventManager(context, "tabs.onHighlighted", "TabSelect", (fire, event) => {
+ let nativeTab = event.originalTarget;
+ let tabIds = [tabTracker.getId(nativeTab)];
+ let windowId = windowTracker.getId(nativeTab.ownerGlobal);
+ fire.async({tabIds, windowId});
+ }).api(),
+
+ onAttached: new SingletonEventManager(context, "tabs.onAttached", fire => {
+ let listener = (eventName, event) => {
+ fire.async(event.tabId, {newWindowId: event.newWindowId, newPosition: event.newPosition});
+ };
- /**
- * Since multiple tabs currently can't be highlighted, onHighlighted
- * essentially acts an alias for self.tabs.onActivated but returns
- * the tabId in an array to match the API.
- * @see https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/Tabs/onHighlighted
- */
- onHighlighted: new WindowEventManager(context, "tabs.onHighlighted", "TabSelect", (fire, event) => {
- let nativeTab = event.originalTarget;
- let tabIds = [tabTracker.getId(nativeTab)];
- let windowId = windowTracker.getId(nativeTab.ownerGlobal);
- fire.async({tabIds, windowId});
- }).api(),
+ tabTracker.on("tab-attached", listener);
+ return () => {
+ tabTracker.off("tab-attached", listener);
+ };
+ }).api(),
+
+ onDetached: new SingletonEventManager(context, "tabs.onDetached", fire => {
+ let listener = (eventName, event) => {
+ fire.async(event.tabId, {oldWindowId: event.oldWindowId, oldPosition: event.oldPosition});
+ };
- onAttached: new SingletonEventManager(context, "tabs.onAttached", fire => {
- let listener = (eventName, event) => {
- fire.async(event.tabId, {newWindowId: event.newWindowId, newPosition: event.newPosition});
- };
+ tabTracker.on("tab-detached", listener);
+ return () => {
+ tabTracker.off("tab-detached", listener);
+ };
+ }).api(),
- tabTracker.on("tab-attached", listener);
- return () => {
- tabTracker.off("tab-attached", listener);
- };
- }).api(),
+ onRemoved: new SingletonEventManager(context, "tabs.onRemoved", fire => {
+ let listener = (eventName, event) => {
+ fire.async(event.tabId, {windowId: event.windowId, isWindowClosing: event.isWindowClosing});
+ };
- onDetached: new SingletonEventManager(context, "tabs.onDetached", fire => {
- let listener = (eventName, event) => {
- fire.async(event.tabId, {oldWindowId: event.oldWindowId, oldPosition: event.oldPosition});
- };
+ tabTracker.on("tab-removed", listener);
+ return () => {
+ tabTracker.off("tab-removed", listener);
+ };
+ }).api(),
- tabTracker.on("tab-detached", listener);
- return () => {
- tabTracker.off("tab-detached", listener);
- };
- }).api(),
+ onReplaced: new SingletonEventManager(context, "tabs.onReplaced", fire => {
+ return () => {};
+ }).api(),
- onRemoved: new SingletonEventManager(context, "tabs.onRemoved", fire => {
- let listener = (eventName, event) => {
- fire.async(event.tabId, {windowId: event.windowId, isWindowClosing: event.isWindowClosing});
- };
+ onMoved: new SingletonEventManager(context, "tabs.onMoved", fire => {
+ // There are certain circumstances where we need to ignore a move event.
+ //
+ // Namely, the first time the tab is moved after it's created, we need
+ // to report the final position as the initial position in the tab's
+ // onAttached or onCreated event. This is because most tabs are inserted
+ // in a temporary location and then moved after the TabOpen event fires,
+ // which generates a TabOpen event followed by a TabMove event, which
+ // does not match the contract of our API.
+ let ignoreNextMove = new WeakSet();
- tabTracker.on("tab-removed", listener);
- return () => {
- tabTracker.off("tab-removed", listener);
- };
- }).api(),
+ let openListener = event => {
+ ignoreNextMove.add(event.target);
+ // Remove the tab from the set on the next tick, since it will already
+ // have been moved by then.
+ Promise.resolve().then(() => {
+ ignoreNextMove.delete(event.target);
+ });
+ };
- onReplaced: new SingletonEventManager(context, "tabs.onReplaced", fire => {
- return () => {};
- }).api(),
+ let moveListener = event => {
+ let nativeTab = event.originalTarget;
- onMoved: new SingletonEventManager(context, "tabs.onMoved", fire => {
- // There are certain circumstances where we need to ignore a move event.
- //
- // Namely, the first time the tab is moved after it's created, we need
- // to report the final position as the initial position in the tab's
- // onAttached or onCreated event. This is because most tabs are inserted
- // in a temporary location and then moved after the TabOpen event fires,
- // which generates a TabOpen event followed by a TabMove event, which
- // does not match the contract of our API.
- let ignoreNextMove = new WeakSet();
+ if (ignoreNextMove.has(nativeTab)) {
+ ignoreNextMove.delete(nativeTab);
+ return;
+ }
+
+ fire.async(tabTracker.getId(nativeTab), {
+ windowId: windowTracker.getId(nativeTab.ownerGlobal),
+ fromIndex: event.detail,
+ toIndex: nativeTab._tPos,
+ });
+ };
- let openListener = event => {
- ignoreNextMove.add(event.target);
- // Remove the tab from the set on the next tick, since it will already
- // have been moved by then.
- Promise.resolve().then(() => {
- ignoreNextMove.delete(event.target);
- });
- };
+ windowTracker.addListener("TabMove", moveListener);
+ windowTracker.addListener("TabOpen", openListener);
+ return () => {
+ windowTracker.removeListener("TabMove", moveListener);
+ windowTracker.removeListener("TabOpen", openListener);
+ };
+ }).api(),
+
+ onUpdated: new SingletonEventManager(context, "tabs.onUpdated", fire => {
+ const restricted = ["url", "favIconUrl", "title"];
- let moveListener = event => {
- let nativeTab = event.originalTarget;
-
- if (ignoreNextMove.has(nativeTab)) {
- ignoreNextMove.delete(nativeTab);
- return;
+ function sanitize(extension, changeInfo) {
+ let result = {};
+ let nonempty = false;
+ for (let prop in changeInfo) {
+ if (extension.hasPermission("tabs") || !restricted.includes(prop)) {
+ nonempty = true;
+ result[prop] = changeInfo[prop];
+ }
+ }
+ return [nonempty, result];
}
- fire.async(tabTracker.getId(nativeTab), {
- windowId: windowTracker.getId(nativeTab.ownerGlobal),
- fromIndex: event.detail,
- toIndex: nativeTab._tPos,
- });
- };
+ let fireForTab = (tab, changed) => {
+ let [needed, changeInfo] = sanitize(extension, changed);
+ if (needed) {
+ fire.async(tab.id, changeInfo, tab.convert());
+ }
+ };
+
+ let listener = event => {
+ let needed = [];
+ if (event.type == "TabAttrModified") {
+ let changed = event.detail.changed;
+ if (changed.includes("image")) {
+ needed.push("favIconUrl");
+ }
+ if (changed.includes("muted")) {
+ needed.push("mutedInfo");
+ }
+ if (changed.includes("soundplaying")) {
+ needed.push("audible");
+ }
+ if (changed.includes("label")) {
+ needed.push("title");
+ }
+ } else if (event.type == "TabPinned") {
+ needed.push("pinned");
+ } else if (event.type == "TabUnpinned") {
+ needed.push("pinned");
+ }
+
+ let tab = tabManager.getWrapper(event.originalTarget);
+ let changeInfo = {};
+ for (let prop of needed) {
+ changeInfo[prop] = tab[prop];
+ }
+
+ fireForTab(tab, changeInfo);
+ };
- windowTracker.addListener("TabMove", moveListener);
- windowTracker.addListener("TabOpen", openListener);
- return () => {
- windowTracker.removeListener("TabMove", moveListener);
- windowTracker.removeListener("TabOpen", openListener);
- };
- }).api(),
+ let statusListener = ({browser, status, url}) => {
+ let {gBrowser} = browser.ownerGlobal;
+ let tabElem = gBrowser.getTabForBrowser(browser);
+ if (tabElem) {
+ let changed = {status};
+ if (url) {
+ changed.url = url;
+ }
+
+ fireForTab(tabManager.wrapTab(tabElem), changed);
+ }
+ };
+
+ windowTracker.addListener("status", statusListener);
+ windowTracker.addListener("TabAttrModified", listener);
+ windowTracker.addListener("TabPinned", listener);
+ windowTracker.addListener("TabUnpinned", listener);
- onUpdated: new SingletonEventManager(context, "tabs.onUpdated", fire => {
- const restricted = ["url", "favIconUrl", "title"];
+ return () => {
+ windowTracker.removeListener("status", statusListener);
+ windowTracker.removeListener("TabAttrModified", listener);
+ windowTracker.removeListener("TabPinned", listener);
+ windowTracker.removeListener("TabUnpinned", listener);
+ };
+ }).api(),
- function sanitize(extension, changeInfo) {
- let result = {};
- let nonempty = false;
- for (let prop in changeInfo) {
- if (extension.hasPermission("tabs") || !restricted.includes(prop)) {
- nonempty = true;
- result[prop] = changeInfo[prop];
+ create(createProperties) {
+ return new Promise((resolve, reject) => {
+ let window = createProperties.windowId !== null ?
+ windowTracker.getWindow(createProperties.windowId, context) :
+ windowTracker.topWindow;
+
+ if (!window.gBrowser) {
+ let obs = (finishedWindow, topic, data) => {
+ if (finishedWindow != window) {
+ return;
+ }
+ Services.obs.removeObserver(obs, "browser-delayed-startup-finished");
+ resolve(window);
+ };
+ Services.obs.addObserver(obs, "browser-delayed-startup-finished", false);
+ } else {
+ resolve(window);
}
- }
- return [nonempty, result];
- }
+ }).then(window => {
+ let url;
+
+ if (createProperties.url !== null) {
+ url = context.uri.resolve(createProperties.url);
+
+ if (!context.checkLoadURL(url, {dontReportErrors: true})) {
+ return Promise.reject({message: `Illegal URL: ${url}`});
+ }
+ }
+
+ if (createProperties.cookieStoreId && !extension.hasPermission("cookies")) {
+ return Promise.reject({message: `No permission for cookieStoreId: ${createProperties.cookieStoreId}`});
+ }
- let fireForTab = (tab, changed) => {
- let [needed, changeInfo] = sanitize(extension, changed);
- if (needed) {
- fire.async(tab.id, changeInfo, tab.convert());
- }
- };
+ let options = {};
+ if (createProperties.cookieStoreId) {
+ if (!global.isValidCookieStoreId(createProperties.cookieStoreId)) {
+ return Promise.reject({message: `Illegal cookieStoreId: ${createProperties.cookieStoreId}`});
+ }
+
+ let privateWindow = PrivateBrowsingUtils.isBrowserPrivate(window.gBrowser);
+ if (privateWindow && !global.isPrivateCookieStoreId(createProperties.cookieStoreId)) {
+ return Promise.reject({message: `Illegal to set non-private cookieStoreId in a private window`});
+ }
+
+ if (!privateWindow && global.isPrivateCookieStoreId(createProperties.cookieStoreId)) {
+ return Promise.reject({message: `Illegal to set private cookieStoreId in a non-private window`});
+ }
+
+ if (global.isContainerCookieStoreId(createProperties.cookieStoreId)) {
+ let containerId = global.getContainerForCookieStoreId(createProperties.cookieStoreId);
+ if (!containerId) {
+ return Promise.reject({message: `No cookie store exists with ID ${createProperties.cookieStoreId}`});
+ }
+
+ options.userContextId = containerId;
+ }
+ }
- let listener = event => {
- let needed = [];
- if (event.type == "TabAttrModified") {
- let changed = event.detail.changed;
- if (changed.includes("image")) {
- needed.push("favIconUrl");
+ // Make sure things like about:blank and data: URIs never inherit,
+ // and instead always get a NullPrincipal.
+ options.disallowInheritPrincipal = true;
+
+ tabListener.initTabReady();
+ let nativeTab = window.gBrowser.addTab(url || window.BROWSER_NEW_TAB_URL, options);
+
+ let active = true;
+ if (createProperties.active !== null) {
+ active = createProperties.active;
}
- if (changed.includes("muted")) {
- needed.push("mutedInfo");
+ if (active) {
+ window.gBrowser.selectedTab = nativeTab;
+ }
+
+ if (createProperties.index !== null) {
+ window.gBrowser.moveTabTo(nativeTab, createProperties.index);
+ }
+
+ if (createProperties.pinned) {
+ window.gBrowser.pinTab(nativeTab);
}
- if (changed.includes("soundplaying")) {
- needed.push("audible");
+
+ if (active && !url) {
+ window.focusAndSelectUrlBar();
}
- if (changed.includes("label")) {
- needed.push("title");
+
+ if (createProperties.url && createProperties.url !== window.BROWSER_NEW_TAB_URL) {
+ // We can't wait for a location change event for about:newtab,
+ // since it may be pre-rendered, in which case its initial
+ // location change event has already fired.
+
+ // Mark the tab as initializing, so that operations like
+ // `executeScript` wait until the requested URL is loaded in
+ // the tab before dispatching messages to the inner window
+ // that contains the URL we're attempting to load.
+ tabListener.initializingTabs.add(nativeTab);
}
- } else if (event.type == "TabPinned") {
- needed.push("pinned");
- } else if (event.type == "TabUnpinned") {
- needed.push("pinned");
+
+ return tabManager.convert(nativeTab);
+ });
+ },
+
+ async remove(tabs) {
+ if (!Array.isArray(tabs)) {
+ tabs = [tabs];
}
- let tab = tabManager.getWrapper(event.originalTarget);
- let changeInfo = {};
- for (let prop of needed) {
- changeInfo[prop] = tab[prop];
+ for (let tabId of tabs) {
+ let nativeTab = tabTracker.getTab(tabId);
+ nativeTab.ownerGlobal.gBrowser.removeTab(nativeTab);
}
-
- fireForTab(tab, changeInfo);
- };
-
- let statusListener = ({browser, status, url}) => {
- let {gBrowser} = browser.ownerGlobal;
- let tabElem = gBrowser.getTabForBrowser(browser);
- if (tabElem) {
- let changed = {status};
- if (url) {
- changed.url = url;
- }
-
- fireForTab(tabManager.wrapTab(tabElem), changed);
- }
- };
-
- windowTracker.addListener("status", statusListener);
- windowTracker.addListener("TabAttrModified", listener);
- windowTracker.addListener("TabPinned", listener);
- windowTracker.addListener("TabUnpinned", listener);
+ },
- return () => {
- windowTracker.removeListener("status", statusListener);
- windowTracker.removeListener("TabAttrModified", listener);
- windowTracker.removeListener("TabPinned", listener);
- windowTracker.removeListener("TabUnpinned", listener);
- };
- }).api(),
-
- create(createProperties) {
- return new Promise((resolve, reject) => {
- let window = createProperties.windowId !== null ?
- windowTracker.getWindow(createProperties.windowId, context) :
- windowTracker.topWindow;
+ async update(tabId, updateProperties) {
+ let nativeTab = getTabOrActive(tabId);
- if (!window.gBrowser) {
- let obs = (finishedWindow, topic, data) => {
- if (finishedWindow != window) {
- return;
- }
- Services.obs.removeObserver(obs, "browser-delayed-startup-finished");
- resolve(window);
- };
- Services.obs.addObserver(obs, "browser-delayed-startup-finished", false);
- } else {
- resolve(window);
- }
- }).then(window => {
- let url;
+ let tabbrowser = nativeTab.ownerGlobal.gBrowser;
- if (createProperties.url !== null) {
- url = context.uri.resolve(createProperties.url);
+ if (updateProperties.url !== null) {
+ let url = context.uri.resolve(updateProperties.url);
if (!context.checkLoadURL(url, {dontReportErrors: true})) {
return Promise.reject({message: `Illegal URL: ${url}`});
}
+
+ nativeTab.linkedBrowser.loadURI(url);
}
- if (createProperties.cookieStoreId && !extension.hasPermission("cookies")) {
- return Promise.reject({message: `No permission for cookieStoreId: ${createProperties.cookieStoreId}`});
+ if (updateProperties.active !== null) {
+ if (updateProperties.active) {
+ tabbrowser.selectedTab = nativeTab;
+ } else {
+ // Not sure what to do here? Which tab should we select?
+ }
+ }
+ if (updateProperties.muted !== null) {
+ if (nativeTab.muted != updateProperties.muted) {
+ nativeTab.toggleMuteAudio(extension.uuid);
+ }
}
+ if (updateProperties.pinned !== null) {
+ if (updateProperties.pinned) {
+ tabbrowser.pinTab(nativeTab);
+ } else {
+ tabbrowser.unpinTab(nativeTab);
+ }
+ }
+ // FIXME: highlighted/selected, openerTabId
+
+ return tabManager.convert(nativeTab);
+ },
- let options = {};
- if (createProperties.cookieStoreId) {
- if (!global.isValidCookieStoreId(createProperties.cookieStoreId)) {
- return Promise.reject({message: `Illegal cookieStoreId: ${createProperties.cookieStoreId}`});
+ async reload(tabId, reloadProperties) {
+ let nativeTab = getTabOrActive(tabId);
+
+ let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
+ if (reloadProperties && reloadProperties.bypassCache) {
+ flags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
+ }
+ nativeTab.linkedBrowser.reloadWithFlags(flags);
+ },
+
+ async get(tabId) {
+ return tabManager.get(tabId).convert();
+ },
+
+ getCurrent() {
+ let tabData;
+ if (context.tabId) {
+ tabData = tabManager.get(context.tabId).convert();
+ }
+ return Promise.resolve(tabData);
+ },
+
+ async query(queryInfo) {
+ if (queryInfo.url !== null) {
+ if (!extension.hasPermission("tabs")) {
+ return Promise.reject({message: 'The "tabs" permission is required to use the query API with the "url" parameter'});
}
- let privateWindow = PrivateBrowsingUtils.isBrowserPrivate(window.gBrowser);
- if (privateWindow && !global.isPrivateCookieStoreId(createProperties.cookieStoreId)) {
- return Promise.reject({message: `Illegal to set non-private cookieStoreId in a private window`});
- }
+ queryInfo = Object.assign({}, queryInfo);
+ queryInfo.url = new MatchPattern(queryInfo.url);
+ }
+
+ return Array.from(tabManager.query(queryInfo, context),
+ tab => tab.convert());
+ },
+
+ async captureVisibleTab(windowId, options) {
+ let window = windowId == null ?
+ windowTracker.topWindow :
+ windowTracker.getWindow(windowId, context);
+
+ let tab = tabManager.wrapTab(window.gBrowser.selectedTab);
+ await tabListener.awaitTabReady(tab.nativeTab);
+
+ return tab.capture(context, options);
+ },
+
+ async detectLanguage(tabId) {
+ let tab = await promiseTabWhenReady(tabId);
+
+ return tab.sendMessage(context, "Extension:DetectLanguage");
+ },
+
+ async executeScript(tabId, details) {
+ let tab = await promiseTabWhenReady(tabId);
- if (!privateWindow && global.isPrivateCookieStoreId(createProperties.cookieStoreId)) {
- return Promise.reject({message: `Illegal to set private cookieStoreId in a non-private window`});
- }
+ return tab.executeScript(context, details);
+ },
+
+ async insertCSS(tabId, details) {
+ let tab = await promiseTabWhenReady(tabId);
+
+ return tab.insertCSS(context, details);
+ },
+
+ async removeCSS(tabId, details) {
+ let tab = await promiseTabWhenReady(tabId);
- if (global.isContainerCookieStoreId(createProperties.cookieStoreId)) {
- let containerId = global.getContainerForCookieStoreId(createProperties.cookieStoreId);
- if (!containerId) {
- return Promise.reject({message: `No cookie store exists with ID ${createProperties.cookieStoreId}`});
- }
+ return tab.removeCSS(context, details);
+ },
- options.userContextId = containerId;
+ async move(tabIds, moveProperties) {
+ let index = moveProperties.index;
+ let tabsMoved = [];
+ if (!Array.isArray(tabIds)) {
+ tabIds = [tabIds];
+ }
+
+ let destinationWindow = null;
+ if (moveProperties.windowId !== null) {
+ destinationWindow = windowTracker.getWindow(moveProperties.windowId);
+ // Fail on an invalid window.
+ if (!destinationWindow) {
+ return Promise.reject({message: `Invalid window ID: ${moveProperties.windowId}`});
}
}
- // Make sure things like about:blank and data: URIs never inherit,
- // and instead always get a NullPrincipal.
- options.disallowInheritPrincipal = true;
-
- tabListener.initTabReady();
- let nativeTab = window.gBrowser.addTab(url || window.BROWSER_NEW_TAB_URL, options);
+ /*
+ Indexes are maintained on a per window basis so that a call to
+ move([tabA, tabB], {index: 0})
+ -> tabA to 0, tabB to 1 if tabA and tabB are in the same window
+ move([tabA, tabB], {index: 0})
+ -> tabA to 0, tabB to 0 if tabA and tabB are in different windows
+ */
+ let indexMap = new Map();
- let active = true;
- if (createProperties.active !== null) {
- active = createProperties.active;
- }
- if (active) {
- window.gBrowser.selectedTab = nativeTab;
- }
+ let tabs = tabIds.map(tabId => tabTracker.getTab(tabId));
+ for (let nativeTab of tabs) {
+ // If the window is not specified, use the window from the tab.
+ let window = destinationWindow || nativeTab.ownerGlobal;
+ let gBrowser = window.gBrowser;
- if (createProperties.index !== null) {
- window.gBrowser.moveTabTo(nativeTab, createProperties.index);
- }
+ let insertionPoint = indexMap.get(window) || index;
+ // If the index is -1 it should go to the end of the tabs.
+ if (insertionPoint == -1) {
+ insertionPoint = gBrowser.tabs.length;
+ }
- if (createProperties.pinned) {
- window.gBrowser.pinTab(nativeTab);
- }
-
- if (active && !url) {
- window.focusAndSelectUrlBar();
- }
+ // We can only move pinned tabs to a point within, or just after,
+ // the current set of pinned tabs. Unpinned tabs, likewise, can only
+ // be moved to a position after the current set of pinned tabs.
+ // Attempts to move a tab to an illegal position are ignored.
+ let numPinned = gBrowser._numPinnedTabs;
+ let ok = nativeTab.pinned ? insertionPoint <= numPinned : insertionPoint >= numPinned;
+ if (!ok) {
+ continue;
+ }
- if (createProperties.url && createProperties.url !== window.BROWSER_NEW_TAB_URL) {
- // We can't wait for a location change event for about:newtab,
- // since it may be pre-rendered, in which case its initial
- // location change event has already fired.
+ indexMap.set(window, insertionPoint + 1);
- // Mark the tab as initializing, so that operations like
- // `executeScript` wait until the requested URL is loaded in
- // the tab before dispatching messages to the inner window
- // that contains the URL we're attempting to load.
- tabListener.initializingTabs.add(nativeTab);
+ if (nativeTab.ownerGlobal != window) {
+ // If the window we are moving the tab in is different, then move the tab
+ // to the new window.
+ nativeTab = gBrowser.adoptTab(nativeTab, insertionPoint, false);
+ } else {
+ // If the window we are moving is the same, just move the tab.
+ gBrowser.moveTabTo(nativeTab, insertionPoint);
+ }
+ tabsMoved.push(nativeTab);
}
- return tabManager.convert(nativeTab);
- });
- },
+ return tabsMoved.map(nativeTab => tabManager.convert(nativeTab));
+ },
+
+ duplicate(tabId) {
+ let nativeTab = tabTracker.getTab(tabId);
+
+ let gBrowser = nativeTab.ownerGlobal.gBrowser;
+ let newTab = gBrowser.duplicateTab(nativeTab);
- async remove(tabs) {
- if (!Array.isArray(tabs)) {
- tabs = [tabs];
- }
+ return new Promise(resolve => {
+ // We need to use SSTabRestoring because any attributes set before
+ // are ignored. SSTabRestored is too late and results in a jump in
+ // the UI. See http://bit.ly/session-store-api for more information.
+ newTab.addEventListener("SSTabRestoring", function() {
+ // As the tab is restoring, move it to the correct position.
- for (let tabId of tabs) {
- let nativeTab = tabTracker.getTab(tabId);
- nativeTab.ownerGlobal.gBrowser.removeTab(nativeTab);
- }
- },
+ // Pinned tabs that are duplicated are inserted
+ // after the existing pinned tab and pinned.
+ if (nativeTab.pinned) {
+ gBrowser.pinTab(newTab);
+ }
+ gBrowser.moveTabTo(newTab, nativeTab._tPos + 1);
+ }, {once: true});
- async update(tabId, updateProperties) {
- let nativeTab = getTabOrActive(tabId);
+ newTab.addEventListener("SSTabRestored", function() {
+ // Once it has been restored, select it and return the promise.
+ gBrowser.selectedTab = newTab;
- let tabbrowser = nativeTab.ownerGlobal.gBrowser;
+ resolve(tabManager.convert(newTab));
+ }, {once: true});
+ });
+ },
- if (updateProperties.url !== null) {
- let url = context.uri.resolve(updateProperties.url);
+ getZoom(tabId) {
+ let nativeTab = getTabOrActive(tabId);
- if (!context.checkLoadURL(url, {dontReportErrors: true})) {
- return Promise.reject({message: `Illegal URL: ${url}`});
- }
+ let {ZoomManager} = nativeTab.ownerGlobal;
+ let zoom = ZoomManager.getZoomForBrowser(nativeTab.linkedBrowser);
+
+ return Promise.resolve(zoom);
+ },
- nativeTab.linkedBrowser.loadURI(url);
- }
+ setZoom(tabId, zoom) {
+ let nativeTab = getTabOrActive(tabId);
+
+ let {FullZoom, ZoomManager} = nativeTab.ownerGlobal;
- if (updateProperties.active !== null) {
- if (updateProperties.active) {
- tabbrowser.selectedTab = nativeTab;
+ if (zoom === 0) {
+ // A value of zero means use the default zoom factor.
+ return FullZoom.reset(nativeTab.linkedBrowser);
+ } else if (zoom >= ZoomManager.MIN && zoom <= ZoomManager.MAX) {
+ FullZoom.setZoom(zoom, nativeTab.linkedBrowser);
} else {
- // Not sure what to do here? Which tab should we select?
- }
- }
- if (updateProperties.muted !== null) {
- if (nativeTab.muted != updateProperties.muted) {
- nativeTab.toggleMuteAudio(extension.uuid);
- }
- }
- if (updateProperties.pinned !== null) {
- if (updateProperties.pinned) {
- tabbrowser.pinTab(nativeTab);
- } else {
- tabbrowser.unpinTab(nativeTab);
- }
- }
- // FIXME: highlighted/selected, openerTabId
-
- return tabManager.convert(nativeTab);
- },
-
- async reload(tabId, reloadProperties) {
- let nativeTab = getTabOrActive(tabId);
-
- let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
- if (reloadProperties && reloadProperties.bypassCache) {
- flags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
- }
- nativeTab.linkedBrowser.reloadWithFlags(flags);
- },
-
- async get(tabId) {
- return tabManager.get(tabId).convert();
- },
-
- getCurrent() {
- let tabData;
- if (context.tabId) {
- tabData = tabManager.get(context.tabId).convert();
- }
- return Promise.resolve(tabData);
- },
-
- async query(queryInfo) {
- if (queryInfo.url !== null) {
- if (!extension.hasPermission("tabs")) {
- return Promise.reject({message: 'The "tabs" permission is required to use the query API with the "url" parameter'});
+ return Promise.reject({
+ message: `Zoom value ${zoom} out of range (must be between ${ZoomManager.MIN} and ${ZoomManager.MAX})`,
+ });
}
- queryInfo = Object.assign({}, queryInfo);
- queryInfo.url = new MatchPattern(queryInfo.url);
- }
-
- return Array.from(tabManager.query(queryInfo, context),
- tab => tab.convert());
- },
+ return Promise.resolve();
+ },
- async captureVisibleTab(windowId, options) {
- let window = windowId == null ?
- windowTracker.topWindow :
- windowTracker.getWindow(windowId, context);
+ _getZoomSettings(tabId) {
+ let nativeTab = getTabOrActive(tabId);
- let tab = tabManager.wrapTab(window.gBrowser.selectedTab);
- await tabListener.awaitTabReady(tab.nativeTab);
-
- return tab.capture(context, options);
- },
+ let {FullZoom} = nativeTab.ownerGlobal;
- async detectLanguage(tabId) {
- let tab = await promiseTabWhenReady(tabId);
-
- return tab.sendMessage(context, "Extension:DetectLanguage");
- },
-
- async executeScript(tabId, details) {
- let tab = await promiseTabWhenReady(tabId);
-
- return tab.executeScript(context, details);
- },
+ return {
+ mode: "automatic",
+ scope: FullZoom.siteSpecific ? "per-origin" : "per-tab",
+ defaultZoomFactor: 1,
+ };
+ },
- async insertCSS(tabId, details) {
- let tab = await promiseTabWhenReady(tabId);
-
- return tab.insertCSS(context, details);
- },
+ getZoomSettings(tabId) {
+ return Promise.resolve(this._getZoomSettings(tabId));
+ },
- async removeCSS(tabId, details) {
- let tab = await promiseTabWhenReady(tabId);
+ setZoomSettings(tabId, settings) {
+ let nativeTab = getTabOrActive(tabId);
- return tab.removeCSS(context, details);
- },
+ let currentSettings = this._getZoomSettings(tabTracker.getId(nativeTab));
- async move(tabIds, moveProperties) {
- let index = moveProperties.index;
- let tabsMoved = [];
- if (!Array.isArray(tabIds)) {
- tabIds = [tabIds];
- }
+ if (!Object.keys(settings).every(key => settings[key] === currentSettings[key])) {
+ return Promise.reject(`Unsupported zoom settings: ${JSON.stringify(settings)}`);
+ }
+ return Promise.resolve();
+ },
- let destinationWindow = null;
- if (moveProperties.windowId !== null) {
- destinationWindow = windowTracker.getWindow(moveProperties.windowId);
- // Fail on an invalid window.
- if (!destinationWindow) {
- return Promise.reject({message: `Invalid window ID: ${moveProperties.windowId}`});
- }
- }
+ onZoomChange: new SingletonEventManager(context, "tabs.onZoomChange", fire => {
+ let getZoomLevel = browser => {
+ let {ZoomManager} = browser.ownerGlobal;
- /*
- Indexes are maintained on a per window basis so that a call to
- move([tabA, tabB], {index: 0})
- -> tabA to 0, tabB to 1 if tabA and tabB are in the same window
- move([tabA, tabB], {index: 0})
- -> tabA to 0, tabB to 0 if tabA and tabB are in different windows
- */
- let indexMap = new Map();
+ return ZoomManager.getZoomForBrowser(browser);
+ };
- let tabs = tabIds.map(tabId => tabTracker.getTab(tabId));
- for (let nativeTab of tabs) {
- // If the window is not specified, use the window from the tab.
- let window = destinationWindow || nativeTab.ownerGlobal;
- let gBrowser = window.gBrowser;
+ // Stores the last known zoom level for each tab's browser.
+ // WeakMap[<browser> -> number]
+ let zoomLevels = new WeakMap();
- let insertionPoint = indexMap.get(window) || index;
- // If the index is -1 it should go to the end of the tabs.
- if (insertionPoint == -1) {
- insertionPoint = gBrowser.tabs.length;
- }
-
- // We can only move pinned tabs to a point within, or just after,
- // the current set of pinned tabs. Unpinned tabs, likewise, can only
- // be moved to a position after the current set of pinned tabs.
- // Attempts to move a tab to an illegal position are ignored.
- let numPinned = gBrowser._numPinnedTabs;
- let ok = nativeTab.pinned ? insertionPoint <= numPinned : insertionPoint >= numPinned;
- if (!ok) {
- continue;
+ // Store the zoom level for all existing tabs.
+ for (let window of windowTracker.browserWindows()) {
+ for (let nativeTab of window.gBrowser.tabs) {
+ let browser = nativeTab.linkedBrowser;
+ zoomLevels.set(browser, getZoomLevel(browser));
+ }
}
- indexMap.set(window, insertionPoint + 1);
-
- if (nativeTab.ownerGlobal != window) {
- // If the window we are moving the tab in is different, then move the tab
- // to the new window.
- nativeTab = gBrowser.adoptTab(nativeTab, insertionPoint, false);
- } else {
- // If the window we are moving is the same, just move the tab.
- gBrowser.moveTabTo(nativeTab, insertionPoint);
- }
- tabsMoved.push(nativeTab);
- }
-
- return tabsMoved.map(nativeTab => tabManager.convert(nativeTab));
- },
-
- duplicate(tabId) {
- let nativeTab = tabTracker.getTab(tabId);
-
- let gBrowser = nativeTab.ownerGlobal.gBrowser;
- let newTab = gBrowser.duplicateTab(nativeTab);
-
- return new Promise(resolve => {
- // We need to use SSTabRestoring because any attributes set before
- // are ignored. SSTabRestored is too late and results in a jump in
- // the UI. See http://bit.ly/session-store-api for more information.
- newTab.addEventListener("SSTabRestoring", function() {
- // As the tab is restoring, move it to the correct position.
-
- // Pinned tabs that are duplicated are inserted
- // after the existing pinned tab and pinned.
- if (nativeTab.pinned) {
- gBrowser.pinTab(newTab);
- }
- gBrowser.moveTabTo(newTab, nativeTab._tPos + 1);
- }, {once: true});
-
- newTab.addEventListener("SSTabRestored", function() {
- // Once it has been restored, select it and return the promise.
- gBrowser.selectedTab = newTab;
-
- resolve(tabManager.convert(newTab));
- }, {once: true});
- });
- },
-
- getZoom(tabId) {
- let nativeTab = getTabOrActive(tabId);
-
- let {ZoomManager} = nativeTab.ownerGlobal;
- let zoom = ZoomManager.getZoomForBrowser(nativeTab.linkedBrowser);
-
- return Promise.resolve(zoom);
- },
-
- setZoom(tabId, zoom) {
- let nativeTab = getTabOrActive(tabId);
-
- let {FullZoom, ZoomManager} = nativeTab.ownerGlobal;
-
- if (zoom === 0) {
- // A value of zero means use the default zoom factor.
- return FullZoom.reset(nativeTab.linkedBrowser);
- } else if (zoom >= ZoomManager.MIN && zoom <= ZoomManager.MAX) {
- FullZoom.setZoom(zoom, nativeTab.linkedBrowser);
- } else {
- return Promise.reject({
- message: `Zoom value ${zoom} out of range (must be between ${ZoomManager.MIN} and ${ZoomManager.MAX})`,
- });
- }
-
- return Promise.resolve();
- },
-
- _getZoomSettings(tabId) {
- let nativeTab = getTabOrActive(tabId);
-
- let {FullZoom} = nativeTab.ownerGlobal;
-
- return {
- mode: "automatic",
- scope: FullZoom.siteSpecific ? "per-origin" : "per-tab",
- defaultZoomFactor: 1,
- };
- },
-
- getZoomSettings(tabId) {
- return Promise.resolve(this._getZoomSettings(tabId));
- },
-
- setZoomSettings(tabId, settings) {
- let nativeTab = getTabOrActive(tabId);
-
- let currentSettings = this._getZoomSettings(tabTracker.getId(nativeTab));
-
- if (!Object.keys(settings).every(key => settings[key] === currentSettings[key])) {
- return Promise.reject(`Unsupported zoom settings: ${JSON.stringify(settings)}`);
- }
- return Promise.resolve();
- },
-
- onZoomChange: new SingletonEventManager(context, "tabs.onZoomChange", fire => {
- let getZoomLevel = browser => {
- let {ZoomManager} = browser.ownerGlobal;
-
- return ZoomManager.getZoomForBrowser(browser);
- };
-
- // Stores the last known zoom level for each tab's browser.
- // WeakMap[<browser> -> number]
- let zoomLevels = new WeakMap();
-
- // Store the zoom level for all existing tabs.
- for (let window of windowTracker.browserWindows()) {
- for (let nativeTab of window.gBrowser.tabs) {
- let browser = nativeTab.linkedBrowser;
+ let tabCreated = (eventName, event) => {
+ let browser = event.nativeTab.linkedBrowser;
zoomLevels.set(browser, getZoomLevel(browser));
- }
- }
-
- let tabCreated = (eventName, event) => {
- let browser = event.nativeTab.linkedBrowser;
- zoomLevels.set(browser, getZoomLevel(browser));
- };
+ };
- let zoomListener = event => {
- let browser = event.originalTarget;
+ let zoomListener = event => {
+ let browser = event.originalTarget;
- // For non-remote browsers, this event is dispatched on the document
- // rather than on the <browser>.
- if (browser instanceof Ci.nsIDOMDocument) {
- browser = browser.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDocShell)
- .chromeEventHandler;
- }
+ // For non-remote browsers, this event is dispatched on the document
+ // rather than on the <browser>.
+ if (browser instanceof Ci.nsIDOMDocument) {
+ browser = browser.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDocShell)
+ .chromeEventHandler;
+ }
- let {gBrowser} = browser.ownerGlobal;
- let nativeTab = gBrowser.getTabForBrowser(browser);
- if (!nativeTab) {
- // We only care about zoom events in the top-level browser of a tab.
- return;
- }
+ let {gBrowser} = browser.ownerGlobal;
+ let nativeTab = gBrowser.getTabForBrowser(browser);
+ if (!nativeTab) {
+ // We only care about zoom events in the top-level browser of a tab.
+ return;
+ }
- let oldZoomFactor = zoomLevels.get(browser);
- let newZoomFactor = getZoomLevel(browser);
+ let oldZoomFactor = zoomLevels.get(browser);
+ let newZoomFactor = getZoomLevel(browser);
- if (oldZoomFactor != newZoomFactor) {
- zoomLevels.set(browser, newZoomFactor);
+ if (oldZoomFactor != newZoomFactor) {
+ zoomLevels.set(browser, newZoomFactor);
- let tabId = tabTracker.getId(nativeTab);
- fire.async({
- tabId,
- oldZoomFactor,
- newZoomFactor,
- zoomSettings: self.tabs._getZoomSettings(tabId),
- });
- }
- };
+ let tabId = tabTracker.getId(nativeTab);
+ fire.async({
+ tabId,
+ oldZoomFactor,
+ newZoomFactor,
+ zoomSettings: self.tabs._getZoomSettings(tabId),
+ });
+ }
+ };
- tabTracker.on("tab-attached", tabCreated);
- tabTracker.on("tab-created", tabCreated);
+ tabTracker.on("tab-attached", tabCreated);
+ tabTracker.on("tab-created", tabCreated);
- windowTracker.addListener("FullZoomChange", zoomListener);
- windowTracker.addListener("TextZoomChange", zoomListener);
- return () => {
- tabTracker.off("tab-attached", tabCreated);
- tabTracker.off("tab-created", tabCreated);
+ windowTracker.addListener("FullZoomChange", zoomListener);
+ windowTracker.addListener("TextZoomChange", zoomListener);
+ return () => {
+ tabTracker.off("tab-attached", tabCreated);
+ tabTracker.off("tab-created", tabCreated);
- windowTracker.removeListener("FullZoomChange", zoomListener);
- windowTracker.removeListener("TextZoomChange", zoomListener);
- };
- }).api(),
- },
- };
- return self;
-});
+ windowTracker.removeListener("FullZoomChange", zoomListener);
+ windowTracker.removeListener("TextZoomChange", zoomListener);
+ };
+ }).api(),
+ },
+ };
+ return self;
+ }
+};
--- a/browser/components/extensions/ext-url-overrides.js
+++ b/browser/components/extensions/ext-url-overrides.js
@@ -1,49 +1,53 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService");
// Bug 1320736 tracks creating a generic precedence manager for handling
// multiple addons modifying the same properties, and bug 1330494 has been filed
// to track utilizing this manager for chrome_url_overrides. Until those land,
// the edge cases surrounding multiple addons using chrome_url_overrides will
// be ignored and precedence will be first come, first serve.
let overrides = {
// A queue of extensions in line to override the newtab page (sorted oldest to newest).
newtab: [],
};
-/* eslint-disable mozilla/balanced-listeners */
-extensions.on("manifest_chrome_url_overrides", (type, directive, extension, manifest) => {
- if (manifest.chrome_url_overrides.newtab) {
- let newtab = manifest.chrome_url_overrides.newtab;
- let url = extension.baseURI.resolve(newtab);
-
- // Only set the newtab URL if no other extension is overriding it.
- if (!overrides.newtab.length) {
- aboutNewTabService.newTabURL = url;
- }
+this.urlOverrides = class extends ExtensionAPI {
+ onManifestEntry(entryName) {
+ let {extension} = this;
+ let {manifest} = extension;
- overrides.newtab.push({id: extension.id, url});
- }
-});
+ if (manifest.chrome_url_overrides.newtab) {
+ let newtab = manifest.chrome_url_overrides.newtab;
+ let url = extension.baseURI.resolve(newtab);
-extensions.on("shutdown", (type, extension) => {
- let i = overrides.newtab.findIndex(o => o.id === extension.id);
- if (i !== -1) {
- overrides.newtab.splice(i, 1);
+ // Only set the newtab URL if no other extension is overriding it.
+ if (!overrides.newtab.length) {
+ aboutNewTabService.newTabURL = url;
+ }
- if (overrides.newtab.length) {
- aboutNewTabService.newTabURL = overrides.newtab[0].url;
- } else {
- aboutNewTabService.resetNewTabURL();
+ overrides.newtab.push({id: extension.id, url});
}
}
-});
-/* eslint-enable mozilla/balanced-listeners */
+
+ onShutdown(reason) {
+ let {extension} = this;
+
+ let i = overrides.newtab.findIndex(o => o.id === extension.id);
+ if (i !== -1) {
+ overrides.newtab.splice(i, 1);
+
+ if (overrides.newtab.length) {
+ aboutNewTabService.newTabURL = overrides.newtab[0].url;
+ } else {
+ aboutNewTabService.resetNewTabURL();
+ }
+ }
+ }
+};
--- a/browser/components/extensions/ext-utils.js
+++ b/browser/components/extensions/ext-utils.js
@@ -7,18 +7,16 @@ XPCOMUtils.defineLazyModuleGetter(this,
/* globals TabBase, WindowBase, TabTrackerBase, WindowTrackerBase, TabManagerBase, WindowManagerBase */
Cu.import("resource://gre/modules/ExtensionTabs.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "styleSheetService",
"@mozilla.org/content/style-sheet-service;1",
"nsIStyleSheetService");
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
-
var {
ExtensionError,
SingletonEventManager,
defineLazyGetter,
} = ExtensionUtils;
let tabTracker;
let windowTracker;
--- a/browser/components/extensions/ext-windows.js
+++ b/browser/components/extensions/ext-windows.js
@@ -5,233 +5,234 @@
XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService");
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
SingletonEventManager,
promiseObserved,
} = ExtensionUtils;
function onXULFrameLoaderCreated({target}) {
target.messageManager.sendAsyncMessage("AllowScriptsToClose", {});
}
-extensions.registerSchemaAPI("windows", "addon_parent", context => {
- let {extension} = context;
-
- const {windowManager} = extension;
+this.windows = class extends ExtensionAPI {
+ getAPI(context) {
+ let {extension} = context;
- return {
- windows: {
- onCreated:
- new WindowEventManager(context, "windows.onCreated", "domwindowopened", (fire, window) => {
- fire.async(windowManager.convert(window));
- }).api(),
-
- onRemoved:
- new WindowEventManager(context, "windows.onRemoved", "domwindowclosed", (fire, window) => {
- fire.async(windowTracker.getId(window));
- }).api(),
+ const {windowManager} = extension;
- onFocusChanged: new SingletonEventManager(context, "windows.onFocusChanged", fire => {
- // Keep track of the last windowId used to fire an onFocusChanged event
- let lastOnFocusChangedWindowId;
+ return {
+ windows: {
+ onCreated:
+ new WindowEventManager(context, "windows.onCreated", "domwindowopened", (fire, window) => {
+ fire.async(windowManager.convert(window));
+ }).api(),
- let listener = event => {
- // Wait a tick to avoid firing a superfluous WINDOW_ID_NONE
- // event when switching focus between two Firefox windows.
- Promise.resolve().then(() => {
- let window = Services.focus.activeWindow;
- let windowId = window ? windowTracker.getId(window) : Window.WINDOW_ID_NONE;
- if (windowId !== lastOnFocusChangedWindowId) {
- fire.async(windowId);
- lastOnFocusChangedWindowId = windowId;
- }
- });
- };
- windowTracker.addListener("focus", listener);
- windowTracker.addListener("blur", listener);
- return () => {
- windowTracker.removeListener("focus", listener);
- windowTracker.removeListener("blur", listener);
- };
- }).api(),
+ onRemoved:
+ new WindowEventManager(context, "windows.onRemoved", "domwindowclosed", (fire, window) => {
+ fire.async(windowTracker.getId(window));
+ }).api(),
+
+ onFocusChanged: new SingletonEventManager(context, "windows.onFocusChanged", fire => {
+ // Keep track of the last windowId used to fire an onFocusChanged event
+ let lastOnFocusChangedWindowId;
- get: function(windowId, getInfo) {
- let window = windowTracker.getWindow(windowId, context);
- if (!window) {
- return Promise.reject({message: `Invalid window ID: ${windowId}`});
- }
- return Promise.resolve(windowManager.convert(window, getInfo));
- },
-
- getCurrent: function(getInfo) {
- let window = context.currentWindow || windowTracker.topWindow;
- return Promise.resolve(windowManager.convert(window, getInfo));
- },
-
- getLastFocused: function(getInfo) {
- let window = windowTracker.topWindow;
- return Promise.resolve(windowManager.convert(window, getInfo));
- },
-
- getAll: function(getInfo) {
- let windows = Array.from(windowManager.getAll(), win => win.convert(getInfo));
+ let listener = event => {
+ // Wait a tick to avoid firing a superfluous WINDOW_ID_NONE
+ // event when switching focus between two Firefox windows.
+ Promise.resolve().then(() => {
+ let window = Services.focus.activeWindow;
+ let windowId = window ? windowTracker.getId(window) : Window.WINDOW_ID_NONE;
+ if (windowId !== lastOnFocusChangedWindowId) {
+ fire.async(windowId);
+ lastOnFocusChangedWindowId = windowId;
+ }
+ });
+ };
+ windowTracker.addListener("focus", listener);
+ windowTracker.addListener("blur", listener);
+ return () => {
+ windowTracker.removeListener("focus", listener);
+ windowTracker.removeListener("blur", listener);
+ };
+ }).api(),
- return Promise.resolve(windows);
- },
-
- create: function(createData) {
- let needResize = (createData.left !== null || createData.top !== null ||
- createData.width !== null || createData.height !== null);
-
- if (needResize) {
- if (createData.state !== null && createData.state != "normal") {
- return Promise.reject({message: `"state": "${createData.state}" may not be combined with "left", "top", "width", or "height"`});
+ get: function(windowId, getInfo) {
+ let window = windowTracker.getWindow(windowId, context);
+ if (!window) {
+ return Promise.reject({message: `Invalid window ID: ${windowId}`});
}
- createData.state = "normal";
- }
+ return Promise.resolve(windowManager.convert(window, getInfo));
+ },
+
+ getCurrent: function(getInfo) {
+ let window = context.currentWindow || windowTracker.topWindow;
+ return Promise.resolve(windowManager.convert(window, getInfo));
+ },
+
+ getLastFocused: function(getInfo) {
+ let window = windowTracker.topWindow;
+ return Promise.resolve(windowManager.convert(window, getInfo));
+ },
- function mkstr(s) {
- let result = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
- result.data = s;
- return result;
- }
+ getAll: function(getInfo) {
+ let windows = Array.from(windowManager.getAll(), win => win.convert(getInfo));
+
+ return Promise.resolve(windows);
+ },
- let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+ create: function(createData) {
+ let needResize = (createData.left !== null || createData.top !== null ||
+ createData.width !== null || createData.height !== null);
- if (createData.tabId !== null) {
- if (createData.url !== null) {
- return Promise.reject({message: "`tabId` may not be used in conjunction with `url`"});
+ if (needResize) {
+ if (createData.state !== null && createData.state != "normal") {
+ return Promise.reject({message: `"state": "${createData.state}" may not be combined with "left", "top", "width", or "height"`});
+ }
+ createData.state = "normal";
}
- if (createData.allowScriptsToClose) {
- return Promise.reject({message: "`tabId` may not be used in conjunction with `allowScriptsToClose`"});
+ function mkstr(s) {
+ let result = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
+ result.data = s;
+ return result;
}
- let tab = tabTracker.getTab(createData.tabId);
+ let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
- // Private browsing tabs can only be moved to private browsing
- // windows.
- let incognito = PrivateBrowsingUtils.isBrowserPrivate(tab.linkedBrowser);
- if (createData.incognito !== null && createData.incognito != incognito) {
- return Promise.reject({message: "`incognito` property must match the incognito state of tab"});
- }
- createData.incognito = incognito;
+ if (createData.tabId !== null) {
+ if (createData.url !== null) {
+ return Promise.reject({message: "`tabId` may not be used in conjunction with `url`"});
+ }
- args.appendElement(tab, /* weak = */ false);
- } else if (createData.url !== null) {
- if (Array.isArray(createData.url)) {
- let array = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
- for (let url of createData.url) {
- array.appendElement(mkstr(url), /* weak = */ false);
+ if (createData.allowScriptsToClose) {
+ return Promise.reject({message: "`tabId` may not be used in conjunction with `allowScriptsToClose`"});
}
- args.appendElement(array, /* weak = */ false);
- } else {
- args.appendElement(mkstr(createData.url), /* weak = */ false);
- }
- } else {
- args.appendElement(mkstr(aboutNewTabService.newTabURL), /* weak = */ false);
- }
+
+ let tab = tabTracker.getTab(createData.tabId);
- let features = ["chrome"];
-
- if (createData.type === null || createData.type == "normal") {
- features.push("dialog=no", "all");
- } else {
- // All other types create "popup"-type windows by default.
- features.push("dialog", "resizable", "minimizable", "centerscreen", "titlebar", "close");
- }
+ // Private browsing tabs can only be moved to private browsing
+ // windows.
+ let incognito = PrivateBrowsingUtils.isBrowserPrivate(tab.linkedBrowser);
+ if (createData.incognito !== null && createData.incognito != incognito) {
+ return Promise.reject({message: "`incognito` property must match the incognito state of tab"});
+ }
+ createData.incognito = incognito;
- if (createData.incognito !== null) {
- if (createData.incognito) {
- features.push("private");
+ args.appendElement(tab, /* weak = */ false);
+ } else if (createData.url !== null) {
+ if (Array.isArray(createData.url)) {
+ let array = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+ for (let url of createData.url) {
+ array.appendElement(mkstr(url), /* weak = */ false);
+ }
+ args.appendElement(array, /* weak = */ false);
+ } else {
+ args.appendElement(mkstr(createData.url), /* weak = */ false);
+ }
} else {
- features.push("non-private");
+ args.appendElement(mkstr(aboutNewTabService.newTabURL), /* weak = */ false);
}
- }
-
- let {allowScriptsToClose, url} = createData;
- if (allowScriptsToClose === null) {
- allowScriptsToClose = typeof url === "string" && url.startsWith("moz-extension://");
- }
-
- let window = Services.ww.openWindow(null, "chrome://browser/content/browser.xul", "_blank",
- features.join(","), args);
-
- let win = windowManager.getWrapper(window);
- win.updateGeometry(createData);
- // TODO: focused, type
+ let features = ["chrome"];
- return new Promise(resolve => {
- window.addEventListener("load", function() {
- if (["maximized", "normal"].includes(createData.state)) {
- window.document.documentElement.setAttribute("sizemode", createData.state);
- }
- resolve(promiseObserved("browser-delayed-startup-finished", win => win == window));
- }, {once: true});
- }).then(() => {
- // Some states only work after delayed-startup-finished
- if (["minimized", "fullscreen", "docked"].includes(createData.state)) {
- win.state = createData.state;
+ if (createData.type === null || createData.type == "normal") {
+ features.push("dialog=no", "all");
+ } else {
+ // All other types create "popup"-type windows by default.
+ features.push("dialog", "resizable", "minimizable", "centerscreen", "titlebar", "close");
}
- if (allowScriptsToClose) {
- for (let {linkedBrowser} of window.gBrowser.tabs) {
- onXULFrameLoaderCreated({target: linkedBrowser});
- linkedBrowser.addEventListener( // eslint-disable-line mozilla/balanced-listeners
- "XULFrameLoaderCreated", onXULFrameLoaderCreated);
+
+ if (createData.incognito !== null) {
+ if (createData.incognito) {
+ features.push("private");
+ } else {
+ features.push("non-private");
}
}
- return win.convert({populate: true});
- });
- },
- update: function(windowId, updateInfo) {
- if (updateInfo.state !== null && updateInfo.state != "normal") {
- if (updateInfo.left !== null || updateInfo.top !== null ||
- updateInfo.width !== null || updateInfo.height !== null) {
- return Promise.reject({message: `"state": "${updateInfo.state}" may not be combined with "left", "top", "width", or "height"`});
+ let {allowScriptsToClose, url} = createData;
+ if (allowScriptsToClose === null) {
+ allowScriptsToClose = typeof url === "string" && url.startsWith("moz-extension://");
}
- }
+
+ let window = Services.ww.openWindow(null, "chrome://browser/content/browser.xul", "_blank",
+ features.join(","), args);
+
+ let win = windowManager.getWrapper(window);
+ win.updateGeometry(createData);
+
+ // TODO: focused, type
- let win = windowManager.get(windowId, context);
- if (updateInfo.focused) {
- Services.focus.activeWindow = win.window;
- }
-
- if (updateInfo.state !== null) {
- win.state = updateInfo.state;
- }
+ return new Promise(resolve => {
+ window.addEventListener("load", function() {
+ if (["maximized", "normal"].includes(createData.state)) {
+ window.document.documentElement.setAttribute("sizemode", createData.state);
+ }
+ resolve(promiseObserved("browser-delayed-startup-finished", win => win == window));
+ }, {once: true});
+ }).then(() => {
+ // Some states only work after delayed-startup-finished
+ if (["minimized", "fullscreen", "docked"].includes(createData.state)) {
+ win.state = createData.state;
+ }
+ if (allowScriptsToClose) {
+ for (let {linkedBrowser} of window.gBrowser.tabs) {
+ onXULFrameLoaderCreated({target: linkedBrowser});
+ linkedBrowser.addEventListener( // eslint-disable-line mozilla/balanced-listeners
+ "XULFrameLoaderCreated", onXULFrameLoaderCreated);
+ }
+ }
+ return win.convert({populate: true});
+ });
+ },
- if (updateInfo.drawAttention) {
- // Bug 1257497 - Firefox can't cancel attention actions.
- win.window.getAttention();
- }
+ update: function(windowId, updateInfo) {
+ if (updateInfo.state !== null && updateInfo.state != "normal") {
+ if (updateInfo.left !== null || updateInfo.top !== null ||
+ updateInfo.width !== null || updateInfo.height !== null) {
+ return Promise.reject({message: `"state": "${updateInfo.state}" may not be combined with "left", "top", "width", or "height"`});
+ }
+ }
- win.updateGeometry(updateInfo);
+ let win = windowManager.get(windowId, context);
+ if (updateInfo.focused) {
+ Services.focus.activeWindow = win.window;
+ }
- // TODO: All the other properties, focused=false...
+ if (updateInfo.state !== null) {
+ win.state = updateInfo.state;
+ }
- return Promise.resolve(win.convert());
- },
+ if (updateInfo.drawAttention) {
+ // Bug 1257497 - Firefox can't cancel attention actions.
+ win.window.getAttention();
+ }
- remove: function(windowId) {
- let window = windowTracker.getWindow(windowId, context);
- window.close();
+ win.updateGeometry(updateInfo);
+
+ // TODO: All the other properties, focused=false...
+
+ return Promise.resolve(win.convert());
+ },
+
+ remove: function(windowId) {
+ let window = windowTracker.getWindow(windowId, context);
+ window.close();
- return new Promise(resolve => {
- let listener = () => {
- windowTracker.removeListener("domwindowclosed", listener);
- resolve();
- };
- windowTracker.addListener("domwindowclosed", listener);
- });
+ return new Promise(resolve => {
+ let listener = () => {
+ windowTracker.removeListener("domwindowclosed", listener);
+ resolve();
+ };
+ windowTracker.addListener("domwindowclosed", listener);
+ });
+ },
},
- },
- };
-});
+ };
+ }
+};
--- a/browser/components/extensions/extensions-browser.manifest
+++ b/browser/components/extensions/extensions-browser.manifest
@@ -1,51 +1,6 @@
-# scripts
-category webextension-scripts bookmarks chrome://browser/content/ext-bookmarks.js
-category webextension-scripts browserAction chrome://browser/content/ext-browserAction.js
-category webextension-scripts browsingData chrome://browser/content/ext-browsingData.js
-category webextension-scripts chrome-settings-overrides chrome://browser/content/ext-chrome-settings-overrides.js
-category webextension-scripts commands chrome://browser/content/ext-commands.js
-category webextension-scripts contextMenus chrome://browser/content/ext-contextMenus.js
-category webextension-scripts desktop-runtime chrome://browser/content/ext-desktop-runtime.js
-category webextension-scripts devtools chrome://browser/content/ext-devtools.js
-category webextension-scripts devtools-inspectedWindow chrome://browser/content/ext-devtools-inspectedWindow.js
-category webextension-scripts devtools-network chrome://browser/content/ext-devtools-network.js
-category webextension-scripts devtools-panels chrome://browser/content/ext-devtools-panels.js
-category webextension-scripts history chrome://browser/content/ext-history.js
-category webextension-scripts omnibox chrome://browser/content/ext-omnibox.js
-category webextension-scripts pageAction chrome://browser/content/ext-pageAction.js
-category webextension-scripts sessions chrome://browser/content/ext-sessions.js
-category webextension-scripts sidebarAction chrome://browser/content/ext-sidebarAction.js
-category webextension-scripts tabs chrome://browser/content/ext-tabs.js
-category webextension-scripts url-overrides chrome://browser/content/ext-url-overrides.js
+category webextension-scripts browser chrome://browser/content/ext-browser.js
category webextension-scripts utils chrome://browser/content/ext-utils.js
-category webextension-scripts windows chrome://browser/content/ext-windows.js
-
-# scripts specific for devtools extension contexts.
-category webextension-scripts-devtools devtools-inspectedWindow chrome://browser/content/ext-c-devtools-inspectedWindow.js
-category webextension-scripts-devtools devtools-panels chrome://browser/content/ext-c-devtools-panels.js
+category webextension-scripts-devtools browser chrome://browser/content/ext-c-browser.js
+category webextension-scripts-addon browser chrome://browser/content/ext-c-browser.js
-# scripts that must run in the same process as addon code.
-category webextension-scripts-addon contextMenus chrome://browser/content/ext-c-contextMenus.js
-category webextension-scripts-addon omnibox chrome://browser/content/ext-c-omnibox.js
-category webextension-scripts-addon tabs chrome://browser/content/ext-c-tabs.js
-
-# schemas
-category webextension-schemas bookmarks chrome://browser/content/schemas/bookmarks.json
-category webextension-schemas browser_action chrome://browser/content/schemas/browser_action.json
-category webextension-schemas browsing_data chrome://browser/content/schemas/browsing_data.json
-category webextension-schemas chrome_settings_overrides chrome://browser/content/schemas/chrome_settings_overrides.json
-category webextension-schemas commands chrome://browser/content/schemas/commands.json
-category webextension-schemas context_menus chrome://browser/content/schemas/context_menus.json
category webextension-schemas context_menus_internal chrome://browser/content/schemas/context_menus_internal.json
-category webextension-schemas devtools chrome://browser/content/schemas/devtools.json
-category webextension-schemas devtools_inspected_window chrome://browser/content/schemas/devtools_inspected_window.json
-category webextension-schemas devtools_network chrome://browser/content/schemas/devtools_network.json
-category webextension-schemas devtools_panels chrome://browser/content/schemas/devtools_panels.json
-category webextension-schemas history chrome://browser/content/schemas/history.json
-category webextension-schemas omnibox chrome://browser/content/schemas/omnibox.json
-category webextension-schemas page_action chrome://browser/content/schemas/page_action.json
-category webextension-schemas sessions chrome://browser/content/schemas/sessions.json
-category webextension-schemas sidebar_action chrome://browser/content/schemas/sidebar_action.json
-category webextension-schemas tabs chrome://browser/content/schemas/tabs.json
-category webextension-schemas url_overrides chrome://browser/content/schemas/url_overrides.json
-category webextension-schemas windows chrome://browser/content/schemas/windows.json
--- a/browser/components/extensions/jar.mn
+++ b/browser/components/extensions/jar.mn
@@ -8,32 +8,34 @@ browser.jar:
content/browser/extension-mac.css
content/browser/extension-mac-panel.css
#endif
#ifdef XP_WIN
content/browser/extension-win-panel.css
#endif
content/browser/extension.svg
content/browser/ext-bookmarks.js
+ content/browser/ext-browser.js
content/browser/ext-browserAction.js
content/browser/ext-browsingData.js
content/browser/ext-chrome-settings-overrides.js
content/browser/ext-commands.js
content/browser/ext-contextMenus.js
- content/browser/ext-desktop-runtime.js
content/browser/ext-devtools.js
content/browser/ext-devtools-inspectedWindow.js
content/browser/ext-devtools-network.js
content/browser/ext-devtools-panels.js
content/browser/ext-history.js
content/browser/ext-omnibox.js
content/browser/ext-pageAction.js
content/browser/ext-sessions.js
content/browser/ext-sidebarAction.js
content/browser/ext-tabs.js
content/browser/ext-url-overrides.js
content/browser/ext-utils.js
content/browser/ext-windows.js
+ content/browser/ext-c-browser.js
content/browser/ext-c-contextMenus.js
content/browser/ext-c-devtools-inspectedWindow.js
content/browser/ext-c-devtools-panels.js
+ content/browser/ext-c-devtools.js
content/browser/ext-c-omnibox.js
content/browser/ext-c-tabs.js
--- a/browser/components/extensions/schemas/context_menus.json
+++ b/browser/components/extensions/schemas/context_menus.json
@@ -215,84 +215,16 @@
"name": "callback",
"optional": true,
"description": "Called when the item has been created in the browser. If there were any problems creating the item, details will be available in $(ref:runtime.lastError).",
"parameters": []
}
]
},
{
- "name": "createInternal",
- "type": "function",
- "allowedContexts": ["addon_parent_only"],
- "async": "callback",
- "description": "Identical to contextMenus.create, except: the 'id' field is required and allows an integer, 'onclick' is not allowed, and the method is async (and the return value is not a menu item ID).",
- "parameters": [
- {
- "type": "object",
- "name": "createProperties",
- "properties": {
- "type": {
- "$ref": "ItemType",
- "optional": true
- },
- "id": {
- "choices": [
- { "type": "integer" },
- { "type": "string" }
- ]
- },
- "title": {
- "type": "string",
- "optional": true
- },
- "checked": {
- "type": "boolean",
- "optional": true
- },
- "contexts": {
- "type": "array",
- "items": {
- "$ref": "ContextType"
- },
- "minItems": 1,
- "optional": true
- },
- "parentId": {
- "choices": [
- { "type": "integer" },
- { "type": "string" }
- ],
- "optional": true
- },
- "documentUrlPatterns": {
- "type": "array",
- "items": {"type": "string"},
- "optional": true
- },
- "targetUrlPatterns": {
- "type": "array",
- "items": {"type": "string"},
- "optional": true
- },
- "enabled": {
- "type": "boolean",
- "optional": true
- }
- }
- },
- {
- "type": "function",
- "name": "callback",
- "optional": true,
- "parameters": []
- }
- ]
- },
- {
"name": "update",
"type": "function",
"description": "Updates a previously created context menu item.",
"async": "callback",
"parameters": [
{
"choices": [
{ "type": "integer" },