Bug 1350646: Part 8 - Remove SDK page-mod modules. r?Mossop draft
authorKris Maglione <maglione.k@gmail.com>
Sat, 05 Aug 2017 22:45:33 -0700
changeset 641188 311c10a72593c99a64dfe750bc40496a5889e0b7
parent 641187 af9341bbfd7c6c882dced65b1c02ade99a8159d1
child 641189 94d0e896615122c79610be399c5cc05a4d47b339
push id72469
push usermaglione.k@gmail.com
push dateSun, 06 Aug 2017 07:23:41 +0000
reviewersMossop
bugs1350646
milestone57.0a1
Bug 1350646: Part 8 - Remove SDK page-mod modules. r?Mossop MozReview-Commit-ID: C3JrCITSjj6
addon-sdk/moz.build
addon-sdk/source/lib/sdk/content/page-mod.js
addon-sdk/source/lib/sdk/page-mod.js
addon-sdk/source/lib/sdk/page-mod/match-pattern.js
addon-sdk/source/lib/sdk/util/match-pattern.js
addon-sdk/source/lib/sdk/util/rules.js
--- a/addon-sdk/moz.build
+++ b/addon-sdk/moz.build
@@ -41,17 +41,16 @@ modules = [
     'sdk/console/plain-text.js',
     'sdk/console/traceback.js',
     'sdk/content/content-worker.js',
     'sdk/content/content.js',
     'sdk/content/events.js',
     'sdk/content/l10n-html.js',
     'sdk/content/loader.js',
     'sdk/content/mod.js',
-    'sdk/content/page-mod.js',
     'sdk/content/sandbox.js',
     'sdk/content/sandbox/events.js',
     'sdk/content/tab-events.js',
     'sdk/content/thumbnail.js',
     'sdk/content/utils.js',
     'sdk/content/worker-child.js',
     'sdk/content/worker.js',
     'sdk/core/disposable.js',
@@ -99,18 +98,16 @@ modules = [
     'sdk/lang/weak-set.js',
     'sdk/loader/sandbox.js',
     'sdk/messaging.js',
     'sdk/model/core.js',
     'sdk/net/url.js',
     'sdk/net/xhr.js',
     'sdk/notifications.js',
     'sdk/output/system.js',
-    'sdk/page-mod.js',
-    'sdk/page-mod/match-pattern.js',
     'sdk/passwords.js',
     'sdk/passwords/utils.js',
     'sdk/platform/xpcom.js',
     'sdk/preferences/event-target.js',
     'sdk/preferences/native-options.js',
     'sdk/preferences/service.js',
     'sdk/preferences/utils.js',
     'sdk/private-browsing.js',
@@ -164,19 +161,17 @@ modules = [
     'sdk/url.js',
     'sdk/url/utils.js',
     'sdk/util/array.js',
     'sdk/util/collection.js',
     'sdk/util/contract.js',
     'sdk/util/deprecate.js',
     'sdk/util/dispatcher.js',
     'sdk/util/list.js',
-    'sdk/util/match-pattern.js',
     'sdk/util/object.js',
-    'sdk/util/rules.js',
     'sdk/util/sequence.js',
     'sdk/util/uuid.js',
     'sdk/view/core.js',
     'sdk/window/browser.js',
     'sdk/window/events.js',
     'sdk/window/helpers.js',
     'sdk/window/namespace.js',
     'sdk/window/utils.js',
deleted file mode 100644
--- a/addon-sdk/source/lib/sdk/content/page-mod.js
+++ /dev/null
@@ -1,230 +0,0 @@
-/* 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";
-
-module.metadata = {
-  "stability": "stable"
-};
-
-lazyRequire(this, '../content/utils', 'getAttachEventType');
-const { Class } = require('../core/heritage');
-const { Disposable } = require('../core/disposable');
-lazyRequire(this, './worker-child', 'WorkerChild');
-const { EventTarget } = require('../event/target');
-const { on, emit, once, setListeners } = require('../event/core');
-lazyRequire(this, '../dom/events',{'on': 'domOn', 'removeListener': 'domOff'});
-lazyRequire(this, '../util/object', "merge");
-lazyRequire(this, '../window/utils', "getFrames");
-lazyRequire(this, '../private-browsing/utils', "ignoreWindow");
-lazyRequire(this, '../stylesheet/style', 'Style');
-lazyRequire(this, '../content/mod', 'attach', 'detach');
-lazyRequire(this, '../util/rules', 'Rules');
-lazyRequire(this, '../util/uuid', 'uuid');
-const { frames, process } = require('../remote/child');
-
-const pagemods = new Map();
-const styles = new WeakMap();
-var styleFor = (mod) => styles.get(mod);
-
-// Helper functions
-var modMatchesURI = (mod, uri) => mod.include.matchesAny(uri) && !mod.exclude.matchesAny(uri);
-
-/**
- * PageMod constructor (exported below).
- * @constructor
- */
-const ChildPageMod = Class({
-  implements: [
-    EventTarget,
-    Disposable,
-  ],
-  setup: function PageMod(model) {
-    merge(this, model);
-
-    // Set listeners on {PageMod} itself, not the underlying worker,
-    // like `onMessage`, as it'll get piped.
-    setListeners(this, model);
-
-    function* deserializeRules(rules) {
-      for (let rule of rules) {
-        yield rule.type == "string" ? rule.value
-                                    : new RegExp(rule.pattern, rule.flags);
-      }
-    }
-
-    let include = [...deserializeRules(this.include)];
-    this.include = Rules();
-    this.include.add.apply(this.include, include);
-
-    let exclude = [...deserializeRules(this.exclude)];
-    this.exclude = Rules();
-    this.exclude.add.apply(this.exclude, exclude);
-
-    if (this.contentStyle || this.contentStyleFile) {
-      styles.set(this, Style({
-        uri: this.contentStyleFile,
-        source: this.contentStyle
-      }));
-    }
-
-    pagemods.set(this.id, this);
-    this.seenDocuments = new WeakMap();
-
-    // `applyOnExistingDocuments` has to be called after `pagemods.add()`
-    // otherwise its calls to `onContent` method won't do anything.
-    if (this.attachTo.includes('existing'))
-      applyOnExistingDocuments(this);
-  },
-
-  dispose: function() {
-    let style = styleFor(this);
-    if (style)
-      detach(style);
-
-    for (let i in this.include)
-      this.include.remove(this.include[i]);
-
-    pagemods.delete(this.id);
-  }
-});
-
-function onContentWindow({ target: document }) {
-  // Return if we have no pagemods
-  if (pagemods.size === 0)
-    return;
-
-  let window = document.defaultView;
-  // XML documents don't have windows, and we don't yet support them.
-  if (!window)
-    return;
-
-  // Frame event listeners are bound to the frame the event came from by default
-  let frame = this;
-  // We apply only on documents in tabs of Firefox
-  if (!frame.isTab)
-    return;
-
-  // When the tab is private, only addons with 'private-browsing' flag in
-  // their package.json can apply content script to private documents
-  if (ignoreWindow(window))
-    return;
-
-  for (let pagemod of pagemods.values()) {
-    if (modMatchesURI(pagemod, window.location.href))
-      onContent(pagemod, window);
-  }
-}
-frames.addEventListener("DOMDocElementInserted", onContentWindow, true);
-
-function applyOnExistingDocuments (mod) {
-  for (let frame of frames) {
-    // Fake a newly created document
-    let window = frame.content;
-    // on startup with e10s, contentWindow might not exist yet,
-    // in which case we will get notified by "document-element-inserted".
-    if (!window || !window.frames)
-      return;
-    let uri = window.location.href;
-    if (mod.attachTo.includes("top") && modMatchesURI(mod, uri))
-      onContent(mod, window);
-    if (mod.attachTo.includes("frame"))
-      getFrames(window).
-        filter(iframe => modMatchesURI(mod, iframe.location.href)).
-        forEach(frame => onContent(mod, frame));
-  }
-}
-
-function createWorker(mod, window) {
-  let workerId = String(uuid());
-
-  // Instruct the parent to connect to this worker. Do this first so the parent
-  // side is connected before the worker attempts to send any messages there
-  let frame = frames.getFrameForWindow(window.top);
-  frame.port.emit('sdk/page-mod/worker-create', mod.id, {
-    id: workerId,
-    url: window.location.href
-  });
-
-  // Create a child worker and notify the parent
-  let worker = WorkerChild({
-    id: workerId,
-    window: window,
-    contentScript: mod.contentScript,
-    contentScriptFile: mod.contentScriptFile,
-    contentScriptOptions: mod.contentScriptOptions
-  });
-
-  once(worker, 'detach', () => worker.destroy());
-}
-
-function onContent (mod, window) {
-  let isTopDocument = window.top === window;
-  // Is a top level document and `top` is not set, ignore
-  if (isTopDocument && !mod.attachTo.includes("top"))
-    return;
-  // Is a frame document and `frame` is not set, ignore
-  if (!isTopDocument && !mod.attachTo.includes("frame"))
-    return;
-
-  // ensure we attach only once per document
-  let seen = mod.seenDocuments;
-  if (seen.has(window.document))
-    return;
-  seen.set(window.document, true);
-
-  let style = styleFor(mod);
-  if (style)
-    attach(style, window);
-
-  // Immediately evaluate content script if the document state is already
-  // matching contentScriptWhen expectations
-  if (isMatchingAttachState(mod, window)) {
-    createWorker(mod, window);
-    return;
-  }
-
-  let eventName = getAttachEventType(mod) || 'load';
-  domOn(window, eventName, function onReady (e) {
-    if (e.target.defaultView !== window)
-      return;
-    domOff(window, eventName, onReady, true);
-    createWorker(mod, window);
-
-    // Attaching is asynchronous so if the document is already loaded we will
-    // miss the pageshow event so send a synthetic one.
-    if (window.document.readyState == "complete") {
-      mod.on('attach', worker => {
-        try {
-          worker.send('pageshow');
-          emit(worker, 'pageshow');
-        }
-        catch (e) {
-          // This can fail if an earlier attach listener destroyed the worker
-        }
-      });
-    }
-  }, true);
-}
-
-function isMatchingAttachState (mod, window) {
-  let state = window.document.readyState;
-  return 'start' === mod.contentScriptWhen ||
-      // Is `load` event already dispatched?
-      'complete' === state ||
-      // Is DOMContentLoaded already dispatched and waiting for it?
-      ('ready' === mod.contentScriptWhen && state === 'interactive')
-}
-
-process.port.on('sdk/page-mod/create', (process, model) => {
-  if (pagemods.has(model.id))
-    return;
-
-  new ChildPageMod(model);
-});
-
-process.port.on('sdk/page-mod/destroy', (process, id) => {
-  let mod = pagemods.get(id);
-  if (mod)
-    mod.destroy();
-});
deleted file mode 100644
--- a/addon-sdk/source/lib/sdk/page-mod.js
+++ /dev/null
@@ -1,190 +0,0 @@
-/* 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";
-
-module.metadata = {
-  "stability": "stable"
-};
-
-const { contract: loaderContract } = require('./content/loader');
-const { contract } = require('./util/contract');
-const { WorkerHost, connect } = require('./content/utils');
-const { Class } = require('./core/heritage');
-const { Disposable } = require('./core/disposable');
-lazyRequire(this, './content/worker', "Worker");
-const { EventTarget } = require('./event/target');
-lazyRequire(this, './event/core', "on", "emit", "once", "setListeners");
-lazyRequire(this, './lang/type', "isRegExp", "isUndefined");
-const { merge, omit } = require('./util/object');
-lazyRequire(this, "./util/array", "remove", "has", "hasAny");
-lazyRequire(this, "./util/rules", "Rules");
-const { processes, frames, remoteRequire } = require('./remote/parent');
-remoteRequire('sdk/content/page-mod');
-
-const pagemods = new Map();
-const workers = new Map();
-const models = new WeakMap();
-var modelFor = (mod) => models.get(mod);
-var workerFor = (mod) => workers.get(mod)[0];
-
-// Helper functions
-var isRegExpOrString = (v) => isRegExp(v) || typeof v === 'string';
-
-var PAGEMOD_ID = 0;
-
-// Validation Contracts
-const modOptions = {
-  // contentStyle* / contentScript* are sharing the same validation constraints,
-  // so they can be mostly reused, except for the messages.
-  contentStyle: merge(Object.create(loaderContract.rules.contentScript), {
-    msg: 'The `contentStyle` option must be a string or an array of strings.'
-  }),
-  contentStyleFile: merge(Object.create(loaderContract.rules.contentScriptFile), {
-    msg: 'The `contentStyleFile` option must be a local URL or an array of URLs'
-  }),
-  include: {
-    is: ['string', 'array', 'regexp'],
-    ok: (rule) => {
-      if (isRegExpOrString(rule))
-        return true;
-      if (Array.isArray(rule) && rule.length > 0)
-        return rule.every(isRegExpOrString);
-      return false;
-    },
-    msg: 'The `include` option must always contain atleast one rule as a string, regular expression, or an array of strings and regular expressions.'
-  },
-  exclude: {
-    is: ['string', 'array', 'regexp', 'undefined'],
-    ok: (rule) => {
-      if (isRegExpOrString(rule) || isUndefined(rule))
-        return true;
-      if (Array.isArray(rule) && rule.length > 0)
-        return rule.every(isRegExpOrString);
-      return false;
-    },
-    msg: 'If set, the `exclude` option must always contain at least one ' +
-      'rule as a string, regular expression, or an array of strings and ' +
-      'regular expressions.'
-  },
-  attachTo: {
-    is: ['string', 'array', 'undefined'],
-    map: function (attachTo) {
-      if (!attachTo) return ['top', 'frame'];
-      if (typeof attachTo === 'string') return [attachTo];
-      return attachTo;
-    },
-    ok: function (attachTo) {
-      return hasAny(attachTo, ['top', 'frame']) &&
-        attachTo.every(has.bind(null, ['top', 'frame', 'existing']));
-    },
-    msg: 'The `attachTo` option must be a string or an array of strings. ' +
-      'The only valid options are "existing", "top" and "frame", and must ' +
-      'contain at least "top" or "frame" values.'
-  },
-};
-
-const modContract = contract(merge({}, loaderContract.rules, modOptions));
-
-/**
- * PageMod constructor (exported below).
- * @constructor
- */
-const PageMod = Class({
-  implements: [
-    modContract.properties(modelFor),
-    EventTarget,
-    Disposable,
-  ],
-  extends: WorkerHost(workerFor),
-  setup: function PageMod(options) {
-    let mod = this;
-    let model = modContract(options);
-    models.set(this, model);
-    model.id = PAGEMOD_ID++;
-
-    let include = model.include;
-    model.include = Rules();
-    model.include.add.apply(model.include, [].concat(include));
-
-    let exclude = isUndefined(model.exclude) ? [] : model.exclude;
-    model.exclude = Rules();
-    model.exclude.add.apply(model.exclude, [].concat(exclude));
-
-    // Set listeners on {PageMod} itself, not the underlying worker,
-    // like `onMessage`, as it'll get piped.
-    setListeners(this, options);
-
-    pagemods.set(model.id, this);
-    workers.set(this, []);
-
-    function* serializeRules(rules) {
-      for (let rule of rules) {
-        yield isRegExp(rule) ? { type: "regexp", pattern: rule.source, flags: rule.flags }
-                             : { type: "string", value: rule };
-      }
-    }
-
-    model.childOptions = omit(model, ["include", "exclude", "contentScriptOptions"]);
-    model.childOptions.include = [...serializeRules(model.include)];
-    model.childOptions.exclude = [...serializeRules(model.exclude)];
-    model.childOptions.contentScriptOptions = model.contentScriptOptions ?
-                                              JSON.stringify(model.contentScriptOptions) :
-                                              null;
-
-    processes.port.emit('sdk/page-mod/create', model.childOptions);
-  },
-
-  dispose: function(reason) {
-    processes.port.emit('sdk/page-mod/destroy', modelFor(this).id);
-    pagemods.delete(modelFor(this).id);
-    workers.delete(this);
-  },
-
-  destroy: function(reason) {
-    // Explicit destroy call, i.e. not via unload so destroy the workers
-    let list = workers.get(this);
-    if (!list)
-      return;
-
-    // Triggers dispose which will cause the child page-mod to be destroyed
-    Disposable.prototype.destroy.call(this, reason);
-
-    // Destroy any active workers
-    for (let worker of list)
-      worker.destroy(reason);
-  }
-});
-exports.PageMod = PageMod;
-
-// Whenever a new process starts send over the list of page-mods
-processes.forEvery(process => {
-  for (let mod of pagemods.values())
-    process.port.emit('sdk/page-mod/create', modelFor(mod).childOptions);
-});
-
-frames.port.on('sdk/page-mod/worker-create', (frame, modId, workerOptions) => {
-  let mod = pagemods.get(modId);
-  if (!mod)
-    return;
-
-  // Attach the parent side of the worker to the child
-  let worker = Worker();
-
-  workers.get(mod).unshift(worker);
-  worker.on('*', (event, ...args) => {
-    // page-mod's "attach" event needs to be passed a worker
-    if (event === 'attach')
-      emit(mod, event, worker)
-    else
-      emit(mod, event, ...args);
-  });
-
-  worker.on('detach', () => {
-    let array = workers.get(mod);
-    if (array)
-      remove(array, worker);
-  });
-
-  connect(worker, frame, workerOptions);
-});
deleted file mode 100644
--- a/addon-sdk/source/lib/sdk/page-mod/match-pattern.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/* 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";
-
-var { deprecateUsage } = require("../util/deprecate");
-
-deprecateUsage("Module 'sdk/page-mod/match-pattern' is deprecated use 'sdk/util/match-pattern' instead");
-
-module.exports = require("../util/match-pattern");
deleted file mode 100644
--- a/addon-sdk/source/lib/sdk/util/match-pattern.js
+++ /dev/null
@@ -1,113 +0,0 @@
-/* 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";
-
-module.metadata = {
-  "stability": "unstable"
-};
-
-lazyRequire(this, '../url', "URL");
-const cache = {};
-
-function MatchPattern(pattern) {
-  if (cache[pattern]) return cache[pattern];
-
-  if (typeof pattern.test == "function") {
-    // For compatibility with -moz-document rules, we require the RegExp's
-    // global, ignoreCase, and multiline flags to be set to false.
-    if (pattern.global) {
-      throw new Error("A RegExp match pattern cannot be set to `global` " +
-                      "(i.e. //g).");
-    }
-    if (pattern.multiline) {
-      throw new Error("A RegExp match pattern cannot be set to `multiline` " +
-                      "(i.e. //m).");
-    }
-
-    this.regexp = pattern;
-  }
-  else {
-    let firstWildcardPosition = pattern.indexOf("*");
-    let lastWildcardPosition = pattern.lastIndexOf("*");
-    if (firstWildcardPosition != lastWildcardPosition)
-      throw new Error("There can be at most one '*' character in a wildcard.");
-
-    if (firstWildcardPosition == 0) {
-      if (pattern.length == 1)
-        this.anyWebPage = true;
-      else if (pattern[1] != ".")
-        throw new Error("Expected a *.<domain name> string, got: " + pattern);
-      else
-        this.domain = pattern.substr(2);
-    }
-    else {
-      if (pattern.indexOf(":") == -1) {
-        throw new Error("When not using *.example.org wildcard, the string " +
-                        "supplied is expected to be either an exact URL to " +
-                        "match or a URL prefix. The provided string ('" +
-                        pattern + "') is unlikely to match any pages.");
-      }
-
-      if (firstWildcardPosition == -1)
-        this.exactURL = pattern;
-      else if (firstWildcardPosition == pattern.length - 1)
-        this.urlPrefix = pattern.substr(0, pattern.length - 1);
-      else {
-        throw new Error("The provided wildcard ('" + pattern + "') has a '*' " +
-                        "in an unexpected position. It is expected to be the " +
-                        "first or the last character in the wildcard.");
-      }
-    }
-  }
-
-  cache[pattern] = this;
-}
-
-MatchPattern.prototype = {
-  test: function MatchPattern_test(urlStr) {
-    try {
-      var url = URL(urlStr);
-    }
-    catch (err) {
-      return false;
-    }
-
-    // Test the URL against a RegExp pattern.  For compatibility with
-    // -moz-document rules, we require the RegExp to match the entire URL,
-    // so we not only test for a match, we also make sure the matched string
-    // is the entire URL string.
-    //
-    // Assuming most URLs don't match most match patterns, we call `test` for
-    // speed when determining whether or not the URL matches, then call `exec`
-    // for the small subset that match to make sure the entire URL matches.
-    if (this.regexp && this.regexp.test(urlStr) &&
-        this.regexp.exec(urlStr)[0] == urlStr)
-      return true;
-
-    if (this.anyWebPage && /^(https?|ftp)$/.test(url.scheme))
-      return true;
-
-    if (this.exactURL && this.exactURL == urlStr)
-      return true;
-
-    // Tests the urlStr against domain and check if
-    // wildcard submitted (*.domain.com), it only allows
-    // subdomains (sub.domain.com) or from the root (http://domain.com)
-    // and reject non-matching domains (otherdomain.com)
-    // bug 856913
-    if (this.domain && url.host &&
-         (url.host === this.domain ||
-          url.host.slice(-this.domain.length - 1) === "." + this.domain))
-      return true;
-
-    if (this.urlPrefix && 0 == urlStr.indexOf(this.urlPrefix))
-      return true;
-
-    return false;
-  },
-
-  toString: () => '[object MatchPattern]'
-};
-
-exports.MatchPattern = MatchPattern;
deleted file mode 100644
--- a/addon-sdk/source/lib/sdk/util/rules.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/* 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";
-
-module.metadata = {
-  "stability": "unstable"
-};
-
-const { Class } = require('../core/heritage');
-lazyRequire(this, './match-pattern', "MatchPattern");
-lazyRequire(this, '../event/core', "emit");
-const { EventTarget } = require('../event/target');
-const { List, addListItem, removeListItem } = require('./list');
-
-// Should deprecate usage of EventEmitter/compose
-const Rules = Class({
-  implements: [
-    EventTarget,
-    List
-  ],
-  add: function(...rules) {
-    return [].concat(rules).forEach(function onAdd(rule) {
-      addListItem(this, rule);
-      emit(this, 'add', rule);
-    }, this);
-  },
-  remove: function(...rules) {
-    return [].concat(rules).forEach(function onRemove(rule) {
-      removeListItem(this, rule);
-      emit(this, 'remove', rule);
-    }, this);
-  },
-  get: function(rule) {
-    let found = false;
-    for (let i in this) if (this[i] === rule) found = true;
-    return found;
-  },
-  // Returns true if uri matches atleast one stored rule
-  matchesAny: function(uri) {
-    return !!filterMatches(this, uri).length;
-  },
-  toString: () => '[object Rules]'
-});
-exports.Rules = Rules;
-
-function filterMatches(instance, uri) {
-  let matches = [];
-  for (let i in instance) {
-    if (new MatchPattern(instance[i]).test(uri)) matches.push(instance[i]);
-  }
-  return matches;
-}