Last active
January 3, 2016 18:09
-
-
Save rogerhub/8500637 to your computer and use it in GitHub Desktop.
A full list of differences between the official Mozilla Jetpack Firefox Addons SDK version 1.14 (legacy) and the Addons SDK bundled with the Youtube Ratings Preview Addon for Firefox.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
./windows/tabs-fennec.js | |
11c11,12 | |
< const { openTab, getTabs, getSelectedTab, getTabForBrowser: getRawTabForBrowser } = require('../tabs/utils'); | |
--- | |
> const { openTab, getTabs, getSelectedTab, getTabForBrowser: getRawTabForBrowser, | |
> getTabContentWindow } = require('../tabs/utils'); | |
21,22c22,23 | |
< const { isPrivateBrowsingSupported } = require('sdk/self'); | |
< const { isTabPBSupported } = require('sdk/private-browsing/utils'); | |
--- | |
> const { isPrivateBrowsingSupported } = require('../self'); | |
> const { isTabPBSupported, ignoreWindow } = require('../private-browsing/utils'); | |
78a80,82 | |
> if (options.onPageShow) | |
> tab.on('pageshow', options.onPageShow); | |
> | |
115a120,123 | |
> // Eventually ignore private tabs | |
> if (ignoreWindow(browser.contentWindow)) | |
> return; | |
> | |
128a137,139 | |
> tab.on('pageshow', function(_tab, persisted) | |
> emit(gTabs, 'pageshow', tab, persisted)); | |
> | |
131c142 | |
< }; | |
--- | |
> } | |
134a146,151 | |
> let browser = event.target; | |
> | |
> // Eventually ignore private tabs | |
> if (ignoreWindow(browser.contentWindow)) | |
> return; | |
> | |
136c153 | |
< let tab = getTabForBrowser(event.target); | |
--- | |
> let tab = getTabForBrowser(browser); | |
145c162 | |
< }; | |
--- | |
> } | |
151c168 | |
< }; | |
--- | |
> } | |
./windows/dom.js | |
32c32 | |
< '`require("private-browsing").isPrivate(browserWindow)` ' + | |
--- | |
> '`require("sdk/private-browsing").isPrivate(browserWindow)` ' + | |
./windows/loader.js | |
42c42,44 | |
< if (this.__window) return; | |
--- | |
> if (this.__window) | |
> return; | |
> | |
./windows/tabs-firefox.js | |
54a55,56 | |
> this._onTabLoad = this._emitEvent.bind(this, "load"); | |
> this._onTabPageShow = this._emitEvent.bind(this, "pageshow"); | |
112c114 | |
< if (type === "open") | |
--- | |
> if (type === "open") { | |
113a116,118 | |
> wrappedTab.on("load", this._onTabLoad); | |
> wrappedTab.on("pageshow", this._onTabPageShow); | |
> } | |
118c123,126 | |
< _emitEvent: function _emitEvent(type, tab) { | |
--- | |
> _emitEvent: function _emitEvent(type, tag) { | |
> // Slices additional arguments and passes them into exposed | |
> // listener like other events (for pageshow) | |
> let args = Array.slice(arguments); | |
120c128 | |
< tabs._emit(type, tab); | |
--- | |
> tabs._emit.apply(tabs, args); | |
122c130 | |
< this._tabs._emit(type, tab); | |
--- | |
> this._tabs._emit.apply(this._tabs, args); | |
./windows/firefox.js | |
21,23c21,23 | |
< { getOwnerWindow } = require('../private-browsing/window/utils'), | |
< viewNS = require('../core/namespace').ns(), | |
< { isPrivateBrowsingSupported } = require('../self'); | |
--- | |
> { getOwnerWindow } = require('../private-browsing/window/utils'); | |
> const { windowNS } = require('../window/namespace'); | |
> const { isPrivateBrowsingSupported } = require('../self'); | |
77c77 | |
< viewNS(this._public).window = this._window; | |
--- | |
> windowNS(this._public).window = this._window; | |
259c259 | |
< return viewNS(window).window; | |
--- | |
> return windowNS(window).window; | |
./windows/fennec.js | |
16c16 | |
< const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec, consider using require("tabs") instead'; | |
--- | |
> const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec, consider using require("sdk/tabs") instead'; | |
./windows/observer.js | |
1d0 | |
< /* vim:set ts=2 sw=2 sts=2 et: */ | |
./platform/xpcom.js | |
./io/file.js | |
./io/data.js | |
13d12 | |
< | |
16a16 | |
> const { deprecateFunction } = require('../util/deprecate'); | |
20d19 | |
< const { deprecateFunction } = require("../util/deprecate"); | |
33c32 | |
< exports.getFaviconURIForLocation = function getFaviconURIForLocation(uri) { | |
--- | |
> function getFaviconURIForLocation(uri) { | |
46a46 | |
> exports.getFaviconURIForLocation = getFaviconURIForLocation; | |
./io/text-streams.js | |
1,3c1 | |
< /* -*- 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 | |
--- | |
> /* This Source Code Form is subject to the terms of the Mozilla Public | |
./io/byte-streams.js | |
1,3c1 | |
< /* -*- 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 | |
--- | |
> /* This Source Code Form is subject to the terms of the Mozilla Public | |
./widget.js | |
1,2d0 | |
< /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | |
< /* vim:set ts=2 sw=2 sts=2 et: */ | |
38a37 | |
> const panels = require("./panel"); | |
49a49 | |
> const { getNodeView } = require("./view/core"); | |
68c68 | |
< ok: function(v) !v || v instanceof require("./panel").Panel | |
--- | |
> ok: function(v) !v || v instanceof panels.Panel | |
364d363 | |
< | |
434c433,436 | |
< this.panel.show(domNode); | |
--- | |
> // This kind of ugly workaround, instead we should implement | |
> // `getNodeView` for the `Widget` class itself, but that's kind of | |
> // hard without cleaning things up. | |
> this.panel.show(null, getNodeView.implement({}, function() domNode)); | |
473a476 | |
> | |
481d483 | |
< | |
601a604,639 | |
> | |
> if (this.window.CustomizableUI) { | |
> let placement = this.window.CustomizableUI.getPlacementOfWidget(node.id); | |
> if (!placement) { | |
> placement = {area: 'nav-bar', position: undefined}; | |
> } | |
> this.window.CustomizableUI.addWidgetToArea(node.id, placement.area, placement.position); | |
> | |
> // Depending on when this gets called, we might be in the right place now. In that case, | |
> // don't run the following code. | |
> if (node.parentNode != palette) { | |
> return; | |
> } | |
> // Otherwise, insert: | |
> let container = this.doc.getElementById(placement.area); | |
> if (container.customizationTarget) { | |
> container = container.customizationTarget; | |
> } | |
> | |
> if (placement.position !== undefined) { | |
> // Find a position: | |
> let items = this.window.CustomizableUI.getWidgetIdsInArea(placement.area); | |
> let itemIndex = placement.position; | |
> for (let l = items.length; itemIndex < l; itemIndex++) { | |
> let realItems = container.getElementsByAttribute("id", items[itemIndex]); | |
> if (realItems[0]) { | |
> container.insertBefore(node, realItems[0]); | |
> break; | |
> } | |
> } | |
> } | |
> if (node.parentNode != container) { | |
> container.appendChild(node); | |
> } | |
> return; | |
> } | |
./l10n/loader.js | |
./l10n/html.js | |
11c11 | |
< const { Ci } = require("chrome"); | |
--- | |
> const { Ci, Cu } = require("chrome"); | |
15a16,19 | |
> const { Services } = Cu.import("resource://gre/modules/Services.jsm"); | |
> | |
> const hideContentStyle = "data:text/css,:root {visibility: hidden !important;}"; | |
> const hideSheetUri = Services.io.newURI(hideContentStyle, null, null); | |
44,45c48,56 | |
< // Finally display document when we finished replacing all text content | |
< document.documentElement.style.visibility = "visible"; | |
--- | |
> try { | |
> // Finally display document when we finished replacing all text content | |
> let winUtils = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) | |
> .getInterface(Ci.nsIDOMWindowUtils); | |
> winUtils.removeSheet(hideSheetUri, winUtils.USER_SHEET); | |
> } | |
> catch(e) { | |
> console.exception(e); | |
> } | |
64,69c75,84 | |
< // First hide content of the document in order to have content blinking | |
< // between untranslated and translated states | |
< // TODO: use result of bug 737003 discussion in order to avoid any conflict | |
< // with document CSS | |
< document.documentElement.style.visibility = "hidden"; | |
< | |
--- | |
> try { | |
> // First hide content of the document in order to have content blinking | |
> // between untranslated and translated states | |
> let winUtils = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) | |
> .getInterface(Ci.nsIDOMWindowUtils); | |
> winUtils.loadSheet(hideSheetUri, winUtils.USER_SHEET); | |
> } | |
> catch(e) { | |
> console.exception(e); | |
> } | |
./l10n/locale.js | |
./l10n/core.js | |
./l10n/prefs.js | |
./base64.js | |
./page-mod.js | |
1,2d0 | |
< /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | |
< /* vim:set ts=2 sw=2 sts=2 et: */ | |
15,16d12 | |
< const { EventEmitter } = require('./deprecated/events'); | |
< const { List } = require('./deprecated/list'); | |
18c14,15 | |
< const { MatchPattern } = require('./page-mod/match-pattern'); | |
--- | |
> const { EventEmitter } = require('./deprecated/events'); | |
> const { on, emit } = require('./event/core'); | |
27d23 | |
< const { has, hasAny } = require('./util/array'); | |
29,36c25,28 | |
< | |
< 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); | |
--- | |
> const { Style } = require("./stylesheet/style"); | |
> const { attach, detach } = require("./content/mod"); | |
> const { has, hasAny } = require("./util/array"); | |
> const { Rules } = require("./util/rules"); | |
40a33,34 | |
> const mods = new WeakMap(); | |
> | |
52,72d45 | |
< // 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)), | |
< }); | |
< | |
130,153c103,113 | |
< rules.on('add', this._onRuleAdd = this._onRuleAdd.bind(this)); | |
< rules.on('remove', this._onRuleRemove = this._onRuleRemove.bind(this)); | |
< | |
< if (Array.isArray(include)) | |
< rules.add.apply(null, include); | |
< else | |
< rules.add(include); | |
< | |
< let styleRules = ""; | |
< | |
< if (contentStyleFile) | |
< styleRules = [].concat(contentStyleFile).map(readURISync).join(""); | |
< | |
< if (contentStyle) | |
< styleRules += [].concat(contentStyle).join(""); | |
< | |
< if (styleRules) { | |
< this._onRuleUpdate = this._onRuleUpdate.bind(this); | |
< | |
< this._styleRules = styleRules; | |
< | |
< this._registerStyleSheet(); | |
< rules.on('add', this._onRuleUpdate); | |
< rules.on('remove', this._onRuleUpdate); | |
--- | |
> | |
> if (!include) | |
> throw new Error('The `include` option must always contain atleast one rule'); | |
> | |
> rules.add.apply(rules, [].concat(include)); | |
> | |
> if (contentStyle || contentStyleFile) { | |
> this._style = Style({ | |
> uri: contentStyleFile, | |
> source: contentStyle | |
> }); | |
158,159c118 | |
< | |
< this._loadingWindows = []; | |
--- | |
> mods.set(this._public, this); | |
167a127,128 | |
> if (this._style) | |
> detach(this._style); | |
169c130,131 | |
< this._unregisterStyleSheet(); | |
--- | |
> for (let i in this.include) | |
> this.include.remove(this.include[i]); | |
171,175c133 | |
< this.include.removeListener('add', this._onRuleUpdate); | |
< this.include.removeListener('remove', this._onRuleUpdate); | |
< | |
< for each (let rule in this.include) | |
< this.include.remove(rule); | |
--- | |
> mods.delete(this._public); | |
177,178d134 | |
< this._loadingWindows = []; | |
< | |
181,182d136 | |
< _loadingWindows: [], | |
< | |
186,191d139 | |
< function isMatchingURI(uri) { | |
< // Use Array.some as `include` isn't a native array | |
< return Array.some(mod.include, function (rule) { | |
< return RULES[rule].test(uri); | |
< }); | |
< } | |
193c141 | |
< return isMatchingURI(getTabURI(tab)); | |
--- | |
> return mod.include.matchesAny(getTabURI(tab)); | |
218a167,169 | |
> if (this._style) | |
> attach(this._style, window); | |
> | |
255,260d205 | |
< _onRuleAdd: function _onRuleAdd(url) { | |
< pageModManager.on(url, this._onContent); | |
< }, | |
< _onRuleRemove: function _onRuleRemove(url) { | |
< pageModManager.off(url, this._onContent); | |
< }, | |
264,316d208 | |
< }, | |
< _onRuleUpdate: function _onRuleUpdate(){ | |
< this._registerStyleSheet(); | |
< }, | |
< | |
< _registerStyleSheet : function _registerStyleSheet() { | |
< let rules = this.include; | |
< let styleRules = this._styleRules; | |
< | |
< let documentRules = []; | |
< | |
< this._unregisterStyleSheet(); | |
< | |
< for each (let rule in rules) { | |
< let pattern = RULES[rule]; | |
< | |
< if (!pattern) | |
< continue; | |
< | |
< if (pattern.regexp) | |
< documentRules.push("regexp(\"" + pattern.regexp.source + "\")"); | |
< else if (pattern.exactURL) | |
< documentRules.push("url(" + pattern.exactURL + ")"); | |
< else if (pattern.domain) | |
< documentRules.push("domain(" + pattern.domain + ")"); | |
< else if (pattern.urlPrefix) | |
< documentRules.push("url-prefix(" + pattern.urlPrefix + ")"); | |
< else if (pattern.anyWebPage) | |
< documentRules.push("regexp(\"^(https?|ftp)://.*?\")"); | |
< } | |
< | |
< let uri = "data:text/css;charset=utf-8,"; | |
< if (documentRules.length > 0) | |
< uri += encodeURIComponent("@-moz-document " + | |
< documentRules.join(",") + " {" + styleRules + "}"); | |
< else | |
< uri += encodeURIComponent(styleRules); | |
< | |
< this._registeredStyleURI = io.newURI(uri, null, null); | |
< | |
< styleSheetService.loadAndRegisterSheet( | |
< this._registeredStyleURI, | |
< USER_SHEET | |
< ); | |
< }, | |
< | |
< _unregisterStyleSheet : function () { | |
< let uri = this._registeredStyleURI; | |
< | |
< if (uri && styleSheetService.sheetRegistered(uri, USER_SHEET)) | |
< styleSheetService.unregisterSheet(uri, USER_SHEET); | |
< | |
< this._registeredStyleURI = null; | |
336,338d227 | |
< for (let rule in RULES) { | |
< delete RULES[rule]; | |
< } | |
363,365c252,255 | |
< for (let rule in RULES) | |
< if (RULES[rule].test(document.URL)) | |
< this._emit(rule, window); | |
--- | |
> this._registry.forEach(function(mod) { | |
> if (mod.include.matchesAny(document.URL)) | |
> mods.get(mod)._onContent(window); | |
> }); | |
369,370d258 | |
< if (!this._listeners(topic).length) | |
< delete RULES[topic]; | |
./frame/hidden-frame.js | |
1,2d0 | |
< /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | |
< /* vim:set ts=2 sw=2 sts=2 et: */ | |
20c18 | |
< const { defer, resolve } = require("../core/promise"); | |
--- | |
> const { defer } = require("../core/promise"); | |
23,36c21,22 | |
< | |
< | |
< let appShellService = Cc["@mozilla.org/appshell/appShellService;1"]. | |
< getService(Ci.nsIAppShellService); | |
< | |
< let hiddenWindow = appShellService.hiddenDOMWindow; | |
< | |
< if (!hiddenWindow) { | |
< throw new Error([ | |
< "The hidden-frame module needs an app that supports a hidden window. ", | |
< "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 { window } = require("../addon/window"); | |
> const { fromIterator } = require("../util/array"); | |
40c26 | |
< let cache = []; | |
--- | |
> let cache = new Set(); | |
56,82d41 | |
< function makeHostFrame() { | |
< // Check if we can use the hidden window itself to host our iframes. | |
< // If it's not a suitable host, the hostFrame will be lazily created | |
< // by the first HiddenFrame instance. | |
< if (hiddenWindow.location.protocol == "chrome:" && | |
< (hiddenWindow.document.contentType == "application/vnd.mozilla.xul+xml" || | |
< hiddenWindow.document.contentType == "application/xhtml+xml")) { | |
< | |
< // Resolve to an object with a shape similar to iframe. | |
< return resolve({ contentDocument: hiddenWindow.document }); | |
< } | |
< else { | |
< return contentLoaded(makeFrame(hiddenWindow.document, { | |
< // Ugly ugly hack. This is the most lightweight "chrome:" file I could | |
< // find on the tree. | |
< // This hack should be removed by proper platform support on bug 565388 | |
< uri: "chrome://global/content/mozilla.xhtml", | |
< namespaceURI: hiddenWindow.document.documentElement.namespaceURI, | |
< nodeName: "iframe", | |
< allowJavascript: true, | |
< allowPlugins: true, | |
< allowAuth: true | |
< })); | |
< } | |
< } | |
< var hostFrame = makeHostFrame(); | |
< | |
118,124d76 | |
< function isFrameCached(frame) { | |
< // Function returns `true` if frame was already cached. | |
< return cache.some(function(value) { | |
< return value === frame | |
< }) | |
< } | |
< | |
130,131c82,92 | |
< if (isFrameCached(frame)) return frame; | |
< else cache.push(frame); | |
--- | |
> if (cache.has(frame)) return frame; | |
> else cache.add(frame); | |
> | |
> let element = makeFrame(window.document, { | |
> nodeName: "iframe", | |
> type: "content", | |
> allowJavascript: true, | |
> allowPlugins: true, | |
> allowAuth: true, | |
> }); | |
> elements.set(frame, element); | |
133,143c94 | |
< hostFrame.then(function({ contentDocument }) { | |
< let element = makeFrame(contentDocument, { | |
< nodeName: "iframe", | |
< type: "content", | |
< allowJavascript: true, | |
< allowPlugins: true, | |
< allowAuth: true, | |
< }); | |
< elements.set(frame, element); | |
< return contentLoaded(element); | |
< }).then(function onFrameReady(element) { | |
--- | |
> contentLoaded(element).then(function onFrameReady(element) { | |
155c106 | |
< if (!isFrameCached(frame)) return; | |
--- | |
> if (!cache.has(frame)) return; | |
158c109 | |
< cache.splice(cache.indexOf(frame), 1); | |
--- | |
> cache.delete(frame); | |
165,171c116 | |
< unload(function () { | |
< cache.slice(0).forEach(removeHiddenFrame); | |
< | |
< hostFrame.then(function(frame) { | |
< if (frame.parentNode) frame.parentNode.removeChild(frame); | |
< }); | |
< }); | |
--- | |
> unload(function() fromIterator(cache).forEach(removeHiddenFrame)); | |
./frame/utils.js | |
43c43,46 | |
< function create(document, options) { | |
--- | |
> function create(target, options) { | |
> target = target instanceof Ci.nsIDOMDocument ? target.documentElement : | |
> target instanceof Ci.nsIDOMWindow ? target.document.documentElement : | |
> target; | |
46d48 | |
< let nodeName = options.nodeName || 'browser'; | |
47a50,52 | |
> let isXUL = namespaceURI === XUL; | |
> let nodeName = isXUL && options.browser ? 'browser' : 'iframe'; | |
> let document = target.ownerDocument; | |
54a60,61 | |
> target.appendChild(frame); | |
> | |
58,62c65,75 | |
< // We remove XBL binding to avoid execution of code that is not going to | |
< // work because browser has no docShell attribute in remote mode | |
< // (for example) | |
< frame.setAttribute('style', '-moz-binding: none;'); | |
< frame.setAttribute('remote', 'true'); | |
--- | |
> if (isXUL) { | |
> // We remove XBL binding to avoid execution of code that is not going to | |
> // work because browser has no docShell attribute in remote mode | |
> // (for example) | |
> frame.setAttribute('style', '-moz-binding: none;'); | |
> frame.setAttribute('remote', 'true'); | |
> } | |
> else { | |
> frame.QueryInterface(Ci.nsIMozBrowserFrame); | |
> frame.createRemoteFrameLoader(null); | |
> } | |
65c78 | |
< document.documentElement.appendChild(frame); | |
--- | |
> | |
72a86,91 | |
> | |
> // Control whether the document can move/resize the window. Requires | |
> // recently added platform capability, so we test to avoid exceptions | |
> // in cases where capability is not present yet. | |
> if ("allowWindowControl" in docShell && "allowWindowControl" in options) | |
> docShell.allowWindowControl = !!options.allowWindowControl; | |
77a97,100 | |
> | |
> function swapFrameLoaders(from, to) | |
> from.QueryInterface(Ci.nsIFrameLoaderOwner).swapFrameLoaders(to) | |
> exports.swapFrameLoaders = swapFrameLoaders; | |
./url.js | |
4d3 | |
< | |
14a14,15 | |
> var tlds = Cc["@mozilla.org/network/effective-tld-service;1"] | |
> .getService(Ci.nsIEffectiveTLDService); | |
21a23,25 | |
> var URLParser = Cc["@mozilla.org/network/url-parser;1?auth=no"] | |
> .getService(Ci.nsIURLParser); | |
> | |
94a99,109 | |
> let uriData = [uri.path, uri.path.length, {}, {}, {}, {}, {}, {}]; | |
> URLParser.parsePath.apply(URLParser, uriData); | |
> let [{ value: filepathPos }, { value: filepathLen }, | |
> { value: queryPos }, { value: queryLen }, | |
> { value: refPos }, { value: refLen }] = uriData.slice(2); | |
> | |
> let hash = uri.ref ? "#" + uri.ref : ""; | |
> let pathname = uri.path.substr(filepathPos, filepathLen); | |
> let search = uri.path.substr(queryPos, queryLen); | |
> search = search ? "?" + search : ""; | |
> | |
97a113 | |
> this.__defineGetter__("hostname", function() host); | |
99a116,121 | |
> this.__defineGetter__("pathname", function() pathname); | |
> this.__defineGetter__("hash", function() hash); | |
> this.__defineGetter__("href", function() uri.spec); | |
> this.__defineGetter__("origin", function() uri.prePath); | |
> this.__defineGetter__("protocol", function() uri.scheme + ":"); | |
> this.__defineGetter__("search", function() search); | |
236a259,291 | |
> | |
> let getTLD = exports.getTLD = function getTLD (url) { | |
> let uri = newURI(url.toString()); | |
> let tld = null; | |
> try { | |
> tld = tlds.getPublicSuffix(uri); | |
> } catch (e if | |
> e.result == Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS || | |
> e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS) {} | |
> return tld; | |
> }; | |
> | |
> let isValidURI = exports.isValidURI = function (uri) { | |
> try { | |
> newURI(uri); | |
> } catch(e) { | |
> return false; | |
> } | |
> return true; | |
> } | |
> | |
> function isLocalURL(url) { | |
> if (String.indexOf(url, './') === 0) | |
> return true; | |
> | |
> try { | |
> return ['resource', 'data', 'chrome'].indexOf(URL(url).scheme) > -1; | |
> } | |
> catch(e) {} | |
> | |
> return false; | |
> } | |
> exports.isLocalURL = isLocalURL; | |
./private-browsing.js | |
10c10 | |
< const { setMode, getMode, on: onStateChange } = require('./private-browsing/utils'); | |
--- | |
> const { setMode, getMode, on: onStateChange, isPermanentPrivateBrowsing } = require('./private-browsing/utils'); | |
67a68,71 | |
> // check if the post pwpb, global pb service is enabled. | |
> if (isPermanentPrivateBrowsing()) | |
> return true; | |
> | |
76c80,81 | |
< 'The require("private-browsing") module\'s "start" and "stop" events are deprecated.', | |
--- | |
> 'The require("sdk/private-browsing") module\'s "start" and "stop" events ' + | |
> 'are deprecated.', | |
./private-browsing/utils.js | |
54a55,58 | |
> | |
> exports.isPermanentPrivateBrowsing = function() { | |
> return !!(PrivateBrowsingUtils && PrivateBrowsingUtils.permanentPrivateBrowsing); | |
> } | |
83,84c87,89 | |
< 'require("private-browsing").activate and require("private-browsing").deactivate ' + | |
< 'is deprecated.' | |
--- | |
> 'require("sdk/private-browsing").activate and ' + | |
> 'require("sdk/private-browsing").deactivate ' + | |
> 'are deprecated.' | |
./private-browsing/window/utils.js | |
./tabs.js | |
./net/url.js | |
./net/xhr.js | |
4d3 | |
< | |
8c7 | |
< "stability": "unstable" | |
--- | |
> "stability": "stable" | |
10a10 | |
> const { deprecateFunction } = require("../util/deprecate"); | |
12,83c12 | |
< const memory = require('../deprecated/memory'); | |
< const { when: unload } = require("../system/unload"); | |
< | |
< // ## Implementation Notes ## | |
< // | |
< // Making `XMLHttpRequest` objects available to Jetpack code involves a | |
< // few key principles universal to all low-level module implementations: | |
< // | |
< // * **Unloadability**. A Jetpack-based extension using this module can be | |
< // asked to unload itself at any time, e.g. because the user decides to | |
< // uninstall or disable the extension. This means we need to keep track of | |
< // all in-progress reqests and abort them on unload. | |
< // | |
< // * **Developer-Ergonomic Tracebacks**. Whenever an exception is raised | |
< // by a Jetpack-based extension, we want it to be logged in a | |
< // place that is specific to that extension--so that a developer | |
< // can distinguish it from an error on a web page or in another | |
< // extension, for instance. We also want it to be logged with a | |
< // full stack traceback, which the Mozilla platform doesn't usually | |
< // do. | |
< // | |
< // Because of this, we don't actually want to give the Mozilla | |
< // platform's "real" XHR implementation to clients, but instead provide | |
< // a simple wrapper that trivially delegates to the implementation in | |
< // all cases except where callbacks are involved: whenever Mozilla | |
< // platform code calls into the extension, such as during the XHR's | |
< // `onreadystatechange` callback, we want to wrap the client's callback | |
< // in a try-catch clause that traps any exceptions raised by the | |
< // callback and logs them via console.exception() instead of allowing | |
< // them to propagate back into Mozilla platform code. | |
< | |
< // This is a private list of all active requests, so we know what to | |
< // abort if we're asked to unload. | |
< var requests = []; | |
< | |
< // Events on XHRs that we should listen for, so we know when to remove | |
< // a request from our private list. | |
< const TERMINATE_EVENTS = ["load", "error", "abort"]; | |
< | |
< // Read-only properties of XMLHttpRequest objects that we want to | |
< // directly delegate to. | |
< const READ_ONLY_PROPS = ["readyState", "responseText", "responseXML", | |
< "status", "statusText"]; | |
< | |
< // Methods of XMLHttpRequest that we want to directly delegate to. | |
< const DELEGATED_METHODS = ["abort", "getAllResponseHeaders", | |
< "getResponseHeader", "overrideMimeType", | |
< "send", "sendAsBinary", "setRequestHeader", | |
< "open"]; | |
< | |
< var getRequestCount = exports.getRequestCount = function getRequestCount() { | |
< return requests.length; | |
< }; | |
< | |
< var XMLHttpRequest = exports.XMLHttpRequest = function XMLHttpRequest() { | |
< var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] | |
< .createInstance(Ci.nsIXMLHttpRequest); | |
< // For the sake of simplicity, don't tie this request to any UI. | |
< req.mozBackgroundRequest = true; | |
< | |
< memory.track(req, "XMLHttpRequest"); | |
< | |
< this._req = req; | |
< this._orsc = null; | |
< | |
< requests.push(this); | |
< | |
< var self = this; | |
< | |
< this._boundCleanup = function _boundCleanup() { | |
< self._cleanup(); | |
< }; | |
--- | |
> const XMLHttpRequest = require("../addon/window").window.XMLHttpRequest; | |
85,89c14,21 | |
< TERMINATE_EVENTS.forEach( | |
< function(name) { | |
< self._req.addEventListener(name, self._boundCleanup, false); | |
< }); | |
< }; | |
--- | |
> Object.defineProperties(XMLHttpRequest.prototype, { | |
> mozBackgroundRequest: { | |
> value: true, | |
> }, | |
> forceAllowThirdPartyCookie: { | |
> configurable: true, | |
> value: deprecateFunction(function() { | |
> forceAllowThirdPartyCookie(this); | |
91,132c23,24 | |
< XMLHttpRequest.prototype = { | |
< _cleanup: function _cleanup() { | |
< this.onreadystatechange = null; | |
< var index = requests.indexOf(this); | |
< if (index != -1) { | |
< var self = this; | |
< TERMINATE_EVENTS.forEach( | |
< function(name) { | |
< self._req.removeEventListener(name, self._boundCleanup, false); | |
< }); | |
< requests.splice(index, 1); | |
< } | |
< }, | |
< _unload: function _unload() { | |
< this._req.abort(); | |
< this._cleanup(); | |
< }, | |
< addEventListener: function addEventListener() { | |
< throw new Error("not implemented"); | |
< }, | |
< removeEventListener: function removeEventListener() { | |
< throw new Error("not implemented"); | |
< }, | |
< set upload(newValue) { | |
< throw new Error("not implemented"); | |
< }, | |
< get onreadystatechange() { | |
< return this._orsc; | |
< }, | |
< set onreadystatechange(cb) { | |
< this._orsc = cb; | |
< if (cb) { | |
< var self = this; | |
< this._req.onreadystatechange = function() { | |
< try { | |
< self._orsc.apply(self, arguments); | |
< } catch (e) { | |
< console.exception(e); | |
< } | |
< }; | |
< } else | |
< this._req.onreadystatechange = null; | |
--- | |
> }, "`xhr.forceAllowThirdPartyCookie()` is deprecated, please use" + | |
> "`require('sdk/net/xhr').forceAllowThirdPartyCookie(request)` instead") | |
134c26,27 | |
< }; | |
--- | |
> }); | |
> exports.XMLHttpRequest = XMLHttpRequest; | |
136,150c29,33 | |
< READ_ONLY_PROPS.forEach( | |
< function(name) { | |
< XMLHttpRequest.prototype.__defineGetter__( | |
< name, | |
< function() { | |
< return this._req[name]; | |
< }); | |
< }); | |
< | |
< DELEGATED_METHODS.forEach( | |
< function(name) { | |
< XMLHttpRequest.prototype[name] = function() { | |
< return this._req[name].apply(this._req, arguments); | |
< }; | |
< }); | |
--- | |
> function forceAllowThirdPartyCookie(xhr) { | |
> if (xhr.channel instanceof Ci.nsIHttpChannelInternal) | |
> xhr.channel.forceAllowThirdPartyCookie = true; | |
> } | |
> exports.forceAllowThirdPartyCookie = forceAllowThirdPartyCookie; | |
152,154c35,36 | |
< unload(function() { | |
< requests.slice().forEach(function(request) { request._unload(); }); | |
< }); | |
--- | |
> // No need to handle add-on unloads as addon/window is closed at unload | |
> // and it will take down all the associated requests. | |
./panel/window.js | |
5a6,14 | |
> // The panel module currently supports only Firefox. | |
> // See: https://bugzilla.mozilla.org/show_bug.cgi?id=jetpack-panel-apps | |
> module.metadata = { | |
> 'stability': 'unstable', | |
> 'engines': { | |
> 'Firefox': '*' | |
> } | |
> }; | |
> | |
./preferences/service.js | |
./preferences/event-target.js | |
./page-mod/match-pattern.js | |
1,5c1 | |
< /* -*- 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/. */ | |
--- | |
> let { deprecateUsage } = require("../util/deprecate"); | |
7c3 | |
< "use strict"; | |
--- | |
> deprecateUsage("Module 'sdk/page-mod/match-pattern' is deprecated use 'sdk/util/match-pattern' instead"); | |
9,108c5 | |
< module.metadata = { | |
< "stability": "unstable" | |
< }; | |
< | |
< const { URL } = require("../url"); | |
< | |
< exports.MatchPattern = MatchPattern; | |
< | |
< function MatchPattern(pattern) { | |
< if (typeof pattern.test == "function") { | |
< | |
< // For compatibility with -moz-document rules, we require the RegExp's | |
< // global, ignoreCase, and multiline flags to be set to false. | |
< if (pattern.global) { | |
< throw new Error("A RegExp match pattern cannot be set to `global` " + | |
< "(i.e. //g)."); | |
< } | |
< if (pattern.ignoreCase) { | |
< throw new Error("A RegExp match pattern cannot be set to `ignoreCase` " + | |
< "(i.e. //i)."); | |
< } | |
< if (pattern.multiline) { | |
< throw new Error("A RegExp match pattern cannot be set to `multiline` " + | |
< "(i.e. //m)."); | |
< } | |
< | |
< this.regexp = pattern; | |
< } | |
< else { | |
< let firstWildcardPosition = pattern.indexOf("*"); | |
< let lastWildcardPosition = pattern.lastIndexOf("*"); | |
< if (firstWildcardPosition != lastWildcardPosition) | |
< throw new Error("There can be at most one '*' character in a wildcard."); | |
< | |
< if (firstWildcardPosition == 0) { | |
< if (pattern.length == 1) | |
< this.anyWebPage = true; | |
< else if (pattern[1] != ".") | |
< throw new Error("Expected a *.<domain name> string, got: " + pattern); | |
< else | |
< this.domain = pattern.substr(2); | |
< } | |
< else { | |
< if (pattern.indexOf(":") == -1) { | |
< throw new Error("When not using *.example.org wildcard, the string " + | |
< "supplied is expected to be either an exact URL to " + | |
< "match or a URL prefix. The provided string ('" + | |
< pattern + "') is unlikely to match any pages."); | |
< } | |
< | |
< if (firstWildcardPosition == -1) | |
< this.exactURL = pattern; | |
< else if (firstWildcardPosition == pattern.length - 1) | |
< this.urlPrefix = pattern.substr(0, pattern.length - 1); | |
< else { | |
< throw new Error("The provided wildcard ('" + pattern + "') has a '*' " + | |
< "in an unexpected position. It is expected to be the " + | |
< "first or the last character in the wildcard."); | |
< } | |
< } | |
< } | |
< } | |
< | |
< MatchPattern.prototype = { | |
< | |
< test: function MatchPattern_test(urlStr) { | |
< try { | |
< var url = URL(urlStr); | |
< } | |
< catch (err) { | |
< return false; | |
< } | |
< | |
< // Test the URL against a RegExp pattern. For compatibility with | |
< // -moz-document rules, we require the RegExp to match the entire URL, | |
< // so we not only test for a match, we also make sure the matched string | |
< // is the entire URL string. | |
< // | |
< // Assuming most URLs don't match most match patterns, we call `test` for | |
< // speed when determining whether or not the URL matches, then call `exec` | |
< // for the small subset that match to make sure the entire URL matches. | |
< // | |
< if (this.regexp && this.regexp.test(urlStr) && | |
< this.regexp.exec(urlStr)[0] == urlStr) | |
< return true; | |
< | |
< if (this.anyWebPage && /^(https?|ftp)$/.test(url.scheme)) | |
< return true; | |
< if (this.exactURL && this.exactURL == urlStr) | |
< return true; | |
< if (this.domain && url.host && | |
< url.host.slice(-this.domain.length) == this.domain) | |
< return true; | |
< if (this.urlPrefix && 0 == urlStr.indexOf(this.urlPrefix)) | |
< return true; | |
< | |
< return false; | |
< } | |
< | |
< }; | |
--- | |
> module.exports = require("../util/match-pattern"); | |
./loader/sandbox.js | |
1,2d0 | |
< /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | |
< /* vim:set ts=2 sw=2 sts=2 et: */ | |
15a14 | |
> const self = require('sdk/self'); | |
22c21,26 | |
< return Cu.Sandbox(target || systemPrincipal, options || {}); | |
--- | |
> options = options || {}; | |
> options.metadata = options.metadata ? options.metadata : {}; | |
> options.metadata.addonID = options.metadata.addonID ? | |
> options.metadata.addonID : self.id; | |
> | |
> return Cu.Sandbox(target || systemPrincipal, options); | |
24c28 | |
< exports.sandbox = sandbox | |
--- | |
> exports.sandbox = sandbox; | |
./loader/cuddlefish.js | |
1d0 | |
< /* vim:set ts=2 sw=2 sts=2 expandtab */ | |
4,6c3 | |
< * file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
< */ | |
< | |
--- | |
> * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
51d47 | |
< | |
66,68c62,70 | |
< applications.forEach(xulappModule.is); | |
< | |
< let versionRange = engines[xulappModule.name]; | |
--- | |
> let versionRange; | |
> applications.forEach(function(name) { | |
> if (xulappModule.is(name)) { | |
> versionRange = engines[name]; | |
> // Continue iteration. We want to ensure the module doesn't | |
> // contain a typo in the applications' name or some unknown | |
> // application - `is` function throws an exception in that case. | |
> } | |
> }); | |
./lang/functional.js | |
15c15,16 | |
< const { setTimeout } = require("../timers"); | |
--- | |
> const { setImmediate, setTimeout } = require("../timers"); | |
> const { deprecateFunction } = require("../util/deprecate"); | |
32c33 | |
< * to do `setTimeout(function() { ... }, 0)`, with a difference that returned | |
--- | |
> * to do `setImmediate(function() { ... })`, with a difference that returned | |
37,38c38 | |
< return function deferred() | |
< setTimeout(invoke, 0, f, arguments, this); | |
--- | |
> return function deferred() setImmediate(invoke, f, arguments, this); | |
43a44,54 | |
> /* | |
> * Takes a funtion and returns a wrapped function that returns `this` | |
> */ | |
> function chain(f) { | |
> return function chainable(...args) { | |
> f.apply(this, args); | |
> return this; | |
> }; | |
> } | |
> exports.chain = chain; | |
> | |
58c69,70 | |
< * Curries a function with the arguments given. | |
--- | |
> * Takes a function and bind values to one or more arguments, returning a new | |
> * function of smaller arity. | |
61c73 | |
< * The function to curry | |
--- | |
> * The function to partial | |
63c75 | |
< * @returns The function curried | |
--- | |
> * @returns The new function with binded values | |
65c77 | |
< function curry(fn) { | |
--- | |
> function partial(fn) { | |
72a85,119 | |
> exports.partial = partial; | |
> | |
> /** | |
> * Returns function with implicit currying, which will continue currying until | |
> * expected number of argument is collected. Expected number of arguments is | |
> * determined by `fn.length`. Using this with variadic functions is stupid, | |
> * so don't do it. | |
> * | |
> * @examples | |
> * | |
> * var sum = curry(function(a, b) { | |
> * return a + b | |
> * }) | |
> * console.log(sum(2, 2)) // 4 | |
> * console.log(sum(2)(4)) // 6 | |
> */ | |
> var curry = new function() { | |
> function currier(fn, arity, params) { | |
> // Function either continues to curry arguments or executes function | |
> // if desired arguments have being collected. | |
> return function curried() { | |
> var input = Array.slice(arguments); | |
> // Prepend all curried arguments to the given arguments. | |
> if (params) input.unshift.apply(input, params); | |
> // If expected number of arguments has being collected invoke fn, | |
> // othrewise return curried version Otherwise continue curried. | |
> return (input.length >= arity) ? fn.apply(this, input) : | |
> currier(fn, arity, input); | |
> }; | |
> } | |
> | |
> return function curry(fn) { | |
> return currier(fn, fn.length); | |
> } | |
> }; | |
./simple-storage.js | |
1,3c1 | |
< /* -*- 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 | |
--- | |
> /* This Source Code Form is subject to the terms of the Mozilla Public | |
./content/content-worker.js | |
227c227 | |
< injectMessageAPI: function injectMessageAPI(exports, pipe) { | |
--- | |
> injectMessageAPI: function injectMessageAPI(exports, pipe, console) { | |
296c296 | |
< ContentWorker.injectMessageAPI(exports, pipe); | |
--- | |
> ContentWorker.injectMessageAPI(exports, pipe, exports.console); | |
./content/symbiont.js | |
1,2d0 | |
< /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | |
< /* vim:set ts=2 sw=2 sts=2 et: */ | |
20c18,19 | |
< const assetsURI = require('../self').data.url(); | |
--- | |
> // Everything coming from add-on's xpi considered an asset. | |
> const assetsURI = require('../self').data.url().replace(/data\/$/, ""); | |
./content/content-proxy.js | |
./content/content.js | |
1,2d0 | |
< /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | |
< /* vim:set ts=2 sw=2 sts=2 et: */ | |
./content/loader.js | |
1,2d0 | |
< /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | |
< /* vim:set ts=2 sw=2 sts=2 et: */ | |
15c13 | |
< const { URL } = require('../url'); | |
--- | |
> const { isValidURI, URL } = require('../url'); | |
16a15 | |
> const { contract } = require('../util/contract'); | |
21,23c20 | |
< function ensureNull(value) { | |
< return value == null ? null : value; | |
< } | |
--- | |
> function ensureNull(value) value == null ? null : value | |
28,35c25,30 | |
< ok: function (value) { | |
< try { | |
< URL(value); | |
< } | |
< catch(e) { | |
< return false; | |
< } | |
< return true; | |
--- | |
> map: function(url) !url ? ensureNull(url) : url.toString(), | |
> is: ['undefined', 'null', 'string'], | |
> ok: function (url) { | |
> if (url === null) | |
> return true; | |
> return isValidURI(url); | |
204a200,201 | |
> | |
> exports.contract = contract(valid); | |
./content/thumbnail.js | |
./content/worker.js | |
1,2d0 | |
< /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | |
< /* vim:set ts=2 sw=2 sts=2 et: */ | |
30d27 | |
< require('./content-proxy.js'); | |
35d31 | |
< const CONTENT_PROXY_URL = prefix + 'content-proxy.js'; | |
37a34,40 | |
> // Fetch additional list of domains to authorize access to for each content | |
> // script. It is stored in manifest `metadata` field which contains | |
> // package.json data. This list is originaly defined by authors in | |
> // `permissions` attribute of their package.json addon file. | |
> const permissions = require('@loader/options').metadata['permissions'] || {}; | |
> const EXPANDED_PRINCIPALS = permissions['cross-domain-content'] || []; | |
> | |
47,55d49 | |
< /** | |
< * This key is not exported and should only be used for proxy tests. | |
< * The following `PRIVATE_KEY` is used in addon module scope in order to tell | |
< * Worker API to expose `UNWRAP_ACCESS_KEY` in content script. | |
< * This key allows test-content-proxy.js to unwrap proxy with valueOf: | |
< * let xpcWrapper = proxyWrapper.valueOf(UNWRAP_ACCESS_KEY); | |
< */ | |
< const PRIVATE_KEY = {}; | |
< | |
86,90d79 | |
< // Bug 732716: Ensure wrapping xrays sent to the content script | |
< // otherwise it will have access to raw xraywrappers and content script | |
< // will assume it is an user object coming from the content script sandbox | |
< if ("_wrap" in this) | |
< args = args.map(this._wrap); | |
130a120,141 | |
> // Eventually use expanded principal sandbox feature, if some are given. | |
> // | |
> // But prevent it when the Worker isn't used for a content script but for | |
> // injecting `addon` object into a Panel, Widget, ... scope. | |
> // That's because: | |
> // 1/ It is useless to use multiple domains as the worker is only used | |
> // to communicate with the addon, | |
> // 2/ By using it it would prevent the document to have access to any JS | |
> // value of the worker. As JS values coming from multiple domain principals | |
> // can't be accessed by "mono-principals" (principal with only one domain). | |
> // Even if this principal is for a domain that is specified in the multiple | |
> // domain principal. | |
> let principals = window; | |
> let wantGlobalProperties = [] | |
> if (EXPANDED_PRINCIPALS.length > 0 && !worker._injectInDocument) { | |
> principals = EXPANDED_PRINCIPALS.concat(window); | |
> // We have to replace XHR constructor of the content document | |
> // with a custom cross origin one, automagically added by platform code: | |
> delete proto.XMLHttpRequest; | |
> wantGlobalProperties.push("XMLHttpRequest"); | |
> } | |
> | |
133c144 | |
< let apiSandbox = sandbox(window, { wantXrays: true }); | |
--- | |
> let apiSandbox = sandbox(principals, { wantXrays: true, sameZoneAs: window }); | |
136,146d146 | |
< // Build content proxies only if the document has a non-system principal | |
< // And only on old firefox versions that doesn't ship bug 738244 | |
< if (USE_JS_PROXIES && XPCNativeWrapper.unwrap(window) !== window) { | |
< // Execute the proxy code | |
< load(apiSandbox, CONTENT_PROXY_URL); | |
< // Get a reference of the window's proxy | |
< proto = apiSandbox.create(window); | |
< // Keep a reference to `wrap` function for `emitSync` usage | |
< this._wrap = apiSandbox.wrap; | |
< } | |
< | |
149c149 | |
< let content = this._sandbox = sandbox(window, { | |
--- | |
> let content = this._sandbox = sandbox(principals, { | |
151c151,154 | |
< wantXrays: true | |
--- | |
> wantXrays: true, | |
> wantGlobalProperties: wantGlobalProperties, | |
> sameZoneAs: window, | |
> metadata: { SDKContentScript: true } | |
233,238d235 | |
< // Internal feature that is only used by SDK tests: | |
< // Expose unlock key to content script context. | |
< // See `PRIVATE_KEY` definition for more information. | |
< if (apiSandbox && worker._expose_key) | |
< content.UNWRAP_ACCESS_KEY = apiSandbox.UNWRAP_ACCESS_KEY; | |
< | |
316d312 | |
< this._wrap = null; | |
375c371 | |
< * @see https://jetpack.mozillalabs.com/sdk/latest/docs/#module/api-utils/content/worker | |
--- | |
> * @see https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/content/worker.html | |
380a377,383 | |
> // List of messages fired before worker is initialized | |
> get _earlyEvents() { | |
> delete this._earlyEvents; | |
> this._earlyEvents = []; | |
> return this._earlyEvents; | |
> }, | |
> | |
393,399c396,402 | |
< postMessage: function postMessage(data) { | |
< if (!this._contentWorker) | |
< throw new Error(ERR_DESTROYED); | |
< if (this._frozen) | |
< throw new Error(ERR_FROZEN); | |
< | |
< this._contentWorker.emit("message", data); | |
--- | |
> postMessage: function (data) { | |
> let args = ['message'].concat(Array.slice(arguments)); | |
> if (!this._inited) { | |
> this._earlyEvents.push(args); | |
> return; | |
> } | |
> processMessage.apply(this, args); | |
413d415 | |
< let self = this; | |
415c417 | |
< emit: function () self._emitEventToContent(Array.slice(arguments)) | |
--- | |
> emit: this._emitEventToContent.bind(this) | |
441,443c443,444 | |
< _emitEventToContent: function _emitEventToContent(args) { | |
< // We need to save events that are emitted before the worker is | |
< // initialized | |
--- | |
> _emitEventToContent: function () { | |
> let args = ['event'].concat(Array.slice(arguments)); | |
448,458c449 | |
< | |
< if (this._frozen) | |
< throw new Error(ERR_FROZEN); | |
< | |
< // We throw exception when the worker has been destroyed | |
< if (!this._contentWorker) { | |
< throw new Error(ERR_DESTROYED); | |
< } | |
< | |
< // Forward the event to the WorkerSandbox object | |
< this._contentWorker.emit.apply(null, ["event"].concat(args)); | |
--- | |
> processMessage.apply(this, args); | |
468,474d458 | |
< // List of custom events fired before worker is initialized | |
< get _earlyEvents() { | |
< delete this._earlyEvents; | |
< this._earlyEvents = []; | |
< return this._earlyEvents; | |
< }, | |
< | |
478,479d461 | |
< if ('window' in options) | |
< this._window = options.window; | |
485a468,484 | |
> | |
> this._setListeners(options); | |
> | |
> unload.ensure(this._public, "destroy"); | |
> | |
> // Ensure that worker._port is initialized for contentWorker to be able | |
> // to send events during worker initialization. | |
> this.port; | |
> | |
> this._documentUnload = this._documentUnload.bind(this); | |
> this._pageShow = this._pageShow.bind(this); | |
> this._pageHide = this._pageHide.bind(this); | |
> | |
> if ("window" in options) this._attach(options.window); | |
> }, | |
> | |
> _setListeners: function(options) { | |
491a491 | |
> }, | |
493,497c493,494 | |
< // Internal feature that is only used by SDK unit tests. | |
< // See `PRIVATE_KEY` definition for more information. | |
< if ('exposeUnlockKey' in options && options.exposeUnlockKey === PRIVATE_KEY) | |
< this._expose_key = true; | |
< | |
--- | |
> _attach: function(window) { | |
> this._window = window; | |
503,504c500 | |
< observers.add("inner-window-destroyed", | |
< this._documentUnload = this._documentUnload.bind(this)); | |
--- | |
> observers.add("inner-window-destroyed", this._documentUnload); | |
508,519c504,505 | |
< this._window.addEventListener("pageshow", | |
< this._pageShow = this._pageShow.bind(this), | |
< true); | |
< this._window.addEventListener("pagehide", | |
< this._pageHide = this._pageHide.bind(this), | |
< true); | |
< | |
< unload.ensure(this._public, "destroy"); | |
< | |
< // Ensure that worker._port is initialized for contentWorker to be able | |
< // to send use event during WorkerSandbox(this) | |
< this.port; | |
--- | |
> this._window.addEventListener("pageshow", this._pageShow, true); | |
> this._window.addEventListener("pagehide", this._pageHide, true); | |
528,530c514,518 | |
< // Flush all events that have been fired before the worker is initialized. | |
< this._earlyEvents.forEach((function (args) this._emitEventToContent(args)). | |
< bind(this)); | |
--- | |
> // Process all events and messages that were fired before the | |
> // worker was initialized. | |
> this._earlyEvents.forEach((function (args) { | |
> processMessage.apply(this, args); | |
> }).bind(this)); | |
569a558 | |
> this._inited = true; | |
593c582 | |
< this._earlyEvents.slice(0, this._earlyEvents.length); | |
--- | |
> this._earlyEvents.length = 0; | |
595a585 | |
> this._inited = false; | |
624a615,630 | |
> | |
> /** | |
> * Fired from postMessage and _emitEventToContent, or from the _earlyMessage | |
> * queue when fired before the content is loaded. Sends arguments to | |
> * contentWorker if able | |
> */ | |
> | |
> function processMessage () { | |
> if (!this._contentWorker) | |
> throw new Error(ERR_DESTROYED); | |
> if (this._frozen) | |
> throw new Error(ERR_FROZEN); | |
> | |
> this._contentWorker.emit.apply(null, Array.slice(arguments)); | |
> } | |
> | |
./event/target.js | |
1,2d0 | |
< /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | |
< /* vim:set ts=2 sw=2 sts=2 et: */ | |
13,14c11,12 | |
< const { on, once, off } = require('./core'); | |
< const { method } = require('../lang/functional'); | |
--- | |
> const { on, once, off, setListeners } = require('./core'); | |
> const { method, chain } = require('../lang/functional'); | |
17,18d14 | |
< const EVENT_TYPE_PATTERN = /^on([A-Z]\w+$)/; | |
< | |
29a26,30 | |
> /** | |
> * Method initializes `this` event source. It goes through properties of a | |
> * given `options` and registers listeners for the ones that look like an | |
> * event listeners. | |
> */ | |
31,41c32 | |
< options = options || {}; | |
< // Go through each property and registers event listeners for those | |
< // that have a name matching following pattern (`onEventType`). | |
< Object.keys(options).forEach(function onEach(key) { | |
< let match = EVENT_TYPE_PATTERN.exec(key); | |
< let type = match && match[1].toLowerCase(); | |
< let listener = options[key]; | |
< | |
< if (type && typeof(listener) === 'function') | |
< this.on(type, listener); | |
< }, this); | |
--- | |
> setListeners(this, options); | |
55c46 | |
< on: method(on), | |
--- | |
> on: chain(method(on)), | |
64c55 | |
< once: method(once), | |
--- | |
> once: chain(method(once)), | |
77a69,73 | |
> return this; | |
> }, | |
> off: function(type, listener) { | |
> off(this, type, listener); | |
> return this; | |
./event/core.js | |
1,2d0 | |
< /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | |
< /* vim:set ts=2 sw=2 sts=2 et: */ | |
19a18,19 | |
> const EVENT_TYPE_PATTERN = /^on([A-Z]\w+$)/; | |
> | |
97,99c97,106 | |
< let args = Array.slice(arguments, 2) | |
< let listeners = observers(target, type).slice() | |
< while (listeners.length) { | |
--- | |
> let args = Array.slice(arguments, 2); | |
> let state = observers(target, type); | |
> let listeners = state.slice(); | |
> let index = 0; | |
> let count = listeners.length; | |
> | |
> // If error event and there are no handlers then print error message | |
> // into a console. | |
> if (count === 0 && type === 'error') console.exception(message); | |
> while (index < count) { | |
101c108,110 | |
< yield listeners.shift().apply(target, args); | |
--- | |
> let listener = listeners[index]; | |
> // Dispatch only if listener is still registered. | |
> if (~state.indexOf(listener)) yield listener.apply(target, args); | |
106,109c115,116 | |
< if (type !== 'error' && observers(target, 'error').length) | |
< emit(target, 'error', error); | |
< else | |
< console.exception(error); | |
--- | |
> if (type !== 'error') emit(target, 'error', error); | |
> else console.exception(error); | |
110a118 | |
> index = index + 1; | |
152a161,183 | |
> | |
> /** | |
> * Registers listeners on the given event `target` from the given `listeners` | |
> * dictionary. Iterates over the listeners and if property name matches name | |
> * pattern `onEventType` and property is a function, then registers it as | |
> * an `eventType` listener on `target`. | |
> * | |
> * @param {Object} target | |
> * The type of event. | |
> * @param {Object} listeners | |
> * Dictionary of listeners. | |
> */ | |
> function setListeners(target, listeners) { | |
> Object.keys(listeners || {}).forEach(function onEach(key) { | |
> let match = EVENT_TYPE_PATTERN.exec(key); | |
> let type = match && match[1].toLowerCase(); | |
> let listener = listeners[key]; | |
> | |
> if (type && typeof(listener) === 'function') | |
> on(target, type, listener); | |
> }); | |
> } | |
> exports.setListeners = setListeners; | |
./tabs/common.js | |
22a23,24 | |
> onLoad: { is: ["undefined", "function"] }, | |
> onPageShow: { is: ["undefined", "function"] }, | |
./tabs/tab.js | |
./tabs/events.js | |
14a15,16 | |
> load: "load", // Used for non-HTML content | |
> pageshow: "pageshow", // Used for cached content | |
27c29 | |
< listener: ON_PREFIX + name.charAt(0).toUpperCase() + name.substr(1), | |
--- | |
> listener: createListenerName(name), | |
30a33,39 | |
> | |
> function createListenerName (name) { | |
> if (name === 'pageshow') | |
> return 'onPageShow'; | |
> else | |
> return ON_PREFIX + name.charAt(0).toUpperCase() + name.substr(1); | |
> } | |
./tabs/tabs.js | |
./tabs/utils.js | |
1d0 | |
< /* vim:set ts=2 sw=2 sts=2 et: */ | |
136c135,138 | |
< if (gBrowser) | |
--- | |
> if (gBrowser) { | |
> // Bug 699450: the tab may already have been detached | |
> if (!tab.parentNode) | |
> return; | |
137a140 | |
> } | |
141c144,147 | |
< if (window && window.BrowserApp) | |
--- | |
> if (window && window.BrowserApp) { | |
> // Bug 699450: the tab may already have been detached | |
> if (!tab.browser) | |
> return; | |
142a149 | |
> } | |
170,175d176 | |
< | |
< function getContentWindowForTab(tab) { | |
< return getBrowserForTab(tab).contentWindow; | |
< } | |
< exports.getContentWindowForTab = getContentWindowForTab; | |
< | |
214,217c215,224 | |
< let browser = window.QueryInterface(Ci.nsIInterfaceRequestor) | |
< .getInterface(Ci.nsIWebNavigation) | |
< .QueryInterface(Ci.nsIDocShell) | |
< .chromeEventHandler; | |
--- | |
> let browser; | |
> try { | |
> browser = window.QueryInterface(Ci.nsIInterfaceRequestor) | |
> .getInterface(Ci.nsIWebNavigation) | |
> .QueryInterface(Ci.nsIDocShell) | |
> .chromeEventHandler; | |
> } catch(e) { | |
> // Bug 699450: The tab may already have been detached so that `window` is | |
> // in a almost destroyed state and can't be queryinterfaced anymore. | |
> } | |
221c228 | |
< return false; | |
--- | |
> return null; | |
310a318,368 | |
> | |
> function pin(tab) { | |
> let gBrowser = getTabBrowserForTab(tab); | |
> // TODO: Implement Fennec support | |
> if (gBrowser) gBrowser.pinTab(tab); | |
> } | |
> exports.pin = pin; | |
> | |
> function unpin(tab) { | |
> let gBrowser = getTabBrowserForTab(tab); | |
> // TODO: Implement Fennec support | |
> if (gBrowser) gBrowser.unpinTab(tab); | |
> } | |
> exports.unpin = unpin; | |
> | |
> function isPinned(tab) !!tab.pinned | |
> exports.isPinned = isPinned; | |
> | |
> function reload(tab) { | |
> let gBrowser = getTabBrowserForTab(tab); | |
> // Firefox | |
> if (gBrowser) gBrowser.unpinTab(tab); | |
> // Fennec | |
> else if (tab.browser) tab.browser.reload(); | |
> } | |
> exports.reload = reload | |
> | |
> function getIndex(tab) { | |
> let gBrowser = getTabBrowserForTab(tab); | |
> // Firefox | |
> if (gBrowser) { | |
> let document = getBrowserForTab(tab).contentDocument; | |
> return gBrowser.getBrowserIndexForDocument(document); | |
> } | |
> // Fennec | |
> else { | |
> let window = getWindowHoldingTab(tab) | |
> let tabs = window.BrowserApp.tabs; | |
> for (let i = tabs.length; i >= 0; i--) | |
> if (tabs[i] === tab) return i; | |
> } | |
> } | |
> exports.getIndex = getIndex; | |
> | |
> function move(tab, index) { | |
> let gBrowser = getTabBrowserForTab(tab); | |
> // Firefox | |
> if (gBrowser) gBrowser.moveTabTo(tab, index); | |
> // TODO: Implement fennec support | |
> } | |
> exports.move = move; | |
./tabs/tabs-firefox.js | |
14a15,22 | |
> function newTabWindow(options) { | |
> // `tabs` option is under review and may be removed. | |
> return windows.open({ | |
> tabs: [ options ], | |
> isPrivate: options.isPrivate | |
> }); | |
> } | |
> | |
18,22c26 | |
< // `tabs` option is under review and may be removed. | |
< windows.open({ | |
< tabs: [ options ], | |
< isPrivate: options.isPrivate | |
< }); | |
--- | |
> newTabWindow(options); | |
25d28 | |
< // Open in active window if new window was not required. | |
28c31,32 | |
< let privateState = !!options.isPrivate; | |
--- | |
> let privateState = (supportPrivateTabs && (options.isPrivate || isPrivate(activeWindow))) || false; | |
> | |
30c34 | |
< if (!supportPrivateTabs || privateState === isPrivate(activeWindow)) { | |
--- | |
> if (activeWindow && (!supportPrivateTabs || privateState === isPrivate(activeWindow))) { | |
41,44c45 | |
< windows.open({ | |
< tabs: [ options ], | |
< isPrivate: options.isPrivate | |
< }); | |
--- | |
> newTabWindow(options); | |
./tabs/namespace.js | |
./tabs/helpers.js | |
./tabs/observer.js | |
1d0 | |
< /* vim:set ts=2 sw=2 sts=2 et: */ | |
./tabs/tab-fennec.js | |
10c10 | |
< const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, getContentWindowForTab, | |
--- | |
> const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, getTabContentWindow, | |
35a36,38 | |
> | |
> let onPageShow = tabInternals.onPageShow = onTabPageShow.bind(this); | |
> tab.browser.addEventListener(EVENTS.pageshow.dom, onPageShow, false); | |
57c60,62 | |
< get url() getTabURL(tabNS(this).tab), | |
--- | |
> get url() { | |
> return tabNS(this).closed ? undefined : getTabURL(tabNS(this).tab); | |
> }, | |
65,66c70,79 | |
< // TODO: provide the real favicon when it is available | |
< console.error(ERR_FENNEC_MSG); | |
--- | |
> /* | |
> * Synchronous favicon services were never supported on Fennec, | |
> * and as of FF22, are now deprecated. When/if favicon services | |
> * are supported for Fennec, this getter should reference | |
> * `require('sdk/places/favicon').getFavicon` | |
> */ | |
> console.error( | |
> 'tab.favicon is deprecated, and currently ' + | |
> 'favicon helpers are not yet supported by Fennec' | |
> ); | |
89a103,104 | |
> if (tabNS(this).closed) return undefined; | |
> | |
132c147 | |
< return Worker(options, tabNS(this).tab.browser.contentWindow); | |
--- | |
> return Worker(options, getTabContentWindow(tabNS(this).tab)); | |
146,147c161,165 | |
< if (callback) | |
< this.once(EVENTS.close.name, callback); | |
--- | |
> let tab = this; | |
> this.once(EVENTS.close.name, function () { | |
> tabNS(tab).closed = true; | |
> if (callback) callback(); | |
> }); | |
167a186 | |
> tabInternals.tab.browser.removeEventListener(EVENTS.pageshow.dom, tabInternals.onPageShow, false); | |
169a189 | |
> tabInternals.onPageShow = null; | |
185a206,211 | |
> function onTabPageShow(event) { | |
> let win = event.target.defaultView; | |
> if (win === win.top) | |
> emit(this, 'pageshow', this, event.persisted); | |
> } | |
> | |
197c223 | |
< return getContentWindowForTab(tabNS(tab).tab); | |
--- | |
> return getTabContentWindow(tabNS(tab).tab); | |
./tabs/tab-firefox.js | |
1c1 | |
< /* This Source Code Form is subject to the terms of the Mozilla Public | |
--- | |
> /*This Source Code Form is subject to the terms of the Mozilla Public | |
8a9 | |
> const { has } = require("../util/array"); | |
15a17,18 | |
> const { deprecateUsage } = require('sdk/util/deprecate'); | |
> const { getURL } = require('sdk/url/utils'); | |
35a39,40 | |
> this._onLoad = this._onLoad.bind(this); | |
> this._onPageShow = this._onPageShow.bind(this); | |
43c48 | |
< if (listener) | |
--- | |
> if (listener) { | |
45c50,52 | |
< if ('ready' != type.name) // window spreads this event. | |
--- | |
> } | |
> // window spreads this event. | |
> if (!has(['ready', 'load', 'pageshow'], (type.name))) | |
49a57 | |
> | |
50a59,60 | |
> this._browser.addEventListener(EVENTS.load.dom, this._onLoad, true); | |
> this._browser.addEventListener(EVENTS.pageshow.dom, this._onPageShow, true); | |
57a68,70 | |
> // Add tabs to getURL method | |
> getURL.implement(this._public, (function (obj) this._public.url).bind(this)); | |
> | |
68c81 | |
< if (browser) | |
--- | |
> if (browser) { | |
69a83,85 | |
> browser.removeEventListener(EVENTS.load.dom, this._onLoad, true); | |
> browser.removeEventListener(EVENTS.pageshow.dom, this._onPageShow, true); | |
> } | |
77c93 | |
< * tab is loaded. | |
--- | |
> * tab is loaded, from DOMContentLoaded | |
83a100,121 | |
> | |
> /** | |
> * Internal listener that emits public event 'load' when the page of this | |
> * tab is loaded, for triggering on non-HTML content, bug #671305 | |
> */ | |
> _onLoad: function _onLoad(event) { | |
> // IFrames events will bubble so we need to ignore those. | |
> if (event.target == this._contentDocument) { | |
> this._emit(EVENTS.load.name, this._public); | |
> } | |
> }, | |
> | |
> /** | |
> * Internal listener that emits public event 'pageshow' when the page of this | |
> * tab is loaded from cache, bug #671305 | |
> */ | |
> _onPageShow: function _onPageShow(event) { | |
> // IFrames events will bubble so we need to ignore those. | |
> if (event.target == this._contentDocument) { | |
> this._emit(EVENTS.pageshow.name, this._public, event.persisted); | |
> } | |
> }, | |
90c128 | |
< if (tab == this._public) | |
--- | |
> if (viewNS(tab).tab == this._tab) | |
141c179,185 | |
< get favicon() this._tab ? getFaviconURIForLocation(this.url) : undefined, | |
--- | |
> get favicon() { | |
> deprecateUsage( | |
> 'tab.favicon is deprecated, ' + | |
> 'please use require("sdk/places/favicon").getFavicon instead.' | |
> ); | |
> return this._tab ? getFaviconURIForLocation(this.url) : undefined | |
> }, | |
207c251,254 | |
< if (!this._tab) | |
--- | |
> // Bug 699450: the tab may already have been detached | |
> if (!this._tab || !this._tab.parentNode) { | |
> if (callback) | |
> callback(); | |
208a256 | |
> } | |
./tabs/worker.js | |
./panel.js | |
17,18d16 | |
< const { Symbiont } = require('./content/content'); | |
< const { EventEmitter } = require('./deprecated/events'); | |
20,22d17 | |
< const runtime = require('./system/runtime'); | |
< const { getDocShell } = require("./frame/utils"); | |
< const { getWindow } = require('./panel/window'); | |
24a20,59 | |
> const { Class } = require("./core/heritage"); | |
> const { merge } = require("./util/object"); | |
> const { WorkerHost, Worker, detach, attach, destroy, | |
> requiresAddonGlobal } = require("./worker/utils"); | |
> const { Disposable } = require("./core/disposable"); | |
> const { contract: loaderContract } = require("./content/loader"); | |
> const { contract } = require("./util/contract"); | |
> const { on, off, emit, setListeners } = require("./event/core"); | |
> const { EventTarget } = require("./event/target"); | |
> const domPanel = require("./panel/utils"); | |
> const { events } = require("./panel/events"); | |
> const systemEvents = require("./system/events"); | |
> const { filter, pipe } = require("./event/utils"); | |
> const { getNodeView, getActiveView } = require("./view/core"); | |
> const { isNil, isObject } = require("./lang/type"); | |
> const { getAttachEventType } = require("./content/utils"); | |
> | |
> let number = { is: ['number', 'undefined', 'null'] }; | |
> let boolean = { is: ['boolean', 'undefined', 'null'] }; | |
> | |
> let rectContract = contract({ | |
> top: number, | |
> right: number, | |
> bottom: number, | |
> left: number | |
> }); | |
> | |
> let rect = { | |
> is: ['object', 'undefined', 'null'], | |
> map: function(v) isNil(v) || !isObject(v) ? v : rectContract(v) | |
> } | |
> | |
> let displayContract = contract({ | |
> width: number, | |
> height: number, | |
> focus: boolean, | |
> position: rect | |
> }); | |
> | |
> let panelContract = contract(merge({}, displayContract.rules, loaderContract.rules)); | |
26,29d60 | |
< const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", | |
< ON_SHOW = 'popupshown', | |
< ON_HIDE = 'popuphidden', | |
< validNumber = { is: ['number', 'undefined', 'null'] }; | |
31,32c62,99 | |
< if (isPrivateBrowsingSupported && isWindowPBSupported) { | |
< throw Error('The panel module cannot be used with per-window private browsing at the moment, see Bug 816257'); | |
--- | |
> function isDisposed(panel) !views.has(panel); | |
> | |
> let panels = new WeakMap(); | |
> let models = new WeakMap(); | |
> let views = new WeakMap(); | |
> let workers = new WeakMap(); | |
> | |
> function viewFor(panel) views.get(panel) | |
> function modelFor(panel) models.get(panel) | |
> function panelFor(view) panels.get(view) | |
> function workerFor(panel) workers.get(panel) | |
> | |
> | |
> // Utility function takes `panel` instance and makes sure it will be | |
> // automatically hidden as soon as other panel is shown. | |
> let setupAutoHide = new function() { | |
> let refs = new WeakMap(); | |
> | |
> return function setupAutoHide(panel) { | |
> // Create system event listener that reacts to any panel showing and | |
> // hides given `panel` if it's not the one being shown. | |
> function listener({subject}) { | |
> // It could be that listener is not GC-ed in the same cycle as | |
> // panel in such case we remove listener manually. | |
> let view = viewFor(panel); | |
> if (!view) systemEvents.off("popupshowing", listener); | |
> else if (subject !== view) panel.hide(); | |
> } | |
> | |
> // system event listener is intentionally weak this way we'll allow GC | |
> // to claim panel if it's no longer referenced by an add-on code. This also | |
> // helps minimizing cleanup required on unload. | |
> systemEvents.on("popupshowing", listener); | |
> // To make sure listener is not claimed by GC earlier than necessary we | |
> // associate it with `panel` it's associated with. This way it won't be | |
> // GC-ed earlier than `panel` itself. | |
> refs.set(panel, listener); | |
> } | |
35,66c102,131 | |
< /** | |
< * 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, | |
--- | |
> const Panel = Class({ | |
> implements: [ | |
> // Generate accessors for the validated properties that update model on | |
> // set and return values from model on get. | |
> panelContract.properties(modelFor), | |
> EventTarget, | |
> Disposable | |
> ], | |
> extends: WorkerHost(workerFor), | |
> setup: function setup(options) { | |
> let model = merge({ | |
> defaultWidth: 320, | |
> defaultHeight: 240, | |
> focus: true, | |
> position: Object.freeze({}), | |
> }, panelContract(options)); | |
> models.set(this, model); | |
> | |
> // Setup listeners. | |
> setListeners(this, options); | |
> | |
> // Setup view | |
> let view = domPanel.make(); | |
> panels.set(view, this); | |
> views.set(this, view); | |
> | |
> // Load panel content. | |
> domPanel.setURL(view, model.contentURL); | |
> | |
> setupAutoHide(this); | |
68,84c133,134 | |
< 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; | |
--- | |
> let worker = new Worker(options); | |
> workers.set(this, worker); | |
86c136,137 | |
< this._init(options); | |
--- | |
> // pipe events from worker to a panel. | |
> pipe(worker, this); | |
88c139 | |
< _destructor: function _destructor() { | |
--- | |
> dispose: function dispose() { | |
90,100c141,150 | |
< 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(); | |
--- | |
> off(this); | |
> | |
> destroy(workerFor(this)); | |
> | |
> domPanel.dispose(viewFor(this)); | |
> | |
> // Release circular reference between view and panel instance. This | |
> // way view will be GC-ed. And panel as well once all the other refs | |
> // will be removed from it. | |
> views.delete(this); | |
103,106c153,154 | |
< get width() this._width, | |
< set width(value) | |
< this._width = valid({ $: value }, { $: validNumber }).$ || this._width, | |
< _width: 320, | |
--- | |
> get width() modelFor(this).width, | |
> set width(value) this.resize(value, this.height), | |
108,111c156,157 | |
< get height() this._height, | |
< set height(value) | |
< this._height = valid({ $: value }, { $: validNumber }).$ || this._height, | |
< _height: 240, | |
--- | |
> get height() modelFor(this).height, | |
> set height(value) this.resize(this.width, value), | |
113,114c159,160 | |
< /* Public API: Panel.isShowing */ | |
< get isShowing() !!this._xulPanel && this._xulPanel.state == "open", | |
--- | |
> /* Public API: Panel.focus */ | |
> get focus() modelFor(this).focus, | |
116,125c162,163 | |
< /* Public API: Panel.show */ | |
< show: function show(anchor) { | |
< anchor = anchor || null; | |
< let anchorWindow = getWindow(anchor); | |
< | |
< // If there is no open window, or the anchor is in a private window | |
< // then we will not be able to display the panel | |
< if (!anchorWindow) { | |
< return; | |
< } | |
--- | |
> /* Public API: Panel.position */ | |
> get position() modelFor(this).position, | |
127,163c165,173 | |
< let document = anchorWindow.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;charset=utf-8,' + | |
< document.defaultView.encodeURIComponent(css) + '"/>' + | |
< '</resources>' + | |
< '</binding>' + | |
< '</bindings>'; | |
< xulPanel.style.MozBinding = 'url("data:text/xml;charset=utf-8,' + | |
< 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:;charset=utf-8,"); | |
--- | |
> get contentURL() modelFor(this).contentURL, | |
> set contentURL(value) { | |
> let model = modelFor(this); | |
> model.contentURL = panelContract({ contentURL: value }).contentURL; | |
> domPanel.setURL(viewFor(this), model.contentURL); | |
> // Detach worker so that messages send will be queued until it's | |
> // reatached once panel content is ready. | |
> detach(workerFor(this)); | |
> }, | |
165,168c175,176 | |
< xulPanel.appendChild(frame); | |
< document.getElementById("mainPopupSet").appendChild(xulPanel); | |
< } | |
< let { width, height } = this, x, y, position; | |
--- | |
> /* Public API: Panel.isShowing */ | |
> get isShowing() !isDisposed(this) && domPanel.isOpen(viewFor(this)), | |
170,206c178,203 | |
< 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"); | |
< } | |
--- | |
> /* Public API: Panel.show */ | |
> show: function show(options, anchor) { | |
> if (options instanceof Ci.nsIDOMElement) { | |
> [anchor, options] = [options, null]; | |
> } | |
> | |
> if (anchor instanceof Ci.nsIDOMElement) { | |
> console.warn( | |
> "Passing a DOM node to Panel.show() method is an unsupported " + | |
> "feature that will be soon replaced. " + | |
> "See: https://bugzilla.mozilla.org/show_bug.cgi?id=878877" | |
> ); | |
> } | |
> | |
> let model = modelFor(this); | |
> let view = viewFor(this); | |
> let anchorView = getNodeView(anchor); | |
> | |
> options = merge({ | |
> position: model.position, | |
> width: model.width, | |
> height: model.height, | |
> defaultWidth: model.defaultWidth, | |
> defaultHeight: model.defaultHeight, | |
> focus: model.focus | |
> }, displayContract(options)); | |
208,221c205,206 | |
< // 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) { | |
< setTimeout(waitForBinding, 50); | |
< return; | |
< } | |
< xulPanel.openPopup(anchor, position, x, y); | |
< } | |
< waitForBinding(); | |
--- | |
> if (!isDisposed(this)) | |
> domPanel.show(view, options, anchorView); | |
223c208 | |
< return this._public; | |
--- | |
> return this; | |
224a210 | |
> | |
227,237c213,216 | |
< // 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; | |
--- | |
> // Quit immediately if panel is disposed or there is no state change. | |
> domPanel.close(viewFor(this)); | |
> | |
> return this; | |
242,251c221,226 | |
< 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"; | |
< } | |
< }, | |
--- | |
> let model = modelFor(this); | |
> let view = viewFor(this); | |
> let change = panelContract({ | |
> width: width || model.width || model.defaultWidth, | |
> height: height || model.height || model.defaultHeight | |
> }); | |
253,284c228,229 | |
< // 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); | |
< } | |
< }, | |
--- | |
> model.width = change.width | |
> model.height = change.height | |
286,317c231 | |
< /** | |
< * 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. | |
< */ | |
< _applyStyleToDocument: function _applyStyleToDocument() { | |
< try { | |
< let win = this._xulPanel.ownerDocument.defaultView; | |
< let node = win.document.getAnonymousElementByAttribute( | |
< this._xulPanel, "class", "panel-arrowcontent"); | |
< if (!node) { | |
< // Before bug 764755, anonymous content was different: | |
< // TODO: Remove this when targeting FF16+ | |
< 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); | |
< } | |
< catch(e) { | |
< console.error("Unable to apply panel style"); | |
< console.exception(e); | |
< } | |
< }, | |
--- | |
> domPanel.resize(view, model.width, model.height); | |
319,352c233,236 | |
< /** | |
< * 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; | |
< this._applyStyleToDocument(); | |
< 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 | |
< let docShell = getDocShell(this._frame); | |
< if (docShell && "allowWindowControl" in docShell) | |
< 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'); | |
< }, | |
--- | |
> return this; | |
> } | |
> }); | |
> exports.Panel = Panel; | |
354,362c238,239 | |
< // 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; | |
< }, | |
--- | |
> // Note must be defined only after value to `Panel` is assigned. | |
> getActiveView.define(Panel, viewFor); | |
364,372c241,263 | |
< _onChange: function _onChange(e) { | |
< this._frameLoadersSwapped = false; | |
< if ('contentURL' in e && this._frame) { | |
< // Cleanup the worker before injecting the content script in the new | |
< // document | |
< this._workerCleanup(); | |
< this._initFrame(this._frame); | |
< } | |
< } | |
--- | |
> // Filter panel events to only panels that are create by this module. | |
> let panelEvents = filter(events, function({target}) panelFor(target)); | |
> | |
> // Panel events emitted after panel has being shown. | |
> let shows = filter(panelEvents, function({type}) type === "popupshown"); | |
> | |
> // Panel events emitted after panel became hidden. | |
> let hides = filter(panelEvents, function({type}) type === "popuphidden"); | |
> | |
> // Panel events emitted after content inside panel is ready. For different | |
> // panels ready may mean different state based on `contentScriptWhen` attribute. | |
> // Weather given event represents readyness is detected by `getAttachEventType` | |
> // helper function. | |
> let ready = filter(panelEvents, function({type, target}) | |
> getAttachEventType(modelFor(panelFor(target))) === type); | |
> | |
> // Forward panel show / hide events to panel's own event listeners. | |
> on(shows, "data", function({target}) emit(panelFor(target), "show")); | |
> on(hides, "data", function({target}) emit(panelFor(target), "hide")); | |
> | |
> on(ready, "data", function({target}) { | |
> let worker = workerFor(panelFor(target)); | |
> attach(worker, domPanel.getContentDocument(target).defaultView); | |
374,375d264 | |
< exports.Panel = function(options) Panel(options) | |
< exports.Panel.prototype = Panel.prototype; | |
./windows.js | |
./system/unload.js | |
./system/events.js | |
36,37c36,49 | |
< let subject = 'subject' in event ? Subject(event.subject) : null; | |
< let data = 'data' in event ? event.data : null; | |
--- | |
> // From bug 910599 | |
> // We must test to see if 'subject' or 'data' is a defined property | |
> // of the event object, but also allow primitives to be passed in, | |
> // which the `in` operator breaks, yet `null` is an object, hence | |
> // the long conditional | |
> let subject = event && typeof event === 'object' && 'subject' in event ? | |
> Subject(event.subject) : | |
> null; | |
> let data = event && typeof event === 'object' ? | |
> // An object either returns its `data` property or null | |
> ('data' in event ? event.data : null) : | |
> // All other types return themselves (and cast to strings/null | |
> // via observer service) | |
> event; | |
./system/runtime.js | |
1d0 | |
< /* vim:set ts=2 sw=2 sts=2 et: */ | |
19c18,27 | |
< exports.XPCOMABI = runtime.XPCOMABI; | |
--- | |
> | |
> // Attempt to access `XPCOMABI` may throw exception, in which case exported | |
> // `XPCOMABI` will be set to `null`. | |
> // https://mxr.mozilla.org/mozilla-central/source/toolkit/xre/nsAppRunner.cpp#732 | |
> try { | |
> exports.XPCOMABI = runtime.XPCOMABI; | |
> } | |
> catch (error) { | |
> exports.XPCOMABI = null; | |
> } | |
./system/environment.js | |
1d0 | |
< /* vim:set ts=2 sw=2 sts=2 expandtab */ | |
44c43,46 | |
< delete: function(name) set(name, null), | |
--- | |
> delete: function(name) { | |
> set(name, null); | |
> return true; | |
> }, | |
./system/globals.js | |
1d0 | |
< /* vim:set ts=2 sw=2 sts=2 expandtab */ | |
25,38c24 | |
< // Bug 718230: We need to send console messages to stdout and JS Console | |
< function forsakenConsoleDump(msg, level) { | |
< stdout.write(msg); | |
< | |
< if (level === 'error') { | |
< let error = ScriptError(); | |
< msg = msg.replace(/^error: /, ''); | |
< error.init(msg, null, null, 0, 0, 0, 'Add-on SDK'); | |
< consoleService.logMessage(error); | |
< } | |
< else | |
< consoleService.logStringMessage(msg); | |
< }; | |
< exports.console = new PlainTextConsole(forsakenConsoleDump); | |
--- | |
> exports.console = new PlainTextConsole(); | |
./system/xul-app.js | |
./util/list.js | |
11a12 | |
> const { iteratorSymbol } = require('../util/iteration'); | |
13c14 | |
< const List = Class({ | |
--- | |
> const listOptions = { | |
48,49c49,54 | |
< } | |
< }); | |
--- | |
> }, | |
> }; | |
> listOptions[iteratorSymbol] = function iterator() { | |
> return listNS(this).keyValueMap.slice(0)[iteratorSymbol](); | |
> }; | |
> const List = Class(listOptions); | |
./util/array.js | |
1d0 | |
< /* vim:set ts=2 sw=2 sts=2 et: */ | |
75,80c74,78 | |
< exports.unique = function unique(array) { | |
< var value = []; | |
< return array.forEach(function(element) { | |
< add(value, element); | |
< }); | |
< return value; | |
--- | |
> function unique(array) { | |
> return array.reduce(function(result, item) { | |
> add(result, item); | |
> return result; | |
> }, []); | |
81a80,89 | |
> exports.unique = unique; | |
> | |
> /** | |
> * Produce an array that contains the union: each distinct element from all | |
> * of the passed-in arrays. | |
> */ | |
> function union() { | |
> return unique(Array.concat.apply(null, arguments)); | |
> }; | |
> exports.union = union; | |
93,94c101,108 | |
< for each (let item in iterator) | |
< array.push(item); | |
--- | |
> if (iterator.__iterator__) { | |
> for each (let item in iterator) | |
> array.push(item); | |
> } | |
> else { | |
> for (let item of iterator) | |
> array.push(item); | |
> } | |
97a112,123 | |
> | |
> function find(array, predicate, fallback) { | |
> var index = 0; | |
> var count = array.length; | |
> while (index < count) { | |
> var value = array[index]; | |
> if (predicate(value)) return value; | |
> else index = index + 1; | |
> } | |
> return fallback; | |
> } | |
> exports.find = find; | |
./util/uuid.js | |
./util/registry.js | |
1,2d0 | |
< /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | |
< /* vim:set ts=2 sw=2 sts=2 et: */ | |
./util/object.js | |
10a11,12 | |
> const { flatten } = require('./array'); | |
> | |
31a34 | |
> | |
56a60,66 | |
> function has(obj, key) obj.hasOwnProperty(key); | |
> exports.has = has; | |
> | |
> function each(obj, fn) { | |
> for (let key in obj) has(obj, key) && fn(obj[key], key, obj); | |
> } | |
> exports.each = each; | |
57a68,92 | |
> /** | |
> * Like `merge`, except no property descriptors are manipulated, for use | |
> * with platform objects. Identical to underscore's `extend`. Useful for | |
> * merging XPCOM objects | |
> */ | |
> function safeMerge(source) { | |
> Array.slice(arguments, 1).forEach(function onEach (obj) { | |
> for (let prop in obj) source[prop] = obj[prop]; | |
> }); | |
> return source; | |
> } | |
> exports.safeMerge = safeMerge; | |
> | |
> /* | |
> * Returns a copy of the object without blacklisted properties | |
> */ | |
> function omit(source, ...values) { | |
> let copy = {}; | |
> let keys = flatten(values); | |
> for (let prop in source) | |
> if (!~keys.indexOf(prop)) | |
> copy[prop] = source[prop]; | |
> return copy; | |
> } | |
> exports.omit = omit; | |
./util/deprecate.js | |
11a12,13 | |
> const { get: getPref } = require("../preferences/service"); | |
> const PREFERENCE = "devtools.errorconsole.deprecation_warnings"; | |
17c19,21 | |
< console.error("DEPRECATED: " + msg + "\n" + format(stack)); | |
--- | |
> | |
> if (getPref(PREFERENCE)) | |
> console.error("DEPRECATED: " + msg + "\n" + format(stack)); | |
./addon/runner.js | |
1d0 | |
< /* vim:set ts=2 sw=2 sts=2 expandtab */ | |
4,5c3 | |
< * file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
< */ | |
--- | |
> * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
14c12 | |
< const { exit, env, staticArgs, name } = require('../system'); | |
--- | |
> const { exit, env, staticArgs } = require('../system'); | |
18a17,19 | |
> const xulApp = require('../system/xul-app'); | |
> const appShellService = Cc['@mozilla.org/appshell/appShellService;1']. | |
> getService(Ci.nsIAppShellService); | |
24,25c25 | |
< 'Thunderbird': 'mail-startup-done', | |
< '*': 'final-ui-startup' | |
--- | |
> 'Thunderbird': 'mail-startup-done' | |
27a28,30 | |
> // Set 'final-ui-startup' as default topic for unknown applications | |
> let appStartup = 'final-ui-startup'; | |
> | |
30c33,38 | |
< const APP_STARTUP = NAME2TOPIC[name] || NAME2TOPIC['*']; | |
--- | |
> for (let name of Object.keys(NAME2TOPIC)) { | |
> if (xulApp.is(name)) { | |
> appStartup = NAME2TOPIC[name]; | |
> break; | |
> } | |
> } | |
67c75 | |
< once(APP_STARTUP, function() { | |
--- | |
> once(appStartup, function() { | |
73c81,90 | |
< if (reason === 'startup') | |
--- | |
> // Try accessing hidden window to guess if we are running during firefox | |
> // startup, so that we should wait for session restore event before | |
> // running the addon | |
> let initialized = false; | |
> try { | |
> appShellService.hiddenDOMWindow; | |
> initialized = true; | |
> } | |
> catch(e) {} | |
> if (reason === 'startup' || !initialized) { | |
74a92 | |
> } | |
78a97,99 | |
> // NOTE: Module is intentionally required only now because it relies | |
> // on existence of hidden window, which does not exists until startup. | |
> let { ready } = require('../addon/window'); | |
89a111,112 | |
> return ready; | |
> }).then(function() { | |
91c114,115 | |
< }); | |
--- | |
> }).then(null, console.exception); | |
> return void 0; // otherwise we raise a warning, see bug 910304 | |
131,132c155,156 | |
< staticArgs: staticArgs | |
< }, { | |
--- | |
> staticArgs: staticArgs | |
> }, { | |
134c158 | |
< quit: exit | |
--- | |
> quit: exit | |
./core/heritage.js | |
1d0 | |
< /* vim:set ts=2 sw=2 sts=2 expandtab */ | |
4,6c3 | |
< * file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
< */ | |
< | |
--- | |
> * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
101c98 | |
< return freeze(create(prototype, getOwnPropertyDescriptors(properties))); | |
--- | |
> return create(prototype, getOwnPropertyDescriptors(properties)); | |
163,164c160,165 | |
< constructor.prototype = prototype; | |
< return freeze(constructor); | |
--- | |
> Object.defineProperty(constructor, 'prototype', { | |
> configurable: false, | |
> writable: false, | |
> value: prototype | |
> }); | |
> return constructor; | |
./core/promise.js | |
1,4d0 | |
< /* vim:set ts=2 sw=2 sts=2 expandtab */ | |
< /*jshint asi: true undef: true es5: true node: true browser: true devel: true | |
< forin: true latedef: false */ | |
< /*global define: true, Cu: true, __URI__: true */ | |
10c6 | |
< } else if (~String(this).indexOf('BackstagePass')) { // JSM | |
--- | |
> } else if (String(this).indexOf('BackstagePass') >= 0) { // JSM | |
11a8,14 | |
> try { | |
> this.console = this['Components'].utils | |
> .import('resource://gre/modules/devtools/Console.jsm', {}).console; | |
> } | |
> catch (ex) { | |
> // Avoid failures on different toolkit configurations. | |
> } | |
17a21,22 | |
> } else if (~String(this).indexOf('Sandbox')) { // Sandbox | |
> factory(function require(uri) {}, this, { id: id }); | |
19c24 | |
< var globals = this | |
--- | |
> var globals = this; | |
24c29 | |
< }).call(this, 'loader', function Promise(require, exports, module) { | |
--- | |
> }).call(this, 'promise/core', function Promise(require, exports, module) { | |
32,57c37,52 | |
< function resolution(value) { | |
< /** | |
< Returns non-standard compliant (`then` does not returns a promise) promise | |
< that resolves to a given `value`. Used just internally only. | |
< **/ | |
< return { then: function then(resolve) { resolve(value) } } | |
< } | |
< | |
< function rejection(reason) { | |
< /** | |
< Returns non-standard compliant promise (`then` does not returns a promise) | |
< that rejects with a given `reason`. This is used internally only. | |
< **/ | |
< return { then: function then(resolve, reject) { reject(reason) } } | |
< } | |
< | |
< function attempt(f) { | |
< /** | |
< Returns wrapper function that delegates to `f`. If `f` throws then captures | |
< error and returns promise that rejects with a thrown error. Otherwise returns | |
< return value. (Internal utility) | |
< **/ | |
< return function effort(options) { | |
< try { return f(options) } | |
< catch(error) { return rejection(error) } | |
< } | |
--- | |
> /** | |
> * Internal utility: Wraps given `value` into simplified promise, successfully | |
> * fulfilled to a given `value`. Note the result is not a complete promise | |
> * implementation, as its method `then` does not returns anything. | |
> */ | |
> function fulfilled(value) { | |
> return { then: function then(fulfill) { fulfill(value); } }; | |
> } | |
> | |
> /** | |
> * Internal utility: Wraps given input into simplified promise, pre-rejected | |
> * with a given `reason`. Note the result is not a complete promise | |
> * implementation, as its method `then` does not returns anything. | |
> */ | |
> function rejected(reason) { | |
> return { then: function then(fulfill, reject) { reject(reason); } }; | |
59a55,58 | |
> /** | |
> * Internal utility: Returns `true` if given `value` is a promise. Value is | |
> * assumed to be a promise if it implements method `then`. | |
> */ | |
61,65c60 | |
< /** | |
< Returns true if given `value` is promise. Value is assumed to be promise if | |
< it implements `then` method. | |
< **/ | |
< return value && typeof(value.then) === 'function' | |
--- | |
> return value && typeof(value.then) === 'function'; | |
67a63,94 | |
> /** | |
> * Creates deferred object containing fresh promise & methods to either resolve | |
> * or reject it. The result is an object with the following properties: | |
> * - `promise` Eventual value representation implementing CommonJS [Promises/A] | |
> * (http://wiki.commonjs.org/wiki/Promises/A) API. | |
> * - `resolve` Single shot function that resolves enclosed `promise` with a | |
> * given `value`. | |
> * - `reject` Single shot function that rejects enclosed `promise` with a given | |
> * `reason`. | |
> * | |
> * An optional `prototype` argument is used as a prototype of the returned | |
> * `promise` allowing one to implement additional API. If prototype is not | |
> * passed then it falls back to `Object.prototype`. | |
> * | |
> * ## Example | |
> * | |
> * function fetchURI(uri, type) { | |
> * var deferred = defer(); | |
> * var request = new XMLHttpRequest(); | |
> * request.open("GET", uri, true); | |
> * request.responseType = type; | |
> * request.onload = function onload() { | |
> * deferred.resolve(request.response); | |
> * } | |
> * request.onerror = function(event) { | |
> * deferred.reject(event); | |
> * } | |
> * request.send(); | |
> * | |
> * return deferred.promise; | |
> * } | |
> */ | |
69,96c96,104 | |
< /** | |
< Returns object containing following properties: | |
< - `promise` Eventual value representation implementing CommonJS [Promises/A] | |
< (http://wiki.commonjs.org/wiki/Promises/A) API. | |
< - `resolve` Single shot function that resolves returned `promise` with a given | |
< `value` argument. | |
< - `reject` Single shot function that rejects returned `promise` with a given | |
< `reason` argument. | |
< | |
< Given `prototype` argument is used as a prototype of the returned `promise` | |
< allowing one to implement additional API. If prototype is not passed then | |
< it falls back to `Object.prototype`. | |
< | |
< ## Examples | |
< | |
< // Simple usage. | |
< var deferred = defer() | |
< deferred.promise.then(console.log, console.error) | |
< deferred.resolve(value) | |
< | |
< // Advanced usage | |
< var prototype = { | |
< get: function get(name) { | |
< return this.then(function(value) { | |
< return value[name]; | |
< }) | |
< } | |
< } | |
--- | |
> // Define FIFO queue of observer pairs. Once promise is resolved & all queued | |
> // observers are forwarded to `result` and variable is set to `null`. | |
> var observers = []; | |
> | |
> // Promise `result`, which will be assigned a resolution value once promise | |
> // is resolved. Note that result will always be assigned promise (or alike) | |
> // object to take care of propagation through promise chains. If result is | |
> // `null` promise is not resolved yet. | |
> var result = null; | |
98,104c106 | |
< var foo = defer(prototype) | |
< deferred.promise.get('name').then(console.log) | |
< deferred.resolve({ name: 'Foo' }) | |
< //=> 'Foo' | |
< */ | |
< var pending = [], result | |
< prototype = (prototype || prototype === null) ? prototype : Object.prototype | |
--- | |
> prototype = (prototype || prototype === null) ? prototype : Object.prototype; | |
108,123c110,111 | |
< then: { value: function then(resolve, reject) { | |
< // create a new deferred using a same `prototype`. | |
< var deferred = defer(prototype) | |
< // If `resolve / reject` callbacks are not provided. | |
< resolve = resolve ? attempt(resolve) : resolution | |
< reject = reject ? attempt(reject) : rejection | |
< | |
< // Create a listeners for a enclosed promise resolution / rejection that | |
< // delegate to an actual callbacks and resolve / reject returned promise. | |
< function resolved(value) { deferred.resolve(resolve(value)) } | |
< function rejected(reason) { deferred.resolve(reject(reason)) } | |
< | |
< // If promise is pending register listeners. Otherwise forward them to | |
< // resulting resolution. | |
< if (pending) pending.push([ resolved, rejected ]) | |
< else result.then(resolved, rejected) | |
--- | |
> then: { value: function then(onFulfill, onError) { | |
> var deferred = defer(prototype); | |
125c113,156 | |
< return deferred.promise | |
--- | |
> function resolve(value) { | |
> // If `onFulfill` handler is provided resolve `deferred.promise` with | |
> // result of invoking it with a resolution value. If handler is not | |
> // provided propagate value through. | |
> try { | |
> deferred.resolve(onFulfill ? onFulfill(value) : value); | |
> } | |
> // `onFulfill` may throw exception in which case resulting promise | |
> // is rejected with thrown exception. | |
> catch(error) { | |
> if (exports._reportErrors && typeof(console) === 'object') | |
> console.error(error); | |
> // Note: Following is equivalent of `deferred.reject(error)`, | |
> // we use this shortcut to reduce a stack. | |
> deferred.resolve(rejected(error)); | |
> } | |
> } | |
> | |
> function reject(reason) { | |
> try { | |
> if (onError) deferred.resolve(onError(reason)); | |
> else deferred.resolve(rejected(reason)); | |
> } | |
> catch(error) { | |
> if (exports._reportErrors && typeof(console) === 'object') | |
> console.error(error); | |
> deferred.resolve(rejected(error)); | |
> } | |
> } | |
> | |
> // If enclosed promise (`this.promise`) observers queue is still alive | |
> // enqueue a new observer pair into it. Note that this does not | |
> // necessary means that promise is pending, it may already be resolved, | |
> // but we still have to queue observers to guarantee an order of | |
> // propagation. | |
> if (observers) { | |
> observers.push({ resolve: resolve, reject: reject }); | |
> } | |
> // Otherwise just forward observer pair right to a `result` promise. | |
> else { | |
> result.then(resolve, reject); | |
> } | |
> | |
> return deferred.promise; | |
130a162,168 | |
> /** | |
> * Resolves associated `promise` to a given `value`, unless it's already | |
> * resolved or rejected. Note that resolved promise is not |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment