diff --git a/mobile/android/components/extensions/ext-android.js b/mobile/android/components/extensions/ext-android.js new file mode 100644 index 000000000000..1149ae4ac87d --- /dev/null +++ b/mobile/android/components/extensions/ext-android.js @@ -0,0 +1,22 @@ +"use strict"; + +extensions.registerModules({ + pageAction: { + url: "chrome://browser/content/ext-pageAction.js", + schema: "chrome://browser/content/schemas/page_action.json", + scopes: ["addon_parent"], + manifest: ["page_action"], + paths: [ + ["pageAction"], + ], + }, + tabs: { + url: "chrome://browser/content/ext-tabs.js", + schema: "chrome://browser/content/schemas/tabs.json", + scopes: ["addon_parent"], + paths: [ + ["tabs"], + ], + }, +}); + diff --git a/mobile/android/components/extensions/ext-c-android.js b/mobile/android/components/extensions/ext-c-android.js new file mode 100644 index 000000000000..c0999a1a482c --- /dev/null +++ b/mobile/android/components/extensions/ext-c-android.js @@ -0,0 +1,11 @@ +"use strict"; + +extensions.registerModules({ + tabs: { + url: "chrome://browser/content/ext-c-tabs.js", + scopes: ["addon_child"], + paths: [ + ["tabs"], + ], + }, +}); diff --git a/mobile/android/components/extensions/ext-c-tabs.js b/mobile/android/components/extensions/ext-c-tabs.js index d5ce9fbf95e0..8faca17c126a 100644 --- a/mobile/android/components/extensions/ext-c-tabs.js +++ b/mobile/android/components/extensions/ext-c-tabs.js @@ -2,34 +2,36 @@ /* 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); + }, }, - }, - }; -}); + }; + } +}; diff --git a/mobile/android/components/extensions/ext-pageAction.js b/mobile/android/components/extensions/ext-pageAction.js index 57618af2dab2..fdc3729cd86f 100644 --- a/mobile/android/components/extensions/ext-pageAction.js +++ b/mobile/android/components/extensions/ext-pageAction.js @@ -112,60 +112,65 @@ PageAction.prototype = { }, }; -/* 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); -}); +this.pageAction = class extends ExtensionAPI { + onManifestEntry(entryName) { + let {extension} = this; + let {manifest} = extension; -extensions.on("shutdown", (type, extension) => { - if (pageActionMap.has(extension)) { - pageActionMap.get(extension).shutdown(); - pageActionMap.delete(extension); + let pageAction = new PageAction(manifest.page_action, extension); + pageActionMap.set(extension, pageAction); } -}); -/* eslint-enable mozilla/balanced-listeners */ -extensions.registerSchemaAPI("pageAction", "addon_parent", context => { - const {extension} = context; - const {tabManager} = extension; + onShutdown(reason) { + let {extension} = this; - return { - pageAction: { - onClicked: new SingletonEventManager(context, "pageAction.onClicked", fire => { - let listener = (event, tab) => { - fire.async(tabManager.convert(tab)); - }; - pageActionMap.get(extension).on("click", listener); - return () => { - pageActionMap.get(extension).off("click", listener); - }; - }).api(), + if (pageActionMap.has(extension)) { + pageActionMap.get(extension).shutdown(); + pageActionMap.delete(extension); + } + } - show(tabId) { - return pageActionMap.get(extension) - .show(tabId, context) - .then(() => {}); + getAPI(context) { + const {extension} = context; + const {tabManager} = extension; + + return { + pageAction: { + onClicked: new SingletonEventManager(context, "pageAction.onClicked", fire => { + let listener = (event, tab) => { + fire.async(tabManager.convert(tab)); + }; + pageActionMap.get(extension).on("click", listener); + return () => { + pageActionMap.get(extension).off("click", listener); + }; + }).api(), + + show(tabId) { + return pageActionMap.get(extension) + .show(tabId, context) + .then(() => {}); + }, + + hide(tabId) { + pageActionMap.get(extension).hide(tabId); + return Promise.resolve(); + }, + + setPopup(details) { + // TODO: Use the Tabs API to get the tab from details.tabId. + let tab = null; + let url = details.popup && context.uri.resolve(details.popup); + pageActionMap.get(extension).setPopup(tab, url); + }, + + getPopup(details) { + // TODO: Use the Tabs API to get the tab from details.tabId. + let tab = null; + let popup = pageActionMap.get(extension).getPopup(tab); + return Promise.resolve(popup); + }, }, - - hide(tabId) { - pageActionMap.get(extension).hide(tabId); - return Promise.resolve(); - }, - - setPopup(details) { - // TODO: Use the Tabs API to get the tab from details.tabId. - let tab = null; - let url = details.popup && context.uri.resolve(details.popup); - pageActionMap.get(extension).setPopup(tab, url); - }, - - getPopup(details) { - // TODO: Use the Tabs API to get the tab from details.tabId. - let tab = null; - let popup = pageActionMap.get(extension).getPopup(tab); - return Promise.resolve(popup); - }, - }, - }; -}); + }; + } +}; diff --git a/mobile/android/components/extensions/ext-tabs.js b/mobile/android/components/extensions/ext-tabs.js index 44ecc8a8dffa..762597ee72f7 100644 --- a/mobile/android/components/extensions/ext-tabs.js +++ b/mobile/android/components/extensions/ext-tabs.js @@ -15,8 +15,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils", XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/ExtensionUtils.jsm"); - var { SingletonEventManager, } = ExtensionUtils; @@ -128,309 +126,311 @@ let tabListener = { }, }; -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); - } - return tabTracker.activeTab; - } - - async function promiseTabWhenReady(tabId) { - let tab; - if (tabId !== null) { - tab = tabManager.get(tabId); - } else { - tab = tabManager.getWrapper(tabTracker.activeTab); + function getTabOrActive(tabId) { + if (tabId !== null) { + return tabTracker.getTab(tabId); + } + return tabTracker.activeTab; } - await tabListener.awaitTabReady(tab.nativeTab); + async function promiseTabWhenReady(tabId) { + let tab; + if (tabId !== null) { + tab = tabManager.get(tabId); + } else { + tab = tabManager.getWrapper(tabTracker.activeTab); + } - return tab; + await tabListener.awaitTabReady(tab.nativeTab); + + return tab; + } + + let self = { + tabs: { + onActivated: new GlobalEventManager(context, "tabs.onActivated", "Tab:Selected", (fire, data) => { + let tab = tabManager.get(data.id); + + fire.async({tabId: tab.id, windowId: tab.windowId}); + }).api(), + + 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(), + + /** + * 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 GlobalEventManager(context, "tabs.onHighlighted", "Tab:Selected", (fire, data) => { + let tab = tabManager.get(data.id); + + fire.async({tabIds: [tab.id], windowId: tab.windowId}); + }).api(), + + onAttached: new SingletonEventManager(context, "tabs.onAttached", fire => { + return () => {}; + }).api(), + + onDetached: new SingletonEventManager(context, "tabs.onDetached", fire => { + return () => {}; + }).api(), + + onRemoved: new SingletonEventManager(context, "tabs.onRemoved", fire => { + let listener = (eventName, event) => { + fire.async(event.tabId, {windowId: event.windowId, isWindowClosing: event.isWindowClosing}); + }; + + tabTracker.on("tab-removed", listener); + return () => { + tabTracker.off("tab-removed", listener); + }; + }).api(), + + onReplaced: new SingletonEventManager(context, "tabs.onReplaced", fire => { + return () => {}; + }).api(), + + onMoved: new SingletonEventManager(context, "tabs.onMoved", fire => { + return () => {}; + }).api(), + + onUpdated: new SingletonEventManager(context, "tabs.onUpdated", fire => { + const restricted = ["url", "favIconUrl", "title"]; + + 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]; + } + + let fireForTab = (tab, changed) => { + let [needed, changeInfo] = sanitize(extension, changed); + if (needed) { + fire.async(tab.id, changeInfo, tab.convert()); + } + }; + + let listener = event => { + let needed = []; + let nativeTab; + switch (event.type) { + case "DOMTitleChanged": { + let {BrowserApp} = getBrowserWindow(event.target.ownerGlobal); + + nativeTab = BrowserApp.getTabForWindow(event.target.ownerGlobal); + needed.push("title"); + break; + } + + case "DOMAudioPlaybackStarted": + case "DOMAudioPlaybackStopped": { + let {BrowserApp} = event.target.ownerGlobal; + nativeTab = BrowserApp.getTabForBrowser(event.originalTarget); + needed.push("audible"); + break; + } + } + + if (!nativeTab) { + return; + } + + let tab = tabManager.getWrapper(nativeTab); + let changeInfo = {}; + for (let prop of needed) { + changeInfo[prop] = tab[prop]; + } + + fireForTab(tab, changeInfo); + }; + + let statusListener = ({browser, status, url}) => { + let {BrowserApp} = browser.ownerGlobal; + let nativeTab = BrowserApp.getTabForBrowser(browser); + if (nativeTab) { + let changed = {status}; + if (url) { + changed.url = url; + } + + fireForTab(tabManager.wrapTab(nativeTab), changed); + } + }; + + windowTracker.addListener("status", statusListener); + windowTracker.addListener("DOMTitleChanged", listener); + return () => { + windowTracker.removeListener("status", statusListener); + windowTracker.removeListener("DOMTitleChanged", listener); + }; + }).api(), + + async create(createProperties) { + let window = createProperties.windowId !== null ? + windowTracker.getWindow(createProperties.windowId, context) : + windowTracker.topWindow; + + let {BrowserApp} = 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}`}); + } + } + + let options = {}; + + let active = true; + if (createProperties.active !== null) { + active = createProperties.active; + } + options.selected = active; + + if (createProperties.index !== null) { + options.tabIndex = createProperties.index; + } + + // Make sure things like about:blank and data: URIs never inherit, + // and instead always get a NullPrincipal. + options.disallowInheritPrincipal = true; + + tabListener.initTabReady(); + let nativeTab = BrowserApp.addTab(url, options); + + if (createProperties.url) { + tabListener.initializingTabs.add(nativeTab); + } + + return tabManager.convert(nativeTab); + }, + + async remove(tabs) { + if (!Array.isArray(tabs)) { + tabs = [tabs]; + } + + for (let tabId of tabs) { + let nativeTab = tabTracker.getTab(tabId); + nativeTab.browser.ownerGlobal.BrowserApp.closeTab(nativeTab); + } + }, + + async update(tabId, updateProperties) { + let nativeTab = getTabOrActive(tabId); + + let {BrowserApp} = nativeTab.browser.ownerGlobal; + + 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.browser.loadURI(url); + } + + if (updateProperties.active !== null) { + if (updateProperties.active) { + BrowserApp.selectTab(nativeTab); + } else { + // Not sure what to do here? Which tab should we select? + } + } + // FIXME: highlighted/selected, muted, pinned, 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.browser.reloadWithFlags(flags); + }, + + async get(tabId) { + return tabManager.get(tabId).convert(); + }, + + async getCurrent() { + if (context.tabId) { + return tabManager.get(context.tabId).convert(); + } + }, + + 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'}); + } + + 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.BrowserApp.selectedTab); + await tabListener.awaitTabReady(tab.nativeTab); + + return tab.capture(context, options); + }, + + async executeScript(tabId, details) { + let tab = await promiseTabWhenReady(tabId); + + 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); + + return tab.removeCSS(context, details); + }, + }, + }; + return self; } - - let self = { - tabs: { - onActivated: new GlobalEventManager(context, "tabs.onActivated", "Tab:Selected", (fire, data) => { - let tab = tabManager.get(data.id); - - fire.async({tabId: tab.id, windowId: tab.windowId}); - }).api(), - - 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(), - - /** - * 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 GlobalEventManager(context, "tabs.onHighlighted", "Tab:Selected", (fire, data) => { - let tab = tabManager.get(data.id); - - fire.async({tabIds: [tab.id], windowId: tab.windowId}); - }).api(), - - onAttached: new SingletonEventManager(context, "tabs.onAttached", fire => { - return () => {}; - }).api(), - - onDetached: new SingletonEventManager(context, "tabs.onDetached", fire => { - return () => {}; - }).api(), - - onRemoved: new SingletonEventManager(context, "tabs.onRemoved", fire => { - let listener = (eventName, event) => { - fire.async(event.tabId, {windowId: event.windowId, isWindowClosing: event.isWindowClosing}); - }; - - tabTracker.on("tab-removed", listener); - return () => { - tabTracker.off("tab-removed", listener); - }; - }).api(), - - onReplaced: new SingletonEventManager(context, "tabs.onReplaced", fire => { - return () => {}; - }).api(), - - onMoved: new SingletonEventManager(context, "tabs.onMoved", fire => { - return () => {}; - }).api(), - - onUpdated: new SingletonEventManager(context, "tabs.onUpdated", fire => { - const restricted = ["url", "favIconUrl", "title"]; - - 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]; - } - - let fireForTab = (tab, changed) => { - let [needed, changeInfo] = sanitize(extension, changed); - if (needed) { - fire.async(tab.id, changeInfo, tab.convert()); - } - }; - - let listener = event => { - let needed = []; - let nativeTab; - switch (event.type) { - case "DOMTitleChanged": { - let {BrowserApp} = getBrowserWindow(event.target.ownerGlobal); - - nativeTab = BrowserApp.getTabForWindow(event.target.ownerGlobal); - needed.push("title"); - break; - } - - case "DOMAudioPlaybackStarted": - case "DOMAudioPlaybackStopped": { - let {BrowserApp} = event.target.ownerGlobal; - nativeTab = BrowserApp.getTabForBrowser(event.originalTarget); - needed.push("audible"); - break; - } - } - - if (!nativeTab) { - return; - } - - let tab = tabManager.getWrapper(nativeTab); - let changeInfo = {}; - for (let prop of needed) { - changeInfo[prop] = tab[prop]; - } - - fireForTab(tab, changeInfo); - }; - - let statusListener = ({browser, status, url}) => { - let {BrowserApp} = browser.ownerGlobal; - let nativeTab = BrowserApp.getTabForBrowser(browser); - if (nativeTab) { - let changed = {status}; - if (url) { - changed.url = url; - } - - fireForTab(tabManager.wrapTab(nativeTab), changed); - } - }; - - windowTracker.addListener("status", statusListener); - windowTracker.addListener("DOMTitleChanged", listener); - return () => { - windowTracker.removeListener("status", statusListener); - windowTracker.removeListener("DOMTitleChanged", listener); - }; - }).api(), - - async create(createProperties) { - let window = createProperties.windowId !== null ? - windowTracker.getWindow(createProperties.windowId, context) : - windowTracker.topWindow; - - let {BrowserApp} = 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}`}); - } - } - - let options = {}; - - let active = true; - if (createProperties.active !== null) { - active = createProperties.active; - } - options.selected = active; - - if (createProperties.index !== null) { - options.tabIndex = createProperties.index; - } - - // Make sure things like about:blank and data: URIs never inherit, - // and instead always get a NullPrincipal. - options.disallowInheritPrincipal = true; - - tabListener.initTabReady(); - let nativeTab = BrowserApp.addTab(url, options); - - if (createProperties.url) { - tabListener.initializingTabs.add(nativeTab); - } - - return tabManager.convert(nativeTab); - }, - - async remove(tabs) { - if (!Array.isArray(tabs)) { - tabs = [tabs]; - } - - for (let tabId of tabs) { - let nativeTab = tabTracker.getTab(tabId); - nativeTab.browser.ownerGlobal.BrowserApp.closeTab(nativeTab); - } - }, - - async update(tabId, updateProperties) { - let nativeTab = getTabOrActive(tabId); - - let {BrowserApp} = nativeTab.browser.ownerGlobal; - - 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.browser.loadURI(url); - } - - if (updateProperties.active !== null) { - if (updateProperties.active) { - BrowserApp.selectTab(nativeTab); - } else { - // Not sure what to do here? Which tab should we select? - } - } - // FIXME: highlighted/selected, muted, pinned, 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.browser.reloadWithFlags(flags); - }, - - async get(tabId) { - return tabManager.get(tabId).convert(); - }, - - async getCurrent() { - if (context.tabId) { - return tabManager.get(context.tabId).convert(); - } - }, - - 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'}); - } - - 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.BrowserApp.selectedTab); - await tabListener.awaitTabReady(tab.nativeTab); - - return tab.capture(context, options); - }, - - async executeScript(tabId, details) { - let tab = await promiseTabWhenReady(tabId); - - 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); - - return tab.removeCSS(context, details); - }, - }, - }; - return self; -}); +}; diff --git a/mobile/android/components/extensions/extensions-mobile.manifest b/mobile/android/components/extensions/extensions-mobile.manifest index 276faf303d44..77fec486600a 100644 --- a/mobile/android/components/extensions/extensions-mobile.manifest +++ b/mobile/android/components/extensions/extensions-mobile.manifest @@ -1,9 +1,4 @@ # scripts -category webextension-scripts pageAction chrome://browser/content/ext-pageAction.js -category webextension-scripts tabs chrome://browser/content/ext-tabs.js +category webextension-scripts android chrome://browser/content/ext-android.js category webextension-scripts utils chrome://browser/content/ext-utils.js -category webextension-scripts-addon tabs chrome://browser/content/ext-c-tabs.js - -# schemas -category webextension-schemas page_action chrome://browser/content/schemas/page_action.json -category webextension-schemas tabs chrome://browser/content/schemas/tabs.json +category webextension-scripts-addon android chrome://browser/content/ext-c-android.js diff --git a/mobile/android/components/extensions/jar.mn b/mobile/android/components/extensions/jar.mn index e06771d0f395..30412827632f 100644 --- a/mobile/android/components/extensions/jar.mn +++ b/mobile/android/components/extensions/jar.mn @@ -3,6 +3,8 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. chrome.jar: + content/ext-android.js + content/ext-c-android.js content/ext-c-tabs.js content/ext-pageAction.js content/ext-tabs.js