diff options
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.js | 380 |
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; -} |