Bug 1468667 - [webext] Add missing pieces to allow WebExtensions to run without error draft
authorGeoff Lankow <geoff@darktrojan.net>
Sat, 16 Jun 2018 10:09:05 +1200
changeset 24806 f1d6fae371a80d5f9dae48de051da5448fd4ba19
parent 24803 cda0810fda3ea47915529284891ac7197c0c11af
push id216
push userbmo:geoff@darktrojan.net
push dateFri, 15 Jun 2018 22:09:25 +0000
bugs1468667
Bug 1468667 - [webext] Add missing pieces to allow WebExtensions to run without error This is largely copied from browser/components/extensions/ and modified where appropriate. MozReview-Commit-ID: 47mw7ILAwjz
mail/components/extensions/extensions-messenger.manifest
mail/components/extensions/jar.mn
mail/components/extensions/moz.build
mail/components/extensions/parent/ext-messenger.js
mail/components/moz.build
mail/installer/package-manifest.in
new file mode 100644
--- /dev/null
+++ b/mail/components/extensions/extensions-messenger.manifest
@@ -0,0 +1,1 @@
+category webextension-scripts messenger chrome://messenger/content/parent/ext-messenger.js
new file mode 100644
--- /dev/null
+++ b/mail/components/extensions/jar.mn
@@ -0,0 +1,6 @@
+# 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/.
+
+messenger.jar:
+    content/messenger/parent/ext-messenger.js (parent/ext-messenger.js)
new file mode 100644
--- /dev/null
+++ b/mail/components/extensions/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+JAR_MANIFESTS += ['jar.mn']
+
+EXTRA_COMPONENTS += [
+    'extensions-messenger.manifest',
+]
new file mode 100644
--- /dev/null
+++ b/mail/components/extensions/parent/ext-messenger.js
@@ -0,0 +1,211 @@
+/* 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/. */
+
+let tabTracker;
+let windowTracker;
+
+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) {
+    window.openContentTab(extension.manifest.options_ui.page);
+    return Promise.resolve();
+  }
+
+  let viewId = `addons://detail/${encodeURIComponent(extension.id)}/preferences`;
+
+  window.openAddonsMgr(viewId);
+  return Promise.resolve();
+};
+
+global.makeWidgetId = id => {
+  id = id.toLowerCase();
+  // FIXME: This allows for collisions.
+  return id.replace(/[^a-z0-9_-]/g, "_");
+};
+
+class WindowTracker extends WindowTrackerBase {
+  /* Override WindowTrackerBase, which looks for navigator:browser windows instead */
+  get topWindow() {
+    return Services.wm.getMostRecentWindow("mail:3pane");
+  }
+
+  isBrowserWindow(window) {
+    let {documentElement} = window.document;
+
+    return documentElement.getAttribute("windowtype") === "mail:3pane";
+  }
+}
+
+windowTracker = new WindowTracker();
+tabTracker = {
+  getBrowserData: function(target) {
+    return {};
+  }
+};
+
+Object.assign(global, {tabTracker, windowTracker});
+
+class Window extends WindowBase {
+  /**
+   * Update the geometry of the browser window.
+   *
+   * @param {Object} options
+   *        An object containing new values for the window's geometry.
+   * @param {integer} [options.left]
+   *        The new pixel distance of the left side of the browser window from
+   *        the left of the screen.
+   * @param {integer} [options.top]
+   *        The new pixel distance of the top side of the browser window from
+   *        the top of the screen.
+   * @param {integer} [options.width]
+   *        The new pixel width of the window.
+   * @param {integer} [options.height]
+   *        The new pixel height of the window.
+   */
+  updateGeometry(options) {
+    let {window} = this;
+
+    if (options.left !== null || options.top !== null) {
+      let left = options.left !== null ? options.left : window.screenX;
+      let top = options.top !== null ? options.top : window.screenY;
+      window.moveTo(left, top);
+    }
+
+    if (options.width !== null || options.height !== null) {
+      let width = options.width !== null ? options.width : window.outerWidth;
+      let height = options.height !== null ? options.height : window.outerHeight;
+      window.resizeTo(width, height);
+    }
+  }
+
+  get _title() {
+    return this.window.document.title;
+  }
+
+  setTitlePreface(titlePreface) {
+    this.window.document.documentElement.setAttribute("titlepreface", titlePreface);
+  }
+
+  get focused() {
+    return this.window.document.hasFocus();
+  }
+
+  get top() {
+    return this.window.screenY;
+  }
+
+  get left() {
+    return this.window.screenX;
+  }
+
+  get width() {
+    return this.window.outerWidth;
+  }
+
+  get height() {
+    return this.window.outerHeight;
+  }
+
+  get incognito() {
+    return PrivateBrowsingUtils.isWindowPrivate(this.window);
+  }
+
+  get alwaysOnTop() {
+    return this.xulWindow.zLevel >= Ci.nsIXULWindow.raisedZ;
+  }
+
+  get isLastFocused() {
+    return this.window === windowTracker.topWindow;
+  }
+
+  static getState(window) {
+    const STATES = {
+      [window.STATE_MAXIMIZED]: "maximized",
+      [window.STATE_MINIMIZED]: "minimized",
+      [window.STATE_NORMAL]: "normal",
+    };
+    let state = STATES[window.windowState];
+    if (window.fullScreen) {
+      state = "fullscreen";
+    }
+    return state;
+  }
+
+  get state() {
+    return Window.getState(this.window);
+  }
+
+  set state(state) {
+    let {window} = this;
+    if (state !== "fullscreen" && window.fullScreen) {
+      window.fullScreen = false;
+    }
+
+    switch (state) {
+      case "maximized":
+        window.maximize();
+        break;
+
+      case "minimized":
+      case "docked":
+        window.minimize();
+        break;
+
+      case "normal":
+        // Restore sometimes returns the window to its previous state, rather
+        // than to the "normal" state, so it may need to be called anywhere from
+        // zero to two times.
+        window.restore();
+        if (window.windowState !== window.STATE_NORMAL) {
+          window.restore();
+        }
+        if (window.windowState !== window.STATE_NORMAL) {
+          // And on OS-X, where normal vs. maximized is basically a heuristic,
+          // we need to cheat.
+          window.sizeToContent();
+        }
+        break;
+
+      case "fullscreen":
+        window.fullScreen = true;
+        break;
+
+      default:
+        throw new Error(`Unexpected window state: ${state}`);
+    }
+  }
+
+  get activeTab() {
+    return null;
+  }
+}
+
+Object.assign(global, {Window});
+
+class WindowManager extends WindowManagerBase {
+  get(windowId, context) {
+    let window = windowTracker.getWindow(windowId, context);
+
+    return this.getWrapper(window);
+  }
+
+  * getAll() {
+    for (let window of windowTracker.browserWindows()) {
+      yield this.getWrapper(window);
+    }
+  }
+
+  wrapWindow(window) {
+    return new Window(this.extension, window, windowTracker.getId(window));
+  }
+}
+
+extensions.on("startup", (type, extension) => { // eslint-disable-line mozilla/balanced-listeners
+  defineLazyGetter(extension, "windowManager",
+                   () => new WindowManager(extension));
+});
--- a/mail/components/moz.build
+++ b/mail/components/moz.build
@@ -15,16 +15,17 @@ DIRS += [
     'migration',
     'activity',
     'search',
     'about-support',
     'accountcreation',
     'wintaskbar',
     'newmailaccount',
     'im',
+    'extensions',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk3', 'cocoa'):
     DIRS += ['shell']
 
 TEST_DIRS += ['test']
 
 DIRS += ['build']
--- a/mail/installer/package-manifest.in
+++ b/mail/installer/package-manifest.in
@@ -570,16 +570,17 @@
 @RESPATH@/contentaccessible/*
 
 ; svg
 @RESPATH@/res/svg.css
 
 ; [Extensions]
 @RESPATH@/components/extensions-toolkit.manifest
 @RESPATH@/components/extension-process-script.js
+@RESPATH@/components/extensions-messenger.manifest
 
 ; [Personal Security Manager]
 ;
 ; NSS libraries are signed in the staging directory,
 ; meaning their .chk files are created there directly.
 ;
 #ifndef MOZ_SYSTEM_NSS
 #if defined(XP_LINUX) && !defined(ANDROID)