Bug 1350646: Part 8 - Remove SDK page-mod modules. r=Mossop
authorKris Maglione <maglione.k@gmail.com>
Sat, 05 Aug 2017 22:45:33 -0700
changeset 373706 ccda4fb13271486b9840c108d0046c3aa9d13598
parent 373705 85d0f12f82e474c11f6e5f9eac6185de73235adf
child 373707 052102e8fdb7052e8f681f9fd3e6c4bf93f7a799
push id93584
push usermaglione.k@gmail.com
push dateThu, 10 Aug 2017 03:41:19 +0000
treeherdermozilla-inbound@24d5dbf3a9dc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMossop
bugs1350646
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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;
-}