Skip to content

Instantly share code, notes, and snippets.

@rogerhub
Last active January 3, 2016 18:09
Show Gist options
  • Save rogerhub/8500637 to your computer and use it in GitHub Desktop.
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.
./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