aboutsummaryrefslogtreecommitdiff
path: root/tools/addon-sdk-1.12/lib/sdk/page-mod.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/addon-sdk-1.12/lib/sdk/page-mod.js')
-rw-r--r--tools/addon-sdk-1.12/lib/sdk/page-mod.js380
1 files changed, 0 insertions, 380 deletions
diff --git a/tools/addon-sdk-1.12/lib/sdk/page-mod.js b/tools/addon-sdk-1.12/lib/sdk/page-mod.js
deleted file mode 100644
index 8bd4e5f..0000000
--- a/tools/addon-sdk-1.12/lib/sdk/page-mod.js
+++ /dev/null
@@ -1,380 +0,0 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
-/* 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 observers = require('./deprecated/observer-service');
-const { Loader, validationAttributes } = require('./content/loader');
-const { Worker } = require('./content/worker');
-const { EventEmitter } = require('./deprecated/events');
-const { List } = require('./deprecated/list');
-const { Registry } = require('./util/registry');
-const { MatchPattern } = require('./page-mod/match-pattern');
-const { validateOptions : validate } = require('./deprecated/api-utils');
-const { Cc, Ci } = require('chrome');
-const { merge } = require('./util/object');
-const { readURISync } = require('./net/url');
-const { windowIterator } = require('./deprecated/window-utils');
-const { isBrowser, getFrames } = require('./window/utils');
-const { getTabs, getTabContentWindow, getTabForContentWindow,
- getURI: getTabURI } = require('./tabs/utils');
-const { has, hasAny } = require('./util/array');
-
-const styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"].
- getService(Ci.nsIStyleSheetService);
-
-const USER_SHEET = styleSheetService.USER_SHEET;
-
-const io = Cc['@mozilla.org/network/io-service;1'].
- getService(Ci.nsIIOService);
-
-// Valid values for `attachTo` option
-const VALID_ATTACHTO_OPTIONS = ['existing', 'top', 'frame'];
-
-// contentStyle* / contentScript* are sharing the same validation constraints,
-// so they can be mostly reused, except for the messages.
-const validStyleOptions = {
- contentStyle: merge(Object.create(validationAttributes.contentScript), {
- msg: 'The `contentStyle` option must be a string or an array of strings.'
- }),
- contentStyleFile: merge(Object.create(validationAttributes.contentScriptFile), {
- msg: 'The `contentStyleFile` option must be a local URL or an array of URLs'
- })
-};
-
-// rules registry
-const RULES = {};
-
-const Rules = EventEmitter.resolve({ toString: null }).compose(List, {
- add: function() Array.slice(arguments).forEach(function onAdd(rule) {
- if (this._has(rule))
- return;
- // registering rule to the rules registry
- if (!(rule in RULES))
- RULES[rule] = new MatchPattern(rule);
- this._add(rule);
- this._emit('add', rule);
- }.bind(this)),
- remove: function() Array.slice(arguments).forEach(function onRemove(rule) {
- if (!this._has(rule))
- return;
- this._remove(rule);
- this._emit('remove', rule);
- }.bind(this)),
-});
-
-/**
- * PageMod constructor (exported below).
- * @constructor
- */
-const PageMod = Loader.compose(EventEmitter, {
- on: EventEmitter.required,
- _listeners: EventEmitter.required,
- attachTo: [],
- contentScript: Loader.required,
- contentScriptFile: Loader.required,
- contentScriptWhen: Loader.required,
- contentScriptOptions: Loader.required,
- include: null,
- constructor: function PageMod(options) {
- this._onContent = this._onContent.bind(this);
- options = options || {};
-
- let { contentStyle, contentStyleFile } = validate(options, validStyleOptions);
-
- if ('contentScript' in options)
- this.contentScript = options.contentScript;
- if ('contentScriptFile' in options)
- this.contentScriptFile = options.contentScriptFile;
- if ('contentScriptOptions' in options)
- this.contentScriptOptions = options.contentScriptOptions;
- if ('contentScriptWhen' in options)
- this.contentScriptWhen = options.contentScriptWhen;
- if ('onAttach' in options)
- this.on('attach', options.onAttach);
- if ('onError' in options)
- this.on('error', options.onError);
- if ('attachTo' in options) {
- if (typeof options.attachTo == 'string')
- this.attachTo = [options.attachTo];
- else if (Array.isArray(options.attachTo))
- this.attachTo = options.attachTo;
- else
- throw new Error('The `attachTo` option must be a string or an array ' +
- 'of strings.');
-
- let isValidAttachToItem = function isValidAttachToItem(item) {
- return typeof item === 'string' &&
- VALID_ATTACHTO_OPTIONS.indexOf(item) !== -1;
- }
- if (!this.attachTo.every(isValidAttachToItem))
- throw new Error('The `attachTo` option valid accept only following ' +
- 'values: '+ VALID_ATTACHTO_OPTIONS.join(', '));
- if (!hasAny(this.attachTo, ["top", "frame"]))
- throw new Error('The `attachTo` option must always contain at least' +
- ' `top` or `frame` value');
- }
- else {
- this.attachTo = ["top", "frame"];
- }
-
- let include = options.include;
- let rules = this.include = Rules();
- rules.on('add', this._onRuleAdd = this._onRuleAdd.bind(this));
- rules.on('remove', this._onRuleRemove = this._onRuleRemove.bind(this));
-
- if (Array.isArray(include))
- rules.add.apply(null, include);
- else
- rules.add(include);
-
- let styleRules = "";
-
- if (contentStyleFile)
- styleRules = [].concat(contentStyleFile).map(readURISync).join("");
-
- if (contentStyle)
- styleRules += [].concat(contentStyle).join("");
-
- if (styleRules) {
- this._onRuleUpdate = this._onRuleUpdate.bind(this);
-
- this._styleRules = styleRules;
-
- this._registerStyleSheet();
- rules.on('add', this._onRuleUpdate);
- rules.on('remove', this._onRuleUpdate);
- }
-
- this.on('error', this._onUncaughtError = this._onUncaughtError.bind(this));
- pageModManager.add(this._public);
-
- this._loadingWindows = [];
-
- // `_applyOnExistingDocuments` has to be called after `pageModManager.add()`
- // otherwise its calls to `_onContent` method won't do anything.
- if ('attachTo' in options && has(options.attachTo, 'existing'))
- this._applyOnExistingDocuments();
- },
-
- destroy: function destroy() {
-
- this._unregisterStyleSheet();
-
- this.include.removeListener('add', this._onRuleUpdate);
- this.include.removeListener('remove', this._onRuleUpdate);
-
- for each (let rule in this.include)
- this.include.remove(rule);
- pageModManager.remove(this._public);
- this._loadingWindows = [];
-
- },
-
- _loadingWindows: [],
-
- _applyOnExistingDocuments: function _applyOnExistingDocuments() {
- let mod = this;
- // Returns true if the tab match one rule
- function isMatchingURI(uri) {
- // Use Array.some as `include` isn't a native array
- return Array.some(mod.include, function (rule) {
- return RULES[rule].test(uri);
- });
- }
- let tabs = getAllTabs().filter(function (tab) {
- return isMatchingURI(getTabURI(tab));
- });
-
- tabs.forEach(function (tab) {
- // Fake a newly created document
- let window = getTabContentWindow(tab);
- if (has(mod.attachTo, "top"))
- mod._onContent(window);
- if (has(mod.attachTo, "frame"))
- getFrames(window).forEach(mod._onContent);
- });
- },
-
- _onContent: function _onContent(window) {
- // not registered yet
- if (!pageModManager.has(this))
- return;
-
- let isTopDocument = window.top === window;
- // Is a top level document and `top` is not set, ignore
- if (isTopDocument && !has(this.attachTo, "top"))
- return;
- // Is a frame document and `frame` is not set, ignore
- if (!isTopDocument && !has(this.attachTo, "frame"))
- return;
-
- // Immediatly evaluate content script if the document state is already
- // matching contentScriptWhen expectations
- let state = window.document.readyState;
- if ('start' === this.contentScriptWhen ||
- // Is `load` event already dispatched?
- 'complete' === state ||
- // Is DOMContentLoaded already dispatched and waiting for it?
- ('ready' === this.contentScriptWhen && state === 'interactive') ) {
- this._createWorker(window);
- return;
- }
-
- let eventName = 'end' == this.contentScriptWhen ? 'load' : 'DOMContentLoaded';
- let self = this;
- window.addEventListener(eventName, function onReady(event) {
- if (event.target.defaultView != window)
- return;
- window.removeEventListener(eventName, onReady, true);
-
- self._createWorker(window);
- }, true);
- },
- _createWorker: function _createWorker(window) {
- let worker = Worker({
- window: window,
- contentScript: this.contentScript,
- contentScriptFile: this.contentScriptFile,
- contentScriptOptions: this.contentScriptOptions,
- onError: this._onUncaughtError
- });
- this._emit('attach', worker);
- let self = this;
- worker.once('detach', function detach() {
- worker.destroy();
- });
- },
- _onRuleAdd: function _onRuleAdd(url) {
- pageModManager.on(url, this._onContent);
- },
- _onRuleRemove: function _onRuleRemove(url) {
- pageModManager.off(url, this._onContent);
- },
- _onUncaughtError: function _onUncaughtError(e) {
- if (this._listeners('error').length == 1)
- console.exception(e);
- },
- _onRuleUpdate: function _onRuleUpdate(){
- this._registerStyleSheet();
- },
-
- _registerStyleSheet : function _registerStyleSheet() {
- let rules = this.include;
- let styleRules = this._styleRules;
-
- let documentRules = [];
-
- this._unregisterStyleSheet();
-
- for each (let rule in rules) {
- let pattern = RULES[rule];
-
- if (!pattern)
- continue;
-
- if (pattern.regexp)
- documentRules.push("regexp(\"" + pattern.regexp.source + "\")");
- else if (pattern.exactURL)
- documentRules.push("url(" + pattern.exactURL + ")");
- else if (pattern.domain)
- documentRules.push("domain(" + pattern.domain + ")");
- else if (pattern.urlPrefix)
- documentRules.push("url-prefix(" + pattern.urlPrefix + ")");
- else if (pattern.anyWebPage) {
- documentRules.push("regexp(\"^(https?|ftp)://.*?\")");
- break;
- }
- }
-
- let uri = "data:text/css;charset=utf-8,";
- if (documentRules.length > 0)
- uri += encodeURIComponent("@-moz-document " +
- documentRules.join(",") + " {" + styleRules + "}");
- else
- uri += encodeURIComponent(styleRules);
-
- this._registeredStyleURI = io.newURI(uri, null, null);
-
- styleSheetService.loadAndRegisterSheet(
- this._registeredStyleURI,
- USER_SHEET
- );
- },
-
- _unregisterStyleSheet : function () {
- let uri = this._registeredStyleURI;
-
- if (uri && styleSheetService.sheetRegistered(uri, USER_SHEET))
- styleSheetService.unregisterSheet(uri, USER_SHEET);
-
- this._registeredStyleURI = null;
- }
-});
-exports.PageMod = function(options) PageMod(options)
-exports.PageMod.prototype = PageMod.prototype;
-
-const PageModManager = Registry.resolve({
- constructor: '_init',
- _destructor: '_registryDestructor'
-}).compose({
- constructor: function PageModRegistry(constructor) {
- this._init(PageMod);
- observers.add(
- 'document-element-inserted',
- this._onContentWindow = this._onContentWindow.bind(this)
- );
- },
- _destructor: function _destructor() {
- observers.remove('document-element-inserted', this._onContentWindow);
- this._removeAllListeners();
- for (let rule in RULES) {
- delete RULES[rule];
- }
-
- // We need to do some cleaning er PageMods, like unregistering any
- // `contentStyle*`
- this._registry.forEach(function(pageMod) {
- pageMod.destroy();
- });
-
- this._registryDestructor();
- },
- _onContentWindow: function _onContentWindow(document) {
- let window = document.defaultView;
- // XML documents don't have windows, and we don't yet support them.
- if (!window)
- return;
- // We apply only on documents in tabs of Firefox
- if (!getTabForContentWindow(window))
- return;
-
- for (let rule in RULES)
- if (RULES[rule].test(document.URL))
- this._emit(rule, window);
- },
- off: function off(topic, listener) {
- this.removeListener(topic, listener);
- if (!this._listeners(topic).length)
- delete RULES[topic];
- }
-});
-const pageModManager = PageModManager();
-
-// Returns all tabs on all currently opened windows
-function getAllTabs() {
- let tabs = [];
- // Iterate over all chrome windows
- for (let window in windowIterator()) {
- if (!isBrowser(window))
- continue;
- tabs = tabs.concat(getTabs(window));
- }
- return tabs;
-}