diff options
Diffstat (limited to 'tools/addon-sdk-1.7/packages/addon-kit/lib')
19 files changed, 0 insertions, 5008 deletions
diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/addon-page.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/addon-page.js deleted file mode 100644 index eeb5461..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/addon-page.js +++ /dev/null @@ -1,33 +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'; - -const { uriPrefix, name } = require('@packaging'); -const { WindowTracker, isBrowser } = require('api-utils/window-utils'); -const { add, remove } = require('api-utils/array'); -const { getTabs, closeTab } = require('api-utils/tabs/utils'); - -// Note: This is an URL that will be returned by calling -// `require('self').data.url('index.html')` from the add-on modules. -// We could not use this expression as in this module it would have -// returned "addon-kit/data/index.html" instead. -const addonURL = uriPrefix + name + '/data/index.html'; - -WindowTracker({ - onTrack: function onTrack(window) { - if (isBrowser(window)) - add(window.XULBrowserWindow.inContentWhitelist, addonURL); - }, - onUntrack: function onUntrack(window) { - getTabs(window). - filter(function(tab) tab.linkedBrowser.currentURI.spec === addonURL). - forEach(function(tab) { - // Note: `onUntrack` will be called for all windows on add-on unloads, - // so we want to clean them up from these URLs. - remove(window.XULBrowserWindow.inContentWhitelist, addonURL); - closeTab(tab); - }); - } -}); diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/clipboard.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/clipboard.js deleted file mode 100644 index 2feab22..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/clipboard.js +++ /dev/null @@ -1,230 +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"; - -const {Cc,Ci} = require("chrome"); -const errors = require("api-utils/errors"); -const apiUtils = require("api-utils/api-utils"); - -/* -While these data flavors resemble Internet media types, they do -no directly map to them. -*/ -const kAllowableFlavors = [ - "text/unicode", - "text/html" - /* CURRENTLY UNSUPPORTED FLAVORS - "text/plain", - "image/png", - "image/jpg", - "image/gif" - "text/x-moz-text-internal", - "AOLMAIL", - "application/x-moz-file", - "text/x-moz-url", - "text/x-moz-url-data", - "text/x-moz-url-desc", - "text/x-moz-url-priv", - "application/x-moz-nativeimage", - "application/x-moz-nativehtml", - "application/x-moz-file-promise-url", - "application/x-moz-file-promise-dest-filename", - "application/x-moz-file-promise", - "application/x-moz-file-promise-dir" - */ -]; - -/* -Aliases for common flavors. Not all flavors will -get an alias. New aliases must be approved by a -Jetpack API druid. -*/ -const kFlavorMap = [ - { short: "text", long: "text/unicode" }, - { short: "html", long: "text/html" } - // Images are currently unsupported. - //{ short: "image", long: "image/png" }, -]; - -let clipboardService = Cc["@mozilla.org/widget/clipboard;1"]. - getService(Ci.nsIClipboard); - -let clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"]. - getService(Ci.nsIClipboardHelper); - - -exports.set = function(aData, aDataType) { - let options = { - data: aData, - datatype: aDataType || "text" - }; - options = apiUtils.validateOptions(options, { - data: { - is: ["string"] - }, - datatype: { - is: ["string"] - } - }); - - var flavor = fromJetpackFlavor(options.datatype); - - if (!flavor) - throw new Error("Invalid flavor"); - - // Additional checks for using the simple case - if (flavor == "text/unicode") { - clipboardHelper.copyString(options.data); - return true; - } - - // Below are the more complex cases where we actually have to work with a - // nsITransferable object - var xferable = Cc["@mozilla.org/widget/transferable;1"]. - createInstance(Ci.nsITransferable); - if (!xferable) - throw new Error("Couldn't set the clipboard due to an internal error " + - "(couldn't create a Transferable object)."); - - switch (flavor) { - case "text/html": - // add text/html flavor - let (str = Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString)) - { - str.data = options.data; - xferable.addDataFlavor(flavor); - xferable.setTransferData(flavor, str, str.data.length * 2); - } - - // add a text/unicode flavor (html converted to plain text) - let (str = Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString), - converter = Cc["@mozilla.org/feed-textconstruct;1"]. - createInstance(Ci.nsIFeedTextConstruct)) - { - converter.type = "html"; - converter.text = options.data; - str.data = converter.plainText(); - xferable.addDataFlavor("text/unicode"); - xferable.setTransferData("text/unicode", str, str.data.length * 2); - } - break; - // TODO: images! - default: - throw new Error("Unable to handle the flavor " + flavor + "."); - } - - // TODO: Not sure if this will ever actually throw. -zpao - try { - clipboardService.setData( - xferable, - null, - clipboardService.kGlobalClipboard - ); - } catch (e) { - throw new Error("Couldn't set clipboard data due to an internal error: " + e); - } - return true; -}; - - -exports.get = function(aDataType) { - let options = { - datatype: aDataType || "text" - }; - options = apiUtils.validateOptions(options, { - datatype: { - is: ["string"] - } - }); - - var xferable = Cc["@mozilla.org/widget/transferable;1"]. - createInstance(Ci.nsITransferable); - if (!xferable) - throw new Error("Couldn't set the clipboard due to an internal error " + - "(couldn't create a Transferable object)."); - - var flavor = fromJetpackFlavor(options.datatype); - - // Ensure that the user hasn't requested a flavor that we don't support. - if (!flavor) - throw new Error("Getting the clipboard with the flavor '" + flavor + - "' is > not supported."); - - // TODO: Check for matching flavor first? Probably not worth it. - - xferable.addDataFlavor(flavor); - - // Get the data into our transferable. - clipboardService.getData( - xferable, - clipboardService.kGlobalClipboard - ); - - var data = {}; - var dataLen = {}; - try { - xferable.getTransferData(flavor, data, dataLen); - } catch (e) { - // Clipboard doesn't contain data in flavor, return null. - return null; - } - - // There's no data available, return. - if (data.value === null) - return null; - - // TODO: Add flavors here as we support more in kAllowableFlavors. - switch (flavor) { - case "text/unicode": - case "text/html": - data = data.value.QueryInterface(Ci.nsISupportsString).data; - break; - default: - data = null; - } - - return data; -}; - -exports.__defineGetter__("currentFlavors", function() { - // Loop over kAllowableFlavors, calling hasDataMatchingFlavors for each. - // This doesn't seem like the most efficient way, but we can't get - // confirmation for specific flavors any other way. This is supposed to be - // an inexpensive call, so performance shouldn't be impacted (much). - var currentFlavors = []; - for each (var flavor in kAllowableFlavors) { - var matches = clipboardService.hasDataMatchingFlavors( - [flavor], - 1, - clipboardService.kGlobalClipboard - ); - if (matches) - currentFlavors.push(toJetpackFlavor(flavor)); - } - return currentFlavors; -}); - -// SUPPORT FUNCTIONS //////////////////////////////////////////////////////// - -function toJetpackFlavor(aFlavor) { - for each (let flavorMap in kFlavorMap) - if (flavorMap.long == aFlavor) - return flavorMap.short; - // Return null in the case where we don't match - return null; -} - -function fromJetpackFlavor(aJetpackFlavor) { - // TODO: Handle proper flavors better - for each (let flavorMap in kFlavorMap) - if (flavorMap.short == aJetpackFlavor || flavorMap.long == aJetpackFlavor) - return flavorMap.long; - // Return null in the case where we don't match. - return null; -} diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/context-menu.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/context-menu.js deleted file mode 100644 index 75ccbea..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/context-menu.js +++ /dev/null @@ -1,1492 +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"; - -const {Ci} = require("chrome"); - -if (!require("api-utils/xul-app").is("Firefox")) { - throw new Error([ - "The context-menu module currently supports only Firefox. In the future ", - "we would like it to support other applications, however. Please see ", - "https://bugzilla.mozilla.org/show_bug.cgi?id=560716 for more information." - ].join("")); -} - -const apiUtils = require("api-utils/api-utils"); -const collection = require("api-utils/collection"); -const { Worker } = require("api-utils/content"); -const { URL } = require("api-utils/url"); -const { MatchPattern } = require("api-utils/match-pattern"); -const { EventEmitterTrait: EventEmitter } = require("api-utils/events"); -const observerServ = require("api-utils/observer-service"); -const jpSelf = require("self"); -const winUtils = require("api-utils/window-utils"); -const { getInnerId } = require("api-utils/window/utils"); -const { Trait } = require("api-utils/light-traits"); -const { Cortex } = require("api-utils/cortex"); -const timer = require("timer"); - -// All user items we add have this class name. -const ITEM_CLASS = "jetpack-context-menu-item"; - -// Items in the top-level context menu also have this class. -const TOPLEVEL_ITEM_CLASS = "jetpack-context-menu-item-toplevel"; - -// Items in the overflow submenu also have this class. -const OVERFLOW_ITEM_CLASS = "jetpack-context-menu-item-overflow"; - -// The ID of the menu separator that separates standard context menu items from -// our user items. -const SEPARATOR_ID = "jetpack-context-menu-separator"; - -// If more than this number of items are added to the context menu, all items -// overflow into a "Jetpack" submenu. -const OVERFLOW_THRESH_DEFAULT = 10; -const OVERFLOW_THRESH_PREF = - "extensions.addon-sdk.context-menu.overflowThreshold"; - -// The label of the overflow sub-xul:menu. -// -// TODO: Localize this. -const OVERFLOW_MENU_LABEL = "Add-ons"; - -// The ID of the overflow sub-xul:menu. -const OVERFLOW_MENU_ID = "jetpack-content-menu-overflow-menu"; - -// The ID of the overflow submenu's xul:menupopup. -const OVERFLOW_POPUP_ID = "jetpack-content-menu-overflow-popup"; - -// These are used by PageContext.isCurrent below. If the popupNode or any of -// its ancestors is one of these, Firefox uses a tailored context menu, and so -// the page context doesn't apply. -const NON_PAGE_CONTEXT_ELTS = [ - Ci.nsIDOMHTMLAnchorElement, - Ci.nsIDOMHTMLAppletElement, - Ci.nsIDOMHTMLAreaElement, - Ci.nsIDOMHTMLButtonElement, - Ci.nsIDOMHTMLCanvasElement, - Ci.nsIDOMHTMLEmbedElement, - Ci.nsIDOMHTMLImageElement, - Ci.nsIDOMHTMLInputElement, - Ci.nsIDOMHTMLMapElement, - Ci.nsIDOMHTMLMediaElement, - Ci.nsIDOMHTMLMenuElement, - Ci.nsIDOMHTMLObjectElement, - Ci.nsIDOMHTMLOptionElement, - Ci.nsIDOMHTMLSelectElement, - Ci.nsIDOMHTMLTextAreaElement, -]; - -// This is used to access private properties of Item and Menu instances. -const PRIVATE_PROPS_KEY = { - valueOf: function valueOf() "private properties key" -}; - -// Used as an internal ID for items and as part of a public ID for item DOM -// elements. Careful: This number is not necessarily unique to any one instance -// of the module. For each module instance, when the first item is created this -// number will be 0, when the second is created it will be 1, and so on. -let nextItemID = 0; - -// The number of items that haven't finished initializing yet. See -// AIT__finishActiveItemInit(). -let numItemsWithUnfinishedInit = 0; - -exports.Item = Item; -exports.Menu = Menu; -exports.Separator = Separator; - - -// A word about traits and privates. `this` inside of traits methods is an -// object private to the implementation. It should never be publicly leaked. -// We use Cortex in the exported menu item constructors to create public -// reflections of the private objects that hide private properties -- those -// prefixed with an underscore. Public reflections are attached to the private -// objects via the `_public` property. -// -// All item objects passed into the implementation by the client will be public -// reflections, not private objects. Likewise, all item objects passed out of -// the implementation to the client must be public, not private. Mixing up -// public and private is bad and easy to do, so not only are private objects -// restricted to the implementation, but as much as possible we try to restrict -// them to the Item, Menu, and Separator traits and constructors. Everybody -// else in the implementation should expect to be passed public reflections, and -// they must specifically request private objects via privateItem(). - -// Item, Menu, and Separator are composed of this trait. -const ItemBaseTrait = Trait({ - - _initBase: function IBT__initBase(opts, optRules, optsToNotSet) { - this._optRules = optRules; - for (let optName in optRules) - if (optsToNotSet.indexOf(optName) < 0) - this[optName] = opts[optName]; - optsToNotSet.forEach(function (opt) validateOpt(opts[opt], optRules[opt])); - this._isInited = true; - - this._id = nextItemID++; - this._parentMenu = null; - - // This makes the private properties accessible to anyone with access to - // PRIVATE_PROPS_KEY. Barring loader tricks, only this file has has access - // to it, so only this file has access to the private properties. - const self = this; - this.valueOf = function IBT_valueOf(key) { - return key === PRIVATE_PROPS_KEY ? self : self._public; - }; - }, - - destroy: function IBT_destroy() { - if (this._wasDestroyed) - return; - if (this.parentMenu) - this.parentMenu.removeItem(this._public); - else if (!(this instanceof Separator) && this._hasFinishedInit) - browserManager.removeTopLevelItem(this._public); - browserManager.unregisterItem(this._public); - this._wasDestroyed = true; - }, - - get parentMenu() { - return this._parentMenu; - }, - - set parentMenu(val) { - throw new Error("The 'parentMenu' property is not intended to be set. " + - "Use menu.addItem(item) instead."); - }, - - set _isTopLevel(val) { - if (val) - this._workerReg = new WorkerRegistry(this._public); - else { - this._workerReg.destroy(); - delete this._workerReg; - } - }, - - get _topLevelItem() { - let topLevelItem = this._public; - let parentMenu = this.parentMenu; - while (parentMenu) { - topLevelItem = parentMenu; - parentMenu = parentMenu.parentMenu; - } - return topLevelItem; - } -}); - -// Item and Menu are composed of this trait. -const ActiveItemTrait = Trait.compose(ItemBaseTrait, EventEmitter, Trait({ - - _initActiveItem: function AIT__initActiveItem(opts, optRules, optsToNotSet) { - this._initBase(opts, optRules, - optsToNotSet.concat(["onMessage", "context"])); - - if ("onMessage" in opts) - this.on("message", opts.onMessage); - - // When a URL context is removed (by calling context.remove(urlContext)), we - // may need to create workers for windows containing pages that the item now - // matches. Likewise, when a URL context is added, we need to destroy - // workers for windows containing pages that the item now does not match. - // - // collection doesn't provide a way to listen for removals. utils/registry - // does, but it doesn't allow its elements to be enumerated. So as a hack, - // use a collection for item.context and replace its add and remove methods. - collection.addCollectionProperty(this, "context"); - if (opts.context) - this.context.add(opts.context); - - const self = this; - - let add = this.context.add; - this.context.add = function itemContextAdd() { - let args = Array.slice(arguments); - add.apply(self.context, args); - if (self._workerReg && args.some(function (a) a instanceof URLContext)) - self._workerReg.destroyUnneededWorkers(); - }; - - let remove = this.context.remove; - this.context.remove = function itemContextRemove() { - let args = Array.slice(arguments); - remove.apply(self.context, args); - if (self._workerReg && args.some(function (a) a instanceof URLContext)) - self._workerReg.createNeededWorkers(); - }; - }, - - // Workers are only created for top-level menu items. When a top-level item - // is later added to a Menu, its workers are destroyed. Well, all items start - // out as top-level because there is, unfortunately, no contextMenu.add(). So - // when an item is created and immediately added to a Menu, workers for it are - // needlessly created and destroyed. The point of this timeout is to avoid - // that. Items that are created and added to Menus in the same turn of the - // event loop won't have workers created for them. - _finishActiveItemInit: function AIT__finishActiveItemInit() { - numItemsWithUnfinishedInit++; - const self = this; - timer.setTimeout(function AIT__finishActiveItemInitTimeout() { - if (!self.parentMenu && !self._wasDestroyed) - browserManager.addTopLevelItem(self._public); - self._hasFinishedInit = true; - numItemsWithUnfinishedInit--; - }, 0); - }, - - get label() { - return this._label; - }, - - set label(val) { - this._label = validateOpt(val, this._optRules.label); - if (this._isInited) - browserManager.setItemLabel(this, this._label); - return this._label; - }, - - get image() { - return this._image; - }, - - set image(val) { - this._image = validateOpt(val, this._optRules.image); - if (this._isInited) - browserManager.setItemImage(this, this._image); - return this._image; - }, - - get contentScript() { - return this._contentScript; - }, - - set contentScript(val) { - this._contentScript = validateOpt(val, this._optRules.contentScript); - return this._contentScript; - }, - - get contentScriptFile() { - return this._contentScriptFile; - }, - - set contentScriptFile(val) { - this._contentScriptFile = - validateOpt(val, this._optRules.contentScriptFile); - return this._contentScriptFile; - } -})); - -// Item is composed of this trait. -const ItemTrait = Trait.compose(ActiveItemTrait, Trait({ - - _initItem: function IT__initItem(opts, optRules) { - this._initActiveItem(opts, optRules, []); - }, - - get data() { - return this._data; - }, - - set data(val) { - this._data = validateOpt(val, this._optRules.data); - if (this._isInited) - browserManager.setItemData(this, this._data); - return this._data; - }, - - toString: function IT_toString() { - return '[object Item "' + this.label + '"]'; - } -})); - -// The exported Item constructor. -function Item(options) { - let optRules = optionsRules(); - optRules.data = { - map: function (v) v.toString(), - is: ["string", "undefined"] - }; - - let item = ItemTrait.create(Item.prototype); - item._initItem(options, optRules); - - item._public = Cortex(item); - browserManager.registerItem(item._public); - item._finishActiveItemInit(); - - return item._public; -} - -// Menu is composed of this trait. -const MenuTrait = Trait.compose( - ActiveItemTrait.resolve({ destroy: "_destroyThisItem" }), - Trait({ - - _initMenu: function MT__initMenu(opts, optRules, optsToNotSet) { - this._items = []; - this._initActiveItem(opts, optRules, optsToNotSet); - }, - - destroy: function MT_destroy() { - while (this.items.length) - this.items[0].destroy(); - this._destroyThisItem(); - }, - - get items() { - return this._items; - }, - - set items(val) { - let newItems = validateOpt(val, this._optRules.items); - while (this.items.length) - this.items[0].destroy(); - newItems.forEach(function (i) this.addItem(i), this); - return newItems; - }, - - addItem: function MT_addItem(item) { - // First, remove the item from its current parent. - let privates = privateItem(item); - if (item.parentMenu) - item.parentMenu.removeItem(item); - else if (!(item instanceof Separator) && privates._hasFinishedInit) - browserManager.removeTopLevelItem(item); - - // Now add the item to this menu. - this._items.push(item); - privates._parentMenu = this._public; - browserManager.addItemToMenu(item, this._public); - }, - - removeItem: function MT_removeItem(item) { - let idx = this._items.indexOf(item); - if (idx < 0) - return; - this._items.splice(idx, 1); - privateItem(item)._parentMenu = null; - browserManager.removeItemFromMenu(item, this._public); - }, - - toString: function MT_toString() { - return '[object Menu "' + this.label + '"]'; - } -})); - -// The exported Menu constructor. -function Menu(options) { - let optRules = optionsRules(); - optRules.items = { - is: ["array"], - ok: function (v) { - return v.every(function (item) { - return (item instanceof Item) || - (item instanceof Menu) || - (item instanceof Separator); - }); - }, - msg: "items must be an array, and each element in the array must be an " + - "Item, Menu, or Separator." - }; - - let menu = MenuTrait.create(Menu.prototype); - - // We can't rely on _initBase to set the `items` property, because the menu - // needs to be registered with and added to the browserManager before any - // child items are added to it. - menu._initMenu(options, optRules, ["items"]); - - menu._public = Cortex(menu); - browserManager.registerItem(menu._public); - menu.items = options.items; - menu._finishActiveItemInit(); - - return menu._public; -} - -// The exported Separator constructor. -function Separator() { - let sep = ItemBaseTrait.create(Separator.prototype); - sep._initBase({}, {}, []); - - sep._public = Cortex(sep); - browserManager.registerItem(sep._public); - sep._hasFinishedInit = true; - return sep._public; -} - - -function Context() {} - -function PageContext() { - this.isCurrent = function PageContext_isCurrent(popupNode) { - let win = popupNode.ownerDocument.defaultView; - if (win && !win.getSelection().isCollapsed) - return false; - - let cursor = popupNode; - while (cursor && !(cursor instanceof Ci.nsIDOMHTMLHtmlElement)) { - if (NON_PAGE_CONTEXT_ELTS.some(function (iface) cursor instanceof iface)) - return false; - cursor = cursor.parentNode; - } - return true; - }; -} - -PageContext.prototype = new Context(); - -function SelectorContext(selector) { - let opts = apiUtils.validateOptions({ selector: selector }, { - selector: { - is: ["string"], - msg: "selector must be a string." - } - }); - - this.adjustPopupNode = function SelectorContext_adjustPopupNode(node) { - return closestMatchingAncestor(node); - }; - - this.isCurrent = function SelectorContext_isCurrent(popupNode) { - return !!closestMatchingAncestor(popupNode); - }; - - // Returns node if it matches selector, or the closest ancestor of node that - // matches, or null if node and none of its ancestors matches. - function closestMatchingAncestor(node) { - let cursor = node; - while (cursor) { - if (cursor.mozMatchesSelector(selector)) - return cursor; - if (cursor instanceof Ci.nsIDOMHTMLHtmlElement) - break; - cursor = cursor.parentNode; - } - return null; - } -} - -SelectorContext.prototype = new Context(); - -function SelectionContext() { - this.isCurrent = function SelectionContext_isCurrent(popupNode) { - let win = popupNode.ownerDocument.defaultView; - if (!win) - return false; - - let hasSelection = !win.getSelection().isCollapsed; - if (!hasSelection) { - // window.getSelection doesn't return a selection for text selected in a - // form field (see bug 85686), so before returning false we want to check - // if the popupNode is a text field. - let { selectionStart, selectionEnd } = popupNode; - hasSelection = !isNaN(selectionStart) && - !isNaN(selectionEnd) && - selectionStart !== selectionEnd; - } - return hasSelection; - }; -} - -SelectionContext.prototype = new Context(); - -function URLContext(patterns) { - let opts = apiUtils.validateOptions({ patterns: patterns }, { - patterns: { - map: function (v) apiUtils.getTypeOf(v) === "array" ? v : [v], - ok: function (v) v.every(function (p) typeof(p) === "string"), - msg: "patterns must be a string or an array of strings." - } - }); - try { - patterns = opts.patterns.map(function (p) new MatchPattern(p)); - } - catch (err) { - console.error("Error creating URLContext match pattern:"); - throw err; - } - - const self = this; - - this.isCurrent = function URLContext_isCurrent(popupNode) { - return self.isCurrentForURL(popupNode.ownerDocument.URL); - }; - - this.isCurrentForURL = function URLContext_isCurrentForURL(url) { - return patterns.some(function (p) p.test(url)); - }; -} - -URLContext.prototype = new Context(); - -exports.PageContext = apiUtils.publicConstructor(PageContext); -exports.SelectorContext = apiUtils.publicConstructor(SelectorContext); -exports.SelectionContext = apiUtils.publicConstructor(SelectionContext); -exports.URLContext = apiUtils.publicConstructor(URLContext); - - -// Returns a version of opt validated against the given rule. -function validateOpt(opt, rule) { - return apiUtils.validateOptions({ opt: opt }, { opt: rule }).opt; -} - -// Returns rules for apiUtils.validateOptions() common to Item and Menu. -function optionsRules() { - return { - context: { - is: ["undefined", "object", "array"], - ok: function (v) { - if (!v) - return true; - let arr = apiUtils.getTypeOf(v) === "array" ? v : [v]; - return arr.every(function (o) o instanceof Context); - }, - msg: "The 'context' option must be a Context object or an array of " + - "Context objects." - }, - label: { - map: function (v) v.toString(), - is: ["string"], - ok: function (v) !!v, - msg: "The item must have a non-empty string label." - }, - image: { - map: function (v) v.toString(), - is: ["string", "undefined", "null"] - }, - contentScript: { - is: ["string", "array", "undefined"], - ok: function (v) { - return apiUtils.getTypeOf(v) !== "array" || - v.every(function (s) typeof(s) === "string"); - } - }, - contentScriptFile: { - is: ["string", "array", "undefined"], - ok: function (v) { - if (!v) - return true; - let arr = apiUtils.getTypeOf(v) === "array" ? v : [v]; - try { - return arr.every(function (s) { - return apiUtils.getTypeOf(s) === "string" && - URL(s).scheme === 'resource'; - }); - } - catch (err) {} - return false; - }, - msg: "The 'contentScriptFile' option must be a local file URL or " + - "an array of local file URLs." - }, - onMessage: { - is: ["function", "undefined"] - } - }; -} - -// Does a binary search on elts, a NodeList, and returns the DOM element -// before which an item with targetLabel should be inserted. null is returned -// if the new item should be inserted at the end. -function insertionPoint(targetLabel, elts) { - let from = 0; - let to = elts.length - 1; - - while (from <= to) { - let i = Math.floor((from + to) / 2); - let comp = targetLabel.localeCompare(elts[i].getAttribute("label")); - if (comp < 0) - to = i - 1; - else if (comp > 0) - from = i + 1; - else - return elts[i]; - } - return elts[from] || null; -} - -// Builds an ID suitable for a DOM element from the given item ID. -// isInOverflowSubtree should be true if the returned element will be inserted -// into the DOM subtree rooted at the overflow menu. -function domEltIDFromItemID(itemID, isInOverflowSubtree) { - let suffix = isInOverflowSubtree ? "-overflow" : ""; - return jpSelf.id + "-context-menu-item-" + itemID + suffix; -} - -// Parses the item ID out of the given DOM element ID and returns it. If the -// element's ID is malformed or it indicates that the element was not created by -// the instance of the module calling this function, returns -1. -function itemIDFromDOMEltID(domEltID) { - let match = /^(.+?)-context-menu-item-([0-9]+)[-a-z]*$/.exec(domEltID); - return !match || match[1] !== jpSelf.id ? -1 : match[2]; -} - -// Returns the private version of the given public reflection. -function privateItem(publicItem) { - return publicItem.valueOf(PRIVATE_PROPS_KEY); -} - - -// A type of Worker tailored to our uses. -const ContextMenuWorker = Worker.compose({ - destroy: Worker.required, - - // Returns true if any context listeners are defined in the worker's port. - anyContextListeners: function CMW_anyContextListeners() { - return this._contentWorker.hasListenerFor("context"); - }, - - // Returns the first string or truthy value returned by a context listener in - // the worker's port. If none return a string or truthy value or if there are - // no context listeners, returns false. popupNode is the node that was - // context-clicked. - isAnyContextCurrent: function CMW_isAnyContextCurrent(popupNode) { - let results = this._contentWorker.emitSync("context", popupNode); - for (let i = 0; i < results.length; i++) { - let val = results[i]; - if (typeof val === "string" || val) - return val; - } - return false; - }, - - // Emits a click event in the worker's port. popupNode is the node that was - // context-clicked, and clickedItemData is the data of the item that was - // clicked. - fireClick: function CMW_fireClick(popupNode, clickedItemData) { - this._contentWorker.emitSync("click", popupNode, clickedItemData); - } -}); - - -// This class creates and stores content workers for pairs of menu items and -// content windows. Use one instance for each item. Not all pairs need a -// worker: if an item has a URL context that does not match a window's page, -// then no worker is created for the pair. -function WorkerRegistry(item) { - this.item = item; - - // inner window ID => { win, worker } - this.winWorkers = {}; - - // inner window ID => content window - this.winsWithoutWorkers = {}; -} - -WorkerRegistry.prototype = { - - // Registers a content window, creating a worker for it if it needs one. - registerContentWin: function WR_registerContentWin(win) { - let innerWinID = getInnerId(win); - if ((innerWinID in this.winWorkers) || - (innerWinID in this.winsWithoutWorkers)) - return; - if (this._doesURLNeedWorker(win.document.URL)) - this.winWorkers[innerWinID] = { win: win, worker: this._makeWorker(win) }; - else - this.winsWithoutWorkers[innerWinID] = win; - }, - - // Unregisters a content window, destroying its related worker if it has one. - unregisterContentWin: function WR_unregisterContentWin(innerWinID) { - if (innerWinID in this.winWorkers) { - let winWorker = this.winWorkers[innerWinID]; - winWorker.worker.destroy(); - delete winWorker.worker; - delete winWorker.win; - delete this.winWorkers[innerWinID]; - } - else - delete this.winsWithoutWorkers[innerWinID]; - }, - - // Creates a worker for each window that needs a worker but doesn't have one. - createNeededWorkers: function WR_createNeededWorkers() { - for (let [innerWinID, win] in Iterator(this.winsWithoutWorkers)) { - delete this.winsWithoutWorkers[innerWinID]; - this.registerContentWin(win); - } - }, - - // Destroys the worker for each window that has a worker but doesn't need it. - destroyUnneededWorkers: function WR_destroyUnneededWorkers() { - for (let [innerWinID, winWorker] in Iterator(this.winWorkers)) { - if (!this._doesURLNeedWorker(winWorker.win.document.URL)) { - this.unregisterContentWin(innerWinID); - this.winsWithoutWorkers[innerWinID] = winWorker.win; - } - } - }, - - // Returns the worker for the item-window pair or null if none exists. - find: function WR_find(contentWin) { - let innerWinID = getInnerId(contentWin); - return (innerWinID in this.winWorkers) ? - this.winWorkers[innerWinID].worker : - null; - }, - - // Unregisters all content windows from the registry, which destroys all - // workers. - destroy: function WR_destroy() { - for (let innerWinID in this.winWorkers) - this.unregisterContentWin(innerWinID); - for (let innerWinID in this.winsWithoutWorkers) - this.unregisterContentWin(innerWinID); - }, - - // Returns false if the item has a URL context that does not match the given - // URL. - _doesURLNeedWorker: function WR__doesURLNeedWorker(url) { - for (let ctxt in this.item.context) - if ((ctxt instanceof URLContext) && !ctxt.isCurrentForURL(url)) - return false; - return true; - }, - - _makeWorker: function WR__makeWorker(win) { - let worker = ContextMenuWorker({ - window: win, - contentScript: this.item.contentScript, - contentScriptFile: this.item.contentScriptFile, - onError: function (err) console.exception(err) - }); - let item = this.item; - worker.on("message", function workerOnMessage(msg) { - try { - privateItem(item)._emitOnObject(item, "message", msg); - } - catch (err) { - console.exception(err); - } - }); - return worker; - } -}; - - -// Mirrors state across all browser windows. Also responsible for detecting -// all content window loads and unloads. -let browserManager = { - topLevelItems: [], - browserWins: [], - - // inner window ID => content window - contentWins: {}, - - // Call this when a new item is created, top-level or not. - registerItem: function BM_registerItem(item) { - this.browserWins.forEach(function (w) w.registerItem(item)); - }, - - // Call this when an item is destroyed and won't be used again, top-level or - // not. - unregisterItem: function BM_unregisterItem(item) { - this.browserWins.forEach(function (w) w.unregisterItem(item)); - }, - - addTopLevelItem: function BM_addTopLevelItem(item) { - this.topLevelItems.push(item); - this.browserWins.forEach(function (w) w.addTopLevelItem(item)); - - // Create the item's worker registry and register all currently loaded - // content windows with it. - let privates = privateItem(item); - privates._isTopLevel = true; - for each (let win in this.contentWins) - privates._workerReg.registerContentWin(win); - }, - - removeTopLevelItem: function BM_removeTopLevelItem(item) { - let idx = this.topLevelItems.indexOf(item); - if (idx < 0) - throw new Error("Internal error: item not in top-level menu: " + item); - this.topLevelItems.splice(idx, 1); - this.browserWins.forEach(function (w) w.removeTopLevelItem(item)); - privateItem(item)._isTopLevel = false; - }, - - addItemToMenu: function BM_addItemToMenu(item, parentMenu) { - this.browserWins.forEach(function (w) w.addItemToMenu(item, parentMenu)); - }, - - removeItemFromMenu: function BM_removeItemFromMenu(item, parentMenu) { - this.browserWins.forEach(function (w) w.removeItemFromMenu(item, - parentMenu)); - }, - - setItemLabel: function BM_setItemLabel(item, label) { - this.browserWins.forEach(function (w) w.setItemLabel(item, label)); - }, - - setItemImage: function BM_setItemImage(item, imageURL) { - this.browserWins.forEach(function (w) w.setItemImage(item, imageURL)); - }, - - setItemData: function BM_setItemData(item, data) { - this.browserWins.forEach(function (w) w.setItemData(item, data)); - }, - - // Note that calling this method will cause onTrack to be called immediately - // for each currently open browser window. - init: function BM_init() { - require("api-utils/unload").ensure(this); - let windowTracker = winUtils.WindowTracker(this); - - // Register content windows on content-document-global-created and - // unregister them on inner-window-destroyed. For rationale, see bug 667957 - // for the former and bug 642004 for the latter. - observerServ.add("content-document-global-created", - this._onDocGlobalCreated, this); - observerServ.add("inner-window-destroyed", - this._onInnerWinDestroyed, this); - }, - - _onDocGlobalCreated: function BM__onDocGlobalCreated(contentWin) { - let doc = contentWin.document; - if (doc.readyState == "loading") { - const self = this; - doc.addEventListener("readystatechange", function onReadyStateChange(e) { - if (e.target != doc || doc.readyState != "complete") - return; - doc.removeEventListener("readystatechange", onReadyStateChange, false); - self._registerContentWin(contentWin); - }, false); - } - else if (doc.readyState == "complete") - this._registerContentWin(contentWin); - }, - - _onInnerWinDestroyed: function BM__onInnerWinDestroyed(subj) { - this._unregisterContentWin( - subj.QueryInterface(Ci.nsISupportsPRUint64).data); - }, - - // Stores the given content window with the manager and registers it with each - // top-level item's worker registry. - _registerContentWin: function BM__registerContentWin(win) { - let innerID = getInnerId(win); - - // It's an error to call this method for the same window more than once, but - // we allow it in one case: when onTrack races _onDocGlobalCreated. (See - // the comment in onTrack.) Make sure the window is registered only once. - if (innerID in this.contentWins) - return; - - this.contentWins[innerID] = win; - this.topLevelItems.forEach(function (item) { - privateItem(item)._workerReg.registerContentWin(win); - }); - }, - - // Removes the given content window from the manager and unregisters it from - // each top-level item's worker registry. - _unregisterContentWin: function BM__unregisterContentWin(innerID) { - delete this.contentWins[innerID]; - this.topLevelItems.forEach(function (item) { - privateItem(item)._workerReg.unregisterContentWin(innerID); - }); - }, - - unload: function BM_unload() { - // The window tracker is unloaded at the same time this method is called, - // which causes onUntrack to be called for each open browser window, so - // there's no need to clean up browser windows here. - - while (this.topLevelItems.length) { - let item = this.topLevelItems[0]; - this.removeTopLevelItem(item); - this.unregisterItem(item); - } - delete this.contentWins; - }, - - // Registers a browser window with the manager. This is a WindowTracker - // callback. Note that this is called in two cases: for each newly opened - // chrome window, and for each chrome window that is open when the loader - // loads this module. - onTrack: function BM_onTrack(window) { - if (!this._isBrowserWindow(window)) - return; - - let browserWin = new BrowserWindow(window); - this.browserWins.push(browserWin); - - // Register all loaded content windows in the browser window. Be sure to - // include frames and iframes. If onTrack is called as a result of a new - // browser window being opened, as opposed to the module being loaded, then - // this will race the content-document-global-created notification. That's - // OK, since _registerContentWin will not register the same content window - // more than once. - window.gBrowser.browsers.forEach(function (browser) { - let topContentWin = browser.contentWindow; - let allContentWins = Array.slice(topContentWin.frames); - allContentWins.push(topContentWin); - allContentWins.forEach(function (contentWin) { - if (contentWin.document.readyState == "complete") - this._registerContentWin(contentWin); - }, this); - }, this); - - // Add all top-level items and, recursively, their child items to the new - // browser window. - function addItemTree(item, parentMenu) { - browserWin.registerItem(item); - if (parentMenu) - browserWin.addItemToMenu(item, parentMenu); - else - browserWin.addTopLevelItem(item); - if (item instanceof Menu) - item.items.forEach(function (subitem) addItemTree(subitem, item)); - } - this.topLevelItems.forEach(function (item) addItemTree(item, null)); - }, - - // Unregisters a browser window from the manager. This is a WindowTracker - // callback. Note that this is called in two cases: for each newly closed - // chrome window, and for each chrome window that is open when this module is - // unloaded. - onUntrack: function BM_onUntrack(window) { - if (!this._isBrowserWindow(window)) - return; - - // Remove the window from the window list. - let idx = 0; - for (; idx < this.browserWins.length; idx++) - if (this.browserWins[idx].window == window) - break; - if (idx == this.browserWins.length) - throw new Error("Internal error: browser window not found"); - let browserWin = this.browserWins.splice(idx, 1)[0]; - - // Remove all top-level items from the window. - this.topLevelItems.forEach(function (i) browserWin.removeTopLevelItem(i)); - browserWin.destroy(); - }, - - _isBrowserWindow: function BM__isBrowserWindow(win) { - let winType = win.document.documentElement.getAttribute("windowtype"); - return winType === "navigator:browser"; - } -}; - - -// Responsible for creating and managing context menu item DOM elements for a -// browser window. Also responsible for providing a description of the window's -// current context and determining whether an item matches the current context. -// -// TODO: If other apps besides Firefox want to support the context menu in -// whatever way is appropriate for them, plugging in a substitute for or an -// adapter to this class should be the way to do it. Make it easy for them. -// See bug 560716. -function BrowserWindow(window) { - this.window = window; - this.doc = window.document; - - let popupDOMElt = this.doc.getElementById("contentAreaContextMenu"); - if (!popupDOMElt) - throw new Error("Internal error: Context menu popup not found."); - this.contextMenuPopup = new ContextMenuPopup(popupDOMElt, this); - - // item ID => { item, domElt, overflowDOMElt, popup, overflowPopup } - // item may or may not be top-level. domElt is the item's DOM element - // contained in the subtree rooted in the top-level context menu. - // overflowDOMElt is the item's DOM element contained in the subtree rooted in - // the overflow submenu. popup and overflowPopup are only defined if the item - // is a Menu; they're the Popup instances containing the Menu's child items, - // with the aforementioned distinction between top-level and overflow - // subtrees. - this.items = {}; -} - -BrowserWindow.prototype = { - - // Creates and stores DOM elements for the given item, top-level or not. - registerItem: function BW_registerItem(item) { - // this.items[id] is referenced by _makeMenu, so it needs to be defined - // before _makeDOMElt is called. - let props = { item: item }; - this.items[privateItem(item)._id] = props; - props.domElt = this._makeDOMElt(item, false); - props.overflowDOMElt = this._makeDOMElt(item, true); - }, - - // Removes the given item's DOM elements from the store. - unregisterItem: function BW_unregisterItem(item) { - delete this.items[privateItem(item)._id]; - }, - - addTopLevelItem: function BW_addTopLevelItem(item) { - this.contextMenuPopup.addItem(item); - }, - - removeTopLevelItem: function BW_removeTopLevelItem(item) { - this.contextMenuPopup.removeItem(item); - }, - - addItemToMenu: function BW_addItemToMenu(item, parentMenu) { - let { popup, overflowPopup } = this.items[privateItem(parentMenu)._id]; - popup.addItem(item); - overflowPopup.addItem(item); - }, - - removeItemFromMenu: function BW_removeItemFromMenu(item, parentMenu) { - let { popup, overflowPopup } = this.items[privateItem(parentMenu)._id]; - popup.removeItem(item); - overflowPopup.removeItem(item); - }, - - setItemLabel: function BW_setItemLabel(item, label) { - let privates = privateItem(item); - let { domElt, overflowDOMElt } = this.items[privates._id]; - this._setDOMEltLabel(domElt, label); - this._setDOMEltLabel(overflowDOMElt, label); - if (!item.parentMenu && privates._hasFinishedInit) - this.contextMenuPopup.itemLabelDidChange(item); - }, - - _setDOMEltLabel: function BW__setDOMEltLabel(domElt, label) { - domElt.setAttribute("label", label); - }, - - setItemImage: function BW_setItemImage(item, imageURL) { - let { domElt, overflowDOMElt } = this.items[privateItem(item)._id]; - let isMenu = item instanceof Menu; - this._setDOMEltImage(domElt, imageURL, isMenu); - this._setDOMEltImage(overflowDOMElt, imageURL, isMenu); - }, - - _setDOMEltImage: function BW__setDOMEltImage(domElt, imageURL, isMenu) { - if (!imageURL) { - domElt.removeAttribute("image"); - domElt.classList.remove("menu-iconic"); - domElt.classList.remove("menuitem-iconic"); - } - else { - domElt.setAttribute("image", imageURL); - domElt.classList.add(isMenu ? "menu-iconic" : "menuitem-iconic"); - } - }, - - setItemData: function BW_setItemData(item, data) { - let { domElt, overflowDOMElt } = this.items[privateItem(item)._id]; - this._setDOMEltData(domElt, data); - this._setDOMEltData(overflowDOMElt, data); - }, - - _setDOMEltData: function BW__setDOMEltData(domElt, data) { - domElt.setAttribute("value", data); - }, - - // The context specified for a top-level item may not match exactly the real - // context that triggers it. For example, if the user context-clicks a span - // inside an anchor, we want items that specify an anchor context to be - // triggered, but the real context will indicate that the span was clicked, - // not the anchor. Where the real context and an item's context conflict, - // clients should be given the item's context, and this method can be used to - // make such adjustments. Returns an adjusted popupNode. - adjustPopupNode: function BW_adjustPopupNode(popupNode, topLevelItem) { - for (let ctxt in topLevelItem.context) { - if (typeof(ctxt.adjustPopupNode) === "function") { - let ctxtNode = ctxt.adjustPopupNode(popupNode); - if (ctxtNode) { - popupNode = ctxtNode; - break; - } - } - } - return popupNode; - }, - - // Returns true if all of item's contexts are current in the window. - areAllContextsCurrent: function BW_areAllContextsCurrent(item, popupNode) { - let win = popupNode.ownerDocument.defaultView; - let worker = privateItem(item)._workerReg.find(win); - - // If the worker for the item-window pair doesn't exist (e.g., because the - // page hasn't loaded yet), we can't really make a good decision since the - // content script may have a context listener. So just don't show the item - // at all. - if (!worker) - return false; - - // If there are no contexts given at all, the page context applies. - let hasContentContext = worker.anyContextListeners(); - if (!hasContentContext && !item.context.length) - return new PageContext().isCurrent(popupNode); - - // Otherwise, determine if all given contexts are current. Evaluate the - // declarative contexts first and the worker's context listeners last. That - // way the listener might be able to avoid some work. - let curr = true; - for (let ctxt in item.context) { - curr = curr && ctxt.isCurrent(popupNode); - if (!curr) - return false; - } - return !hasContentContext || worker.isAnyContextCurrent(popupNode); - }, - - // Returns the node the user context-clicked to invoke the context menu. - // For Gecko 2.0 and later, triggerNode is this node; - // if it's falsey, document.popupNode is used. - capturePopupNode: function BW_capturePopupNode(triggerNode) { - var popupNode = triggerNode || this.doc.popupNode; - if (!popupNode) - console.warn("popupNode is null."); - return popupNode; - }, - - destroy: function BW_destroy() { - this.contextMenuPopup.destroy(); - delete this.window; - delete this.doc; - delete this.items; - }, - - // Emits a click event in the port of the content worker related to given top- - // level item and popupNode's content window. Listeners will be passed - // popupNode and clickedItemData. - fireClick: function BW_fireClick(topLevelItem, popupNode, clickedItemData) { - let win = popupNode.ownerDocument.defaultView; - let worker = privateItem(topLevelItem)._workerReg.find(win); - if (worker) - worker.fireClick(popupNode, clickedItemData); - }, - - _makeDOMElt: function BW__makeDOMElt(item, isInOverflowSubtree) { - let elt = item instanceof Item ? this._makeMenuitem(item) : - item instanceof Menu ? this._makeMenu(item, isInOverflowSubtree) : - item instanceof Separator ? this._makeSeparator() : - null; - if (!elt) - throw new Error("Internal error: can't make element, unknown item type"); - - elt.id = domEltIDFromItemID(privateItem(item)._id, isInOverflowSubtree); - elt.classList.add(ITEM_CLASS); - return elt; - }, - - // Returns a new xul:menu representing the menu. - _makeMenu: function BW__makeMenu(menu, isInOverflowSubtree) { - let menuElt = this.doc.createElement("menu"); - this._setDOMEltLabel(menuElt, menu.label); - if (menu.image) - this._setDOMEltImage(menuElt, menu.image, true); - let popupElt = this.doc.createElement("menupopup"); - menuElt.appendChild(popupElt); - - let popup = new Popup(popupElt, this, isInOverflowSubtree); - let props = this.items[privateItem(menu)._id]; - if (isInOverflowSubtree) - props.overflowPopup = popup; - else - props.popup = popup; - - return menuElt; - }, - - // Returns a new xul:menuitem representing the item. - _makeMenuitem: function BW__makeMenuitem(item) { - let elt = this.doc.createElement("menuitem"); - this._setDOMEltLabel(elt, item.label); - if (item.image) - this._setDOMEltImage(elt, item.image, false); - if (item.data) - this._setDOMEltData(elt, item.data); - return elt; - }, - - // Returns a new xul:menuseparator. - _makeSeparator: function BW__makeSeparator() { - return this.doc.createElement("menuseparator"); - } -}; - - -// Responsible for adding DOM elements to and removing them from poupDOMElt. -function Popup(popupDOMElt, browserWin, isInOverflowSubtree) { - this.popupDOMElt = popupDOMElt; - this.browserWin = browserWin; - this.isInOverflowSubtree = isInOverflowSubtree; -} - -Popup.prototype = { - - addItem: function Popup_addItem(item) { - let props = this.browserWin.items[privateItem(item)._id]; - let elt = this.isInOverflowSubtree ? props.overflowDOMElt : props.domElt; - this.popupDOMElt.appendChild(elt); - }, - - removeItem: function Popup_removeItem(item) { - let props = this.browserWin.items[privateItem(item)._id]; - let elt = this.isInOverflowSubtree ? props.overflowDOMElt : props.domElt; - this.popupDOMElt.removeChild(elt); - } -}; - - -// Represents a browser window's context menu popup. Responsible for hiding and -// showing items according to the browser window's current context and for -// handling item clicks. -function ContextMenuPopup(popupDOMElt, browserWin) { - this.popupDOMElt = popupDOMElt; - this.browserWin = browserWin; - this.doc = popupDOMElt.ownerDocument; - - // item ID => item - // Calling this variable "topLevelItems" is redundant, since Popup and - // ContextMenuPopup are only responsible for their child items, not all their - // descendant items. But calling it "items" might encourage one to believe - // otherwise, so topLevelItems it is. - this.topLevelItems = {}; - - popupDOMElt.addEventListener("popupshowing", this, false); - popupDOMElt.addEventListener("command", this, false); -} - -ContextMenuPopup.prototype = { - - addItem: function CMP_addItem(item) { - this._ensureStaticEltsExist(); - let itemID = privateItem(item)._id; - this.topLevelItems[itemID] = item; - let props = this.browserWin.items[itemID]; - props.domElt.classList.add(TOPLEVEL_ITEM_CLASS); - props.overflowDOMElt.classList.add(OVERFLOW_ITEM_CLASS); - this._insertItemInSortedOrder(item); - }, - - removeItem: function CMP_removeItem(item) { - let itemID = privateItem(item)._id; - delete this.topLevelItems[itemID]; - let { domElt, overflowDOMElt } = this.browserWin.items[itemID]; - domElt.classList.remove(TOPLEVEL_ITEM_CLASS); - overflowDOMElt.classList.remove(OVERFLOW_ITEM_CLASS); - this.popupDOMElt.removeChild(domElt); - this._overflowPopup.removeChild(overflowDOMElt); - }, - - // Call this after the item's label changes. This re-inserts the item into - // the popup so that it remains in sorted order. - itemLabelDidChange: function CMP_itemLabelDidChange(item) { - let itemID = privateItem(item)._id; - let { domElt, overflowDOMElt } = this.browserWin.items[itemID]; - this.popupDOMElt.removeChild(domElt); - this._overflowPopup.removeChild(overflowDOMElt); - this._insertItemInSortedOrder(item); - }, - - destroy: function CMP_destroy() { - // If there are no more items from any instance of the module, remove the - // separator and overflow submenu, if they exist. - let elts = this._topLevelElts; - if (!elts.length) { - let submenu = this._overflowMenu; - if (submenu) - this.popupDOMElt.removeChild(submenu); - - let sep = this._separator; - if (sep) - this.popupDOMElt.removeChild(sep); - } - - this.popupDOMElt.removeEventListener("popupshowing", this, false); - this.popupDOMElt.removeEventListener("command", this, false); - }, - - handleEvent: function CMP_handleEvent(event) { - try { - if (event.type === "command") - this._handleClick(event.target); - else if (event.type === "popupshowing" && - event.target === this.popupDOMElt) - this._handlePopupShowing(); - } - catch (err) { - console.exception(err); - } - }, - - // command events bubble to the context menu's top-level xul:menupopup and are - // caught here. - _handleClick: function CMP__handleClick(clickedDOMElt) { - if (!clickedDOMElt.classList.contains(ITEM_CLASS)) - return; - let itemID = itemIDFromDOMEltID(clickedDOMElt.id); - if (itemID < 0) - return; - let { item, domElt, overflowDOMElt } = this.browserWin.items[itemID]; - - // Bail if the DOM element was not created by this module instance. In - // real-world add-ons, the itemID < 0 check above is sufficient, but for the - // unit test the JID never changes, making this necessary. - if (clickedDOMElt != domElt && clickedDOMElt != overflowDOMElt) - return; - - let topLevelItem = privateItem(item)._topLevelItem; - let popupNode = this.browserWin.adjustPopupNode(this._getPopupNode(), - topLevelItem); - this.browserWin.fireClick(topLevelItem, popupNode, item.data); - }, - - _getPopupNode: function CMP__getPopupNode() { - // popupDOMElt.triggerNode was added in Gecko 2.0 by bug 383930. The || is - // to avoid a Spidermonkey strict warning on earlier versions. - let triggerNode = this.popupDOMElt.triggerNode || undefined; - return this.browserWin.capturePopupNode(triggerNode); - }, - - // popupshowing is used to show top-level items that match the browser - // window's current context and hide items that don't. Each module instance - // is responsible for showing and hiding the items it owns. - _handlePopupShowing: function CMP__handlePopupShowing() { - - // If there are items queued up to finish initializing, let them go first. - // Otherwise the overflow submenu and menu separator may end up in an - // inappropriate state when those items are later added to the menu. - if (numItemsWithUnfinishedInit) { - const self = this; - timer.setTimeout(function popupShowingTryAgain() { - self._handlePopupShowing(); - }, 0); - return; - } - - let popupNode = this._getPopupNode(); - // Show and hide items. Set a "jetpackContextCurrent" property on the - // DOM elements to signal which of our items match the current context. - for (let [itemID, item] in Iterator(this.topLevelItems)) { - let areContextsCurr = - this.browserWin.areAllContextsCurrent(item, popupNode); - - // Change the item's label if the return value was a string. - if (typeof(areContextsCurr) === "string") { - item.label = areContextsCurr; - areContextsCurr = true; - } - - let { domElt, overflowDOMElt } = this.browserWin.items[itemID]; - domElt.jetpackContextCurrent = areContextsCurr; - domElt.hidden = !areContextsCurr; - overflowDOMElt.jetpackContextCurrent = areContextsCurr; - overflowDOMElt.hidden = !areContextsCurr; - } - - // Get the total number of items that match the current context. It's a - // little tricky: There may be other instances of this module loaded, - // each hiding and showing their items. So we can't base this number on - // only our items, or on the hidden state of items. That's why we set - // the jetpackContextCurrent property above. The last instance to run - // will leave the menupopup in the correct state. - let elts = this._topLevelElts; - let numShown = Array.reduce(elts, function (total, elt) { - return total + (elt.jetpackContextCurrent ? 1 : 0); - }, 0); - - // If too many items are shown, show the submenu and hide the top-level - // items. Otherwise, hide the submenu and show the top-level items. - let overflow = numShown > this._overflowThreshold; - if (overflow) - Array.forEach(elts, function (e) e.hidden = true); - - let submenu = this._overflowMenu; - if (submenu) - submenu.hidden = !overflow; - - // If no items are shown, hide the menu separator. - let sep = this._separator; - if (sep) - sep.hidden = numShown === 0; - }, - - // Adds the menu separator and overflow submenu if they don't exist. - _ensureStaticEltsExist: function CMP__ensureStaticEltsExist() { - let sep = this._separator; - if (!sep) { - sep = this._makeSeparator(); - this.popupDOMElt.appendChild(sep); - } - - let submenu = this._overflowMenu; - if (!submenu) { - submenu = this._makeOverflowMenu(); - submenu.hidden = true; - this.popupDOMElt.insertBefore(submenu, sep.nextSibling); - } - }, - - // Inserts the given item's DOM element into the popup in sorted order. - _insertItemInSortedOrder: function CMP__insertItemInSortedOrder(item) { - let props = this.browserWin.items[privateItem(item)._id]; - this.popupDOMElt.insertBefore( - props.domElt, insertionPoint(item.label, this._topLevelElts)); - this._overflowPopup.insertBefore( - props.overflowDOMElt, insertionPoint(item.label, this._overflowElts)); - }, - - // Creates and returns the xul:menu that's shown when too many items are added - // to the popup. - _makeOverflowMenu: function CMP__makeOverflowMenu() { - let submenu = this.doc.createElement("menu"); - submenu.id = OVERFLOW_MENU_ID; - submenu.setAttribute("label", OVERFLOW_MENU_LABEL); - let popup = this.doc.createElement("menupopup"); - popup.id = OVERFLOW_POPUP_ID; - submenu.appendChild(popup); - return submenu; - }, - - // Creates and returns the xul:menuseparator that separates the standard - // context menu items from our items. - _makeSeparator: function CMP__makeSeparator() { - let elt = this.doc.createElement("menuseparator"); - elt.id = SEPARATOR_ID; - return elt; - }, - - // Returns the item elements contained in the overflow menu, a NodeList. - get _overflowElts() { - return this._overflowPopup.getElementsByClassName(OVERFLOW_ITEM_CLASS); - }, - - // Returns the overflow xul:menu. - get _overflowMenu() { - return this.doc.getElementById(OVERFLOW_MENU_ID); - }, - - // Returns the overflow xul:menupopup. - get _overflowPopup() { - return this.doc.getElementById(OVERFLOW_POPUP_ID); - }, - - // Returns the OVERFLOW_THRESH_PREF pref value if it exists or - // OVERFLOW_THRESH_DEFAULT if it doesn't. - get _overflowThreshold() { - let prefs = require("api-utils/preferences-service"); - return prefs.get(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT); - }, - - // Returns the xul:menuseparator. - get _separator() { - return this.doc.getElementById(SEPARATOR_ID); - }, - - // Returns the item elements contained in the top-level menu, a NodeList. - get _topLevelElts() { - return this.popupDOMElt.getElementsByClassName(TOPLEVEL_ITEM_CLASS); - } -}; - - -// Init the browserManager only after setting prototypes and such above, because -// it will cause browserManager.onTrack to be called immediately if there are -// open windows. -browserManager.init(); diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/hotkeys.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/hotkeys.js deleted file mode 100644 index f864f1f..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/hotkeys.js +++ /dev/null @@ -1,37 +0,0 @@ -/* 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"; - -const INVALID_HOTKEY = "Hotkey must have at least one modifier."; - -const { toJSON: jsonify, toString: stringify, - isFunctionKey } = require("api-utils/keyboard/utils"); -const { register, unregister } = require("api-utils/keyboard/hotkeys"); - -const Hotkey = exports.Hotkey = function Hotkey(options) { - if (!(this instanceof Hotkey)) - return new Hotkey(options); - - // Parsing key combination string. - let hotkey = jsonify(options.combo); - if (!isFunctionKey(hotkey.key) && !hotkey.modifiers.length) { - throw new TypeError(INVALID_HOTKEY); - } - - this.onPress = options.onPress; - this.toString = stringify.bind(null, hotkey); - // Registering listener on keyboard combination enclosed by this hotkey. - // Please note that `this.toString()` is a normalized version of - // `options.combination` where order of modifiers is sorted and `accel` is - // replaced with platform specific key. - register(this.toString(), this.onPress); - // We freeze instance before returning it in order to make it's properties - // read-only. - return Object.freeze(this); -}; -Hotkey.prototype.destroy = function destroy() { - unregister(this.toString(), this.onPress); -}; diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/l10n.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/l10n.js deleted file mode 100644 index 198f711..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/l10n.js +++ /dev/null @@ -1,149 +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"; - -const { Cc, Ci } = require("chrome"); -const { getPreferedLocales, findClosestLocale } = require("api-utils/l10n/locale"); -const { getRulesForLocale } = require("api-utils/l10n/plural-rules"); - -// Get URI for the addon root folder: -const { rootURI } = require("@packaging"); - -let globalHash = {}; -let pluralMappingFunction = getRulesForLocale("en"); - -exports.get = function get(k) { - - // For now, we only accept a "string" as first argument - // TODO: handle plural forms in gettext pattern - if (typeof k !== "string") - throw new Error("First argument of localization method should be a string"); - - // Get translation from big hashmap or default to hard coded string: - let localized = globalHash[k] || k; - - // # Simplest usecase: - // // String hard coded in source code: - // _("Hello world") - // // Identifier of a key stored in properties file - // _("helloString") - if (arguments.length <= 1) - return localized; - - let args = arguments; - - if (typeof localized == "object" && "other" in localized) { - // # Plural form: - // // Strings hard coded in source code: - // _(["One download", "%d downloads"], 10); - // // Identifier of a key stored in properties file - // _("downloadNumber", 0); - let n = arguments[1]; - - // First handle simple universal forms that may not be mandatory - // for each language, (i.e. not different than 'other' form, - // but still usefull for better phrasing) - // For example 0 in english is the same form than 'other' - // but we accept 'zero' form if specified in localization file - if (n === 0 && "zero" in localized) - localized = localized["zero"]; - else if (n === 1 && "one" in localized) - localized = localized["one"]; - else if (n === 2 && "two" in localized) - localized = localized["two"]; - else { - let pluralForm = pluralMappingFunction(n); - if (pluralForm in localized) - localized = localized[pluralForm]; - else // Fallback in case of error: missing plural form - localized = localized["other"]; - } - - // Simulate a string with one placeholder: - args = [null, n]; - } - - // # String with placeholders: - // // Strings hard coded in source code: - // _("Hello %s", username) - // // Identifier of a key stored in properties file - // _("helloString", username) - // * We supports `%1s`, `%2s`, ... pattern in order to change arguments order - // in translation. - // * In case of plural form, we has `%d` instead of `%s`. - let offset = 1; - localized = localized.replace(/%(\d*)(s|d)/g, function (v, n) { - let rv = args[n != "" ? n : offset]; - offset++; - return rv; - }); - - return localized; -} - -function readURI(uri) { - let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]. - createInstance(Ci.nsIXMLHttpRequest); - request.open('GET', uri, false); - request.overrideMimeType('text/plain'); - request.send(); - return request.responseText; -} - -function readJsonUri(uri) { - try { - return JSON.parse(readURI(uri)); - } - catch(e) { - console.error("Error while reading locale file:\n" + uri + "\n" + e); - } - return {}; -} - -// Returns the array stored in `locales.json` manifest that list available -// locales files -function getAvailableLocales() { - let uri = rootURI + "locales.json"; - let manifest = readJsonUri(uri); - - return "locales" in manifest && Array.isArray(manifest.locales) ? - manifest.locales : []; -} - -// Returns URI of the best locales file to use from the XPI -function getBestLocaleFile() { - - // Read localization manifest file that contains list of available languages - let availableLocales = getAvailableLocales(); - - // Retrieve list of prefered locales to use - let preferedLocales = getPreferedLocales(); - - // Compute the most preferable locale to use by using these two lists - let bestMatchingLocale = findClosestLocale(availableLocales, preferedLocales); - - // It may be null if the addon doesn't have any locale file - if (!bestMatchingLocale) - return null; - - // Retrieve the related plural mapping function - let shortLocaleCode = bestMatchingLocale.split("-")[0].toLowerCase(); - pluralMappingFunction = getRulesForLocale(shortLocaleCode); - - return rootURI + "locale/" + bestMatchingLocale + ".json"; -} - -function init() { - // First, search for a locale file: - let localeURI = getBestLocaleFile(); - if (!localeURI) - return; - - // Locale files only contains one big JSON object that is used as - // an hashtable of: "key to translate" => "translated key" - // TODO: We are likely to change this in order to be able to overload - // a specific key translation. For a specific package, module or line? - globalHash = readJsonUri(localeURI); -} -init(); diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/notifications.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/notifications.js deleted file mode 100644 index 8e66355..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/notifications.js +++ /dev/null @@ -1,79 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim:set ts=2 sw=2 sts=2 et filetype=javascript - * 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"; - -const { Cc, Ci, Cr } = require("chrome"); -const apiUtils = require("api-utils/api-utils"); -const errors = require("api-utils/errors"); - -try { - let alertServ = Cc["@mozilla.org/alerts-service;1"]. - getService(Ci.nsIAlertsService); - - // The unit test sets this to a mock notification function. - var notify = alertServ.showAlertNotification.bind(alertServ); -} -catch (err) { - // An exception will be thrown if the platform doesn't provide an alert - // service, e.g., if Growl is not installed on OS X. In that case, use a - // mock notification function that just logs to the console. - notify = notifyUsingConsole; -} - -exports.notify = function notifications_notify(options) { - let valOpts = validateOptions(options); - let clickObserver = !valOpts.onClick ? null : { - observe: function notificationClickObserved(subject, topic, data) { - if (topic === "alertclickcallback") - errors.catchAndLog(valOpts.onClick).call(exports, valOpts.data); - } - }; - function notifyWithOpts(notifyFn) { - notifyFn(valOpts.iconURL, valOpts.title, valOpts.text, !!clickObserver, - valOpts.data, clickObserver); - } - try { - notifyWithOpts(notify); - } - catch (err if err instanceof Ci.nsIException && - err.result == Cr.NS_ERROR_FILE_NOT_FOUND) { - console.warn("The notification icon named by " + valOpts.iconURL + - " does not exist. A default icon will be used instead."); - delete valOpts.iconURL; - notifyWithOpts(notify); - } - catch (err) { - notifyWithOpts(notifyUsingConsole); - } -}; - -function notifyUsingConsole(iconURL, title, text) { - title = title ? "[" + title + "]" : ""; - text = text || ""; - let str = [title, text].filter(function (s) s).join(" "); - console.log(str); -} - -function validateOptions(options) { - return apiUtils.validateOptions(options, { - data: { - is: ["string", "undefined"] - }, - iconURL: { - is: ["string", "undefined"] - }, - onClick: { - is: ["function", "undefined"] - }, - text: { - is: ["string", "undefined"] - }, - title: { - is: ["string", "undefined"] - } - }); -} diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/page-mod.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/page-mod.js deleted file mode 100644 index 2cc75f9..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/page-mod.js +++ /dev/null @@ -1,319 +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"; - -const observers = require("api-utils/observer-service"); -const { Worker, Loader } = require('api-utils/content'); -const { EventEmitter } = require('api-utils/events'); -const { List } = require('api-utils/list'); -const { Registry } = require('api-utils/utils/registry'); -const xulApp = require("api-utils/xul-app"); -const { MatchPattern } = require('api-utils/match-pattern'); -const { validateOptions : validate } = require('api-utils/api-utils'); -const { validationAttributes } = require('api-utils/content/loader'); -const { Cc, Ci } = require('chrome'); -const { merge } = require('api-utils/utils/object'); - -// Whether or not the host application dispatches a document-element-inserted -// notification when the document element is inserted into the DOM of a page. -// The notification was added in Gecko 2.0b6, it's a better time to attach -// scripts with contentScriptWhen "start" than content-document-global-created, -// since libraries like jQuery assume the presence of the document element. -const HAS_DOCUMENT_ELEMENT_INSERTED = - xulApp.versionInRange(xulApp.platformVersion, "2.0b6", "*"); -const ON_CONTENT = HAS_DOCUMENT_ELEMENT_INSERTED ? 'document-element-inserted' : - 'content-document-global-created'; - -// Workaround bug 642145: document-element-inserted is fired multiple times. -// This bug is fixed in Firefox 4.0.1, but we want to keep FF 4.0 compatibility -// Tracking bug 641457. To be removed when 4.0 has disappeared from earth. -const HAS_BUG_642145_FIXED = - xulApp.versionInRange(xulApp.platformVersion, "2.0.1", "*"); - -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); - -// 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)), -}); - -/** - * Returns the content of the uri given - */ -function readURI(uri) { - let channel = io.newChannel(uri, null, null); - - let stream = Cc["@mozilla.org/scriptableinputstream;1"]. - createInstance(Ci.nsIScriptableInputStream); - - stream.init(channel.open()); - - let data = stream.read(stream.available()); - - stream.close(); - - return data; -} - -/** - * PageMod constructor (exported below). - * @constructor - */ -const PageMod = Loader.compose(EventEmitter, { - on: EventEmitter.required, - _listeners: EventEmitter.required, - contentScript: Loader.required, - contentScriptFile: Loader.required, - contentScriptWhen: 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 ('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); - - 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(readURI).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 = []; - }, - - 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: [], - - _onContent: function _onContent(window) { - // not registered yet - if (!pageModManager.has(this)) - return; - - if (!HAS_BUG_642145_FIXED) { - if (this._loadingWindows.indexOf(window) != -1) - return; - this._loadingWindows.push(window); - } - - if ('start' == this.contentScriptWhen) { - 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, - onError: this._onUncaughtError - }); - this._emit('attach', worker); - let self = this; - worker.once('detach', function detach() { - worker.destroy(); - - if (!HAS_BUG_642145_FIXED) { - let idx = self._loadingWindows.indexOf(window); - if (idx != -1) - self._loadingWindows.splice(idx, 1); - } - }); - }, - _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.length = 0; - break; - } - } - - let uri = "data:text/css,"; - 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( - ON_CONTENT, this._onContentWindow = this._onContentWindow.bind(this) - ); - }, - _destructor: function _destructor() { - observers.remove(ON_CONTENT, this._onContentWindow); - this._removeAllListeners(); - for (let rule in RULES) { - delete RULES[rule]; - } - this._registryDestructor(); - }, - _onContentWindow: function _onContentWindow(domObj) { - let window = HAS_DOCUMENT_ELEMENT_INSERTED ? domObj.defaultView : domObj; - // XML documents don't have windows, and we don't yet support them. - if (!window) - return; - for (let rule in RULES) - if (RULES[rule].test(window.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();
\ No newline at end of file diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/page-worker.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/page-worker.js deleted file mode 100644 index 7e7b73e..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/page-worker.js +++ /dev/null @@ -1,65 +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"; - -const { Symbiont } = require("api-utils/content"); -const { Trait } = require("api-utils/traits"); - -if (!require("api-utils/xul-app").isOneOf(["Firefox", "Thunderbird"])) { - throw new Error([ - "The page-worker module currently supports only Firefox and Thunderbird. ", - "In the future, we would like it to support other applications, however. ", - "Please see https://bugzilla.mozilla.org/show_bug.cgi?id=546740 for more ", - "information." - ].join("")); -} - -const Page = Trait.compose( - Symbiont.resolve({ - constructor: '_initSymbiont' - }), - { - _frame: Trait.required, - _initFrame: Trait.required, - postMessage: Symbiont.required, - on: Symbiont.required, - destroy: Symbiont.required, - - constructor: function Page(options) { - options = options || {}; - - this.contentURL = 'contentURL' in options ? options.contentURL - : 'about:blank'; - if ('contentScriptWhen' in options) - this.contentScriptWhen = options.contentScriptWhen; - if ('contentScriptFile' in options) - this.contentScriptFile = options.contentScriptFile; - if ('contentScript' in options) - this.contentScript = options.contentScript; - if ('allow' in options) - this.allow = options.allow; - if ('onError' in options) - this.on('error', options.onError); - if ('onMessage' in options) - this.on('message', options.onMessage); - - this.on('propertyChange', this._onChange.bind(this)); - - this._initSymbiont(); - }, - - _onChange: function _onChange(e) { - if ('contentURL' in e && this._frame) { - // Cleanup the worker before injecting the content script in the new - // document - this._workerCleanup(); - this._initFrame(this._frame); - } - } - } -); -exports.Page = function(options) Page(options); -exports.Page.prototype = Page.prototype; diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/panel.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/panel.js deleted file mode 100644 index 5593276..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/panel.js +++ /dev/null @@ -1,381 +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"; - -if (!require("api-utils/xul-app").is("Firefox")) { - throw new Error([ - "The panel module currently supports only Firefox. In the future ", - "we would like it to support other applications, however. Please see ", - "https://bugzilla.mozilla.org/show_bug.cgi?id=jetpack-panel-apps ", - "for more information." - ].join("")); -} - -const { Cc, Ci } = require("chrome"); - -const { validateOptions: valid } = require("api-utils/api-utils"); -const { Symbiont } = require("api-utils/content"); -const { EventEmitter } = require('api-utils/events'); -const timer = require("api-utils/timer"); -const runtime = require("api-utils/runtime"); - -const windowMediator = Cc['@mozilla.org/appshell/window-mediator;1']. - getService(Ci.nsIWindowMediator); - -const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", - ON_SHOW = 'popupshown', - ON_HIDE = 'popuphidden', - validNumber = { is: ['number', 'undefined', 'null'] }; - -/** - * Emits show and hide events. - */ -const Panel = Symbiont.resolve({ - constructor: '_init', - _onInit: '_onSymbiontInit', - destroy: '_symbiontDestructor', - _documentUnload: '_workerDocumentUnload' -}).compose({ - _frame: Symbiont.required, - _init: Symbiont.required, - _onSymbiontInit: Symbiont.required, - _symbiontDestructor: Symbiont.required, - _emit: Symbiont.required, - on: Symbiont.required, - removeListener: Symbiont.required, - - _inited: false, - - /** - * If set to `true` frame loaders between xul panel frame and - * hidden frame are swapped. If set to `false` frame loaders are - * set back to normal. Setting the value that was already set will - * have no effect. - */ - set _frameLoadersSwapped(value) { - if (this.__frameLoadersSwapped == value) return; - this._frame.QueryInterface(Ci.nsIFrameLoaderOwner) - .swapFrameLoaders(this._viewFrame); - this.__frameLoadersSwapped = value; - }, - __frameLoadersSwapped: false, - - constructor: function Panel(options) { - this._onShow = this._onShow.bind(this); - this._onHide = this._onHide.bind(this); - this.on('inited', this._onSymbiontInit.bind(this)); - this.on('propertyChange', this._onChange.bind(this)); - - options = options || {}; - if ('onShow' in options) - this.on('show', options.onShow); - if ('onHide' in options) - this.on('hide', options.onHide); - if ('width' in options) - this.width = options.width; - if ('height' in options) - this.height = options.height; - if ('contentURL' in options) - this.contentURL = options.contentURL; - - this._init(options); - }, - _destructor: function _destructor() { - this.hide(); - this._removeAllListeners('show'); - this._removeAllListeners('hide'); - this._removeAllListeners('propertyChange'); - this._removeAllListeners('inited'); - // defer cleanup to be performed after panel gets hidden - this._xulPanel = null; - this._symbiontDestructor(this); - this._removeAllListeners(); - }, - destroy: function destroy() { - this._destructor(); - }, - /* Public API: Panel.width */ - get width() this._width, - set width(value) - this._width = valid({ $: value }, { $: validNumber }).$ || this._width, - _width: 320, - /* Public API: Panel.height */ - get height() this._height, - set height(value) - this._height = valid({ $: value }, { $: validNumber }).$ || this._height, - _height: 240, - - /* Public API: Panel.isShowing */ - get isShowing() !!this._xulPanel && this._xulPanel.state == "open", - - /* Public API: Panel.show */ - show: function show(anchor) { - anchor = anchor || null; - let document = getWindow(anchor).document; - let xulPanel = this._xulPanel; - if (!xulPanel) { - xulPanel = this._xulPanel = document.createElementNS(XUL_NS, 'panel'); - xulPanel.setAttribute("type", "arrow"); - - // One anonymous node has a big padding that doesn't work well with - // Jetpack, as we would like to display an iframe that completely fills - // the panel. - // -> Use a XBL wrapper with inner stylesheet to remove this padding. - let css = ".panel-inner-arrowcontent, .panel-arrowcontent {padding: 0;}"; - let originalXBL = "chrome://global/content/bindings/popup.xml#arrowpanel"; - let binding = - '<bindings xmlns="http://www.mozilla.org/xbl">' + - '<binding id="id" extends="' + originalXBL + '">' + - '<resources>' + - '<stylesheet src="data:text/css,' + - document.defaultView.encodeURIComponent(css) + '"/>' + - '</resources>' + - '</binding>' + - '</bindings>'; - xulPanel.style.MozBinding = 'url("data:text/xml,' + - document.defaultView.encodeURIComponent(binding) + '")'; - - let frame = document.createElementNS(XUL_NS, 'iframe'); - frame.setAttribute('type', 'content'); - frame.setAttribute('flex', '1'); - frame.setAttribute('transparent', 'transparent'); - if (runtime.OS === "Darwin") { - frame.style.borderRadius = "6px"; - frame.style.padding = "1px"; - } - - // Load an empty document in order to have an immediatly loaded iframe, - // so swapFrameLoaders is going to work without having to wait for load. - frame.setAttribute("src","data:,"); - - xulPanel.appendChild(frame); - document.getElementById("mainPopupSet").appendChild(xulPanel); - } - let { width, height } = this, x, y, position; - - if (!anchor) { - // Open the popup in the middle of the window. - x = document.documentElement.clientWidth / 2 - width / 2; - y = document.documentElement.clientHeight / 2 - height / 2; - position = null; - } - else { - // Open the popup by the anchor. - let rect = anchor.getBoundingClientRect(); - - let window = anchor.ownerDocument.defaultView; - - let zoom = window.mozScreenPixelsPerCSSPixel; - let screenX = rect.left + window.mozInnerScreenX * zoom; - let screenY = rect.top + window.mozInnerScreenY * zoom; - - // Set up the vertical position of the popup relative to the anchor - // (always display the arrow on anchor center) - let horizontal, vertical; - if (screenY > window.screen.availHeight / 2 + height) - vertical = "top"; - else - vertical = "bottom"; - - if (screenY > window.screen.availWidth / 2 + width) - horizontal = "left"; - else - horizontal = "right"; - - let verticalInverse = vertical == "top" ? "bottom" : "top"; - position = vertical + "center " + verticalInverse + horizontal; - - // Allow panel to flip itself if the panel can't be displayed at the - // specified position (useful if we compute a bad position or if the - // user moves the window and panel remains visible) - xulPanel.setAttribute("flip","both"); - } - - // Resize the iframe instead of using panel.sizeTo - // because sizeTo doesn't work with arrow panels - xulPanel.firstChild.style.width = width + "px"; - xulPanel.firstChild.style.height = height + "px"; - - // Wait for the XBL binding to be constructed - function waitForBinding() { - if (!xulPanel.openPopup) { - timer.setTimeout(waitForBinding, 50); - return; - } - xulPanel.openPopup(anchor, position, x, y); - } - waitForBinding(); - - return this._public; - }, - /* Public API: Panel.hide */ - hide: function hide() { - // The popuphiding handler takes care of swapping back the frame loaders - // and removing the XUL panel from the application window, we just have to - // trigger it by hiding the popup. - // XXX Sometimes I get "TypeError: xulPanel.hidePopup is not a function" - // when quitting the host application while a panel is visible. To suppress - // them, this now checks for "hidePopup" in xulPanel before calling it. - // It's not clear if there's an actual issue or the error is just normal. - let xulPanel = this._xulPanel; - if (xulPanel && "hidePopup" in xulPanel) - xulPanel.hidePopup(); - return this._public; - }, - - /* Public API: Panel.resize */ - resize: function resize(width, height) { - this.width = width; - this.height = height; - // Resize the iframe instead of using panel.sizeTo - // because sizeTo doesn't work with arrow panels - let xulPanel = this._xulPanel; - if (xulPanel) { - xulPanel.firstChild.style.width = width + "px"; - xulPanel.firstChild.style.height = height + "px"; - } - }, - - // While the panel is visible, this is the XUL <panel> we use to display it. - // Otherwise, it's null. - get _xulPanel() this.__xulPanel, - set _xulPanel(value) { - let xulPanel = this.__xulPanel; - if (value === xulPanel) return; - if (xulPanel) { - xulPanel.removeEventListener(ON_HIDE, this._onHide, false); - xulPanel.removeEventListener(ON_SHOW, this._onShow, false); - xulPanel.parentNode.removeChild(xulPanel); - } - if (value) { - value.addEventListener(ON_HIDE, this._onHide, false); - value.addEventListener(ON_SHOW, this._onShow, false); - } - this.__xulPanel = value; - }, - __xulPanel: null, - get _viewFrame() this.__xulPanel.children[0], - /** - * When the XUL panel becomes hidden, we swap frame loaders back to move - * the content of the panel to the hidden frame & remove panel element. - */ - _onHide: function _onHide() { - try { - this._frameLoadersSwapped = false; - this._xulPanel = null; - this._emit('hide'); - } catch(e) { - this._emit('error', e); - } - }, - /** - * When the XUL panel becomes shown, we swap frame loaders between panel - * frame and hidden frame to preserve state of the content dom. - */ - _onShow: function _onShow() { - try { - if (!this._inited) { // defer if not initialized yet - this.on('inited', this._onShow.bind(this)); - } else { - this._frameLoadersSwapped = true; - - // Retrieve computed text color style in order to apply to the iframe - // document. As MacOS background is dark gray, we need to use skin's - // text color. - let win = this._xulPanel.ownerDocument.defaultView; - let node = win.document.getAnonymousElementByAttribute(this._xulPanel, - "class", "panel-inner-arrowcontent"); - let textColor = win.getComputedStyle(node).getPropertyValue("color"); - let doc = this._xulPanel.firstChild.contentDocument; - let style = doc.createElement("style"); - style.textContent = "body { color: " + textColor + "; }"; - let container = doc.head ? doc.head : doc.documentElement; - - if (container.firstChild) - container.insertBefore(style, container.firstChild); - else - container.appendChild(style); - - this._emit('show'); - } - } catch(e) { - this._emit('error', e); - } - }, - /** - * Notification that panel was fully initialized. - */ - _onInit: function _onInit() { - this._inited = true; - - // Avoid panel document from resizing the browser window - // New platform capability added through bug 635673 - if ("allowWindowControl" in this._frame.docShell) - this._frame.docShell.allowWindowControl = false; - - // perform all deferred tasks like initSymbiont, show, hide ... - // TODO: We're publicly exposing a private event here; this - // 'inited' event should really be made private, somehow. - this._emit('inited'); - }, - - // Catch document unload event in order to rebind load event listener with - // Symbiont._initFrame if Worker._documentUnload destroyed the worker - _documentUnload: function(subject, topic, data) { - if (this._workerDocumentUnload(subject, topic, data)) { - this._initFrame(this._frame); - return true; - } - return false; - }, - - _onChange: function _onChange(e) { - if ('contentURL' in e && this._frame) { - // Cleanup the worker before injecting the content script in the new - // document - this._workerCleanup(); - this._initFrame(this._frame); - } - } -}); -exports.Panel = function(options) Panel(options) -exports.Panel.prototype = Panel.prototype; - -function getWindow(anchor) { - let window; - - if (anchor) { - let anchorWindow = anchor.ownerDocument.defaultView.top; - let anchorDocument = anchorWindow.document; - - let enumerator = windowMediator.getEnumerator("navigator:browser"); - while (enumerator.hasMoreElements()) { - let enumWindow = enumerator.getNext(); - - // Check if the anchor is in this browser window. - if (enumWindow == anchorWindow) { - window = anchorWindow; - break; - } - - // Check if the anchor is in a browser tab in this browser window. - let browser = enumWindow.gBrowser.getBrowserForDocument(anchorDocument); - if (browser) { - window = enumWindow; - break; - } - - // Look in other subdocuments (sidebar, etc.)? - } - } - - // If we didn't find the anchor's window (or we have no anchor), - // return the most recent browser window. - if (!window) - window = windowMediator.getMostRecentWindow("navigator:browser"); - - return window; -} - diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/passwords.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/passwords.js deleted file mode 100644 index 3da63ad..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/passwords.js +++ /dev/null @@ -1,59 +0,0 @@ -/* 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"; - -const { search, remove, store } = require("api-utils/passwords/utils"); -const { defer, delay } = require("api-utils/functional"); - -/** - * Utility function that returns `onComplete` and `onError` callbacks form the - * given `options` objects. Also properties are removed from the passed - * `options` objects. - * @param {Object} options - * Object that is passed to the exported functions of this module. - * @returns {Function[]} - * Array with two elements `onComplete` and `onError` functions. - */ -function getCallbacks(options) { - let value = [ - 'onComplete' in options ? options.onComplete : null, - 'onError' in options ? defer(options.onError) : console.exception - ]; - - delete options.onComplete; - delete options.onError; - - return value; -}; - -/** - * Creates a wrapper function that tries to call `onComplete` with a return - * value of the wrapped function or falls back to `onError` if wrapped function - * throws an exception. - */ -function createWrapperMethod(wrapped) { - return function (options) { - let [ onComplete, onError ] = getCallbacks(options); - try { - let value = wrapped(options); - if (onComplete) { - delay(function() { - try { - onComplete(value); - } catch (exception) { - onError(exception); - } - }); - } - } catch (exception) { - onError(exception); - } - }; -} - -exports.search = createWrapperMethod(search); -exports.store = createWrapperMethod(store); -exports.remove = createWrapperMethod(remove); diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/private-browsing.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/private-browsing.js deleted file mode 100644 index ff40c98..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/private-browsing.js +++ /dev/null @@ -1,61 +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"; - -const { Cc, Ci } = require("chrome"); -const { emit, on, once, off } = require("api-utils/event/core"); -const { defer } = require("api-utils/functional"); -const { when: unload } = require("api-utils/unload"); -const observers = require("api-utils/observer-service"); - -// Model holding a state. -const model = { active: false }; - -let deferredEmit = defer(emit); - -let pbService; -// Currently, only Firefox implements the private browsing service. -if (require("api-utils/xul-app").is("Firefox")) { - pbService = Cc["@mozilla.org/privatebrowsing;1"]. - getService(Ci.nsIPrivateBrowsingService); - - // Update model state. - model.active = pbService.privateBrowsingEnabled; - - // set up an observer for private browsing switches. - observers.add('private-browsing-transition-complete', function onChange() { - // Update model state. - model.active = pbService.privateBrowsingEnabled; - // Emit event with in next turn of event loop. - deferredEmit(exports, model.active ? 'start' : 'stop'); - }); -} - -let setMode = defer(function setMode(value) { - // We toggle private browsing mode asynchronously in order to work around - // bug 659629. Since private browsing transitions are asynchronous - // anyway, this doesn't significantly change the behavior of the API. - pbService.privateBrowsingEnabled = !!value -}); - - -// Make sure listeners are cleaned up. -unload(function() off(exports)); - -Object.defineProperty(exports, "isActive", { get: function() model.active }); -exports.activate = function activate() pbService && setMode(true) -exports.deactivate = function deactivate() pbService && setMode(false) -exports.on = on.bind(null, exports); -exports.once = once.bind(null, exports); -exports.removeListener = function removeListener(type, listener) { - // Note: We can't just bind `off` as we do it for other methods cause skipping - // a listener argument will remove all listeners for the given event type - // causing misbehavior. This way we make sure all arguments are passed. - off(exports, type, listener); -}; - -// This is workaround making sure that exports is wrapped before it's -// frozen, which needs to happen in order to workaround Bug 673468. -off(exports, 'workaround-bug-673468'); diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/request.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/request.js deleted file mode 100644 index b213af8..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/request.js +++ /dev/null @@ -1,208 +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"; - -const { Base, Class } = require("api-utils/base"); -const { ns } = require("api-utils/namespace"); -const { emit } = require("api-utils/event/core"); -const { merge } = require("api-utils/utils/object"); -const { stringify } = require("api-utils/querystring"); -const { EventTarget } = require("api-utils/event/target"); -const { XMLHttpRequest } = require("api-utils/xhr"); -const apiUtils = require("api-utils/api-utils"); - -const response = ns(); -const request = ns(); - -// Instead of creating a new validator for each request, just make one and -// reuse it. -const { validateOptions, validateSingleOption } = new OptionsValidator({ - url: { - //XXXzpao should probably verify that url is a valid url as well - is: ["string"] - }, - headers: { - map: function (v) v || {}, - is: ["object"], - }, - content: { - map: function (v) v || null, - is: ["string", "object", "null"], - }, - contentType: { - map: function (v) v || "application/x-www-form-urlencoded", - is: ["string"], - }, - overrideMimeType: { - map: function(v) v || null, - is: ["string", "null"], - } -}); - -const REUSE_ERROR = "This request object has been used already. You must " + - "create a new one to make a new request." - -// Utility function to prep the request since it's the same between GET and -// POST -function runRequest(mode, target) { - let source = request(target) - let { xhr, url, content, contentType, headers, overrideMimeType } = source; - - // If this request has already been used, then we can't reuse it. - // Throw an error. - if (xhr) - throw new Error(REUSE_ERROR); - - xhr = source.xhr = new XMLHttpRequest(); - - // Build the data to be set. For GET requests, we want to append that to - // the URL before opening the request. - let data = stringify(content); - // If the URL already has ? in it, then we want to just use & - if (mode == "GET" && data) - url = url + (/\?/.test(url) ? "&" : "?") + data; - - // open the request - xhr.open(mode, url); - - // request header must be set after open, but before send - xhr.setRequestHeader("Content-Type", contentType); - - // set other headers - Object.keys(headers).forEach(function(name) { - xhr.setRequestHeader(name, headers[name]); - }); - - // set overrideMimeType - if (overrideMimeType) - xhr.overrideMimeType(overrideMimeType); - - // handle the readystate, create the response, and call the callback - xhr.onreadystatechange = function onreadystatechange() { - if (xhr.readyState === 4) { - let response = Response.new(xhr); - source.response = response; - emit(target, 'complete', response); - } - }; - - // actually send the request. - // We don't want to send data on GET requests. - xhr.send(mode !== "GET" ? data : null); -} - -const Request = EventTarget.extend({ - initialize: function initialize(options) { - // `EventTarget.initialize` will set event listeners that are named - // like `onEvent` in this case `onComplete` listener will be set to - // `complete` event. - EventTarget.initialize.call(this, options); - - // Copy normalized options. - merge(request(this), validateOptions(options)); - }, - get url() { return request(this).url; }, - set url(value) { request(this).url = validateSingleOption('url', value); }, - get headers() { return request(this).headers; }, - set headers(value) { - return request(this).headers = validateSingleOption('headers', value); - }, - get content() { return request(this).content; }, - set content(value) { - request(this).content = validateSingleOption('content', value); - }, - get contentType() { return request(this).contentType; }, - set contentType(value) { - request(this).contentType = validateSingleOption('contentType', value); - }, - get response() { return request(this).response; }, - get: function() { - runRequest('GET', this); - return this; - }, - post: function() { - runRequest('POST', this); - return this; - }, - put: function() { - runRequest('PUT', this); - return this; - } -}); -exports.Request = Class(Request); - -const Response = Base.extend({ - initialize: function initialize(request) { - response(this).request = request; - }, - get text() response(this).request.responseText, - get xml() { - throw new Error("Sorry, the 'xml' property is no longer available. " + - "see bug 611042 for more information."); - }, - get status() response(this).request.status, - get statusText() response(this).request.statusText, - get json() { - try { - return JSON.parse(this.text); - } catch(error) { - return null; - } - }, - get headers() { - let headers = {}, lastKey; - // Since getAllResponseHeaders() will return null if there are no headers, - // defend against it by defaulting to "" - let rawHeaders = response(this).request.getAllResponseHeaders() || ""; - rawHeaders.split("\n").forEach(function (h) { - // According to the HTTP spec, the header string is terminated by an empty - // line, so we can just skip it. - if (!h.length) { - return; - } - - let index = h.indexOf(":"); - // The spec allows for leading spaces, so instead of assuming a single - // leading space, just trim the values. - let key = h.substring(0, index).trim(), - val = h.substring(index + 1).trim(); - - // For empty keys, that means that the header value spanned multiple lines. - // In that case we should append the value to the value of lastKey with a - // new line. We'll assume lastKey will be set because there should never - // be an empty key on the first pass. - if (key) { - headers[key] = val; - lastKey = key; - } - else { - headers[lastKey] += "\n" + val; - } - }); - return headers; - } -}); - -// apiUtils.validateOptions doesn't give the ability to easily validate single -// options, so this is a wrapper that provides that ability. -function OptionsValidator(rules) { - return { - validateOptions: function (options) { - return apiUtils.validateOptions(options, rules); - }, - validateSingleOption: function (field, value) { - // We need to create a single rule object from our listed rules. To avoid - // JavaScript String warnings, check for the field & default to an empty object. - let singleRule = {}; - if (field in rules) { - singleRule[field] = rules[field]; - } - let singleOption = {}; - singleOption[field] = value; - // This should throw if it's invalid, which will bubble up & out. - return apiUtils.validateOptions(singleOption, singleRule)[field]; - } - }; -} diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/selection.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/selection.js deleted file mode 100644 index f10d890..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/selection.js +++ /dev/null @@ -1,421 +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"; - -if (!require("api-utils/xul-app").is("Firefox")) { - throw new Error([ - "The selection module currently supports only Firefox. In the future ", - "we would like it to support other applications, however. Please see ", - "https://bugzilla.mozilla.org/show_bug.cgi?id=560716 for more information." - ].join("")); -} - -let { Ci, Cc } = require("chrome"), - { setTimeout } = require("api-utils/timer"), - { emit, off } = require("api-utils/event/core"), - { Unknown } = require("api-utils/xpcom"), - { Base } = require("api-utils/base"), - { EventTarget } = require("api-utils/event/target"); - - -const windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"]. - getService(Ci.nsIWindowMediator); - - -// The selection type HTML -const HTML = 0x01; - -// The selection type TEXT -const TEXT = 0x02; - -// The selection type DOM (internal use only) -const DOM = 0x03; - -// A more developer-friendly message than the caught exception when is not -// possible change a selection. -const ERR_CANNOT_CHANGE_SELECTION = - "It isn't possible to change the selection, as there isn't currently a selection"; - -const Selection = Base.extend({ - /** - * Creates an object from which a selection can be set, get, etc. Each - * object has an associated with a range number. Range numbers are the - * 0-indexed counter of selection ranges as explained at - * https://developer.mozilla.org/en/DOM/Selection. - * - * @param rangeNumber - * The zero-based range index into the selection - */ - initialize: function initialize(rangeNumber) { - // In order to hide the private `rangeNumber` argument from API consumers - // while still enabling Selection getters/setters to access it, we define - // it as non enumerable, non configurable property. While consumers still - // may discover it they won't be able to do any harm which is good enough - // in this case. - Object.defineProperties(this, { - rangeNumber: { - enumerable: false, - configurable: false, - value: rangeNumber - } - }); - }, - get text() { return getSelection(TEXT, this.rangeNumber); }, - set text(value) { setSelection(value, this.rangeNumber); }, - get html() { return getSelection(HTML, this.rangeNumber); }, - set html(value) { setSelection(value, this.rangeNumber); }, - get isContiguous() { - let selection = getSelection(DOM); - - // If there are multiple ranges, the selection is definitely discontiguous. - // It returns `false` also if there are no selection; and `true` if there is - // a single non empty range, or a selection in a text field - contiguous or - // not (text field selection APIs doesn't support multiple selections). - - if (selection.rangeCount > 1) - return false; - - return !!(safeGetRange(selection, 0) || getElementWithSelection()); - } -}); - -/** - * Returns the most recent content window - */ -function context() { - // Overlay names should probably go into the xul-app module instead of here - return windowMediator.getMostRecentWindow("navigator:browser").document. - commandDispatcher.focusedWindow; -} - -/** - * Returns the current selection from most recent content window. Depending on - * the specified |type|, the value returned can be a string of text, stringified - * HTML, or a DOM selection object as described at - * https://developer.mozilla.org/en/DOM/Selection. - * - * @param type - * Specifies the return type of the selection. Valid values are the one - * of the constants HTML, TEXT, or DOM. - * - * @param rangeNumber - * Specifies the zero-based range index of the returned selection. - */ -function getSelection(type, rangeNumber) { - let window, selection; - try { - window = context(); - selection = window.getSelection(); - } - catch (e) { - return null; - } - - // Get the selected content as the specified type - if (type == DOM) - return selection; - else if (type == TEXT) { - let range = safeGetRange(selection, rangeNumber); - - if (range) - return range.toString(); - - let node = getElementWithSelection(window); - - if (!node) - return null; - - return node.value.substring(node.selectionStart, node.selectionEnd); - } - else if (type == HTML) { - let range = safeGetRange(selection, rangeNumber); - // Another way, but this includes the xmlns attribute for all elements in - // Gecko 1.9.2+ : - // return Cc["@mozilla.org/xmlextras/xmlserializer;1"]. - // createInstance(Ci.nsIDOMSerializer).serializeToSTring(range. - // cloneContents()); - if (!range) - return null; - let node = window.document.createElement("span"); - node.appendChild(range.cloneContents()); - return node.innerHTML; - } - throw new Error("Type " + type + " is unrecognized."); -} - -/** - * Returns the specified range in a selection without throwing an exception. - * - * @param selection - * A selection object as described at - * https://developer.mozilla.org/en/DOM/Selection - * - * @param rangeNumber - * Specifies the zero-based range index of the returned selection. - */ -function safeGetRange(selection, rangeNumber) { - try { - let range = selection.getRangeAt(rangeNumber); - if (!range || range.toString() == "") - return null; - return range; - } - catch (e) { - return null; - } -} - -/** - * Returns a reference of the DOM's active element for the window given, if it - * supports the text field selection API and has a text selected. - * - * Note: - * we need this method because window.getSelection doesn't return a selection - * for text selected in a form field (see bug 85686) - * - * @param {nsIWindow} [window] - * A reference to a window - */ -function getElementWithSelection(window) { - let element; - - try { - element = (window || context()).document.activeElement; - } - catch (e) { - element = null; - } - - if (!element) - return null; - - let { value, selectionStart, selectionEnd } = element; - - let hasSelection = typeof value === "string" && - !isNaN(selectionStart) && - !isNaN(selectionEnd) && - selectionStart !== selectionEnd; - - return hasSelection ? element : null; -} -/** - * Sets the current selection of the most recent content document by changing - * the existing selected text/HTML range to the specified value. - * - * @param val - * The value for the new selection - * - * @param rangeNumber - * The zero-based range index of the selection to be set - * - */ -function setSelection(val, rangeNumber) { - // Make sure we have a window context & that there is a current selection. - // Selection cannot be set unless there is an existing selection. - let window, selection; - - try { - window = context(); - selection = window.getSelection(); - } - catch (e) { - throw new Error(ERR_CANNOT_CHANGE_SELECTION); - } - - let range = safeGetRange(selection, rangeNumber); - - if (range) { - // Get rid of the current selection and insert our own - range.deleteContents(); - let node = window.document.createElement("span"); - range.surroundContents(node); - - // Some relevant JEP-111 requirements: - - // Setting the text property replaces the selection with the value to - // which the property is set and sets the html property to the same value - // to which the text property is being set. - - // Setting the html property replaces the selection with the value to - // which the property is set and sets the text property to the text version - // of the HTML value. - - // This sets both the HTML and text properties. - node.innerHTML = val; - } else { - let node = getElementWithSelection(window); - - if (!node) - throw new Error(ERR_CANNOT_CHANGE_SELECTION); - - let { value, selectionStart, selectionEnd } = node; - - let newSelectionEnd = selectionStart + val.length; - - node.value = value.substring(0, selectionStart) + - val + - value.substring(selectionEnd, value.length); - - node.setSelectionRange(selectionStart, newSelectionEnd); - } -} - -function onLoad(event) { - SelectionListenerManager.onLoad(event); -} - -function onUnload(event) { - SelectionListenerManager.onUnload(event); -} - -function onSelect() { - SelectionListenerManager.onSelect(); -} - -let SelectionListenerManager = Unknown.extend({ - interfaces: [ 'nsISelectionListener' ], - /** - * This is the nsISelectionListener implementation. This function is called - * by Gecko when a selection is changed interactively. - * - * We only pay attention to the SELECTALL, KEYPRESS, and MOUSEUP selection - * reasons. All reasons are listed here: - * - * http://mxr.mozilla.org/mozilla1.9.2/source/content/base/public/ - * nsISelectionListener.idl - * - * The other reasons (NO_REASON, DRAG_REASON, MOUSEDOWN_REASON) aren't - * applicable to us. - */ - notifySelectionChanged: function notifySelectionChanged(document, selection, - reason) { - if (!["SELECTALL", "KEYPRESS", "MOUSEUP"].some(function(type) reason & - Ci.nsISelectionListener[type + "_REASON"]) || selection.toString() == "") - return; - - this.onSelect(); - }, - - onSelect : function onSelect() { - setTimeout(emit, 0, module.exports, "select"); - }, - - /** - * Part of the Tracker implementation. This function is called by the - * tabs module when a browser is being tracked. Often, that means a new tab - * has been opened, but it can also mean an addon has been installed while - * tabs are already opened. In that case, this function is called for those - * already-opened tabs. - * - * @param browser - * The browser being tracked - */ - onTrack: function onTrack(browser) { - browser.addEventListener("load", onLoad, true); - browser.addEventListener("unload", onUnload, true); - }, - - onLoad: function onLoad(event) { - // Nothing to do without a useful window - let window = event.target.defaultView; - if (!window) - return; - - // Wrap the add selection call with some number of setTimeout 0 because some - // reason it's possible to add a selection listener "too early". 2 sometimes - // works for gmail, and more consistently with 3, so make it 5 to be safe. - let count = 0; - let self = this; - function wrap(count, func) { - if (count-- > 0) - setTimeout(wrap, 0); - else - self.addSelectionListener(window); - } - wrap(); - }, - - addSelectionListener: function addSelectionListener(window) { - if (window.jetpack_core_selection_listener) - return; - let selection = window.getSelection(); - if (selection instanceof Ci.nsISelectionPrivate) - selection.addSelectionListener(this); - - // nsISelectionListener implementation seems not fire a notification if - // a selection is in a text field, therefore we need to add a listener to - // window.onselect, that is fired only for text fields. - // https://developer.mozilla.org/en/DOM/window.onselect - window.addEventListener("select", onSelect, true); - - window.jetpack_core_selection_listener = true; - }, - - onUnload: function onUnload(event) { - // Nothing to do without a useful window - let window = event.target.defaultView; - if (!window) - return; - this.removeSelectionListener(window); - off(exports); - }, - - removeSelectionListener: function removeSelectionListener(window) { - if (!window.jetpack_core_selection_listener) - return; - let selection = window.getSelection(); - if (selection instanceof Ci.nsISelectionPrivate) - selection.removeSelectionListener(this); - - window.removeEventListener("select", onSelect); - - window.jetpack_core_selection_listener = false; - }, - - /** - * Part of the TabTracker implementation. This function is called by the - * tabs module when a browser is being untracked. Usually, that means a tab - * has been closed. - * - * @param browser - * The browser being untracked - */ - onUntrack: function onUntrack(browser) { - browser.removeEventListener("load", onLoad, true); - browser.removeEventListener("unload", onUnload, true); - } -}); - -/** - * Install |SelectionListenerManager| as tab tracker in order to watch - * tab opening/closing - */ -require("api-utils/tab-browser").Tracker(SelectionListenerManager); - -// Note: We use `Object.create` form just in order to define `__iterator__` -// as non-enumerable, to ensure that it won't be returned by an `Object.keys`. -var SelectionIterator = Object.create(Object.prototype, { - /** - * Exports an iterator so that discontiguous selections can be iterated. - * - * If discontiguous selections are in a text field, only the first one - * is returned because the text field selection APIs doesn't support - * multiple selections. - */ - __iterator__: { enumerable: false, value: function() { - let selection = getSelection(DOM); - let count = selection.rangeCount || (getElementWithSelection() ? 1 : 0); - - for (let i = 0; i < count; i++) - yield Selection.new(i); - }} -}); - -var selection = EventTarget.extend(Selection, SelectionIterator).new(0); - -// This is workaround making sure that exports is wrapped before it's -// frozen, which needs to happen in order to workaround Bug 673468. -off(selection, 'workaround-bug-673468'); -module.exports = selection; diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/simple-prefs.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/simple-prefs.js deleted file mode 100644 index 5f73cc5..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/simple-prefs.js +++ /dev/null @@ -1,68 +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/. */ - -const { Cc, Ci } = require("chrome"); -const { emit, off } = require("api-utils/event/core"); -const { EventTarget } = require("api-utils/event/target"); -const { when: unload } = require("api-utils/unload"); -const { jetpackID } = require("@packaging"); -const prefService = require("api-utils/preferences-service"); -const observers = require("api-utils/observer-service"); - -const ADDON_BRANCH = "extensions." + jetpackID + "."; -const BUTTON_PRESSED = jetpackID + "-cmdPressed"; - -// XXX Currently, only Firefox implements the inline preferences. -if (!require("xul-app").is("Firefox")) - throw Error("This API is only supported in Firefox"); - -const branch = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefService). - getBranch(ADDON_BRANCH). - QueryInterface(Ci.nsIPrefBranch2); - -// Listen to changes in the preferences -function preferenceChange(subject, topic, name) { - if (topic === 'nsPref:changed') - emit(target, name, name); -} -branch.addObserver('', preferenceChange, false); - -// Listen to clicks on buttons -function buttonClick(subject, data) { - emit(target, data); -} -observers.add(BUTTON_PRESSED, buttonClick); - -// Make sure we cleanup listeners on unload. -unload(function() { - off(exports); - branch.removeObserver('', preferenceChange, false); - observers.remove(BUTTON_PRESSED, buttonClick); -}); - -const prefs = Proxy.create({ - get: function(receiver, pref) { - return prefService.get(ADDON_BRANCH + pref); - }, - set: function(receiver, pref, val) { - prefService.set(ADDON_BRANCH + pref, val); - }, - delete: function(pref) { - prefService.reset(ADDON_BRANCH + pref); - return true; - }, - has: function(pref) { - return prefService.has(ADDON_BRANCH + pref); - } -}); - -// Event target we will expose as module exports in order to be able to -// emit events on it. -const target = EventTarget.extend({ prefs: prefs }).new(); -module.exports = target; - -// This is workaround making sure that exports is wrapped before it's -// frozen, which needs to happen in order to workaround Bug 673468. -off(target, 'workaround-bug-673468'); diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/simple-storage.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/simple-storage.js deleted file mode 100644 index 85395f8..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/simple-storage.js +++ /dev/null @@ -1,237 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim:set ts=2 sw=2 sts=2 et filetype=javascript - * 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"; - -const { Cc, Ci } = require("chrome"); -const file = require("api-utils/file"); -const prefs = require("api-utils/preferences-service"); -const jpSelf = require("self"); -const timer = require("api-utils/timer"); -const unload = require("api-utils/unload"); -const { emit, on, off } = require("api-utils/event/core"); - -const WRITE_PERIOD_PREF = "extensions.addon-sdk.simple-storage.writePeriod"; -const WRITE_PERIOD_DEFAULT = 300000; // 5 minutes - -const QUOTA_PREF = "extensions.addon-sdk.simple-storage.quota"; -const QUOTA_DEFAULT = 5242880; // 5 MiB - -const JETPACK_DIR_BASENAME = "jetpack"; - -Object.defineProperties(exports, { - storage: { - enumerable: true, - get: function() { return manager.root; }, - set: function(value) { manager.root = value; } - }, - quotaUsage: { - get: function() { return manager.quotaUsage; } - } -}); - -// A generic JSON store backed by a file on disk. This should be isolated -// enough to move to its own module if need be... -function JsonStore(options) { - this.filename = options.filename; - this.quota = options.quota; - this.writePeriod = options.writePeriod; - this.onOverQuota = options.onOverQuota; - this.onWrite = options.onWrite; - - unload.ensure(this); - - this.writeTimer = timer.setInterval(this.write.bind(this), - this.writePeriod); -} - -JsonStore.prototype = { - // The store's root. - get root() { - return this.isRootInited ? this._root : {}; - }, - - // Performs some type checking. - set root(val) { - let types = ["array", "boolean", "null", "number", "object", "string"]; - if (types.indexOf(typeof(val)) < 0) { - throw new Error("storage must be one of the following types: " + - types.join(", ")); - } - this._root = val; - return val; - }, - - // True if the root has ever been set (either via the root setter or by the - // backing file's having been read). - get isRootInited() { - return this._root !== undefined; - }, - - // Percentage of quota used, as a number [0, Inf). > 1 implies over quota. - // Undefined if there is no quota. - get quotaUsage() { - return this.quota > 0 ? - JSON.stringify(this.root).length / this.quota : - undefined; - }, - - // Removes the backing file and all empty subdirectories. - purge: function JsonStore_purge() { - try { - // This'll throw if the file doesn't exist. - file.remove(this.filename); - let parentPath = this.filename; - do { - parentPath = file.dirname(parentPath); - // This'll throw if the dir isn't empty. - file.rmdir(parentPath); - } while (file.basename(parentPath) !== JETPACK_DIR_BASENAME); - } - catch (err) {} - }, - - // Initializes the root by reading the backing file. - read: function JsonStore_read() { - try { - let str = file.read(this.filename); - - // Ideally we'd log the parse error with console.error(), but logged - // errors cause tests to fail. Supporting "known" errors in the test - // harness appears to be non-trivial. Maybe later. - this.root = JSON.parse(str); - } - catch (err) { - this.root = {}; - } - }, - - // If the store is under quota, writes the root to the backing file. - // Otherwise quota observers are notified and nothing is written. - write: function JsonStore_write() { - if (this.quotaUsage > 1) - this.onOverQuota(this); - else - this._write(); - }, - - // Cleans up on unload. If unloading because of uninstall, the store is - // purged; otherwise it's written. - unload: function JsonStore_unload(reason) { - timer.clearInterval(this.writeTimer); - this.writeTimer = null; - - if (reason === "uninstall") - this.purge(); - else - this._write(); - }, - - // True if the root is an empty object. - get _isEmpty() { - if (this.root && typeof(this.root) === "object") { - let empty = true; - for (let key in this.root) { - empty = false; - break; - } - return empty; - } - return false; - }, - - // Writes the root to the backing file, notifying write observers when - // complete. If the store is over quota or if it's empty and the store has - // never been written, nothing is written and write observers aren't notified. - _write: function JsonStore__write() { - // Don't write if the root is uninitialized or if the store is empty and the - // backing file doesn't yet exist. - if (!this.isRootInited || (this._isEmpty && !file.exists(this.filename))) - return; - - // If the store is over quota, don't write. The current under-quota state - // should persist. - if (this.quotaUsage > 1) - return; - - // Finally, write. - let stream = file.open(this.filename, "w"); - try { - stream.writeAsync(JSON.stringify(this.root), function writeAsync(err) { - if (err) - console.error("Error writing simple storage file: " + this.filename); - else if (this.onWrite) - this.onWrite(this); - }.bind(this)); - } - catch (err) { - // writeAsync closes the stream after it's done, so only close on error. - stream.close(); - } - } -}; - - -// This manages a JsonStore singleton and tailors its use to simple storage. -// The root of the JsonStore is lazy-loaded: The backing file is only read the -// first time the root's gotten. -let manager = ({ - jsonStore: null, - - // The filename of the store, based on the profile dir and extension ID. - get filename() { - let storeFile = Cc["@mozilla.org/file/directory_service;1"]. - getService(Ci.nsIProperties). - get("ProfD", Ci.nsIFile); - storeFile.append(JETPACK_DIR_BASENAME); - storeFile.append(jpSelf.id); - storeFile.append("simple-storage"); - file.mkpath(storeFile.path); - storeFile.append("store.json"); - return storeFile.path; - }, - - get quotaUsage() { - return this.jsonStore.quotaUsage; - }, - - get root() { - if (!this.jsonStore.isRootInited) - this.jsonStore.read(); - return this.jsonStore.root; - }, - - set root(val) { - return this.jsonStore.root = val; - }, - - unload: function manager_unload() { - off(this); - }, - - new: function manager_constructor() { - let manager = Object.create(this); - unload.ensure(manager); - - manager.jsonStore = new JsonStore({ - filename: manager.filename, - writePeriod: prefs.get(WRITE_PERIOD_PREF, WRITE_PERIOD_DEFAULT), - quota: prefs.get(QUOTA_PREF, QUOTA_DEFAULT), - onOverQuota: emit.bind(null, exports, "OverQuota") - }); - - return manager; - } -}).new(); - -// This is workaround making sure that exports is wrapped before it's -// frozen, which needs to happen in order to workaround Bug 673468. -off(exports, 'workaround-bug-673468'); - -exports.on = on.bind(null, exports); -exports.removeListener = function(type, listener) { - off(exports, type, listener); -}; diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/tabs.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/tabs.js deleted file mode 100644 index ab915f5..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/tabs.js +++ /dev/null @@ -1,28 +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"; - -if (!require("api-utils/xul-app").is("Firefox")) { - throw new Error([ - "The tabs module currently supports only Firefox. In the future ", - "we would like it to support other applications, however. Please see ", - "https://bugzilla.mozilla.org/show_bug.cgi?id=560716 for more information." - ].join("")); -} - -const { browserWindows } = require("./windows"); -const { tabs } = require("api-utils/windows/tabs"); - -Object.defineProperties(tabs, { - open: { value: function open(options) { - if (options.inNewWindow) - // `tabs` option is under review and may be removed. - return browserWindows.open({ tabs: [ options ] }); - // Open in active window if new window was not required. - return browserWindows.activeWindow.tabs.open(options); - }} -}); -// It's a hack but we will be able to remove it once will implement CommonJS -// feature that would allow us to override exports. -exports.__proto__ = tabs; diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/timers.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/timers.js deleted file mode 100644 index 45fcf8d..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/timers.js +++ /dev/null @@ -1,8 +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"; - -// This module just proxies to the low level equivalent "timer" in "api-utils". -module.exports = require("api-utils/timer"); diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/widget.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/widget.js deleted file mode 100644 index e0bbfb6..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/widget.js +++ /dev/null @@ -1,923 +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"; - -// Widget content types -const CONTENT_TYPE_URI = 1; -const CONTENT_TYPE_HTML = 2; -const CONTENT_TYPE_IMAGE = 3; - -const ERR_CONTENT = "No content or contentURL property found. Widgets must " - + "have one or the other.", - ERR_LABEL = "The widget must have a non-empty label property.", - ERR_ID = "You have to specify a unique value for the id property of " + - "your widget in order for the application to remember its " + - "position.", - ERR_DESTROYED = "The widget has been destroyed and can no longer be used."; - -// Supported events, mapping from DOM event names to our event names -const EVENTS = { - "click": "click", - "mouseover": "mouseover", - "mouseout": "mouseout", -}; - -if (!require("api-utils/xul-app").is("Firefox")) { - throw new Error([ - "The widget module currently supports only Firefox. In the future ", - "it will support other applications. Please see ", - "https://bugzilla.mozilla.org/show_bug.cgi?id=560716 for more information." - ].join("")); -} - -const { validateOptions } = require("api-utils/api-utils"); -const panels = require("./panel"); -const { EventEmitter, EventEmitterTrait } = require("api-utils/events"); -const { Trait } = require("api-utils/traits"); -const LightTrait = require('api-utils/light-traits').Trait; -const { Loader, Symbiont } = require("api-utils/content"); -const { Cortex } = require('api-utils/cortex'); -const windowsAPI = require("./windows"); -const { setTimeout } = require("api-utils/timer"); -const unload = require("api-utils/unload"); -const { uuid } = require("api-utils/uuid"); - -// Data types definition -const valid = { - number: { is: ["null", "undefined", "number"] }, - string: { is: ["null", "undefined", "string"] }, - id: { - is: ["string"], - ok: function (v) v.length > 0, - msg: ERR_ID, - readonly: true - }, - label: { - is: ["string"], - ok: function (v) v.length > 0, - msg: ERR_LABEL - }, - panel: { - is: ["null", "undefined", "object"], - ok: function(v) !v || v instanceof panels.Panel - }, - width: { - is: ["null", "undefined", "number"], - map: function (v) { - if (null === v || undefined === v) v = 16; - return v; - }, - defaultValue: 16 - }, - allow: { - is: ["null", "undefined", "object"], - map: function (v) { - if (!v) v = { script: true }; - return v; - }, - get defaultValue() ({ script: true }) - }, -}; - -// Widgets attributes definition -let widgetAttributes = { - label: valid.label, - id: valid.id, - tooltip: valid.string, - width: valid.width, - content: valid.string, - panel: valid.panel, - allow: valid.allow -}; - -// Import data definitions from loader, but don't compose with it as Model -// functions allow us to recreate easily all Loader code. -let loaderAttributes = require("api-utils/content/loader").validationAttributes; -for (let i in loaderAttributes) - widgetAttributes[i] = loaderAttributes[i]; - -widgetAttributes.contentURL.optional = true; - -// Widgets public events list, that are automatically binded in options object -const WIDGET_EVENTS = [ - "click", - "mouseover", - "mouseout", - "error", - "message", - "attach" -]; - -// `Model` utility functions that help creating these various Widgets objects -let model = { - - // Validate one attribute using api-utils.js:validateOptions function - _validate: function _validate(name, suspect, validation) { - let $1 = {}; - $1[name] = suspect; - let $2 = {}; - $2[name] = validation; - return validateOptions($1, $2)[name]; - }, - - /** - * This method has two purposes: - * 1/ Validate and define, on a given object, a set of attribute - * 2/ Emit a "change" event on this object when an attribute is changed - * - * @params {Object} object - * Object on which we can bind attributes on and watch for their changes. - * This object must have an EventEmitter interface, or, at least `_emit` - * method - * @params {Object} attrs - * Dictionary of attributes definition following api-utils:validateOptions - * scheme - * @params {Object} values - * Dictionary of attributes default values - */ - setAttributes: function setAttributes(object, attrs, values) { - let properties = {}; - for (let name in attrs) { - let value = values[name]; - let req = attrs[name]; - - // Retrieve default value from typedef if the value is not defined - if ((typeof value == "undefined" || value == null) && req.defaultValue) - value = req.defaultValue; - - // Check for valid value if value is defined or mandatory - if (!req.optional || typeof value != "undefined") - value = model._validate(name, value, req); - - // In any case, define this property on `object` - let property = null; - if (req.readonly) { - property = { - value: value, - writable: false, - enumerable: true, - configurable: false - }; - } - else { - property = model._createWritableProperty(name, value); - } - - properties[name] = property; - } - Object.defineProperties(object, properties); - }, - - // Generate ES5 property definition for a given attribute - _createWritableProperty: function _createWritableProperty(name, value) { - return { - get: function () { - return value; - }, - set: function (newValue) { - value = newValue; - // The main goal of all this Model stuff is here: - // We want to forward all changes to some listeners - this._emit("change", name, value); - }, - enumerable: true, - configurable: false - }; - }, - - /** - * Automagically register listeners in options dictionary - * by detecting listener attributes with name starting with `on` - * - * @params {Object} object - * Target object that need to follow EventEmitter interface, or, at least, - * having `on` method. - * @params {Array} events - * List of events name to automatically bind. - * @params {Object} listeners - * Dictionary of event listener functions to register. - */ - setEvents: function setEvents(object, events, listeners) { - for (let i = 0, l = events.length; i < l; i++) { - let name = events[i]; - let onName = "on" + name[0].toUpperCase() + name.substr(1); - if (!listeners[onName]) - continue; - object.on(name, listeners[onName].bind(object)); - } - } - -}; - - -/** - * Main Widget class: entry point of the widget API - * - * Allow to control all widget across all existing windows with a single object. - * Widget.getView allow to retrieve a WidgetView instance to control a widget - * specific to one window. - */ -const WidgetTrait = LightTrait.compose(EventEmitterTrait, LightTrait({ - - _initWidget: function _initWidget(options) { - model.setAttributes(this, widgetAttributes, options); - - browserManager.validate(this); - - // We must have at least content or contentURL defined - if (!(this.content || this.contentURL)) - throw new Error(ERR_CONTENT); - - this._views = []; - - // Set tooltip to label value if we don't have tooltip defined - if (!this.tooltip) - this.tooltip = this.label; - - model.setEvents(this, WIDGET_EVENTS, options); - - this.on('change', this._onChange.bind(this)); - - let self = this; - this._port = EventEmitterTrait.create({ - emit: function () { - let args = arguments; - self._views.forEach(function(v) v.port.emit.apply(v.port, args)); - } - }); - // expose wrapped port, that exposes only public properties. - this._port._public = Cortex(this._port); - - // Register this widget to browser manager in order to create new widget on - // all new windows - browserManager.addItem(this); - }, - - _onChange: function _onChange(name, value) { - // Set tooltip to label value if we don't have tooltip defined - if (name == 'tooltip' && !value) { - // we need to change tooltip again in order to change the value of the - // attribute itself - this.tooltip = this.label; - return; - } - - // Forward attributes changes to WidgetViews - if (['width', 'tooltip', 'content', 'contentURL'].indexOf(name) != -1) { - this._views.forEach(function(v) v[name] = value); - } - }, - - _onEvent: function _onEvent(type, eventData) { - this._emit(type, eventData); - }, - - _createView: function _createView() { - // Create a new WidgetView instance - let view = WidgetView(this); - - // Keep a reference to it - this._views.push(view); - - // Emit an `attach` event with a WidgetView instance without private attrs - this._emit("attach", view._public); - - return view; - }, - - // a WidgetView instance is destroyed - _onViewDestroyed: function _onViewDestroyed(view) { - let idx = this._views.indexOf(view); - this._views.splice(idx, 1); - }, - - /** - * Called on browser window closed, to destroy related WidgetViews - * @params {ChromeWindow} window - * Window that has been closed - */ - _onWindowClosed: function _onWindowClosed(window) { - for each (let view in this._views) { - if (view._isInChromeWindow(window)) { - view.destroy(); - break; - } - } - }, - - /** - * Get the WidgetView instance related to a BrowserWindow instance - * @params {BrowserWindow} window - * BrowserWindow reference from "windows" module - */ - getView: function getView(window) { - for each (let view in this._views) { - if (view._isInWindow(window)) { - return view._public; - } - } - return null; - }, - - get port() this._port._public, - set port(v) {}, // Work around Cortex failure with getter without setter - // See bug 653464 - _port: null, - - postMessage: function postMessage(message) { - this._views.forEach(function(v) v.postMessage(message)); - }, - - destroy: function destroy() { - if (this.panel) - this.panel.destroy(); - - // Dispatch destroy calls to views - // we need to go backward as we remove items from this array in - // _onViewDestroyed - for (let i = this._views.length - 1; i >= 0; i--) - this._views[i].destroy(); - - // Unregister widget to stop creating it over new windows - // and allow creation of new widget with same id - browserManager.removeItem(this); - } - -})); - -// Widget constructor -const Widget = function Widget(options) { - let w = WidgetTrait.create(Widget.prototype); - w._initWidget(options); - - // Return a Cortex of widget in order to hide private attributes like _onEvent - let _public = Cortex(w); - unload.ensure(_public, "destroy"); - return _public; -} -exports.Widget = Widget; - - - -/** - * WidgetView is an instance of a widget for a specific window. - * - * This is an external API that can be retrieved by calling Widget.getView or - * by watching `attach` event on Widget. - */ -const WidgetViewTrait = LightTrait.compose(EventEmitterTrait, LightTrait({ - - // Reference to the matching WidgetChrome - // set right after constructor call - _chrome: null, - - // Public interface of the WidgetView, passed in `attach` event or in - // Widget.getView - _public: null, - - _initWidgetView: function WidgetView__initWidgetView(baseWidget) { - this._baseWidget = baseWidget; - - model.setAttributes(this, widgetAttributes, baseWidget); - - this.on('change', this._onChange.bind(this)); - - let self = this; - this._port = EventEmitterTrait.create({ - emit: function () { - if (!self._chrome) - throw new Error(ERR_DESTROYED); - self._chrome.update(self._baseWidget, "emit", arguments); - } - }); - // expose wrapped port, that exposes only public properties. - this._port._public = Cortex(this._port); - - this._public = Cortex(this); - }, - - _onChange: function WidgetView__onChange(name, value) { - if (name == 'tooltip' && !value) { - this.tooltip = this.label; - return; - } - - // Forward attributes changes to WidgetChrome instance - if (['width', 'tooltip', 'content', 'contentURL'].indexOf(name) != -1) { - this._chrome.update(this._baseWidget, name, value); - } - }, - - _onEvent: function WidgetView__onEvent(type, eventData, domNode) { - // Dispatch event in view - this._emit(type, eventData); - - // And forward it to the main Widget object - if ("click" == type || type.indexOf("mouse") == 0) - this._baseWidget._onEvent(type, this._public); - else - this._baseWidget._onEvent(type, eventData); - - // Special case for click events: if the widget doesn't have a click - // handler, but it does have a panel, display the panel. - if ("click" == type && !this._listeners("click").length && this.panel) - this.panel.show(domNode); - }, - - _isInWindow: function WidgetView__isInWindow(window) { - return windowsAPI.BrowserWindow({ - window: this._chrome.window - }) == window; - }, - - _isInChromeWindow: function WidgetView__isInChromeWindow(window) { - return this._chrome.window == window; - }, - - _onPortEvent: function WidgetView__onPortEvent(args) { - let port = this._port; - port._emit.apply(port, args); - let basePort = this._baseWidget._port; - basePort._emit.apply(basePort, args); - }, - - get port() this._port._public, - set port(v) {}, // Work around Cortex failure with getter without setter - // See bug 653464 - _port: null, - - postMessage: function WidgetView_postMessage(message) { - if (!this._chrome) - throw new Error(ERR_DESTROYED); - this._chrome.update(this._baseWidget, "postMessage", message); - }, - - destroy: function WidgetView_destroy() { - this._chrome.destroy(); - delete this._chrome; - this._baseWidget._onViewDestroyed(this); - this._emit("detach"); - } - -})); - -const WidgetView = function WidgetView(baseWidget) { - let w = WidgetViewTrait.create(WidgetView.prototype); - w._initWidgetView(baseWidget); - return w; -} - - - -/** - * Keeps track of all browser windows. - * Exposes methods for adding/removing widgets - * across all open windows (and future ones). - * Create a new instance of BrowserWindow per window. - */ -let browserManager = { - items: [], - windows: [], - - // Registers the manager to listen for window openings and closings. Note - // that calling this method can cause onTrack to be called immediately if - // there are open windows. - init: function () { - let windowTracker = new (require("api-utils/window-utils").WindowTracker)(this); - unload.ensure(windowTracker); - }, - - // Registers a window with the manager. This is a WindowTracker callback. - onTrack: function browserManager_onTrack(window) { - if (this._isBrowserWindow(window)) { - let win = new BrowserWindow(window); - win.addItems(this.items); - this.windows.push(win); - } - }, - - // Unregisters a window from the manager. It's told to undo all - // modifications. This is a WindowTracker callback. Note that when - // WindowTracker is unloaded, it calls onUntrack for every currently opened - // window. The browserManager therefore doesn't need to specially handle - // unload itself, since unloading the browserManager means untracking all - // currently opened windows. - onUntrack: function browserManager_onUntrack(window) { - if (this._isBrowserWindow(window)) { - this.items.forEach(function(i) i._onWindowClosed(window)); - for (let i = 0; i < this.windows.length; i++) { - if (this.windows[i].window == window) { - this.windows.splice(i, 1)[0]; - return; - } - } - - } - }, - - // Used to validate widget by browserManager before adding it, - // in order to check input very early in widget constructor - validate : function (item) { - let idx = this.items.indexOf(item); - if (idx > -1) - throw new Error("The widget " + item + " has already been added."); - if (item.id) { - let sameId = this.items.filter(function(i) i.id == item.id); - if (sameId.length > 0) - throw new Error("This widget ID is already used: " + item.id); - } else { - item.id = this.items.length; - } - }, - - // Registers an item with the manager. It's added to all currently registered - // windows, and when new windows are registered it will be added to them, too. - addItem: function browserManager_addItem(item) { - this.items.push(item); - this.windows.forEach(function (w) w.addItems([item])); - }, - - // Unregisters an item from the manager. It's removed from all windows that - // are currently registered. - removeItem: function browserManager_removeItem(item) { - let idx = this.items.indexOf(item); - if (idx > -1) - this.items.splice(idx, 1); - }, - - _isBrowserWindow: function browserManager__isBrowserWindow(win) { - let winType = win.document.documentElement.getAttribute("windowtype"); - return winType === "navigator:browser"; - } -}; - - - -/** - * Keeps track of a single browser window. - * - * This is where the core of how a widget's content is added to a window lives. - */ -function BrowserWindow(window) { - this.window = window; - this.doc = window.document; -} - -BrowserWindow.prototype = { - - // Adds an array of items to the window. - addItems: function BW_addItems(items) { - items.forEach(this._addItemToWindow, this); - }, - - _addItemToWindow: function BW__addItemToWindow(baseWidget) { - // Create a WidgetView instance - let widget = baseWidget._createView(); - - // Create a WidgetChrome instance - let item = new WidgetChrome({ - widget: widget, - doc: this.doc, - window: this.window - }); - - widget._chrome = item; - - this._insertNodeInToolbar(item.node); - - // We need to insert Widget DOM Node before finishing widget view creation - // (because fill creates an iframe and tries to access its docShell) - item.fill(); - }, - - _insertNodeInToolbar: function BW__insertNodeInToolbar(node) { - // Add to the customization palette - let toolbox = this.doc.getElementById("navigator-toolbox"); - let palette = toolbox.palette; - palette.appendChild(node); - - // Search for widget toolbar by reading toolbar's currentset attribute - let container = null; - let toolbars = this.doc.getElementsByTagName("toolbar"); - let id = node.getAttribute("id"); - for (let i = 0, l = toolbars.length; i < l; i++) { - let toolbar = toolbars[i]; - if (toolbar.getAttribute("currentset").indexOf(id) == -1) - continue; - container = toolbar; - } - - // if widget isn't in any toolbar, add it to the addon-bar - // TODO: we may want some "first-launch" module to do this only on very - // first execution - if (!container) { - container = this.doc.getElementById("addon-bar"); - // TODO: find a way to make the following code work when we use "cfx run": - // http://mxr.mozilla.org/mozilla-central/source/browser/base/content/browser.js#8586 - // until then, force display of addon bar directly from sdk code - // https://bugzilla.mozilla.org/show_bug.cgi?id=627484 - if (container.collapsed) - this.window.toggleAddonBar(); - } - - // Now retrieve a reference to the next toolbar item - // by reading currentset attribute on the toolbar - let nextNode = null; - let currentSet = container.getAttribute("currentset"); - let ids = (currentSet == "__empty") ? [] : currentSet.split(","); - let idx = ids.indexOf(id); - if (idx != -1) { - for (let i = idx; i < ids.length; i++) { - nextNode = this.doc.getElementById(ids[i]); - if (nextNode) - break; - } - } - - // Finally insert our widget in the right toolbar and in the right position - container.insertItem(id, nextNode, null, false); - - // Update DOM in order to save position: which toolbar, and which position - // in this toolbar. But only do this the first time we add it to the toolbar - // Otherwise, this code will collide with other instance of Widget module - // during Firefox startup. See bug 685929. - if (ids.indexOf(id) == -1) { - container.setAttribute("currentset", container.currentSet); - // Save DOM attribute in order to save position on new window opened - this.window.document.persist(container.id, "currentset"); - } - } -} - - -/** - * Final Widget class that handles chrome DOM Node: - * - create initial DOM nodes - * - receive instruction from WidgetView through update method and update DOM - * - watch for DOM events and forward them to WidgetView - */ -function WidgetChrome(options) { - this.window = options.window; - this._doc = options.doc; - this._widget = options.widget; - this._symbiont = null; // set later - this.node = null; // set later - - this._createNode(); -} - -// Update a property of a widget. -WidgetChrome.prototype.update = function WC_update(updatedItem, property, value) { - switch(property) { - case "contentURL": - case "content": - this.setContent(); - break; - case "width": - this.node.style.minWidth = value + "px"; - this.node.querySelector("iframe").style.width = value + "px"; - break; - case "tooltip": - this.node.setAttribute("tooltiptext", value); - break; - case "postMessage": - this._symbiont.postMessage(value); - break; - case "emit": - let port = this._symbiont.port; - port.emit.apply(port, value); - break; - } -} - -// Add a widget to this window. -WidgetChrome.prototype._createNode = function WC__createNode() { - // XUL element container for widget - let node = this._doc.createElement("toolbaritem"); - let guid = String(uuid()); - - // Temporary work around require("self") failing on unit-test execution ... - let jetpackID = "testID"; - try { - jetpackID = require("self").id; - } catch(e) {} - - // Compute an unique and stable widget id with jetpack id and widget.id - let id = "widget:" + jetpackID + "-" + this._widget.id; - node.setAttribute("id", id); - node.setAttribute("label", this._widget.label); - node.setAttribute("tooltiptext", this._widget.tooltip); - node.setAttribute("align", "center"); - // Bug 626326: Prevent customize toolbar context menu to appear - node.setAttribute("context", ""); - - // TODO move into a stylesheet, configurable by consumers. - // Either widget.style, exposing the style object, or a URL - // (eg, can load local stylesheet file). - node.setAttribute("style", [ - "overflow: hidden; margin: 1px 2px 1px 2px; padding: 0px;", - "min-height: 16px;", - ].join("")); - - node.style.minWidth = this._widget.width + "px"; - - this.node = node; -} - -// Initial population of a widget's content. -WidgetChrome.prototype.fill = function WC_fill() { - // Create element - var iframe = this._doc.createElement("iframe"); - iframe.setAttribute("type", "content"); - iframe.setAttribute("transparent", "transparent"); - iframe.style.overflow = "hidden"; - iframe.style.height = "16px"; - iframe.style.maxHeight = "16px"; - iframe.style.width = this._widget.width + "px"; - iframe.setAttribute("flex", "1"); - iframe.style.border = "none"; - iframe.style.padding = "0px"; - - // Do this early, because things like contentWindow are null - // until the node is attached to a document. - this.node.appendChild(iframe); - - // add event handlers - this.addEventHandlers(); - - // set content - this.setContent(); -} - -// Get widget content type. -WidgetChrome.prototype.getContentType = function WC_getContentType() { - if (this._widget.content) - return CONTENT_TYPE_HTML; - return (this._widget.contentURL && /\.(jpg|gif|png|ico)$/.test(this._widget.contentURL)) - ? CONTENT_TYPE_IMAGE : CONTENT_TYPE_URI; -} - -// Set widget content. -WidgetChrome.prototype.setContent = function WC_setContent() { - let type = this.getContentType(); - let contentURL = null; - - switch (type) { - case CONTENT_TYPE_HTML: - contentURL = "data:text/html," + encodeURIComponent(this._widget.content); - break; - case CONTENT_TYPE_URI: - contentURL = this._widget.contentURL; - break; - case CONTENT_TYPE_IMAGE: - let imageURL = this._widget.contentURL; - contentURL = "data:text/html,<html><body><img src='" + - encodeURI(imageURL) + "'></body></html>"; - break; - default: - throw new Error("The widget's type cannot be determined."); - } - - let iframe = this.node.firstElementChild; - - let self = this; - // Cleanup previously created symbiont (in case we are update content) - if (this._symbiont) - this._symbiont.destroy(); - - this._symbiont = Trait.compose(Symbiont.resolve({ - _onContentScriptEvent: "_onContentScriptEvent-not-used" - }), { - _onContentScriptEvent: function () { - // Redirect events to WidgetView - self._widget._onPortEvent(arguments); - } - })({ - frame: iframe, - contentURL: contentURL, - contentScriptFile: this._widget.contentScriptFile, - contentScript: this._widget.contentScript, - contentScriptWhen: this._widget.contentScriptWhen, - allow: this._widget.allow, - onMessage: function(message) { - setTimeout(function() { - self._widget._onEvent("message", message); - }, 0); - } - }); -} - -// Detect if document consists of a single image. -WidgetChrome._isImageDoc = function WC__isImageDoc(doc) { - return doc.body.childNodes.length == 1 && - doc.body.firstElementChild && - doc.body.firstElementChild.tagName == "IMG"; -} - -// Set up all supported events for a widget. -WidgetChrome.prototype.addEventHandlers = function WC_addEventHandlers() { - let contentType = this.getContentType(); - - let self = this; - let listener = function(e) { - // Ignore event firings that target the iframe. - if (e.target == self.node.firstElementChild) - return; - - // The widget only supports left-click for now, - // so ignore right-clicks. - if (e.type == "click" && e.button == 2) - return; - - // Proxy event to the widget - setTimeout(function() { - self._widget._onEvent(EVENTS[e.type], null, self.node); - }, 0); - }; - - this.eventListeners = {}; - let iframe = this.node.firstElementChild; - for (let [type, method] in Iterator(EVENTS)) { - iframe.addEventListener(type, listener, true, true); - - // Store listeners for later removal - this.eventListeners[type] = listener; - } - - // On document load, make modifications required for nice default - // presentation. - function loadListener(e) { - let containerStyle = self.window.getComputedStyle(self.node.parentNode); - // Ignore event firings that target the iframe - if (e.target == iframe) - return; - // Ignore about:blank loads - if (e.type == "load" && e.target.location == "about:blank") - return; - - // We may have had an unload event before that cleaned up the symbiont - if (!self._symbiont) - self.setContent(); - - let doc = e.target; - if (contentType == CONTENT_TYPE_IMAGE || WidgetChrome._isImageDoc(doc)) { - // Force image content to size. - // Add-on authors must size their images correctly. - doc.body.firstElementChild.style.width = self._widget.width + "px"; - doc.body.firstElementChild.style.height = "16px"; - } - - // Extend the add-on bar's default text styles to the widget. - doc.body.style.color = containerStyle.color; - doc.body.style.fontFamily = containerStyle.fontFamily; - doc.body.style.fontSize = containerStyle.fontSize; - doc.body.style.fontWeight = containerStyle.fontWeight; - doc.body.style.textShadow = containerStyle.textShadow; - // Allow all content to fill the box by default. - doc.body.style.margin = "0"; - } - iframe.addEventListener("load", loadListener, true); - this.eventListeners["load"] = loadListener; - - // Register a listener to unload symbiont if the toolbaritem is moved - // on user toolbars customization - function unloadListener(e) { - if (e.target.location == "about:blank") - return; - self._symbiont.destroy(); - self._symbiont = null; - // This may fail but not always, it depends on how the node is - // moved or removed - try { - self.setContent(); - } catch(e) {} - - } - - iframe.addEventListener("unload", unloadListener, true); - this.eventListeners["unload"] = unloadListener; -} - -// Remove and unregister the widget from everything -WidgetChrome.prototype.destroy = function WC_destroy(removedItems) { - // remove event listeners - for (let [type, listener] in Iterator(this.eventListeners)) - this.node.firstElementChild.removeEventListener(type, listener, true); - // remove dom node - this.node.parentNode.removeChild(this.node); - // cleanup symbiont - this._symbiont.destroy(); - // cleanup itself - this.eventListeners = null; - this._widget = null; - this._symbiont = null; -} - -// Init the browserManager only after setting prototypes and such above, because -// it will cause browserManager.onTrack to be called immediately if there are -// open windows. -browserManager.init(); diff --git a/tools/addon-sdk-1.7/packages/addon-kit/lib/windows.js b/tools/addon-sdk-1.7/packages/addon-kit/lib/windows.js deleted file mode 100644 index 8f13e6c..0000000 --- a/tools/addon-sdk-1.7/packages/addon-kit/lib/windows.js +++ /dev/null @@ -1,210 +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"; - -if (!require("api-utils/xul-app").is("Firefox")) { - throw new Error([ - "The windows module currently supports only Firefox. In the future", - " we would like it to support other applications, however. Please see ", - "https://bugzilla.mozilla.org/show_bug.cgi?id=571449 for more information." - ].join("")); -} - -const { Cc, Ci } = require('chrome'), - { Trait } = require('api-utils/traits'), - { List } = require('api-utils/list'), - { EventEmitter } = require('api-utils/events'), - { WindowTabs, WindowTabTracker } = require('api-utils/windows/tabs'), - { WindowDom } = require('api-utils/windows/dom'), - { WindowLoader } = require('api-utils/windows/loader'), - { WindowTrackerTrait } = require('api-utils/window-utils'), - { Options } = require('api-utils/tabs/tab'), - apiUtils = require('api-utils/api-utils'), - unload = require('api-utils/unload'), - - WM = Cc['@mozilla.org/appshell/window-mediator;1']. - getService(Ci.nsIWindowMediator), - - BROWSER = 'navigator:browser'; - -/** - * Window trait composes safe wrappers for browser window that are E10S - * compatible. - */ -const BrowserWindowTrait = Trait.compose( - EventEmitter, - WindowDom.resolve({ close: '_close' }), - WindowTabs, - WindowTabTracker, - WindowLoader, - /* WindowSidebars, */ - Trait.compose({ - _emit: Trait.required, - _close: Trait.required, - _load: Trait.required, - /** - * Constructor returns wrapper of the specified chrome window. - * @param {nsIWindow} window - */ - constructor: function BrowserWindow(options) { - // Register this window ASAP, in order to avoid loop that would try - // to create this window instance over and over (see bug 648244) - windows.push(this); - - // make sure we don't have unhandled errors - this.on('error', console.exception.bind(console)); - - if ('onOpen' in options) - this.on('open', options.onOpen); - if ('onClose' in options) - this.on('close', options.onClose); - if ('window' in options) - this._window = options.window; - if ('tabs' in options) { - this._tabOptions = Array.isArray(options.tabs) ? - options.tabs.map(Options) : - [ Options(options.tabs) ]; - } - else if ('url' in options) { - this._tabOptions = [ Options(options.url) ]; - } - this._load(); - return this; - }, - destroy: function () this._onUnload(), - _tabOptions: [], - _onLoad: function() { - try { - this._initWindowTabTracker(); - } catch(e) { - this._emit('error', e) - } - this._emitOnObject(browserWindows, 'open', this._public); - }, - _onUnload: function() { - if (!this._window) - return; - this._destroyWindowTabTracker(); - this._emitOnObject(browserWindows, 'close', this._public); - this._window = null; - // Removing reference from the windows array. - windows.splice(windows.indexOf(this), 1); - this._removeAllListeners(); - }, - close: function close(callback) { - // maybe we should deprecate this with message ? - if (callback) this.on('close', callback); - return this._close(); - } - }) -); -/** - * Wrapper for `BrowserWindowTrait`. Creates new instance if wrapper for - * window doesn't exists yet. If wrapper already exists then returns it - * instead. - * @params {Object} options - * Options that are passed to the the `BrowserWindowTrait` - * @returns {BrowserWindow} - * @see BrowserWindowTrait - */ -function BrowserWindow(options) { - let chromeWindow = options.window; - for each (let window in windows) { - if (chromeWindow == window._window) - return window._public - } - let window = BrowserWindowTrait(options); - return window._public; -} -// to have proper `instanceof` behavior will go away when #596248 is fixed. -BrowserWindow.prototype = BrowserWindowTrait.prototype; -exports.BrowserWindow = BrowserWindow -const windows = []; - -/** - * `BrowserWindows` trait is composed out of `List` trait and it represents - * "live" list of currently open browser windows. Instance mutates itself - * whenever new browser window gets opened / closed. - */ -// Very stupid to resolve all `toStrings` but this will be fixed by #596248 -const browserWindows = Trait.resolve({ toString: null }).compose( - List.resolve({ constructor: '_initList' }), - EventEmitter.resolve({ toString: null }), - WindowTrackerTrait.resolve({ constructor: '_initTracker', toString: null }), - Trait.compose({ - _emit: Trait.required, - _add: Trait.required, - _remove: Trait.required, - - // public API - - /** - * Constructor creates instance of `Windows` that represents live list of open - * windows. - */ - constructor: function BrowserWindows() { - this._trackedWindows = []; - this._initList(); - this._initTracker(); - unload.ensure(this, "_destructor"); - }, - _destructor: function _destructor() { - this._removeAllListeners('open'); - this._removeAllListeners('close'); - this._clear(); - }, - /** - * This property represents currently active window. - * Property is non-enumerable, in order to preserve array like enumeration. - * @type {Window|null} - */ - get activeWindow() { - let window = WM.getMostRecentWindow(BROWSER); - return this._isBrowser(window) ? BrowserWindow({ window: window }) : null; - }, - open: function open(options) { - if (typeof options === "string") - // `tabs` option is under review and may be removed. - options = { tabs: [Options(options)] }; - return BrowserWindow(options); - }, - /** - * Returns true if specified window is a browser window. - * @param {nsIWindow} window - * @returns {Boolean} - */ - _isBrowser: function _isBrowser(window) - BROWSER === window.document.documentElement.getAttribute("windowtype") - , - /** - * Internal listener which is called whenever new window gets open. - * Creates wrapper and adds to this list. - * @param {nsIWindow} chromeWindow - */ - _onTrack: function _onTrack(chromeWindow) { - if (!this._isBrowser(chromeWindow)) return; - let window = BrowserWindow({ window: chromeWindow }); - this._add(window); - this._emit('open', window); - }, - /** - * Internal listener which is called whenever window gets closed. - * Cleans up references and removes wrapper from this list. - * @param {nsIWindow} window - */ - _onUntrack: function _onUntrack(chromeWindow) { - if (!this._isBrowser(chromeWindow)) return; - let window = BrowserWindow({ window: chromeWindow }); - this._remove(window); - this._emit('close', window); - - // Bug 724404: do not leak this module and linked windows: - // We have to do it on untrack and not only when `_onUnload` is called - // when windows are closed, otherwise, we will leak on addon disabling. - window.destroy(); - } - }).resolve({ toString: null }) -)(); -exports.browserWindows = browserWindows; - |