Bug 1350522: Part 3 - Convert android APIs to lazy loading. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Sat, 25 Mar 2017 10:45:39 -0700
changeset 551551 b0b6b8246e0749f23a1cc0bf5d38b61185ac77e3
parent 551550 d99db1966660f4121466d8f172e1bd09b1f6551e
child 551552 18aabb5231deeccbe5e9643317be124056a1f944
push id51079
push usermaglione.k@gmail.com
push dateMon, 27 Mar 2017 01:43:58 +0000
reviewersaswan
bugs1350522
milestone55.0a1
Bug 1350522: Part 3 - Convert android APIs to lazy loading. r?aswan MozReview-Commit-ID: GaxLICqzdXz
mobile/android/components/extensions/ext-c-tabs.js
mobile/android/components/extensions/ext-pageAction.js
mobile/android/components/extensions/ext-tabs.js
mobile/android/components/extensions/extensions-mobile.manifest
--- a/mobile/android/components/extensions/ext-c-tabs.js
+++ b/mobile/android/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/mobile/android/components/extensions/ext-pageAction.js
+++ b/mobile/android/components/extensions/ext-pageAction.js
@@ -107,65 +107,70 @@ PageAction.prototype = {
     return this.popupUrl;
   },
 
   shutdown() {
     this.hide();
   },
 };
 
-/* 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;
+
+    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);
+  onShutdown(reason) {
+    let {extension} = this;
+
+    if (pageActionMap.has(extension)) {
+      pageActionMap.get(extension).shutdown();
+      pageActionMap.delete(extension);
+    }
   }
-});
-/* eslint-enable mozilla/balanced-listeners */
 
-extensions.registerSchemaAPI("pageAction", "addon_parent", context => {
-  const {extension} = context;
-  const {tabManager} = extension;
+  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(),
+    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(() => {});
-      },
+        show(tabId) {
+          return pageActionMap.get(extension)
+                              .show(tabId, context)
+                              .then(() => {});
+        },
 
-      hide(tabId) {
-        pageActionMap.get(extension).hide(tabId);
-        return Promise.resolve();
-      },
+        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);
+        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);
+        },
       },
-
-      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);
-      },
-    },
-  };
-});
+    };
+  }
+};
--- a/mobile/android/components/extensions/ext-tabs.js
+++ b/mobile/android/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;
@@ -123,314 +121,316 @@ 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);
+    let self = {
+      tabs: {
+        onActivated: new GlobalEventManager(context, "tabs.onActivated", "Tab:Selected", (fire, data) => {
+          let tab = tabManager.get(data.id);
 
-    return tab;
-  }
+          fire.async({tabId: tab.id, windowId: tab.windowId});
+        }).api(),
 
-  let self = {
-    tabs: {
-      onActivated: new GlobalEventManager(context, "tabs.onActivated", "Tab:Selected", (fire, data) => {
-        let tab = tabManager.get(data.id);
+        onCreated: new SingletonEventManager(context, "tabs.onCreated", fire => {
+          let listener = (eventName, event) => {
+            fire.async(tabManager.convert(event.nativeTab));
+          };
 
-        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(),
 
-        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(),
 
-      /**
-       * 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);
+        onAttached: new SingletonEventManager(context, "tabs.onAttached", fire => {
+          return () => {};
+        }).api(),
+
+        onDetached: new SingletonEventManager(context, "tabs.onDetached", fire => {
+          return () => {};
+        }).api(),
 
-        fire.async({tabIds: [tab.id], windowId: tab.windowId});
-      }).api(),
+        onRemoved: new SingletonEventManager(context, "tabs.onRemoved", fire => {
+          let listener = (eventName, event) => {
+            fire.async(event.tabId, {windowId: event.windowId, isWindowClosing: event.isWindowClosing});
+          };
 
-      onAttached: new SingletonEventManager(context, "tabs.onAttached", fire => {
-        return () => {};
-      }).api(),
-
-      onDetached: new SingletonEventManager(context, "tabs.onDetached", fire => {
-        return () => {};
-      }).api(),
+          tabTracker.on("tab-removed", listener);
+          return () => {
+            tabTracker.off("tab-removed", listener);
+          };
+        }).api(),
 
-      onRemoved: new SingletonEventManager(context, "tabs.onRemoved", fire => {
-        let listener = (eventName, event) => {
-          fire.async(event.tabId, {windowId: event.windowId, isWindowClosing: event.isWindowClosing});
-        };
+        onReplaced: new SingletonEventManager(context, "tabs.onReplaced", fire => {
+          return () => {};
+        }).api(),
 
-        tabTracker.on("tab-removed", listener);
-        return () => {
-          tabTracker.off("tab-removed", listener);
-        };
-      }).api(),
+        onMoved: new SingletonEventManager(context, "tabs.onMoved", fire => {
+          return () => {};
+        }).api(),
+
+        onUpdated: new SingletonEventManager(context, "tabs.onUpdated", fire => {
+          const restricted = ["url", "favIconUrl", "title"];
 
-      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];
+          }
 
-        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];
+          let fireForTab = (tab, changed) => {
+            let [needed, changeInfo] = sanitize(extension, changed);
+            if (needed) {
+              fire.async(tab.id, changeInfo, tab.convert());
             }
-          }
-          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);
 
-        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;
+              }
 
-              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;
+              }
             }
 
-            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}`});
             }
           }
 
-          if (!nativeTab) {
-            return;
+          let options = {};
+
+          let active = true;
+          if (createProperties.active !== null) {
+            active = createProperties.active;
+          }
+          options.selected = active;
+
+          if (createProperties.index !== null) {
+            options.tabIndex = createProperties.index;
           }
 
-          let tab = tabManager.getWrapper(nativeTab);
-          let changeInfo = {};
-          for (let prop of needed) {
-            changeInfo[prop] = tab[prop];
+          // 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);
           }
 
-          fireForTab(tab, changeInfo);
-        };
+          return tabManager.convert(nativeTab);
+        },
+
+        async remove(tabs) {
+          if (!Array.isArray(tabs)) {
+            tabs = [tabs];
+          }
 
-        let statusListener = ({browser, status, url}) => {
-          let {BrowserApp} = browser.ownerGlobal;
-          let nativeTab = BrowserApp.getTabForBrowser(browser);
-          if (nativeTab) {
-            let changed = {status};
-            if (url) {
-              changed.url = url;
+          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}`});
             }
 
-            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);
           }
 
-          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?
+          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
+          // FIXME: highlighted/selected, muted, pinned, openerTabId
 
-        return tabManager.convert(nativeTab);
-      },
+          return tabManager.convert(nativeTab);
+        },
 
-      async reload(tabId, reloadProperties) {
-        let nativeTab = getTabOrActive(tabId);
+        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);
-      },
+          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 get(tabId) {
+          return tabManager.get(tabId).convert();
+        },
 
-      async getCurrent() {
-        if (context.tabId) {
-          return tabManager.get(context.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'});
+        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);
           }
 
-          queryInfo = Object.assign({}, queryInfo);
-          queryInfo.url = new MatchPattern(queryInfo.url);
-        }
+          return Array.from(tabManager.query(queryInfo, context),
+                            tab => tab.convert());
+        },
 
-        return Array.from(tabManager.query(queryInfo, context),
-                          tab => tab.convert());
-      },
+        async captureVisibleTab(windowId, options) {
+          let window = windowId == null ?
+            windowTracker.topWindow :
+            windowTracker.getWindow(windowId, context);
 
-      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);
 
-        let tab = tabManager.wrapTab(window.BrowserApp.selectedTab);
-        await tabListener.awaitTabReady(tab.nativeTab);
+          return tab.capture(context, options);
+        },
 
-        return tab.capture(context, options);
-      },
+        async executeScript(tabId, details) {
+          let tab = await promiseTabWhenReady(tabId);
 
-      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.executeScript(context, details);
-      },
+          return tab.insertCSS(context, details);
+        },
 
-      async insertCSS(tabId, details) {
-        let tab = await promiseTabWhenReady(tabId);
+        async removeCSS(tabId, details) {
+          let tab = await promiseTabWhenReady(tabId);
 
-        return tab.insertCSS(context, details);
+          return tab.removeCSS(context, details);
+        },
       },
-
-      async removeCSS(tabId, details) {
-        let tab = await promiseTabWhenReady(tabId);
-
-        return tab.removeCSS(context, details);
-      },
-    },
-  };
-  return self;
-});
+    };
+    return self;
+  }
+};
--- 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