mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central to b2g-inbound
This commit is contained in:
commit
75419df577
@ -37,10 +37,10 @@ endif
|
||||
ifndef MOZ_PROFILE_USE
|
||||
# We need to explicitly put backend.RecursiveMakeBackend here
|
||||
# otherwise the rule in rules.mk doesn't run early enough.
|
||||
libs binaries export tools:: CLOBBER $(configure_dir)/configure config.status backend.RecursiveMakeBackend
|
||||
$(TIERS) binaries:: CLOBBER $(configure_dir)/configure config.status backend.RecursiveMakeBackend
|
||||
ifndef JS_STANDALONE
|
||||
ifndef LIBXUL_SDK
|
||||
libs binaries export tools:: $(topsrcdir)/js/src/configure js/src/config.status
|
||||
$(TIERS) binaries:: $(topsrcdir)/js/src/configure js/src/config.status
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@ -174,7 +174,7 @@ endif
|
||||
endif
|
||||
|
||||
default all::
|
||||
$(call BUILDSTATUS,TIERS export $(if $(COMPILE_ENVIRONMENT),compile )libs tools $(if $(MOZ_AUTOMATION),$(MOZ_AUTOMATION_TIERS)))
|
||||
$(call BUILDSTATUS,TIERS $(TIERS) $(if $(MOZ_AUTOMATION),$(MOZ_AUTOMATION_TIERS)))
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
|
@ -181,13 +181,18 @@ already_AddRefed<nsIURI>
|
||||
ImageAccessible::GetLongDescURI() const
|
||||
{
|
||||
if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::longdesc)) {
|
||||
nsGenericHTMLElement* element =
|
||||
nsGenericHTMLElement::FromContent(mContent);
|
||||
if (element) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
element->GetURIAttr(nsGkAtoms::longdesc, nullptr, getter_AddRefs(uri));
|
||||
return uri.forget();
|
||||
// To check if longdesc contains an invalid url.
|
||||
nsAutoString longdesc;
|
||||
mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::longdesc, longdesc);
|
||||
if (longdesc.FindChar(' ') != -1 || longdesc.FindChar('\t') != -1 ||
|
||||
longdesc.FindChar('\r') != -1 || longdesc.FindChar('\n') != -1) {
|
||||
return nullptr;
|
||||
}
|
||||
nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI();
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), longdesc,
|
||||
mContent->OwnerDoc(), baseURI);
|
||||
return uri.forget();
|
||||
}
|
||||
|
||||
DocAccessible* document = Document();
|
||||
|
@ -93,13 +93,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=429659
|
||||
var attributes = {"src": aSRC};
|
||||
testAttrs(acc, attributes, true);
|
||||
|
||||
if (aActionCount) {
|
||||
is(acc.actionCount, aActionCount,
|
||||
"Wrong number of actions for " + aID + "!");
|
||||
|
||||
for (index = 0; index < aActionNames.length; index++)
|
||||
var actionCount = aActionCount || 0;
|
||||
is(acc.actionCount, actionCount,
|
||||
"Wrong number of actions for " + aID + "!");
|
||||
if (actionCount) {
|
||||
for (index = 0; index < aActionNames.length; index++) {
|
||||
is(acc.getActionName(index), aActionNames[index],
|
||||
"Wrong action name for " + aID + ", index " + index +"!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,13 +110,18 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=429659
|
||||
testThis("nonLinkedImage", "moz.png", 89, 38);
|
||||
|
||||
// Test linked image
|
||||
testThis("linkedImage", "moz.png", 89, 38);
|
||||
var actionNamesArray = new Array("jump");
|
||||
testThis("linkedImage", "moz.png", 89, 38, 1,
|
||||
actionNamesArray);
|
||||
|
||||
// Image with long desc
|
||||
var actionNamesArray = new Array("showlongdesc");
|
||||
testThis("longdesc", "moz.png", 89, 38, 1,
|
||||
actionNamesArray);
|
||||
|
||||
// Image with invalid url in long desc
|
||||
testThis("invalidLongdesc", "moz.png", 89, 38, 0);
|
||||
|
||||
// Image with click and long desc
|
||||
actionNamesArray = null;
|
||||
actionNamesArray = new Array("click", "showlongdesc");
|
||||
@ -134,6 +140,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=429659
|
||||
testThis("longdesc2", "moz.png",
|
||||
89, 38, 1, actionNamesArray);
|
||||
|
||||
// Image described by HTML:a@href with whitespaces
|
||||
actionNamesArray = null;
|
||||
actionNamesArray = new Array("showlongdesc");
|
||||
testThis("longdesc3", "moz.png",
|
||||
89, 38, 1, actionNamesArray);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
@ -162,6 +174,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=429659
|
||||
<br>Image with longdesc:<br>
|
||||
<img id="longdesc" src="moz.png" longdesc="longdesc_src.html"
|
||||
alt="Image of Mozilla logo"/>
|
||||
<br>Image with invalid url in longdesc:<br>
|
||||
<img id="invalidLongdesc" src="moz.png" longdesc="longdesc src.html"
|
||||
alt="Image of Mozilla logo"/>
|
||||
<br>Image with click and longdesc:<br>
|
||||
<img id="clickAndLongdesc" src="moz.png" longdesc="longdesc_src.html"
|
||||
alt="Another image of Mozilla logo" onclick="alert('Clicked!');"/>
|
||||
@ -171,6 +186,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=429659
|
||||
alt="Second Image of Mozilla logo"/>
|
||||
<a id="describing_link" href="longdesc_src.html">link to description of image</a>
|
||||
|
||||
<br>Image described by a link to be treated as longdesc with whitespaces<br>
|
||||
<img id="longdesc3" src="moz.png" aria-describedby="describing_link2"
|
||||
alt="Second Image of Mozilla logo"/>
|
||||
<a id="describing_link2" href="longdesc src.html">link to description of image</a>
|
||||
|
||||
<br>Image with click:<br>
|
||||
<img id="click" src="moz.png"
|
||||
alt="A third image of Mozilla logo" onclick="alert('Clicked, too!');"/>
|
||||
|
@ -172,6 +172,7 @@ EXTRA_JS_MODULES.commonjs.diffpatcher.test += [
|
||||
|
||||
EXTRA_JS_MODULES.commonjs.framescript += [
|
||||
'source/lib/framescript/FrameScriptManager.jsm',
|
||||
'source/lib/framescript/LoaderHelper.jsm',
|
||||
'source/lib/framescript/tab-events.js',
|
||||
]
|
||||
|
||||
@ -240,6 +241,8 @@ EXTRA_JS_MODULES.commonjs.sdk.content += [
|
||||
'source/lib/sdk/content/sandbox.js',
|
||||
'source/lib/sdk/content/thumbnail.js',
|
||||
'source/lib/sdk/content/utils.js',
|
||||
'source/lib/sdk/content/worker-child.js',
|
||||
'source/lib/sdk/content/worker-parent.js',
|
||||
'source/lib/sdk/content/worker.js',
|
||||
]
|
||||
|
||||
@ -429,7 +432,6 @@ EXTRA_JS_MODULES.commonjs.sdk.util += [
|
||||
'source/lib/sdk/util/contract.js',
|
||||
'source/lib/sdk/util/deprecate.js',
|
||||
'source/lib/sdk/util/dispatcher.js',
|
||||
'source/lib/sdk/util/iteration.js',
|
||||
'source/lib/sdk/util/list.js',
|
||||
'source/lib/sdk/util/match-pattern.js',
|
||||
'source/lib/sdk/util/object.js',
|
||||
@ -453,4 +455,5 @@ EXTRA_JS_MODULES.commonjs.sdk.zip += [
|
||||
|
||||
EXTRA_JS_MODULES.commonjs.toolkit += [
|
||||
'source/lib/toolkit/loader.js',
|
||||
'source/lib/toolkit/require.js',
|
||||
]
|
||||
|
File diff suppressed because one or more lines are too long
33
addon-sdk/source/lib/framescript/LoaderHelper.jsm
Normal file
33
addon-sdk/source/lib/framescript/LoaderHelper.jsm
Normal file
@ -0,0 +1,33 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const { utils: Cu, classes: Cc, interfaces: Ci } = Components;
|
||||
const { Loader } = Cu.import('resource://gre/modules/commonjs/toolkit/loader.js', {});
|
||||
const cpmm = Cc['@mozilla.org/childprocessmessagemanager;1'].getService(Ci.nsISyncMessageSender);
|
||||
|
||||
// one Loader instance per addon (per @loader/options to be precise)
|
||||
let addons = new Map();
|
||||
|
||||
cpmm.addMessageListener('sdk/loader/unload', ({ data: options }) => {
|
||||
let key = JSON.stringify(options);
|
||||
let addon = addons.get(key);
|
||||
if (addon)
|
||||
addon.loader.unload();
|
||||
addons.delete(key);
|
||||
})
|
||||
|
||||
// create a Loader instance from @loader/options
|
||||
function loader(options) {
|
||||
let key = JSON.stringify(options);
|
||||
let addon = addons.get(key) || {};
|
||||
if (!addon.loader) {
|
||||
addon.loader = Loader.Loader(options);
|
||||
addon.require = Loader.Require(addon.loader, { id: 'LoaderHelper' });
|
||||
addons.set(key, addon);
|
||||
}
|
||||
return addon;
|
||||
}
|
||||
|
||||
const EXPORTED_SYMBOLS = ['loader'];
|
@ -3,8 +3,8 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const observerSvc = Components.classes["@mozilla.org/observer-service;1"].
|
||||
getService(Components.interfaces.nsIObserverService);
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
const observerSvc = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
||||
|
||||
// map observer topics to tab event names
|
||||
const EVENTS = {
|
||||
@ -32,3 +32,20 @@ addEventListener('pageshow', ({ target, type, persisted }) => {
|
||||
if (target === content.document)
|
||||
sendAsyncMessage('sdk/tab/event', { type, persisted });
|
||||
}, true);
|
||||
|
||||
|
||||
// workers for windows in this tab
|
||||
let keepAlive = new Map();
|
||||
|
||||
addMessageListener('sdk/worker/create', ({ data: { options, addon }}) => {
|
||||
options.manager = this;
|
||||
let { loader } = Cu.import(addon.paths[''] + 'framescript/LoaderHelper.jsm', {});
|
||||
let { WorkerChild } = loader(addon).require('sdk/content/worker-child');
|
||||
sendAsyncMessage('sdk/worker/attach', { id: options.id });
|
||||
keepAlive.set(options.id, new WorkerChild(options));
|
||||
})
|
||||
|
||||
addMessageListener('sdk/worker/event', ({ data: { id, args: [event]}}) => {
|
||||
if (event === 'detach')
|
||||
keepAlive.delete(id);
|
||||
})
|
||||
|
@ -29,14 +29,15 @@ function isUTF8(charset) {
|
||||
|
||||
exports.decode = function (data, charset) {
|
||||
if (isUTF8(charset))
|
||||
return decodeURIComponent(escape(atob(data)))
|
||||
return decodeURIComponent(escape(atob(data)))
|
||||
|
||||
return atob(data);
|
||||
return atob(data);
|
||||
}
|
||||
|
||||
exports.encode = function (data, charset) {
|
||||
if (isUTF8(charset))
|
||||
return btoa(unescape(encodeURIComponent(data)))
|
||||
|
||||
return btoa(data);
|
||||
data = String.fromCharCode(...[(c.charCodeAt(0) & 0xff) for (c of data)]);
|
||||
return btoa(data);
|
||||
}
|
||||
|
@ -8,7 +8,8 @@ module.metadata = {
|
||||
"stability": "stable",
|
||||
"engines": {
|
||||
// TODO Fennec Support 789757
|
||||
"Firefox": "*"
|
||||
"Firefox": "*",
|
||||
"SeaMonkey": "*"
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -280,36 +280,6 @@ Object.freeze({
|
||||
Object.defineProperty(exports, "self", {
|
||||
value: self
|
||||
});
|
||||
|
||||
exports.on = function deprecatedOn() {
|
||||
console.error("DEPRECATED: The global `on()` function in content " +
|
||||
"scripts is deprecated in favor of the `self.on()` " +
|
||||
"function, which works the same. Replace calls to `on()` " +
|
||||
"with calls to `self.on()`" +
|
||||
"For more info on `self.on`, see " +
|
||||
"<https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Content_Scripts/using_postMessage>.");
|
||||
return self.on.apply(null, arguments);
|
||||
};
|
||||
|
||||
// Deprecated use of `onMessage` from globals
|
||||
let onMessage = null;
|
||||
Object.defineProperty(exports, "onMessage", {
|
||||
get: function () onMessage,
|
||||
set: function (v) {
|
||||
if (onMessage)
|
||||
self.removeListener("message", onMessage);
|
||||
console.error("DEPRECATED: The global `onMessage` function in content" +
|
||||
"scripts is deprecated in favor of the `self.on()` " +
|
||||
"function. Replace `onMessage = function (data){}` " +
|
||||
"definitions with calls to `self.on('message', " +
|
||||
"function (data){})`. " +
|
||||
"For more info on `self.on`, see " +
|
||||
"<https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Content_Scripts/using_postMessage>.");
|
||||
onMessage = v;
|
||||
if (typeof onMessage == "function")
|
||||
self.on("message", onMessage);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
injectOptions: function (exports, options) {
|
||||
|
@ -44,3 +44,8 @@ exports.getThumbnailCanvasForWindow = getThumbnailCanvasForWindow;
|
||||
exports.getThumbnailURIForWindow = function getThumbnailURIForWindow(window) {
|
||||
return getThumbnailCanvasForWindow(window).toDataURL()
|
||||
};
|
||||
|
||||
// default 80x45 blank when not available
|
||||
exports.BLANK = 'data:image/png;base64,' +
|
||||
'iVBORw0KGgoAAAANSUhEUgAAAFAAAAAtCAYAAAA5reyyAAAAJElEQVRoge3BAQ'+
|
||||
'EAAACCIP+vbkhAAQAAAAAAAAAAAAAAAADXBjhtAAGQ0AF/AAAAAElFTkSuQmCC';
|
||||
|
88
addon-sdk/source/lib/sdk/content/worker-child.js
Normal file
88
addon-sdk/source/lib/sdk/content/worker-child.js
Normal file
@ -0,0 +1,88 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
'use strict';
|
||||
|
||||
const { merge } = require('../util/object');
|
||||
const { Class } = require('../core/heritage');
|
||||
const { EventTarget } = require('../event/target');
|
||||
const { getInnerId, getByInnerId } = require('../window/utils');
|
||||
const { instanceOf, isObject } = require('../lang/type');
|
||||
const { on: observe } = require('../system/events');
|
||||
const { WorkerSandbox } = require('./sandbox');
|
||||
const { Ci } = require('chrome');
|
||||
|
||||
const EVENTS = {
|
||||
'chrome-page-shown': 'pageshow',
|
||||
'content-page-shown': 'pageshow',
|
||||
'chrome-page-hidden': 'pagehide',
|
||||
'content-page-hidden': 'pagehide',
|
||||
'inner-window-destroyed': 'detach',
|
||||
}
|
||||
|
||||
const WorkerChild = Class({
|
||||
implements: [EventTarget],
|
||||
initialize(options) {
|
||||
merge(this, options);
|
||||
|
||||
this.port = EventTarget();
|
||||
this.port.on('*', this.send.bind(this, 'event'));
|
||||
this.on('*', this.send.bind(this));
|
||||
|
||||
this.observe = this.observe.bind(this);
|
||||
|
||||
for (let topic in EVENTS)
|
||||
observe(topic, this.observe);
|
||||
|
||||
this.receive = this.receive.bind(this);
|
||||
this.manager.addMessageListener('sdk/worker/message', this.receive);
|
||||
|
||||
this.sandbox = WorkerSandbox(this, getByInnerId(this.window));
|
||||
},
|
||||
// messages
|
||||
receive({ data: { id, args }}) {
|
||||
if (id !== this.id)
|
||||
return;
|
||||
this.sandbox.emit(...args);
|
||||
if (args[0] === 'detach')
|
||||
this.destroy(args[1]);
|
||||
},
|
||||
send(...args) {
|
||||
args = JSON.parse(JSON.stringify(args, exceptions));
|
||||
if (this.manager.content)
|
||||
this.manager.sendAsyncMessage('sdk/worker/event', { id: this.id, args });
|
||||
},
|
||||
// notifications
|
||||
observe({ type, subject }) {
|
||||
if (!this.sandbox)
|
||||
return;
|
||||
if (subject.defaultView && getInnerId(subject.defaultView) === this.window) {
|
||||
this.sandbox.emitSync(EVENTS[type]);
|
||||
this.send(EVENTS[type]);
|
||||
}
|
||||
if (type === 'inner-window-destroyed' &&
|
||||
subject.QueryInterface(Ci.nsISupportsPRUint64).data === this.window) {
|
||||
this.destroy();
|
||||
}
|
||||
},
|
||||
// detach/destroy: unload and release the sandbox
|
||||
destroy(reason) {
|
||||
if (!this.sandbox)
|
||||
return;
|
||||
if (this.manager.content)
|
||||
this.manager.removeMessageListener('sdk/worker/message', this.receive);
|
||||
this.sandbox.destroy(reason);
|
||||
this.sandbox = null;
|
||||
this.send('detach');
|
||||
}
|
||||
})
|
||||
exports.WorkerChild = WorkerChild;
|
||||
|
||||
// Error instances JSON poorly
|
||||
function exceptions(key, value) {
|
||||
if (!isObject(value) || !instanceOf(value, Error))
|
||||
return value;
|
||||
let _errorType = value.constructor.name;
|
||||
let { message, fileName, lineNumber, stack, name } = value;
|
||||
return { _errorType, message, fileName, lineNumber, stack, name };
|
||||
}
|
184
addon-sdk/source/lib/sdk/content/worker-parent.js
Normal file
184
addon-sdk/source/lib/sdk/content/worker-parent.js
Normal file
@ -0,0 +1,184 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "unstable"
|
||||
};
|
||||
|
||||
const { emit } = require('../event/core');
|
||||
const { omit } = require('../util/object');
|
||||
const { Class } = require('../core/heritage');
|
||||
const { method } = require('../lang/functional');
|
||||
const { getInnerId } = require('../window/utils');
|
||||
const { EventTarget } = require('../event/target');
|
||||
const { when, ensure } = require('../system/unload');
|
||||
const { getTabForWindow } = require('../tabs/helpers');
|
||||
const { getTabForContentWindow, getBrowserForTab } = require('../tabs/utils');
|
||||
const { isPrivate } = require('../private-browsing/utils');
|
||||
const { getFrameElement } = require('../window/utils');
|
||||
const { attach, detach, destroy } = require('./utils');
|
||||
const { on: observe } = require('../system/events');
|
||||
const { uuid } = require('../util/uuid');
|
||||
const { Ci, Cc } = require('chrome');
|
||||
|
||||
const ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"].
|
||||
getService(Ci.nsIMessageBroadcaster);
|
||||
|
||||
// null-out cycles in .modules to make @loader/options JSONable
|
||||
const ADDON = omit(require('@loader/options'), ['modules', 'globals']);
|
||||
|
||||
const workers = new WeakMap();
|
||||
let modelFor = (worker) => workers.get(worker);
|
||||
|
||||
const ERR_DESTROYED = "Couldn't find the worker to receive this message. " +
|
||||
"The script may not be initialized yet, or may already have been unloaded.";
|
||||
|
||||
const ERR_FROZEN = "The page is currently hidden and can no longer be used " +
|
||||
"until it is visible again.";
|
||||
|
||||
// a handle for communication between content script and addon code
|
||||
const Worker = Class({
|
||||
implements: [EventTarget],
|
||||
initialize(options = {}) {
|
||||
|
||||
let model = {
|
||||
inited: false,
|
||||
earlyEvents: [], // fired before worker was inited
|
||||
frozen: true, // document is in BFcache, let it go
|
||||
options,
|
||||
};
|
||||
workers.set(this, model);
|
||||
|
||||
ensure(this, 'destroy');
|
||||
this.on('detach', this.detach);
|
||||
EventTarget.prototype.initialize.call(this, options);
|
||||
|
||||
this.receive = this.receive.bind(this);
|
||||
|
||||
model.observe = ({ subject }) => {
|
||||
let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
|
||||
if (model.window && getInnerId(model.window) === id)
|
||||
this.detach();
|
||||
}
|
||||
|
||||
observe('inner-window-destroyed', model.observe);
|
||||
|
||||
this.port = EventTarget();
|
||||
this.port.emit = this.send.bind(this, 'event');
|
||||
this.postMessage = this.send.bind(this, 'message');
|
||||
|
||||
if ('window' in options)
|
||||
attach(this, options.window);
|
||||
},
|
||||
// messages
|
||||
receive({ data: { id, args }}) {
|
||||
let model = modelFor(this);
|
||||
if (id !== model.id || !model.childWorker)
|
||||
return;
|
||||
if (args[0] === 'event')
|
||||
emit(this.port, ...args.slice(1))
|
||||
else
|
||||
emit(this, ...args);
|
||||
},
|
||||
send(...args) {
|
||||
let model = modelFor(this);
|
||||
if (!model.inited) {
|
||||
model.earlyEvents.push(args);
|
||||
return;
|
||||
}
|
||||
if (!model.childWorker && args[0] !== 'detach')
|
||||
throw new Error(ERR_DESTROYED);
|
||||
if (model.frozen && args[0] !== 'detach')
|
||||
throw new Error(ERR_FROZEN);
|
||||
try {
|
||||
model.manager.sendAsyncMessage('sdk/worker/message', { id: model.id, args });
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
},
|
||||
// properties
|
||||
get url() {
|
||||
let { window } = modelFor(this);
|
||||
return window && window.document.location.href;
|
||||
},
|
||||
get contentURL() {
|
||||
let { window } = modelFor(this);
|
||||
return window && window.document.URL;
|
||||
},
|
||||
get tab() {
|
||||
let { window } = modelFor(this);
|
||||
return window && getTabForWindow(window);
|
||||
},
|
||||
toString: () => '[object Worker]',
|
||||
// methods
|
||||
attach: method(attach),
|
||||
detach: method(detach),
|
||||
destroy: method(destroy),
|
||||
})
|
||||
exports.Worker = Worker;
|
||||
|
||||
attach.define(Worker, function(worker, window) {
|
||||
let model = modelFor(worker);
|
||||
|
||||
model.window = window;
|
||||
model.options.window = getInnerId(window);
|
||||
model.id = model.options.id = String(uuid());
|
||||
|
||||
let tab = getTabForContentWindow(window);
|
||||
if (tab) {
|
||||
model.manager = getBrowserForTab(tab).messageManager;
|
||||
} else {
|
||||
model.manager = getFrameElement(window.top).frameLoader.messageManager;
|
||||
}
|
||||
|
||||
model.manager.addMessageListener('sdk/worker/event', worker.receive);
|
||||
model.manager.addMessageListener('sdk/worker/attach', attach);
|
||||
|
||||
model.manager.sendAsyncMessage('sdk/worker/create', {
|
||||
options: model.options,
|
||||
addon: ADDON
|
||||
});
|
||||
|
||||
function attach({ data }) {
|
||||
if (data.id !== model.id)
|
||||
return;
|
||||
model.manager.removeMessageListener('sdk/worker/attach', attach);
|
||||
model.childWorker = true;
|
||||
|
||||
worker.on('pageshow', () => model.frozen = false);
|
||||
worker.on('pagehide', () => model.frozen = true);
|
||||
|
||||
model.inited = true;
|
||||
model.frozen = false;
|
||||
|
||||
model.earlyEvents.forEach(args => worker.send(...args));
|
||||
emit(worker, 'attach', window);
|
||||
}
|
||||
})
|
||||
|
||||
// unload and release the child worker, release window reference
|
||||
detach.define(Worker, function(worker, reason) {
|
||||
let model = modelFor(worker);
|
||||
worker.send('detach', reason);
|
||||
if (!model.childWorker)
|
||||
return;
|
||||
|
||||
model.childWorker = null;
|
||||
model.earlyEvents = [];
|
||||
model.window = null;
|
||||
emit(worker, 'detach');
|
||||
model.manager.removeMessageListener('sdk/worker/event', this.receive);
|
||||
})
|
||||
|
||||
isPrivate.define(Worker, ({ tab }) => isPrivate(tab));
|
||||
|
||||
// unlod worker, release references
|
||||
destroy.define(Worker, function(worker, reason) {
|
||||
detach(worker, reason);
|
||||
modelFor(worker).inited = true;
|
||||
})
|
||||
|
||||
// unload Loaders used for creating WorkerChild instances in each process
|
||||
when(() => ppmm.broadcastAsyncMessage('sdk/loader/unload', { data: ADDON }));
|
@ -7,7 +7,8 @@ module.metadata = {
|
||||
"stability": "stable",
|
||||
"engines": {
|
||||
// TODO Fennec support Bug 788334
|
||||
"Firefox": "*"
|
||||
"Firefox": "*",
|
||||
"SeaMonkey": "*"
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -8,7 +8,8 @@ module.metadata = {
|
||||
};
|
||||
|
||||
var getPrototypeOf = Object.getPrototypeOf;
|
||||
var getNames = Object.getOwnPropertyNames;
|
||||
var getNames = x => [...Object.getOwnPropertyNames(x),
|
||||
...Object.getOwnPropertySymbols(x)];
|
||||
var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
||||
var create = Object.create;
|
||||
var freeze = Object.freeze;
|
||||
|
@ -58,9 +58,9 @@ let promised = (function() {
|
||||
promise.then(console.log) // => [ 1, 2, 3 ]
|
||||
**/
|
||||
|
||||
return function promised() {
|
||||
return function promised(...args) {
|
||||
// create array of [ f, this, args... ]
|
||||
return concat.apply([ f, this ], arguments).
|
||||
return [f, this, ...args].
|
||||
// reduce it via `promisedConcat` to get promised array of fulfillments
|
||||
reduce(promisedConcat, resolve([], prototype)).
|
||||
// finally map that to promise of `f.apply(this, args...)`
|
||||
|
@ -8,6 +8,10 @@ module.metadata = {
|
||||
"stability": "deprecated"
|
||||
};
|
||||
|
||||
const getOwnIdentifiers = x => [...Object.getOwnPropertyNames(x),
|
||||
...Object.getOwnPropertySymbols(x)];
|
||||
|
||||
|
||||
// `var` is being used in the module in order to make it reusable in
|
||||
// environments in which `let` and `const` is not yet supported.
|
||||
|
||||
@ -46,7 +50,7 @@ function createAliasProperty(object, name) {
|
||||
descriptor.get = property.get.bind(object);
|
||||
if ("set" in property && property.set)
|
||||
descriptor.set = property.set.bind(object);
|
||||
|
||||
|
||||
// If original property was a value property.
|
||||
if ("value" in property) {
|
||||
// If original property is a method using it's `object` bounded copy.
|
||||
@ -104,8 +108,8 @@ exports.Cortex = function Cortex(object, names, prototype) {
|
||||
// properties of the original `object` that are contained in `names` array.
|
||||
// If `names` array is not provided then all the properties that don't
|
||||
// start with `"_"` are aliased.
|
||||
Object.getOwnPropertyNames(object).forEach(function (name) {
|
||||
if ((!names && "_" !== name.charAt(0)) || (names && ~names.indexOf(name)))
|
||||
getOwnIdentifiers(object).forEach(function (name) {
|
||||
if ((!names && "_" !== name.toString().charAt(0)) || (names && ~names.indexOf(name)))
|
||||
defineAlias(object, cortex, name);
|
||||
});
|
||||
return cortex;
|
||||
|
@ -8,7 +8,6 @@ module.metadata = {
|
||||
};
|
||||
|
||||
const { Trait } = require('../deprecated/traits');
|
||||
const { iteratorSymbol } = require('../util/iteration');
|
||||
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/util_list
|
||||
@ -116,7 +115,7 @@ const listOptions = {
|
||||
yield onKeyValue ? [++i, element] : onKeys ? ++i : element;
|
||||
},
|
||||
};
|
||||
listOptions[iteratorSymbol] = function* iterator() {
|
||||
listOptions[Symbol.iterator] = function* iterator() {
|
||||
let array = this._keyValueMap.slice(0);
|
||||
|
||||
for (let element of array)
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
module.metadata = {
|
||||
@ -17,9 +16,10 @@ const {
|
||||
required,
|
||||
} = require('./traits/core');
|
||||
|
||||
const defineProperties = Object.defineProperties,
|
||||
freeze = Object.freeze,
|
||||
create = Object.create;
|
||||
const { getOwnPropertyIdentifiers } = require('../util/object');
|
||||
const defineProperties = Object.defineProperties;
|
||||
const freeze = Object.freeze;
|
||||
const create = Object.create;
|
||||
|
||||
/**
|
||||
* Work around bug 608959 by defining the _create function here instead of
|
||||
@ -31,7 +31,7 @@ const defineProperties = Object.defineProperties,
|
||||
*/
|
||||
function _create(proto, trait) {
|
||||
let properties = {},
|
||||
keys = Object.getOwnPropertyNames(trait);
|
||||
keys = getOwnPropertyIdentifiers(trait);
|
||||
for (let key of keys) {
|
||||
let descriptor = trait[key];
|
||||
if (descriptor.required &&
|
||||
@ -72,9 +72,9 @@ function TraitDescriptor(object)
|
||||
|
||||
function Public(instance, trait) {
|
||||
let result = {},
|
||||
keys = Object.getOwnPropertyNames(trait);
|
||||
keys = getOwnPropertyIdentifiers(trait);
|
||||
for (let key of keys) {
|
||||
if ('_' === key.charAt(0) && '__iterator__' !== key )
|
||||
if (typeof key === 'string' && '_' === key.charAt(0) && '__iterator__' !== key )
|
||||
continue;
|
||||
let property = trait[key],
|
||||
descriptor = {
|
||||
@ -184,4 +184,3 @@ const Trait = Composition({
|
||||
});
|
||||
TraitProto = Trait.prototype;
|
||||
exports.Trait = Trait;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
module.metadata = {
|
||||
@ -10,11 +9,10 @@ module.metadata = {
|
||||
|
||||
// Design inspired by: http://www.traitsjs.org/
|
||||
|
||||
// shortcuts
|
||||
const getOwnPropertyNames = Object.getOwnPropertyNames,
|
||||
getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor,
|
||||
hasOwn = Object.prototype.hasOwnProperty,
|
||||
_create = Object.create;
|
||||
const { getOwnPropertyIdentifiers } = require('../../util/object');
|
||||
const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
||||
const hasOwn = Object.prototype.hasOwnProperty;
|
||||
const _create = Object.create;
|
||||
|
||||
function doPropertiesMatch(object1, object2, name) {
|
||||
// If `object1` has property with the given `name`
|
||||
@ -114,12 +112,12 @@ function Conflict(name) {
|
||||
*/
|
||||
function trait(properties) {
|
||||
let result = {},
|
||||
keys = getOwnPropertyNames(properties);
|
||||
for (let key of keys) {
|
||||
let descriptor = getOwnPropertyDescriptor(properties, key);
|
||||
result[key] = (required === descriptor.value) ? Required(key) : descriptor;
|
||||
}
|
||||
return result;
|
||||
keys = getOwnPropertyIdentifiers(properties);
|
||||
for (let key of keys) {
|
||||
let descriptor = getOwnPropertyDescriptor(properties, key);
|
||||
result[key] = (required === descriptor.value) ? Required(key) : descriptor;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
exports.Trait = exports.trait = trait;
|
||||
|
||||
@ -140,7 +138,7 @@ function compose(trait1, trait2) {
|
||||
let traits = Array.slice(arguments, 0),
|
||||
result = {};
|
||||
for (let trait of traits) {
|
||||
let keys = getOwnPropertyNames(trait);
|
||||
let keys = getOwnPropertyIdentifiers(trait);
|
||||
for (let key of keys) {
|
||||
let descriptor = trait[key];
|
||||
// if property already exists and it's not a requirement
|
||||
@ -174,7 +172,7 @@ function exclude(keys, trait) {
|
||||
let exclusions = Map(keys),
|
||||
result = {};
|
||||
|
||||
keys = getOwnPropertyNames(trait);
|
||||
keys = getOwnPropertyIdentifiers(trait);
|
||||
|
||||
for (let key of keys) {
|
||||
if (!hasOwn.call(exclusions, key) || trait[key].required)
|
||||
@ -210,7 +208,7 @@ function override() {
|
||||
let traits = Array.slice(arguments, 0),
|
||||
result = {};
|
||||
for (let trait of traits) {
|
||||
let keys = getOwnPropertyNames(trait);
|
||||
let keys = getOwnPropertyIdentifiers(trait);
|
||||
for (let key of keys) {
|
||||
let descriptor = trait[key];
|
||||
if (!hasOwn.call(result, key) || result[key].required)
|
||||
@ -236,7 +234,7 @@ exports.override = override;
|
||||
*/
|
||||
function rename(map, trait) {
|
||||
let result = {},
|
||||
keys = getOwnPropertyNames(trait);
|
||||
keys = getOwnPropertyIdentifiers(trait);
|
||||
for (let key of keys) {
|
||||
// must be renamed & it's not requirement
|
||||
if (hasOwn.call(map, key) && !trait[key].required) {
|
||||
@ -281,7 +279,7 @@ function rename(map, trait) {
|
||||
function resolve(resolutions, trait) {
|
||||
let renames = {},
|
||||
exclusions = [],
|
||||
keys = getOwnPropertyNames(resolutions);
|
||||
keys = getOwnPropertyIdentifiers(resolutions);
|
||||
for (let key of keys) { // pre-process renamed and excluded properties
|
||||
if (resolutions[key]) // old name -> new name
|
||||
renames[key] = resolutions[key];
|
||||
@ -306,7 +304,7 @@ exports.resolve = resolve;
|
||||
*/
|
||||
function create(proto, trait) {
|
||||
let properties = {},
|
||||
keys = getOwnPropertyNames(trait);
|
||||
keys = getOwnPropertyIdentifiers(trait);
|
||||
for (let key of keys) {
|
||||
let descriptor = trait[key];
|
||||
if (descriptor.required && !hasOwn.call(proto, key))
|
||||
@ -319,4 +317,3 @@ function create(proto, trait) {
|
||||
return _create(proto, properties);
|
||||
}
|
||||
exports.create = create;
|
||||
|
||||
|
@ -26,7 +26,8 @@ const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {}
|
||||
var ios = Cc['@mozilla.org/network/io-service;1']
|
||||
.getService(Ci.nsIIOService);
|
||||
|
||||
const TEST_REGEX = /(([^\/]+\/)(?:lib\/)?)?(tests?\/test-[^\.\/]+)\.js$/;
|
||||
const CFX_TEST_REGEX = /(([^\/]+\/)(?:lib\/)?)?(tests?\/test-[^\.\/]+)\.js$/;
|
||||
const JPM_TEST_REGEX = /^()(tests?\/test-[^\.\/]+)\.js$/;
|
||||
|
||||
const { mapcat, map, filter, fromEnumerator } = require("sdk/util/sequence");
|
||||
|
||||
@ -51,6 +52,8 @@ const removeDups = (array) => array.reduce((result, value) => {
|
||||
}, []);
|
||||
|
||||
const getSuites = function getSuites({ id, filter }) {
|
||||
const TEST_REGEX = isNative ? JPM_TEST_REGEX : CFX_TEST_REGEX;
|
||||
|
||||
return getAddon(id).then(addon => {
|
||||
let fileURI = addon.getResourceURI("tests/");
|
||||
let isPacked = fileURI.scheme == "jar";
|
||||
@ -77,9 +80,13 @@ const getSuites = function getSuites({ id, filter }) {
|
||||
suites = removeDups(suites.sort());
|
||||
return suites;
|
||||
})
|
||||
} else {
|
||||
let tests = getTestEntries(file);
|
||||
[...tests].forEach(addEntry);
|
||||
}
|
||||
else {
|
||||
let tests = [...getTestEntries(file)];
|
||||
let rootURI = addon.getResourceURI("/");
|
||||
tests.forEach((entry) => {
|
||||
addEntry(entry.replace(rootURI.spec, ""));
|
||||
});
|
||||
}
|
||||
|
||||
// sort and remove dups
|
||||
@ -102,7 +109,8 @@ const makeFilters = function makeFilters(options) {
|
||||
if (colonPos === -1) {
|
||||
filterFileRegex = new RegExp(options.filter);
|
||||
filterNameRegex = { test: () => true }
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
filterFileRegex = new RegExp(options.filter.substr(0, colonPos));
|
||||
filterNameRegex = new RegExp(options.filter.substr(colonPos + 1));
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
module.metadata = {
|
||||
@ -80,14 +79,16 @@ exports.once = once;
|
||||
* Arguments that will be passed to listeners.
|
||||
*/
|
||||
function emit (target, type, ...args) {
|
||||
let all = observers(target, '*').length;
|
||||
let state = observers(target, type);
|
||||
let listeners = state.slice();
|
||||
let count = listeners.length;
|
||||
let index = 0;
|
||||
|
||||
// If error event and there are no handlers then print error message
|
||||
// into a console.
|
||||
if (count === 0 && type === 'error') console.exception(args[0]);
|
||||
// If error event and there are no handlers (explicit or catch-all)
|
||||
// then print error message to the console.
|
||||
if (count === 0 && type === 'error' && all === 0)
|
||||
console.exception(args[0]);
|
||||
while (index < count) {
|
||||
try {
|
||||
let listener = listeners[index];
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
module.metadata = {
|
||||
@ -68,9 +67,8 @@ const EventTarget = Class({
|
||||
off(this, type, listener);
|
||||
return this;
|
||||
},
|
||||
off: function(type, listener) {
|
||||
off(this, type, listener);
|
||||
return this;
|
||||
}
|
||||
// but we can wrap `off` here, as the semantics are the same
|
||||
off: chainable(method(off))
|
||||
|
||||
});
|
||||
exports.EventTarget = EventTarget;
|
||||
|
@ -7,15 +7,14 @@ module.metadata = {
|
||||
"stability": "unstable"
|
||||
};
|
||||
|
||||
const { Ci, Cu } = require("chrome");
|
||||
const { Ci } = require("chrome");
|
||||
const events = require("../system/events");
|
||||
const core = require("./core");
|
||||
const { loadSheet, removeSheet } = require("../stylesheet/utils");
|
||||
|
||||
const assetsURI = require('../self').data.url();
|
||||
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);
|
||||
const hideSheetUri = "data:text/css,:root {visibility: hidden !important;}";
|
||||
|
||||
// Taken from Gaia:
|
||||
// https://github.com/andreasgal/gaia/blob/04fde2640a7f40314643016a5a6c98bf3755f5fd/webapi.js#L1470
|
||||
@ -46,11 +45,8 @@ function onDocumentReady2Translate(event) {
|
||||
|
||||
try {
|
||||
// Finally display document when we finished replacing all text content
|
||||
if (document.defaultView) {
|
||||
let winUtils = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
winUtils.removeSheet(hideSheetUri, winUtils.USER_SHEET);
|
||||
}
|
||||
if (document.defaultView)
|
||||
removeSheet(document.defaultView, hideSheetUri, 'user');
|
||||
}
|
||||
catch(e) {
|
||||
console.exception(e);
|
||||
@ -76,9 +72,7 @@ function onContentWindow(event) {
|
||||
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);
|
||||
loadSheet(document.defaultView, hideSheetUri, 'user');
|
||||
}
|
||||
catch(e) {
|
||||
console.exception(e);
|
||||
|
@ -14,11 +14,10 @@ const { getAttachEventType, WorkerHost } = require('./content/utils');
|
||||
const { Class } = require('./core/heritage');
|
||||
const { Disposable } = require('./core/disposable');
|
||||
const { WeakReference } = require('./core/reference');
|
||||
const { Worker } = require('./content/worker');
|
||||
const { Worker } = require('./content/worker-parent');
|
||||
const { EventTarget } = require('./event/target');
|
||||
const { on, emit, once, setListeners } = require('./event/core');
|
||||
const { on: domOn, removeListener: domOff } = require('./dom/events');
|
||||
const { pipe } = require('./event/utils');
|
||||
const { isRegExp, isUndefined } = require('./lang/type');
|
||||
const { merge } = require('./util/object');
|
||||
const { windowIterator } = require('./deprecated/window-utils');
|
||||
@ -114,7 +113,6 @@ const PageMod = Class({
|
||||
modContract.properties(modelFor),
|
||||
EventTarget,
|
||||
Disposable,
|
||||
WeakReference
|
||||
],
|
||||
extends: WorkerHost(workerFor),
|
||||
setup: function PageMod(options) {
|
||||
@ -213,11 +211,15 @@ function createWorker (mod, window) {
|
||||
onError: (e) => emit(mod, 'error', e)
|
||||
});
|
||||
workers.set(mod, worker);
|
||||
pipe(worker, mod);
|
||||
emit(mod, 'attach', worker);
|
||||
once(worker, 'detach', function detach() {
|
||||
worker.destroy();
|
||||
});
|
||||
worker.on('*', (event, ...args) => {
|
||||
// worker's "attach" event passes a window as the argument
|
||||
// page-mod's "attach" event needs a worker
|
||||
if (event === 'attach')
|
||||
emit(mod, event, worker)
|
||||
else
|
||||
emit(mod, event, ...args);
|
||||
})
|
||||
once(worker, 'detach', () => worker.destroy());
|
||||
}
|
||||
|
||||
function onContent (mod, window) {
|
||||
|
@ -3,12 +3,13 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
// The panel module currently supports only Firefox.
|
||||
// The panel module currently supports only Firefox and SeaMonkey.
|
||||
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=jetpack-panel-apps
|
||||
module.metadata = {
|
||||
"stability": "stable",
|
||||
"engines": {
|
||||
"Firefox": "*"
|
||||
"Firefox": "*",
|
||||
"SeaMonkey": "*"
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -7,7 +7,8 @@
|
||||
module.metadata = {
|
||||
"stability": "stable",
|
||||
"engines": {
|
||||
"Firefox": "*"
|
||||
"Firefox": "*",
|
||||
"SeaMonkey": "*"
|
||||
}
|
||||
};
|
||||
|
||||
@ -22,8 +23,7 @@ const { Ci, Cc } = require("chrome"),
|
||||
{ getTabs, getTabContentWindow, getTabForContentWindow,
|
||||
getAllTabContentWindows } = require('./tabs/utils'),
|
||||
winUtils = require("./window/utils"),
|
||||
events = require("./system/events"),
|
||||
{ iteratorSymbol, forInIterator } = require("./util/iteration");
|
||||
events = require("./system/events");
|
||||
|
||||
// The selection types
|
||||
const HTML = 0x01,
|
||||
@ -116,9 +116,12 @@ function* forOfIterator() {
|
||||
}
|
||||
|
||||
const selectionIteratorOptions = {
|
||||
__iterator__: forInIterator
|
||||
__iterator__: function() {
|
||||
for (let item of this)
|
||||
yield item;
|
||||
}
|
||||
}
|
||||
selectionIteratorOptions[iteratorSymbol] = forOfIterator;
|
||||
selectionIteratorOptions[Symbol.iterator] = forOfIterator;
|
||||
const selectionIterator = obscure(selectionIteratorOptions);
|
||||
|
||||
/**
|
||||
|
@ -30,13 +30,18 @@ const isPacked = rootURI && rootURI.indexOf("jar:") === 0;
|
||||
const uri = (path="") =>
|
||||
path.contains(":") ? path : addonDataURI + path.replace(/^\.\//, "");
|
||||
|
||||
let { preferencesBranch } = options;
|
||||
if (/[^\w{@}.-]/.test(preferencesBranch)) {
|
||||
preferencesBranch = id;
|
||||
console.warn("Ignoring preferences-branch (not a valid branch name)");
|
||||
}
|
||||
|
||||
// Some XPCOM APIs require valid URIs as an argument for certain operations
|
||||
// (see `nsILoginManager` for example). This property represents add-on
|
||||
// associated unique URI string that can be used for that.
|
||||
exports.uri = 'addon:' + id;
|
||||
exports.id = id;
|
||||
exports.preferencesBranch = options.preferencesBranch || id;
|
||||
exports.preferencesBranch = preferencesBranch || id;
|
||||
exports.name = name;
|
||||
exports.loadReason = loadReason;
|
||||
exports.version = version;
|
||||
|
@ -1,17 +1,13 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "experimental"
|
||||
};
|
||||
|
||||
const { Cc, Ci } = require("chrome");
|
||||
|
||||
const io = Cc['@mozilla.org/network/io-service;1'].
|
||||
getService(Ci.nsIIOService);
|
||||
const { Ci } = require("chrome");
|
||||
|
||||
const SHEET_TYPE = {
|
||||
"agent": "AGENT_SHEET",
|
||||
@ -36,12 +32,12 @@ function loadSheet(window, url, type) {
|
||||
|
||||
type = SHEET_TYPE[type];
|
||||
|
||||
if (!(url instanceof Ci.nsIURI))
|
||||
url = io.newURI(url, null, null);
|
||||
if (url instanceof Ci.nsIURI)
|
||||
url = url.spec;
|
||||
|
||||
let winUtils = getDOMWindowUtils(window);
|
||||
try {
|
||||
winUtils.loadSheet(url, winUtils[type]);
|
||||
winUtils.loadSheetUsingURIString(url, winUtils[type]);
|
||||
}
|
||||
catch (e) {};
|
||||
};
|
||||
@ -57,13 +53,13 @@ function removeSheet(window, url, type) {
|
||||
|
||||
type = SHEET_TYPE[type];
|
||||
|
||||
if (!(url instanceof Ci.nsIURI))
|
||||
url = io.newURI(url, null, null);
|
||||
if (url instanceof Ci.nsIURI)
|
||||
url = url.spec;
|
||||
|
||||
let winUtils = getDOMWindowUtils(window);
|
||||
|
||||
try {
|
||||
winUtils.removeSheet(url, winUtils[type]);
|
||||
winUtils.removeSheetUsingURIString(url, winUtils[type]);
|
||||
}
|
||||
catch (e) {};
|
||||
};
|
||||
|
@ -14,6 +14,7 @@ const { emit } = require('../event/core');
|
||||
const { isPrivate } = require('../private-browsing/utils');
|
||||
const { isWindowPrivate } = require('../window/utils');
|
||||
const { when: unload } = require('../system/unload');
|
||||
const { BLANK } = require('../content/thumbnail');
|
||||
const { viewFor } = require('../view/core');
|
||||
const { EVENTS } = require('./events');
|
||||
|
||||
@ -94,7 +95,7 @@ const Tab = Class({
|
||||
console.error(ERR_FENNEC_MSG);
|
||||
|
||||
// return 80x45 blank default
|
||||
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAtCAYAAAA5reyyAAAAJElEQVRoge3BAQEAAACCIP+vbkhAAQAAAAAAAAAAAAAAAADXBjhtAAGQ0AF/AAAAAElFTkSuQmCC';
|
||||
return BLANK;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -9,7 +9,7 @@ const { defer } = require("../lang/functional");
|
||||
const { has } = require("../util/array");
|
||||
const { each } = require("../util/object");
|
||||
const { EVENTS } = require("./events");
|
||||
const { getThumbnailURIForWindow } = require("../content/thumbnail");
|
||||
const { getThumbnailURIForWindow, BLANK } = require("../content/thumbnail");
|
||||
const { getFaviconURIForLocation } = require("../io/data");
|
||||
const { activateTab, getOwnerWindow, getBrowserForTab, getTabTitle,
|
||||
setTabTitle, getTabContentDocument, getTabURL, setTabURL,
|
||||
@ -199,8 +199,15 @@ const TabTrait = Trait.compose(EventEmitter, {
|
||||
* Thumbnail data URI of the page currently loaded in this tab.
|
||||
* @type {String}
|
||||
*/
|
||||
getThumbnail: function getThumbnail()
|
||||
this._tab ? getThumbnailURIForWindow(this._contentWindow) : undefined,
|
||||
getThumbnail() {
|
||||
if (!this._tab)
|
||||
return undefined;
|
||||
if (this._tab.getAttribute('remote')) {
|
||||
console.error('This method is not supported with E10S');
|
||||
return BLANK;
|
||||
}
|
||||
return getThumbnailURIForWindow(this._contentWindow);
|
||||
},
|
||||
/**
|
||||
* Whether or not tab is pinned (Is an app-tab).
|
||||
* @type {Boolean}
|
||||
|
@ -3,7 +3,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
'use strict';
|
||||
|
||||
const ContentWorker = require('../content/worker').Worker;
|
||||
const ContentWorker = require('../content/worker-parent').Worker;
|
||||
|
||||
function Worker(options, window) {
|
||||
options.window = window;
|
||||
|
@ -108,4 +108,5 @@ on(actionButtonStateEvents, 'data', ({target, window, state}) => {
|
||||
view.setIcon(id, window, state.icon);
|
||||
view.setLabel(id, window, state.label);
|
||||
view.setDisabled(id, window, state.disabled);
|
||||
view.setBadge(id, window, state.badge, state.badgeColor);
|
||||
});
|
||||
|
@ -6,14 +6,13 @@
|
||||
const { contract } = require('../../util/contract');
|
||||
const { isLocalURL } = require('../../url');
|
||||
const { isNil, isObject, isString } = require('../../lang/type');
|
||||
const { required, either, string, boolean, object } = require('../../deprecated/api-utils');
|
||||
const { required, either, string, boolean, object, number } = require('../../deprecated/api-utils');
|
||||
const { merge } = require('../../util/object');
|
||||
const { freeze } = Object;
|
||||
|
||||
function isIconSet(icons) {
|
||||
return Object.keys(icons).
|
||||
every(size => String(size >>> 0) === size && isLocalURL(icons[size]))
|
||||
}
|
||||
const isIconSet = (icons) =>
|
||||
Object.keys(icons).
|
||||
every(size => String(size >>> 0) === size && isLocalURL(icons[size]));
|
||||
|
||||
let iconSet = {
|
||||
is: either(object, string),
|
||||
@ -36,10 +35,22 @@ let label = {
|
||||
msg: 'The option "label" must be a non empty string'
|
||||
}
|
||||
|
||||
let badge = {
|
||||
is: either(string, number),
|
||||
msg: 'The option "badge" must be a string or a number'
|
||||
}
|
||||
|
||||
let badgeColor = {
|
||||
is: string,
|
||||
msg: 'The option "badgeColor" must be a string'
|
||||
}
|
||||
|
||||
let stateContract = contract({
|
||||
label: label,
|
||||
icon: iconSet,
|
||||
disabled: boolean
|
||||
disabled: boolean,
|
||||
badge: badge,
|
||||
badgeColor: badgeColor
|
||||
});
|
||||
|
||||
exports.stateContract = stateContract;
|
||||
|
@ -100,6 +100,7 @@ on(toggleButtonStateEvents, 'data', ({target, window, state}) => {
|
||||
view.setLabel(id, window, state.label);
|
||||
view.setDisabled(id, window, state.disabled);
|
||||
view.setChecked(id, window, state.checked);
|
||||
view.setBadge(id, window, state.badge, state.badgeColor);
|
||||
});
|
||||
|
||||
on(clickEvents, 'data', ({target: id, window, checked }) => {
|
||||
|
@ -15,7 +15,7 @@ const { on, off, emit } = require('../../event/core');
|
||||
|
||||
const { data } = require('sdk/self');
|
||||
|
||||
const { isObject } = require('../../lang/type');
|
||||
const { isObject, isNil } = require('../../lang/type');
|
||||
|
||||
const { getMostRecentBrowserWindow } = require('../../window/utils');
|
||||
const { ignoreWindow } = require('../../private-browsing/utils');
|
||||
@ -114,7 +114,7 @@ function nodeFor(id, window=getMostRecentBrowserWindow()) {
|
||||
exports.nodeFor = nodeFor;
|
||||
|
||||
function create(options) {
|
||||
let { id, label, icon, type } = options;
|
||||
let { id, label, icon, type, badge } = options;
|
||||
|
||||
if (views.has(id))
|
||||
throw new Error('The ID "' + id + '" seems already used.');
|
||||
@ -137,7 +137,7 @@ function create(options) {
|
||||
node.style.display = 'none';
|
||||
|
||||
node.setAttribute('id', this.id);
|
||||
node.setAttribute('class', 'toolbarbutton-1 chromeclass-toolbar-additional');
|
||||
node.setAttribute('class', 'toolbarbutton-1 chromeclass-toolbar-additional badged-button');
|
||||
node.setAttribute('type', type);
|
||||
node.setAttribute('label', label);
|
||||
node.setAttribute('tooltiptext', label);
|
||||
@ -213,6 +213,27 @@ function setChecked(id, window, checked) {
|
||||
}
|
||||
exports.setChecked = setChecked;
|
||||
|
||||
function setBadge(id, window, badge, color) {
|
||||
let node = nodeFor(id, window);
|
||||
|
||||
if (node) {
|
||||
// `Array.from` is needed to handle unicode symbol properly:
|
||||
// '𝐀𝐁'.length is 4 where Array.from('𝐀𝐁').length is 2
|
||||
let text = isNil(badge)
|
||||
? ''
|
||||
: Array.from(String(badge)).slice(0, 4).join('');
|
||||
|
||||
node.setAttribute('badge', text);
|
||||
|
||||
let badgeNode = node.ownerDocument.getAnonymousElementByAttribute(node,
|
||||
'class', 'toolbarbutton-badge');
|
||||
|
||||
if (badgeNode)
|
||||
badgeNode.style.backgroundColor = isNil(color) ? '' : color;
|
||||
}
|
||||
}
|
||||
exports.setBadge = setBadge;
|
||||
|
||||
function click(id) {
|
||||
let node = nodeFor(id);
|
||||
|
||||
|
@ -1,24 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
'use strict';
|
||||
|
||||
module.metadata = {
|
||||
"stability": "experimental"
|
||||
};
|
||||
|
||||
// This is known as @@iterator in the ES6 spec. In builds that have ES6
|
||||
// Symbols, use Symbol.iterator; otherwise use the legacy method name,
|
||||
// "@@iterator".
|
||||
const JS_HAS_SYMBOLS = typeof Symbol === "function";
|
||||
exports.iteratorSymbol = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator";
|
||||
|
||||
// An adaptor that, given an object that is iterable with for-of, is
|
||||
// suitable for being bound to __iterator__ in order to make the object
|
||||
// iterable in the same way via for-in.
|
||||
function forInIterator() {
|
||||
for (let item of this)
|
||||
yield item;
|
||||
}
|
||||
|
||||
exports.forInIterator = forInIterator;
|
@ -9,7 +9,6 @@ module.metadata = {
|
||||
|
||||
const { Class } = require('../core/heritage');
|
||||
const listNS = require('../core/namespace').ns();
|
||||
const { iteratorSymbol } = require('../util/iteration');
|
||||
|
||||
const listOptions = {
|
||||
/**
|
||||
@ -48,8 +47,8 @@ const listOptions = {
|
||||
yield onKeyValue ? [++i, element] : onKeys ? ++i : element;
|
||||
},
|
||||
};
|
||||
listOptions[iteratorSymbol] = function iterator() {
|
||||
return listNS(this).keyValueMap.slice(0)[iteratorSymbol]();
|
||||
listOptions[Symbol.iterator] = function iterator() {
|
||||
return listNS(this).keyValueMap.slice(0)[Symbol.iterator]();
|
||||
};
|
||||
const List = Class(listOptions);
|
||||
exports.List = List;
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
module.metadata = {
|
||||
@ -36,7 +35,7 @@ function merge(source) {
|
||||
// converted to `true` where `null` and `undefined` becames `false`. Therefore
|
||||
// the `filter` method will keep only objects that are defined and not null.
|
||||
Array.slice(arguments, 1).filter(Boolean).forEach(function onEach(properties) {
|
||||
Object.getOwnPropertyNames(properties).forEach(function(name) {
|
||||
getOwnPropertyIdentifiers(properties).forEach(function(name) {
|
||||
descriptor[name] = Object.getOwnPropertyDescriptor(properties, name);
|
||||
});
|
||||
});
|
||||
@ -85,8 +84,19 @@ function omit(source, ...values) {
|
||||
let copy = {};
|
||||
let keys = flatten(values);
|
||||
for (let prop in source)
|
||||
if (!~keys.indexOf(prop))
|
||||
if (!~keys.indexOf(prop))
|
||||
copy[prop] = source[prop];
|
||||
return copy;
|
||||
}
|
||||
exports.omit = omit;
|
||||
|
||||
// get object's own property Symbols and/or Names, including nonEnumerables by default
|
||||
function getOwnPropertyIdentifiers(object, options = { names: true, symbols: true, nonEnumerables: true }) {
|
||||
const symbols = !options.symbols ? [] :
|
||||
Object.getOwnPropertySymbols(object);
|
||||
const names = !options.names ? [] :
|
||||
options.nonEnumerables ? Object.getOwnPropertyNames(object) :
|
||||
Object.keys(object);
|
||||
return [...names, ...symbols];
|
||||
}
|
||||
exports.getOwnPropertyIdentifiers = getOwnPropertyIdentifiers;
|
||||
|
@ -22,13 +22,12 @@ module.metadata = {
|
||||
// - `_` used for argument(s) or variable(s) who's values are ignored.
|
||||
|
||||
const { complement, flip, identity } = require("../lang/functional");
|
||||
const { iteratorSymbol } = require("../util/iteration");
|
||||
const { isArray, isArguments, isMap, isSet,
|
||||
isString, isBoolean, isNumber } = require("../lang/type");
|
||||
|
||||
const Sequence = function Sequence(iterator) {
|
||||
if (iterator.isGenerator && iterator.isGenerator())
|
||||
this[iteratorSymbol] = iterator;
|
||||
this[Symbol.iterator] = iterator;
|
||||
else
|
||||
throw TypeError("Expected generator argument");
|
||||
};
|
||||
@ -216,7 +215,7 @@ const map = (f, ...sequences) => seq(function* () {
|
||||
|
||||
let index = 0;
|
||||
while (index < count) {
|
||||
inputs[index] = sequences[index][iteratorSymbol]();
|
||||
inputs[index] = sequences[index][Symbol.iterator]();
|
||||
index = index + 1;
|
||||
}
|
||||
|
||||
|
@ -59,8 +59,11 @@ const WindowLoader = Trait.compose({
|
||||
|
||||
if (window !== _window) {
|
||||
if (_window) {
|
||||
_window.removeEventListener(ON_UNLOAD, this.__unloadListener, false);
|
||||
_window.removeEventListener(ON_LOAD, this.__loadListener, false);
|
||||
if (this.__unloadListener)
|
||||
_window.removeEventListener(ON_UNLOAD, this.__unloadListener, false);
|
||||
|
||||
if (this.__loadListener)
|
||||
_window.removeEventListener(ON_LOAD, this.__loadListener, false);
|
||||
}
|
||||
|
||||
if (window) {
|
||||
@ -123,4 +126,3 @@ const WindowLoader = Trait.compose({
|
||||
__unloadListener: null
|
||||
});
|
||||
exports.WindowLoader = WindowLoader;
|
||||
|
||||
|
@ -45,12 +45,13 @@ const { join: pathJoin, normalize, dirname } = Cu.import("resource://gre/modules
|
||||
|
||||
// Define some shortcuts.
|
||||
const bind = Function.call.bind(Function.bind);
|
||||
const getOwnPropertyNames = Object.getOwnPropertyNames;
|
||||
const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
||||
const define = Object.defineProperties;
|
||||
const prototypeOf = Object.getPrototypeOf;
|
||||
const create = Object.create;
|
||||
const keys = Object.keys;
|
||||
const getOwnIdentifiers = x => [...Object.getOwnPropertyNames(x),
|
||||
...Object.getOwnPropertySymbols(x)];
|
||||
|
||||
const NODE_MODULES = ["assert", "buffer_ieee754", "buffer", "child_process", "cluster", "console", "constants", "crypto", "_debugger", "dgram", "dns", "domain", "events", "freelist", "fs", "http", "https", "_linklist", "module", "net", "os", "path", "punycode", "querystring", "readline", "repl", "stream", "string_decoder", "sys", "timers", "tls", "tty", "url", "util", "vm", "zlib"];
|
||||
|
||||
@ -84,7 +85,7 @@ function freeze(object) {
|
||||
// Returns map of given `object`-s own property descriptors.
|
||||
const descriptor = iced(function descriptor(object) {
|
||||
let value = {};
|
||||
getOwnPropertyNames(object).forEach(function(name) {
|
||||
getOwnIdentifiers(object).forEach(function(name) {
|
||||
value[name] = getOwnPropertyDescriptor(object, name)
|
||||
});
|
||||
return value;
|
||||
@ -121,7 +122,7 @@ function iced(f) {
|
||||
const override = iced(function override(target, source) {
|
||||
let properties = descriptor(target)
|
||||
let extension = descriptor(source || {})
|
||||
getOwnPropertyNames(extension).forEach(function(name) {
|
||||
getOwnIdentifiers(extension).forEach(function(name) {
|
||||
properties[name] = extension[name];
|
||||
});
|
||||
return define({}, properties);
|
||||
@ -294,7 +295,7 @@ const load = iced(function load(loader, module) {
|
||||
// the scope object for this particular module
|
||||
sandbox = new loader.sharedGlobalSandbox.Object();
|
||||
// Inject all expected globals in the scope object
|
||||
getOwnPropertyNames(globals).forEach(function(name) {
|
||||
getOwnIdentifiers(globals).forEach(function(name) {
|
||||
descriptors[name] = getOwnPropertyDescriptor(globals, name)
|
||||
});
|
||||
define(sandbox, descriptors);
|
||||
@ -521,7 +522,7 @@ const resolveURI = iced(function resolveURI(id, mapping) {
|
||||
let count = mapping.length, index = 0;
|
||||
|
||||
// Do not resolve if already a resource URI
|
||||
if (isResourceURI(id)) return normalizeExt(id);
|
||||
if (isAbsoluteURI(id)) return normalizeExt(id);
|
||||
|
||||
while (index < count) {
|
||||
let [ path, uri ] = mapping[index ++];
|
||||
@ -844,7 +845,9 @@ exports.Loader = Loader;
|
||||
let isJSONURI = uri => uri.substr(-5) === '.json';
|
||||
let isJSMURI = uri => uri.substr(-4) === '.jsm';
|
||||
let isJSURI = uri => uri.substr(-3) === '.js';
|
||||
let isResourceURI = uri => uri.substr(0, 11) === 'resource://';
|
||||
let isAbsoluteURI = uri => uri.indexOf("resource://") >= 0 ||
|
||||
uri.indexOf("chrome://") >= 0 ||
|
||||
uri.indexOf("file://") >= 0
|
||||
let isRelative = id => id[0] === '.'
|
||||
|
||||
const generateMap = iced(function generateMap(options, callback) {
|
||||
@ -928,7 +931,7 @@ function findAllModuleIncludes (uri, options, results, callback) {
|
||||
// Given a resource URI or source, return an array of strings passed into
|
||||
// the require statements from the source
|
||||
function findModuleIncludes (uri, callback) {
|
||||
let src = isResourceURI(uri) ? readURI(uri) : uri;
|
||||
let src = isAbsoluteURI(uri) ? readURI(uri) : uri;
|
||||
let modules = [];
|
||||
|
||||
walk(src, function (node) {
|
||||
@ -979,4 +982,3 @@ function isRequire (node) {
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
54
addon-sdk/source/lib/toolkit/require.js
Normal file
54
addon-sdk/source/lib/toolkit/require.js
Normal file
@ -0,0 +1,54 @@
|
||||
const make = (exports, rootURI, components) => {
|
||||
const { Loader: { Loader, Require, Module, main } } =
|
||||
components.utils.import(rootURI + "toolkit/loader.js", {});
|
||||
|
||||
const loader = Loader({
|
||||
id: "toolkit/require",
|
||||
rootURI: rootURI,
|
||||
isNative: true,
|
||||
paths: {
|
||||
"": rootURI,
|
||||
"devtools/": "resource://gre/modules/devtools/"
|
||||
}
|
||||
});
|
||||
|
||||
// Below we define `require` & `require.resolve` that resolve passed
|
||||
// module id relative to the caller URI. This is not perfect but good
|
||||
// enough for common case & there is always an option to pass absolute
|
||||
// id when that
|
||||
// but presumably well enough to cover
|
||||
|
||||
const require = id => {
|
||||
const requirerURI = components.stack.caller.filename;
|
||||
const requirer = Module(requirerURI, requirerURI);
|
||||
return Require(loader, requirer)(id);
|
||||
};
|
||||
|
||||
require.resolve = id => {
|
||||
const requirerURI = components.stack.caller.filename;
|
||||
const requirer = Module(requirerURI, requirerURI);
|
||||
return Require(loader, requirer).resolve(id);
|
||||
};
|
||||
|
||||
exports.require = require;
|
||||
}
|
||||
|
||||
// If loaded in the context of commonjs module, reload as JSM into an
|
||||
// exports object.
|
||||
if (typeof(require) === "function" && typeof(module) === "object") {
|
||||
require("chrome").Cu.import(module.uri, module.exports);
|
||||
}
|
||||
// If loaded in the context of JSM make a loader & require and define
|
||||
// new symbols as exported ones.
|
||||
else if (typeof(__URI__) === "string" && this["Components"]) {
|
||||
const builtin = Object.keys(this);
|
||||
const uri = __URI__.replace("toolkit/require.js", "");
|
||||
make(this, uri, this["Components"]);
|
||||
|
||||
this.EXPORTED_SYMBOLS = Object.
|
||||
keys(this).
|
||||
filter($ => builtin.indexOf($) < 0);
|
||||
}
|
||||
else {
|
||||
throw Error("Loading require.js in this environment isn't supported")
|
||||
}
|
@ -401,12 +401,7 @@ def generate_build_for_target(pkg_cfg, target, deps,
|
||||
build['preferencesBranch'] = jid
|
||||
|
||||
if 'preferences-branch' in target_cfg:
|
||||
# check it's a non-empty, valid branch name
|
||||
preferencesBranch = target_cfg['preferences-branch']
|
||||
if re.match('^[\w{@}-]+$', preferencesBranch):
|
||||
build['preferencesBranch'] = preferencesBranch
|
||||
elif not is_running_tests:
|
||||
print >>sys.stderr, "IGNORING preferences-branch (not a valid branch name)"
|
||||
build['preferencesBranch'] = target_cfg['preferences-branch']
|
||||
|
||||
return build
|
||||
|
||||
|
@ -34,6 +34,9 @@ DEFAULT_COMMON_PREFS = {
|
||||
# Allow installing extensions dropped into the profile folder
|
||||
'extensions.autoDisableScopes' : 10,
|
||||
|
||||
# shut up some warnings on `about:` page
|
||||
'app.releaseNotesURL': 'http://localhost/app-dummy/',
|
||||
'app.vendorURL': 'http://localhost/app-dummy/'
|
||||
}
|
||||
|
||||
DEFAULT_NO_CONNECTIONS_PREFS = {
|
||||
|
@ -17,11 +17,6 @@ exports.testCurlyID = function(assert) {
|
||||
assert.equal(service.get('extensions.{34a1eae1-c20a-464f-9b0e-000000000000}.test14'), simple.prefs.test14, 'simple test14 also 15');
|
||||
}
|
||||
|
||||
exports.testInvalidPreferencesBranch = function(assert) {
|
||||
assert.notEqual(preferencesBranch, 'invalid^branch*name', 'invalid preferences-branch value ignored');
|
||||
assert.equal(preferencesBranch, '{34a1eae1-c20a-464f-9b0e-000000000000}', 'preferences-branch is {34a1eae1-c20a-464f-9b0e-000000000000}');
|
||||
}
|
||||
|
||||
// from `/test/test-self.js`, adapted to `sdk/test/assert` API
|
||||
exports.testSelfID = function*(assert) {
|
||||
assert.equal(typeof(id), 'string', 'self.id is a string');
|
||||
|
@ -8,7 +8,5 @@
|
||||
"type": "integer",
|
||||
"title": "test13",
|
||||
"value": 26
|
||||
}],
|
||||
|
||||
"preferences-branch": "invalid^branch*name"
|
||||
}]
|
||||
}
|
||||
|
@ -19,11 +19,6 @@ exports.testStandardID = function(assert) {
|
||||
assert.equal(service.get('extensions.standard-id@jetpack.test14'), simple.prefs.test14, 'simple test14 also 15');
|
||||
}
|
||||
|
||||
exports.testInvalidPreferencesBranch = function(assert) {
|
||||
assert.notEqual(preferencesBranch, 'invalid^branch*name', 'invalid preferences-branch value ignored');
|
||||
assert.equal(preferencesBranch, 'standard-id@jetpack', 'preferences-branch is standard-id@jetpack');
|
||||
}
|
||||
|
||||
// from `/test/test-self.js`, adapted to `sdk/test/assert` API
|
||||
exports.testSelfID = function*(assert) {
|
||||
assert.equal(typeof(id), 'string', 'self.id is a string');
|
||||
|
@ -8,7 +8,5 @@
|
||||
"type": "integer",
|
||||
"title": "test13",
|
||||
"value": 26
|
||||
}],
|
||||
|
||||
"preferences-branch": "invalid^branch*name"
|
||||
}]
|
||||
}
|
||||
|
1
addon-sdk/source/test/fixtures/addon-sdk/data/border-style.css
vendored
Normal file
1
addon-sdk/source/test/fixtures/addon-sdk/data/border-style.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
div { border-style: dashed; }
|
5
addon-sdk/source/test/fixtures/addon-sdk/data/test-contentScriptFile.js
vendored
Normal file
5
addon-sdk/source/test/fixtures/addon-sdk/data/test-contentScriptFile.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/* 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/. */
|
||||
|
||||
self.postMessage("msg from contentScriptFile");
|
13
addon-sdk/source/test/fixtures/addon-sdk/data/test.html
vendored
Normal file
13
addon-sdk/source/test/fixtures/addon-sdk/data/test.html
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<!-- 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/. -->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>foo</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>bar</p>
|
||||
</body>
|
||||
</html>
|
@ -37,6 +37,7 @@ support-files =
|
||||
[test-content-script.js]
|
||||
[test-content-symbiont.js]
|
||||
[test-content-worker.js]
|
||||
[test-content-worker-parent.js]
|
||||
[test-context-menu.js]
|
||||
[test-cortex.js]
|
||||
[test-cuddlefish.js]
|
||||
|
@ -1,45 +1,31 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {Cc,Ci} = require("chrome");
|
||||
const timer = require("sdk/timers");
|
||||
const xulApp = require("sdk/system/xul-app");
|
||||
const { Cc, Ci } = require("chrome");
|
||||
const { setTimeout } = require("sdk/timers");
|
||||
const { Loader } = require("sdk/test/loader");
|
||||
const { openTab, getBrowserForTab, closeTab } = require("sdk/tabs/utils");
|
||||
const self = require("sdk/self");
|
||||
const { merge } = require("sdk/util/object");
|
||||
const httpd = require("./lib/httpd");
|
||||
|
||||
/**
|
||||
* A helper function that creates a PageMod, then opens the specified URL
|
||||
* and checks the effect of the page mod on 'onload' event via testCallback.
|
||||
*/
|
||||
const PORT = 8099;
|
||||
const PATH = '/test-contentScriptWhen.html';
|
||||
|
||||
// an evil function enables the creation of tests
|
||||
// that depend on delicate event timing. do not use.
|
||||
exports.testPageMod = function testPageMod(assert, done, testURL, pageModOptions,
|
||||
testCallback, timeout) {
|
||||
if (!xulApp.versionInRange(xulApp.platformVersion, "1.9.3a3", "*") &&
|
||||
!xulApp.versionInRange(xulApp.platformVersion, "1.9.2.7", "1.9.2.*")) {
|
||||
assert.pass("Note: not testing PageMod, as it doesn't work on this platform version");
|
||||
return null;
|
||||
}
|
||||
|
||||
var wm = Cc['@mozilla.org/appshell/window-mediator;1']
|
||||
.getService(Ci.nsIWindowMediator);
|
||||
var browserWindow = wm.getMostRecentWindow("navigator:browser");
|
||||
if (!browserWindow) {
|
||||
assert.pass("page-mod tests: could not find the browser window, so " +
|
||||
"will not run. Use -a firefox to run the pagemod tests.")
|
||||
return null;
|
||||
}
|
||||
|
||||
let loader = Loader(module, null, null, {
|
||||
modules: {
|
||||
"sdk/self": merge({}, self, {
|
||||
data: merge({}, self.data, require("./fixtures"))
|
||||
})
|
||||
}
|
||||
});
|
||||
let options = merge({}, require('@loader/options'),
|
||||
{ prefixURI: require('./fixtures').url() });
|
||||
|
||||
let loader = Loader(module, null, options);
|
||||
let pageMod = loader.require("sdk/page-mod");
|
||||
|
||||
var pageMods = [new pageMod.PageMod(opts) for each(opts in pageModOptions)];
|
||||
@ -55,7 +41,7 @@ exports.testPageMod = function testPageMod(assert, done, testURL, pageModOptions
|
||||
// load event. So page-mod actions may not be already done.
|
||||
// If we delay even more contentScriptWhen:'end', we may want to modify
|
||||
// this code again.
|
||||
timer.setTimeout(testCallback, 0,
|
||||
setTimeout(testCallback, timeout,
|
||||
b.contentWindow.wrappedJSObject,
|
||||
function () {
|
||||
pageMods.forEach(function(mod) mod.destroy());
|
||||
@ -76,7 +62,8 @@ exports.testPageMod = function testPageMod(assert, done, testURL, pageModOptions
|
||||
* based on the value of document.readyState at the time contentScript is attached
|
||||
*/
|
||||
exports.handleReadyState = function(url, contentScriptWhen, callbacks) {
|
||||
const { PageMod } = Loader(module).require('sdk/page-mod');
|
||||
const loader = Loader(module);
|
||||
const { PageMod } = loader.require('sdk/page-mod');
|
||||
|
||||
let pagemod = PageMod({
|
||||
include: url,
|
||||
@ -86,13 +73,39 @@ exports.handleReadyState = function(url, contentScriptWhen, callbacks) {
|
||||
onAttach: worker => {
|
||||
let { tab } = worker;
|
||||
worker.on('message', readyState => {
|
||||
pagemod.destroy();
|
||||
// generate event name from `readyState`, e.g. `"loading"` becomes `onLoading`.
|
||||
let type = 'on' + readyState[0].toUpperCase() + readyState.substr(1);
|
||||
|
||||
if (type in callbacks)
|
||||
callbacks[type](tab);
|
||||
|
||||
pagemod.destroy();
|
||||
loader.unload();
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// serves a slow page which takes 1.5 seconds to load,
|
||||
// 0.5 seconds in each readyState: uninitialized, loading, interactive.
|
||||
exports.contentScriptWhenServer = function() {
|
||||
const URL = 'http://localhost:' + PORT + PATH;
|
||||
|
||||
const HTML = `/* polyglot js
|
||||
<script src="${URL}"></script>
|
||||
delay both the "DOMContentLoaded"
|
||||
<script async src="${URL}"></script>
|
||||
and "load" events */`;
|
||||
|
||||
let srv = httpd.startServerAsync(PORT);
|
||||
|
||||
srv.registerPathHandler(PATH, (_, response) => {
|
||||
response.processAsync();
|
||||
response.setHeader('Content-Type', 'text/html', false);
|
||||
setTimeout(_ => response.finish(), 500);
|
||||
response.write(HTML);
|
||||
})
|
||||
|
||||
srv.URL = URL;
|
||||
return srv;
|
||||
}
|
||||
|
@ -9,7 +9,8 @@ const base64 = require("sdk/base64");
|
||||
const text = "Awesome!";
|
||||
const b64text = "QXdlc29tZSE=";
|
||||
|
||||
const utf8text = "✓ à la mode";
|
||||
const utf8text = "\u2713 à la mode";
|
||||
const badutf8text = "\u0013 à la mode";
|
||||
const b64utf8text = "4pyTIMOgIGxhIG1vZGU=";
|
||||
|
||||
exports["test base64.encode"] = function (assert) {
|
||||
@ -66,8 +67,8 @@ exports["test base64.decode with wrong charset"] = function (assert) {
|
||||
|
||||
exports["test encode/decode Unicode without utf-8 as charset"] = function (assert) {
|
||||
|
||||
assert.notEqual(base64.decode(base64.encode(utf8text)), utf8text,
|
||||
"Unicode strings needs 'utf-8' charset"
|
||||
assert.equal(base64.decode(base64.encode(utf8text)), badutf8text,
|
||||
"Unicode strings needs 'utf-8' charset or will be mangled"
|
||||
);
|
||||
|
||||
}
|
||||
|
@ -557,7 +557,8 @@ exports["test Collections 2"] = createProxyTest(html, function (helper) {
|
||||
for(let i in body.childNodes) {
|
||||
count++;
|
||||
}
|
||||
assert(count == 6, "body.childNodes is iterable");
|
||||
|
||||
assert(count >= 3, "body.childNodes is iterable");
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
982
addon-sdk/source/test/test-content-worker-parent.js
Normal file
982
addon-sdk/source/test/test-content-worker-parent.js
Normal file
@ -0,0 +1,982 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Skipping due to window creation being unsupported in Fennec
|
||||
module.metadata = {
|
||||
engines: {
|
||||
'Firefox': '*'
|
||||
}
|
||||
};
|
||||
|
||||
const { Cc, Ci } = require("chrome");
|
||||
const { on } = require("sdk/event/core");
|
||||
const { setTimeout } = require("sdk/timers");
|
||||
const { LoaderWithHookedConsole } = require("sdk/test/loader");
|
||||
const { Worker } = require("sdk/content/worker-parent");
|
||||
const { close } = require("sdk/window/helpers");
|
||||
const { set: setPref } = require("sdk/preferences/service");
|
||||
const { isArray } = require("sdk/lang/type");
|
||||
const { URL } = require('sdk/url');
|
||||
const fixtures = require("./fixtures");
|
||||
const system = require("sdk/system/events");
|
||||
|
||||
const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
|
||||
|
||||
const DEFAULT_CONTENT_URL = "data:text/html;charset=utf-8,foo";
|
||||
|
||||
const WINDOW_SCRIPT_URL = "data:text/html;charset=utf-8," +
|
||||
"<script>window.addEventListener('message', function (e) {" +
|
||||
" if (e.data === 'from -> content-script')" +
|
||||
" window.postMessage('from -> window', '*');" +
|
||||
"});</script>";
|
||||
|
||||
function makeWindow() {
|
||||
let content =
|
||||
"<?xml version=\"1.0\"?>" +
|
||||
"<window " +
|
||||
"xmlns=\"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul\">" +
|
||||
"<script>var documentValue=true;</script>" +
|
||||
"</window>";
|
||||
var url = "data:application/vnd.mozilla.xul+xml;charset=utf-8," +
|
||||
encodeURIComponent(content);
|
||||
var features = ["chrome", "width=10", "height=10"];
|
||||
|
||||
return Cc["@mozilla.org/embedcomp/window-watcher;1"].
|
||||
getService(Ci.nsIWindowWatcher).
|
||||
openWindow(null, url, null, features.join(","), null);
|
||||
}
|
||||
|
||||
// Listen for only first one occurence of DOM event
|
||||
function listenOnce(node, eventName, callback) {
|
||||
node.addEventListener(eventName, function onevent(event) {
|
||||
node.removeEventListener(eventName, onevent, true);
|
||||
callback(node);
|
||||
}, true);
|
||||
}
|
||||
|
||||
// Load a given url in a given browser and fires the callback when it is loaded
|
||||
function loadAndWait(browser, url, callback) {
|
||||
listenOnce(browser, "load", callback);
|
||||
// We have to wait before calling `loadURI` otherwise, if we call
|
||||
// `loadAndWait` during browser load event, the history will be broken
|
||||
setTimeout(function () {
|
||||
browser.loadURI(url);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// Returns a test function that will automatically open a new chrome window
|
||||
// with a <browser> element loaded on a given content URL
|
||||
// The callback receive 3 arguments:
|
||||
// - test: reference to the jetpack test object
|
||||
// - browser: a reference to the <browser> xul node
|
||||
// - done: a callback to call when test is over
|
||||
function WorkerTest(url, callback) {
|
||||
return function testFunction(assert, done) {
|
||||
let chromeWindow = makeWindow();
|
||||
chromeWindow.addEventListener("load", function onload() {
|
||||
chromeWindow.removeEventListener("load", onload, true);
|
||||
let browser = chromeWindow.document.createElement("browser");
|
||||
browser.setAttribute("type", "content");
|
||||
chromeWindow.document.documentElement.appendChild(browser);
|
||||
// Wait for about:blank load event ...
|
||||
listenOnce(browser, "load", function onAboutBlankLoad() {
|
||||
// ... before loading the expected doc and waiting for its load event
|
||||
loadAndWait(browser, url, function onDocumentLoaded() {
|
||||
callback(assert, browser, function onTestDone() {
|
||||
|
||||
close(chromeWindow).then(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
}, true);
|
||||
};
|
||||
}
|
||||
|
||||
exports["test:sample"] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
|
||||
assert.notEqual(browser.contentWindow.location.href, "about:blank",
|
||||
"window is now on the right document");
|
||||
|
||||
let window = browser.contentWindow
|
||||
let worker = Worker({
|
||||
window: window,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
// window is accessible
|
||||
let myLocation = window.location.toString();
|
||||
self.on("message", function(data) {
|
||||
if (data == "hi!")
|
||||
self.postMessage("bye!");
|
||||
});
|
||||
},
|
||||
contentScriptWhen: "ready",
|
||||
onMessage: function(msg) {
|
||||
assert.equal("bye!", msg);
|
||||
assert.equal(worker.url, window.location.href,
|
||||
"worker.url still works");
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(worker.url, window.location.href,
|
||||
"worker.url works");
|
||||
assert.equal(worker.contentURL, window.location.href,
|
||||
"worker.contentURL works");
|
||||
worker.postMessage("hi!");
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:emit"] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
// Validate self.on and self.emit
|
||||
self.port.on("addon-to-content", function (data) {
|
||||
self.port.emit("content-to-addon", data);
|
||||
});
|
||||
|
||||
// Check for global pollution
|
||||
//if (typeof on != "undefined")
|
||||
// self.postMessage("`on` is in globals");
|
||||
if (typeof once != "undefined")
|
||||
self.postMessage("`once` is in globals");
|
||||
if (typeof emit != "undefined")
|
||||
self.postMessage("`emit` is in globals");
|
||||
|
||||
},
|
||||
onMessage: function(msg) {
|
||||
assert.fail("Got an unexpected message : "+msg);
|
||||
}
|
||||
});
|
||||
|
||||
// Validate worker.port
|
||||
worker.port.on("content-to-addon", function (data) {
|
||||
assert.equal(data, "event data");
|
||||
done();
|
||||
});
|
||||
worker.port.emit("addon-to-content", "event data");
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:emit hack message"] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
// Validate self.port
|
||||
self.port.on("message", function (data) {
|
||||
self.port.emit("message", data);
|
||||
});
|
||||
// We should not receive message on self, but only on self.port
|
||||
self.on("message", function (data) {
|
||||
self.postMessage("message", data);
|
||||
});
|
||||
},
|
||||
onError: function(e) {
|
||||
assert.fail("Got exception: "+e);
|
||||
}
|
||||
});
|
||||
|
||||
worker.port.on("message", function (data) {
|
||||
assert.equal(data, "event data");
|
||||
done();
|
||||
});
|
||||
worker.on("message", function (data) {
|
||||
assert.fail("Got an unexpected message : "+msg);
|
||||
});
|
||||
worker.port.emit("message", "event data");
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:n-arguments emit"] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
let repeat = 0;
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
// Validate self.on and self.emit
|
||||
self.port.on("addon-to-content", function (a1, a2, a3) {
|
||||
self.port.emit("content-to-addon", a1, a2, a3);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Validate worker.port
|
||||
worker.port.on("content-to-addon", function (arg1, arg2, arg3) {
|
||||
if (!repeat++) {
|
||||
this.emit("addon-to-content", "first argument", "second", "third");
|
||||
} else {
|
||||
assert.equal(arg1, "first argument");
|
||||
assert.equal(arg2, "second");
|
||||
assert.equal(arg3, "third");
|
||||
done();
|
||||
}
|
||||
});
|
||||
worker.port.emit("addon-to-content", "first argument", "second", "third");
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:post-json-values-only"] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
self.on("message", function (message) {
|
||||
self.postMessage([ message.fun === undefined,
|
||||
typeof message.w,
|
||||
message.w && "port" in message.w,
|
||||
message.w._url,
|
||||
Array.isArray(message.array),
|
||||
JSON.stringify(message.array)]);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Validate worker.onMessage
|
||||
let array = [1, 2, 3];
|
||||
worker.on("message", function (message) {
|
||||
assert.ok(message[0], "function becomes undefined");
|
||||
assert.equal(message[1], "object", "object stays object");
|
||||
assert.ok(message[2], "object's attributes are enumerable");
|
||||
assert.equal(message[3], DEFAULT_CONTENT_URL,
|
||||
"jsonable attributes are accessible");
|
||||
// See bug 714891, Arrays may be broken over compartements:
|
||||
assert.ok(message[4], "Array keeps being an array");
|
||||
assert.equal(message[5], JSON.stringify(array),
|
||||
"Array is correctly serialized");
|
||||
done();
|
||||
});
|
||||
// Add a new url property sa the Class function used by
|
||||
// Worker doesn't set enumerables to true for non-functions
|
||||
worker._url = DEFAULT_CONTENT_URL;
|
||||
|
||||
worker.postMessage({ fun: function () {}, w: worker, array: array });
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:emit-json-values-only"] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
// Validate self.on and self.emit
|
||||
self.port.on("addon-to-content", function (fun, w, obj, array) {
|
||||
self.port.emit("content-to-addon", [
|
||||
fun === null,
|
||||
typeof w,
|
||||
"port" in w,
|
||||
w._url,
|
||||
"fun" in obj,
|
||||
Object.keys(obj.dom).length,
|
||||
Array.isArray(array),
|
||||
JSON.stringify(array)
|
||||
]);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Validate worker.port
|
||||
let array = [1, 2, 3];
|
||||
worker.port.on("content-to-addon", function (result) {
|
||||
assert.ok(result[0], "functions become null");
|
||||
assert.equal(result[1], "object", "objects stay objects");
|
||||
assert.ok(result[2], "object's attributes are enumerable");
|
||||
assert.equal(result[3], DEFAULT_CONTENT_URL,
|
||||
"json attribute is accessible");
|
||||
assert.ok(!result[4], "function as object attribute is removed");
|
||||
assert.equal(result[5], 0, "DOM nodes are converted into empty object");
|
||||
// See bug 714891, Arrays may be broken over compartments:
|
||||
assert.ok(result[6], "Array keeps being an array");
|
||||
assert.equal(result[7], JSON.stringify(array),
|
||||
"Array is correctly serialized");
|
||||
done();
|
||||
});
|
||||
|
||||
let obj = {
|
||||
fun: function () {},
|
||||
dom: browser.contentWindow.document.createElement("div")
|
||||
};
|
||||
// Add a new url property sa the Class function used by
|
||||
// Worker doesn't set enumerables to true for non-functions
|
||||
worker._url = DEFAULT_CONTENT_URL;
|
||||
worker.port.emit("addon-to-content", function () {}, worker, obj, array);
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:content is wrapped"] = WorkerTest(
|
||||
"data:text/html;charset=utf-8,<script>var documentValue=true;</script>",
|
||||
function(assert, browser, done) {
|
||||
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
self.postMessage(!window.documentValue);
|
||||
},
|
||||
contentScriptWhen: "ready",
|
||||
onMessage: function(msg) {
|
||||
assert.ok(msg,
|
||||
"content script has a wrapped access to content document");
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// ContentWorker is not for chrome
|
||||
/*
|
||||
exports["test:chrome is unwrapped"] = function(assert, done) {
|
||||
let window = makeWindow();
|
||||
|
||||
listenOnce(window, "load", function onload() {
|
||||
|
||||
let worker = Worker({
|
||||
window: window,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
self.postMessage(window.documentValue);
|
||||
},
|
||||
contentScriptWhen: "ready",
|
||||
onMessage: function(msg) {
|
||||
assert.ok(msg,
|
||||
"content script has an unwrapped access to chrome document");
|
||||
close(window).then(done);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
exports["test:nothing is leaked to content script"] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
self.postMessage([
|
||||
"ContentWorker" in window,
|
||||
"UNWRAP_ACCESS_KEY" in window,
|
||||
"getProxyForObject" in window
|
||||
]);
|
||||
},
|
||||
contentScriptWhen: "ready",
|
||||
onMessage: function(list) {
|
||||
assert.ok(!list[0], "worker API contrustor isn't leaked");
|
||||
assert.ok(!list[1], "Proxy API stuff isn't leaked 1/2");
|
||||
assert.ok(!list[2], "Proxy API stuff isn't leaked 2/2");
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:ensure console.xxx works in cs"] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
const EXPECTED = ["time", "log", "info", "warn", "error", "error", "timeEnd"];
|
||||
|
||||
let calls = [];
|
||||
let levels = [];
|
||||
|
||||
system.on('console-api-log-event', onMessage);
|
||||
|
||||
function onMessage({ subject }) {
|
||||
calls.push(subject.wrappedJSObject.arguments[0]);
|
||||
levels.push(subject.wrappedJSObject.level);
|
||||
}
|
||||
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
console.time("time");
|
||||
console.log("log");
|
||||
console.info("info");
|
||||
console.warn("warn");
|
||||
console.error("error");
|
||||
console.debug("debug");
|
||||
console.exception("error");
|
||||
console.timeEnd("timeEnd");
|
||||
self.postMessage();
|
||||
},
|
||||
onMessage: function() {
|
||||
system.off('console-api-log-event', onMessage);
|
||||
|
||||
assert.equal(JSON.stringify(calls),
|
||||
JSON.stringify(EXPECTED),
|
||||
"console methods have been called successfully, in expected order");
|
||||
|
||||
assert.equal(JSON.stringify(levels),
|
||||
JSON.stringify(EXPECTED),
|
||||
"console messages have correct log levels, in expected order");
|
||||
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:setTimeout works with string argument"] = WorkerTest(
|
||||
"data:text/html;charset=utf-8,<script>var docVal=5;</script>",
|
||||
function(assert, browser, done) {
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "new " + function ContentScriptScope() {
|
||||
// must use "window.scVal" instead of "var csVal"
|
||||
// since we are inside ContentScriptScope function.
|
||||
// i'm NOT putting code-in-string inside code-in-string </YO DAWG>
|
||||
window.csVal = 13;
|
||||
setTimeout("self.postMessage([" +
|
||||
"csVal, " +
|
||||
"window.docVal, " +
|
||||
"'ContentWorker' in window, " +
|
||||
"'UNWRAP_ACCESS_KEY' in window, " +
|
||||
"'getProxyForObject' in window, " +
|
||||
"])", 1);
|
||||
},
|
||||
contentScriptWhen: "ready",
|
||||
onMessage: function([csVal, docVal, chrome1, chrome2, chrome3]) {
|
||||
// test timer code is executed in the correct context
|
||||
assert.equal(csVal, 13, "accessing content-script values");
|
||||
assert.notEqual(docVal, 5, "can't access document values (directly)");
|
||||
assert.ok(!chrome1 && !chrome2 && !chrome3, "nothing is leaked from chrome");
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:setInterval works with string argument"] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
let count = 0;
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "setInterval('self.postMessage(1)', 50)",
|
||||
contentScriptWhen: "ready",
|
||||
onMessage: function(one) {
|
||||
count++;
|
||||
assert.equal(one, 1, "got " + count + " message(s) from setInterval");
|
||||
if (count >= 3) done();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:setInterval async Errors passed to .onError"] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
let count = 0;
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "setInterval(() => { throw Error('ubik') }, 50)",
|
||||
contentScriptWhen: "ready",
|
||||
onError: function(err) {
|
||||
count++;
|
||||
assert.equal(err.message, "ubik",
|
||||
"error (corectly) propagated " + count + " time(s)");
|
||||
if (count >= 3) done();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:setTimeout throws array, passed to .onError"] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "setTimeout(function() { throw ['array', 42] }, 1)",
|
||||
contentScriptWhen: "ready",
|
||||
onError: function(arr) {
|
||||
assert.ok(isArray(arr),
|
||||
"the type of thrown/propagated object is array");
|
||||
assert.ok(arr.length==2,
|
||||
"the propagated thrown array is the right length");
|
||||
assert.equal(arr[1], 42,
|
||||
"element inside the thrown array correctly propagated");
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:setTimeout string arg with SyntaxError to .onError"] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "setTimeout('syntax 123 error', 1)",
|
||||
contentScriptWhen: "ready",
|
||||
onError: function(err) {
|
||||
assert.equal(err.name, "SyntaxError",
|
||||
"received SyntaxError thrown from bad code in string argument to setTimeout");
|
||||
assert.ok('fileName' in err,
|
||||
"propagated SyntaxError contains a fileName property");
|
||||
assert.ok('stack' in err,
|
||||
"propagated SyntaxError contains a stack property");
|
||||
assert.equal(err.message, "missing ; before statement",
|
||||
"propagated SyntaxError has the correct (helpful) message");
|
||||
assert.equal(err.lineNumber, 1,
|
||||
"propagated SyntaxError was thrown on the right lineNumber");
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:setTimeout can't be cancelled by content"] = WorkerTest(
|
||||
"data:text/html;charset=utf-8,<script>var documentValue=true;</script>",
|
||||
function(assert, browser, done) {
|
||||
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
let id = setTimeout(function () {
|
||||
self.postMessage("timeout");
|
||||
}, 100);
|
||||
unsafeWindow.eval("clearTimeout("+id+");");
|
||||
},
|
||||
contentScriptWhen: "ready",
|
||||
onMessage: function(msg) {
|
||||
assert.ok(msg,
|
||||
"content didn't managed to cancel our setTimeout");
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:clearTimeout"] = WorkerTest(
|
||||
"data:text/html;charset=utf-8,clear timeout",
|
||||
function(assert, browser, done) {
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
let id1 = setTimeout(function() {
|
||||
self.postMessage("failed");
|
||||
}, 10);
|
||||
let id2 = setTimeout(function() {
|
||||
self.postMessage("done");
|
||||
}, 100);
|
||||
clearTimeout(id1);
|
||||
},
|
||||
contentScriptWhen: "ready",
|
||||
onMessage: function(msg) {
|
||||
if (msg === "failed") {
|
||||
assert.fail("failed to cancel timer");
|
||||
} else {
|
||||
assert.pass("timer cancelled");
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:clearInterval"] = WorkerTest(
|
||||
"data:text/html;charset=utf-8,clear timeout",
|
||||
function(assert, browser, done) {
|
||||
let called = 0;
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
let id = setInterval(function() {
|
||||
self.postMessage("intreval")
|
||||
clearInterval(id)
|
||||
setTimeout(function() {
|
||||
self.postMessage("done")
|
||||
}, 100)
|
||||
}, 10);
|
||||
},
|
||||
contentScriptWhen: "ready",
|
||||
onMessage: function(msg) {
|
||||
if (msg === "intreval") {
|
||||
called = called + 1;
|
||||
if (called > 1) assert.fail("failed to cancel timer");
|
||||
} else {
|
||||
assert.pass("interval cancelled");
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
)
|
||||
|
||||
exports["test:setTimeout are unregistered on content unload"] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
|
||||
let originalWindow = browser.contentWindow;
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
document.title = "ok";
|
||||
let i = 0;
|
||||
setInterval(function () {
|
||||
document.title = i++;
|
||||
}, 10);
|
||||
},
|
||||
contentScriptWhen: "ready"
|
||||
});
|
||||
|
||||
// Change location so that content script is destroyed,
|
||||
// and all setTimeout/setInterval should be unregistered.
|
||||
// Wait some cycles in order to execute some intervals.
|
||||
setTimeout(function () {
|
||||
// Bug 689621: Wait for the new document load so that we are sure that
|
||||
// previous document cancelled its intervals
|
||||
let url2 = "data:text/html;charset=utf-8,<title>final</title>";
|
||||
loadAndWait(browser, url2, function onload() {
|
||||
let titleAfterLoad = originalWindow.document.title;
|
||||
// Wait additional cycles to verify that intervals are really cancelled
|
||||
setTimeout(function () {
|
||||
assert.equal(browser.contentDocument.title, "final",
|
||||
"New document has not been modified");
|
||||
assert.equal(originalWindow.document.title, titleAfterLoad,
|
||||
"Nor previous one");
|
||||
|
||||
done();
|
||||
}, 100);
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
);
|
||||
|
||||
exports['test:check window attribute in iframes'] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
|
||||
// Create a first iframe and wait for its loading
|
||||
let contentWin = browser.contentWindow;
|
||||
let contentDoc = contentWin.document;
|
||||
let iframe = contentDoc.createElement("iframe");
|
||||
contentDoc.body.appendChild(iframe);
|
||||
|
||||
listenOnce(iframe, "load", function onload() {
|
||||
|
||||
// Create a second iframe inside the first one and wait for its loading
|
||||
let iframeDoc = iframe.contentWindow.document;
|
||||
let subIframe = iframeDoc.createElement("iframe");
|
||||
iframeDoc.body.appendChild(subIframe);
|
||||
|
||||
listenOnce(subIframe, "load", function onload() {
|
||||
subIframe.removeEventListener("load", onload, true);
|
||||
|
||||
// And finally create a worker against this second iframe
|
||||
let worker = Worker({
|
||||
window: subIframe.contentWindow,
|
||||
contentScript: 'new ' + function WorkerScope() {
|
||||
self.postMessage([
|
||||
window.top !== window,
|
||||
frameElement,
|
||||
window.parent !== window,
|
||||
top.location.href,
|
||||
parent.location.href,
|
||||
]);
|
||||
},
|
||||
onMessage: function(msg) {
|
||||
assert.ok(msg[0], "window.top != window");
|
||||
assert.ok(msg[1], "window.frameElement is defined");
|
||||
assert.ok(msg[2], "window.parent != window");
|
||||
assert.equal(msg[3], contentWin.location.href,
|
||||
"top.location refers to the toplevel content doc");
|
||||
assert.equal(msg[4], iframe.contentWindow.location.href,
|
||||
"parent.location refers to the first iframe doc");
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
subIframe.setAttribute("src", "data:text/html;charset=utf-8,bar");
|
||||
|
||||
});
|
||||
iframe.setAttribute("src", "data:text/html;charset=utf-8,foo");
|
||||
}
|
||||
);
|
||||
|
||||
exports['test:check window attribute in toplevel documents'] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: 'new ' + function WorkerScope() {
|
||||
self.postMessage([
|
||||
window.top === window,
|
||||
frameElement,
|
||||
window.parent === window
|
||||
]);
|
||||
},
|
||||
onMessage: function(msg) {
|
||||
assert.ok(msg[0], "window.top == window");
|
||||
assert.ok(!msg[1], "window.frameElement is null");
|
||||
assert.ok(msg[2], "window.parent == window");
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:check worker API with page history"] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
let url2 = "data:text/html;charset=utf-8,bar";
|
||||
|
||||
loadAndWait(browser, url2, function () {
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
// Just before the content script is disable, we register a timeout
|
||||
// that will be disable until the page gets visible again
|
||||
self.on("pagehide", function () {
|
||||
setTimeout(function () {
|
||||
self.postMessage("timeout restored");
|
||||
}, 0);
|
||||
});
|
||||
},
|
||||
contentScriptWhen: "start"
|
||||
});
|
||||
|
||||
// postMessage works correctly when the page is visible
|
||||
worker.postMessage("ok");
|
||||
|
||||
// We have to wait before going back into history,
|
||||
// otherwise `goBack` won't do anything.
|
||||
setTimeout(function () {
|
||||
browser.goBack();
|
||||
}, 0);
|
||||
|
||||
// Wait for the document to be hidden
|
||||
browser.addEventListener("pagehide", function onpagehide() {
|
||||
browser.removeEventListener("pagehide", onpagehide, false);
|
||||
// Now any event sent to this worker should throw
|
||||
|
||||
setTimeout(_ => {
|
||||
assert.throws(
|
||||
function () { worker.postMessage("data"); },
|
||||
/The page is currently hidden and can no longer be used/,
|
||||
"postMessage should throw when the page is hidden in history"
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
function () { worker.port.emit("event"); },
|
||||
/The page is currently hidden and can no longer be used/,
|
||||
"port.emit should throw when the page is hidden in history"
|
||||
);
|
||||
})
|
||||
|
||||
// Display the page with attached content script back in order to resume
|
||||
// its timeout and receive the expected message.
|
||||
// We have to delay this in order to not break the history.
|
||||
// We delay for a non-zero amount of time in order to ensure that we
|
||||
// do not receive the message immediatly, so that the timeout is
|
||||
// actually disabled
|
||||
setTimeout(function () {
|
||||
worker.on("message", function (data) {
|
||||
assert.ok(data, "timeout restored");
|
||||
done();
|
||||
});
|
||||
browser.goForward();
|
||||
}, 500);
|
||||
|
||||
}, false);
|
||||
});
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
exports['test:conentScriptFile as URL instance'] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
|
||||
let url = new URL(fixtures.url("test-contentScriptFile.js"));
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScriptFile: url,
|
||||
onMessage: function(msg) {
|
||||
assert.equal(msg, "msg from contentScriptFile",
|
||||
"received a wrong message from contentScriptFile");
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:worker events"] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function (assert, browser, done) {
|
||||
let window = browser.contentWindow;
|
||||
let events = [];
|
||||
let worker = Worker({
|
||||
window: window,
|
||||
contentScript: 'new ' + function WorkerScope() {
|
||||
self.postMessage('start');
|
||||
},
|
||||
onAttach: win => {
|
||||
events.push('attach');
|
||||
assert.pass('attach event called when attached');
|
||||
assert.equal(window, win, 'attach event passes in attached window');
|
||||
},
|
||||
onError: err => {
|
||||
assert.equal(err.message, 'Custom',
|
||||
'Error passed into error event');
|
||||
worker.detach();
|
||||
},
|
||||
onMessage: msg => {
|
||||
assert.pass('`onMessage` handles postMessage')
|
||||
throw new Error('Custom');
|
||||
},
|
||||
onDetach: _ => {
|
||||
assert.pass('`onDetach` called when worker detached');
|
||||
done();
|
||||
}
|
||||
});
|
||||
// `attach` event is called synchronously during instantiation,
|
||||
// so we can't listen to that, TODO FIX?
|
||||
// worker.on('attach', obj => console.log('attach', obj));
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:onDetach in contentScript on destroy"] = WorkerTest(
|
||||
"data:text/html;charset=utf-8,foo#detach",
|
||||
function(assert, browser, done) {
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: 'new ' + function WorkerScope() {
|
||||
self.port.on('detach', function(reason) {
|
||||
window.location.hash += '!' + reason;
|
||||
})
|
||||
},
|
||||
});
|
||||
browser.contentWindow.addEventListener('hashchange', _ => {
|
||||
assert.equal(browser.contentWindow.location.hash, '#detach!',
|
||||
"location.href is as expected");
|
||||
done();
|
||||
})
|
||||
worker.destroy();
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:onDetach in contentScript on unload"] = WorkerTest(
|
||||
"data:text/html;charset=utf-8,foo#detach",
|
||||
function(assert, browser, done) {
|
||||
let { loader } = LoaderWithHookedConsole(module);
|
||||
let worker = loader.require("sdk/content/worker-parent").Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: 'new ' + function WorkerScope() {
|
||||
self.port.on('detach', function(reason) {
|
||||
window.location.hash += '!' + reason;
|
||||
})
|
||||
},
|
||||
});
|
||||
browser.contentWindow.addEventListener('hashchange', _ => {
|
||||
assert.equal(browser.contentWindow.location.hash, '#detach!shutdown',
|
||||
"location.href is as expected");
|
||||
done();
|
||||
})
|
||||
loader.unload('shutdown');
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:console method log functions properly"] = WorkerTest(
|
||||
DEFAULT_CONTENT_URL,
|
||||
function(assert, browser, done) {
|
||||
let logs = [];
|
||||
|
||||
system.on('console-api-log-event', onMessage);
|
||||
|
||||
function onMessage({ subject }) {
|
||||
logs.push(clean(subject.wrappedJSObject.arguments[0]));
|
||||
}
|
||||
|
||||
let clean = message =>
|
||||
message.trim().
|
||||
replace(/[\r\n]/g, " ").
|
||||
replace(/ +/g, " ");
|
||||
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
console.log(Function);
|
||||
console.log((foo) => foo * foo);
|
||||
console.log(function foo(bar) { return bar + bar });
|
||||
|
||||
self.postMessage();
|
||||
},
|
||||
onMessage: () => {
|
||||
system.off('console-api-log-event', onMessage);
|
||||
|
||||
assert.deepEqual(logs, [
|
||||
"function Function() { [native code] }",
|
||||
"(foo) => foo * foo",
|
||||
"function foo(bar) { \"use strict\"; return bar + bar }"
|
||||
]);
|
||||
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
exports["test:global postMessage"] = WorkerTest(
|
||||
WINDOW_SCRIPT_URL,
|
||||
function(assert, browser, done) {
|
||||
let contentScript = "window.addEventListener('message', function (e) {" +
|
||||
" if (e.data === 'from -> window')" +
|
||||
" self.port.emit('response', e.data, e.origin);" +
|
||||
"});" +
|
||||
"postMessage('from -> content-script', '*');";
|
||||
let { loader } = LoaderWithHookedConsole(module);
|
||||
let worker = loader.require("sdk/content/worker-parent").Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScriptWhen: "ready",
|
||||
contentScript: contentScript
|
||||
});
|
||||
|
||||
worker.port.on("response", (data, origin) => {
|
||||
assert.equal(data, "from -> window", "Communication from content-script to window completed");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
exports["test:destroy unbinds listeners from port"] = WorkerTest(
|
||||
"data:text/html;charset=utf-8,portdestroyer",
|
||||
function(assert, browser, done) {
|
||||
let destroyed = false;
|
||||
let worker = Worker({
|
||||
window: browser.contentWindow,
|
||||
contentScript: "new " + function WorkerScope() {
|
||||
self.port.emit("destroy");
|
||||
setInterval(self.port.emit, 10, "ping");
|
||||
},
|
||||
onDestroy: done
|
||||
});
|
||||
worker.port.on("ping", () => {
|
||||
if (destroyed) {
|
||||
assert.fail("Should not call events on port after destroy.");
|
||||
}
|
||||
});
|
||||
worker.port.on("destroy", () => {
|
||||
destroyed = true;
|
||||
worker.destroy();
|
||||
assert.pass("Worker destroyed, waiting for no future listeners handling events.");
|
||||
setTimeout(done, 500);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
require("test").run(exports);
|
@ -1,7 +1,6 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
const { on, once, off, emit, count } = require('sdk/event/core');
|
||||
@ -199,7 +198,7 @@ exports['test unhandled errors'] = function(assert) {
|
||||
let exceptions = [];
|
||||
let { loader, messages } = LoaderWithHookedConsole(module);
|
||||
|
||||
let { emit, on } = loader.require('sdk/event/core');
|
||||
let { emit } = loader.require('sdk/event/core');
|
||||
let target = {};
|
||||
let boom = Error('Boom!');
|
||||
|
||||
@ -210,6 +209,24 @@ exports['test unhandled errors'] = function(assert) {
|
||||
'unhandled exception is logged');
|
||||
};
|
||||
|
||||
exports['test piped errors'] = function(assert) {
|
||||
let exceptions = [];
|
||||
let { loader, messages } = LoaderWithHookedConsole(module);
|
||||
|
||||
let { emit } = loader.require('sdk/event/core');
|
||||
let { pipe } = loader.require('sdk/event/utils');
|
||||
let target = {};
|
||||
let second = {};
|
||||
|
||||
pipe(target, second);
|
||||
emit(target, 'error', 'piped!');
|
||||
|
||||
assert.equal(messages.length, 1, 'error logged only once, ' +
|
||||
'considered "handled" on `target` by the catch-all pipe');
|
||||
assert.equal(messages[0].type, 'exception', 'The console message is exception');
|
||||
assert.ok(~String(messages[0].msg).indexOf('piped!'),
|
||||
'unhandled (piped) exception is logged on `second` target');
|
||||
};
|
||||
|
||||
exports['test count'] = function(assert) {
|
||||
let target = {};
|
||||
@ -242,4 +259,4 @@ exports['test listen to all events'] = function(assert) {
|
||||
'wildcard listener called for unbound event name');
|
||||
};
|
||||
|
||||
require('test').run(exports);
|
||||
require('sdk/test').run(exports);
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
const { emit } = require('sdk/event/core');
|
||||
@ -115,7 +114,6 @@ exports['test remove a listener'] = function(assert) {
|
||||
})
|
||||
});
|
||||
|
||||
target.off('message'); // must do nothing.
|
||||
emit(target, 'message');
|
||||
assert.deepEqual([ 1 ], actual, 'first listener called');
|
||||
emit(target, 'message');
|
||||
@ -124,6 +122,26 @@ exports['test remove a listener'] = function(assert) {
|
||||
assert.deepEqual([ 1, 1, 2, 2, 2 ], actual, 'first listener removed');
|
||||
};
|
||||
|
||||
exports['test .off() removes all listeners'] = function(assert) {
|
||||
let target = EventTarget();
|
||||
let actual = [];
|
||||
target.on('message', function listener() {
|
||||
actual.push(1);
|
||||
target.on('message', function() {
|
||||
target.removeListener('message', listener);
|
||||
actual.push(2);
|
||||
})
|
||||
});
|
||||
|
||||
emit(target, 'message');
|
||||
assert.deepEqual([ 1 ], actual, 'first listener called');
|
||||
emit(target, 'message');
|
||||
assert.deepEqual([ 1, 1, 2 ], actual, 'second listener called');
|
||||
target.off();
|
||||
emit(target, 'message');
|
||||
assert.deepEqual([ 1, 1, 2 ], actual, 'target.off() removed all listeners');
|
||||
};
|
||||
|
||||
exports['test error handling'] = function(assert) {
|
||||
let target = EventTarget();
|
||||
let error = Error('boom!');
|
||||
@ -201,5 +219,4 @@ exports['test target is chainable'] = function (assert, done) {
|
||||
emit(emitter, 'data', 'message');
|
||||
};
|
||||
|
||||
require('test').run(exports);
|
||||
|
||||
require('sdk/test').run(exports);
|
||||
|
@ -4,17 +4,13 @@
|
||||
"use strict";
|
||||
|
||||
const { PageMod } = require("sdk/page-mod");
|
||||
const { testPageMod, handleReadyState } = require("./pagemod-test-helpers");
|
||||
const { testPageMod, handleReadyState, contentScriptWhenServer } = require("./pagemod-test-helpers");
|
||||
const { Loader } = require('sdk/test/loader');
|
||||
const tabs = require("sdk/tabs");
|
||||
const { setTimeout } = require("sdk/timers");
|
||||
const { Cc, Ci, Cu } = require("chrome");
|
||||
const {
|
||||
open,
|
||||
getFrames,
|
||||
getMostRecentBrowserWindow,
|
||||
getInnerId
|
||||
} = require('sdk/window/utils');
|
||||
const system = require("sdk/system/events");
|
||||
const { open, getFrames, getMostRecentBrowserWindow, getInnerId } = require('sdk/window/utils');
|
||||
const { getTabContentWindow, getActiveTab, setTabURL, openTab, closeTab } = require('sdk/tabs/utils');
|
||||
const xulApp = require("sdk/system/xul-app");
|
||||
const { isPrivateBrowsingSupported } = require('sdk/self');
|
||||
@ -24,7 +20,6 @@ const { isTabPBSupported, isWindowPBSupported, isGlobalPBSupported } = require('
|
||||
const promise = require("sdk/core/promise");
|
||||
const { pb } = require('./private-browsing/helper');
|
||||
const { URL } = require("sdk/url");
|
||||
const { LoaderWithHookedConsole } = require('sdk/test/loader');
|
||||
|
||||
const { waitUntil } = require("sdk/test/utils");
|
||||
const data = require("./fixtures");
|
||||
@ -64,7 +59,8 @@ exports.testPageMod1 = function(assert, done) {
|
||||
"PageMod.onReady test"
|
||||
);
|
||||
done();
|
||||
}
|
||||
},
|
||||
100
|
||||
);
|
||||
};
|
||||
|
||||
@ -96,7 +92,9 @@ exports.testPageMod2 = function(assert, done) {
|
||||
assert.equal("AUQLUE" in win, false,
|
||||
"PageMod test #2: scripts get a wrapped window");
|
||||
done();
|
||||
});
|
||||
},
|
||||
100
|
||||
);
|
||||
};
|
||||
|
||||
exports.testPageModIncludes = function(assert, done) {
|
||||
@ -622,15 +620,14 @@ exports.testContentScriptWhenDefault = function(assert) {
|
||||
// test timing for all 3 contentScriptWhen options (start, ready, end)
|
||||
// for new pages, or tabs opened after PageMod is created
|
||||
exports.testContentScriptWhenForNewTabs = function(assert, done) {
|
||||
const url = "data:text/html;charset=utf-8,testContentScriptWhenForNewTabs";
|
||||
|
||||
let srv = contentScriptWhenServer();
|
||||
let url = srv.URL + '?ForNewTabs';
|
||||
let count = 0;
|
||||
|
||||
handleReadyState(url, 'start', {
|
||||
onLoading: (tab) => {
|
||||
assert.pass("PageMod is attached while document is loading");
|
||||
if (++count === 3)
|
||||
tab.close(done);
|
||||
checkDone(++count, tab, srv, done);
|
||||
},
|
||||
onInteractive: () => assert.fail("onInteractive should not be called with 'start'."),
|
||||
onComplete: () => assert.fail("onComplete should not be called with 'start'."),
|
||||
@ -639,8 +636,7 @@ exports.testContentScriptWhenForNewTabs = function(assert, done) {
|
||||
handleReadyState(url, 'ready', {
|
||||
onInteractive: (tab) => {
|
||||
assert.pass("PageMod is attached while document is interactive");
|
||||
if (++count === 3)
|
||||
tab.close(done);
|
||||
checkDone(++count, tab, srv, done);
|
||||
},
|
||||
onLoading: () => assert.fail("onLoading should not be called with 'ready'."),
|
||||
onComplete: () => assert.fail("onComplete should not be called with 'ready'."),
|
||||
@ -649,8 +645,7 @@ exports.testContentScriptWhenForNewTabs = function(assert, done) {
|
||||
handleReadyState(url, 'end', {
|
||||
onComplete: (tab) => {
|
||||
assert.pass("PageMod is attached when document is complete");
|
||||
if (++count === 3)
|
||||
tab.close(done);
|
||||
checkDone(++count, tab, srv, done);
|
||||
},
|
||||
onLoading: () => assert.fail("onLoading should not be called with 'end'."),
|
||||
onInteractive: () => assert.fail("onInteractive should not be called with 'end'."),
|
||||
@ -662,18 +657,18 @@ exports.testContentScriptWhenForNewTabs = function(assert, done) {
|
||||
// test timing for all 3 contentScriptWhen options (start, ready, end)
|
||||
// for PageMods created right as the tab is created (in tab.onOpen)
|
||||
exports.testContentScriptWhenOnTabOpen = function(assert, done) {
|
||||
const url = "data:text/html;charset=utf-8,testContentScriptWhenOnTabOpen";
|
||||
let srv = contentScriptWhenServer();
|
||||
let url = srv.URL + '?OnTabOpen';
|
||||
let count = 0;
|
||||
|
||||
tabs.open({
|
||||
url: url,
|
||||
onOpen: function(tab) {
|
||||
let count = 0;
|
||||
|
||||
handleReadyState(url, 'start', {
|
||||
onLoading: () => {
|
||||
assert.pass("PageMod is attached while document is loading");
|
||||
if (++count === 3)
|
||||
tab.close(done);
|
||||
checkDone(++count, tab, srv, done);
|
||||
},
|
||||
onInteractive: () => assert.fail("onInteractive should not be called with 'start'."),
|
||||
onComplete: () => assert.fail("onComplete should not be called with 'start'."),
|
||||
@ -682,8 +677,7 @@ exports.testContentScriptWhenOnTabOpen = function(assert, done) {
|
||||
handleReadyState(url, 'ready', {
|
||||
onInteractive: () => {
|
||||
assert.pass("PageMod is attached while document is interactive");
|
||||
if (++count === 3)
|
||||
tab.close(done);
|
||||
checkDone(++count, tab, srv, done);
|
||||
},
|
||||
onLoading: () => assert.fail("onLoading should not be called with 'ready'."),
|
||||
onComplete: () => assert.fail("onComplete should not be called with 'ready'."),
|
||||
@ -692,8 +686,7 @@ exports.testContentScriptWhenOnTabOpen = function(assert, done) {
|
||||
handleReadyState(url, 'end', {
|
||||
onComplete: () => {
|
||||
assert.pass("PageMod is attached when document is complete");
|
||||
if (++count === 3)
|
||||
tab.close(done);
|
||||
checkDone(++count, tab, srv, done);
|
||||
},
|
||||
onLoading: () => assert.fail("onLoading should not be called with 'end'."),
|
||||
onInteractive: () => assert.fail("onInteractive should not be called with 'end'."),
|
||||
@ -706,20 +699,18 @@ exports.testContentScriptWhenOnTabOpen = function(assert, done) {
|
||||
// test timing for all 3 contentScriptWhen options (start, ready, end)
|
||||
// for PageMods created while the tab is interactive (in tab.onReady)
|
||||
exports.testContentScriptWhenOnTabReady = function(assert, done) {
|
||||
// need a bit bigger document to get the right timing of events with e10s
|
||||
let iframeURL = 'data:text/html;charset=utf-8,testContentScriptWhenOnTabReady';
|
||||
let iframe = '<iframe src="' + iframeURL + '" />';
|
||||
let url = 'data:text/html;charset=utf-8,' + encodeURIComponent(iframe);
|
||||
let srv = contentScriptWhenServer();
|
||||
let url = srv.URL + '?OnTabReady';
|
||||
let count = 0;
|
||||
|
||||
tabs.open({
|
||||
url: url,
|
||||
onReady: function(tab) {
|
||||
let count = 0;
|
||||
|
||||
handleReadyState(url, 'start', {
|
||||
onInteractive: () => {
|
||||
assert.pass("PageMod is attached while document is interactive");
|
||||
if (++count === 3)
|
||||
tab.close(done);
|
||||
checkDone(++count, tab, srv, done);
|
||||
},
|
||||
onLoading: () => assert.fail("onLoading should not be called with 'start'."),
|
||||
onComplete: () => assert.fail("onComplete should not be called with 'start'."),
|
||||
@ -728,8 +719,7 @@ exports.testContentScriptWhenOnTabReady = function(assert, done) {
|
||||
handleReadyState(url, 'ready', {
|
||||
onInteractive: () => {
|
||||
assert.pass("PageMod is attached while document is interactive");
|
||||
if (++count === 3)
|
||||
tab.close(done);
|
||||
checkDone(++count, tab, srv, done);
|
||||
},
|
||||
onLoading: () => assert.fail("onLoading should not be called with 'ready'."),
|
||||
onComplete: () => assert.fail("onComplete should not be called with 'ready'."),
|
||||
@ -738,8 +728,7 @@ exports.testContentScriptWhenOnTabReady = function(assert, done) {
|
||||
handleReadyState(url, 'end', {
|
||||
onComplete: () => {
|
||||
assert.pass("PageMod is attached when document is complete");
|
||||
if (++count === 3)
|
||||
tab.close(done);
|
||||
checkDone(++count, tab, srv, done);
|
||||
},
|
||||
onLoading: () => assert.fail("onLoading should not be called with 'end'."),
|
||||
onInteractive: () => assert.fail("onInteractive should not be called with 'end'."),
|
||||
@ -752,18 +741,18 @@ exports.testContentScriptWhenOnTabReady = function(assert, done) {
|
||||
// test timing for all 3 contentScriptWhen options (start, ready, end)
|
||||
// for PageMods created after a tab has completed loading (in tab.onLoad)
|
||||
exports.testContentScriptWhenOnTabLoad = function(assert, done) {
|
||||
const url = "data:text/html;charset=utf-8,testContentScriptWhenOnTabLoad";
|
||||
let srv = contentScriptWhenServer();
|
||||
let url = srv.URL + '?OnTabLoad';
|
||||
let count = 0;
|
||||
|
||||
tabs.open({
|
||||
url: url,
|
||||
onLoad: function(tab) {
|
||||
let count = 0;
|
||||
|
||||
handleReadyState(url, 'start', {
|
||||
onComplete: () => {
|
||||
assert.pass("PageMod is attached when document is complete");
|
||||
if (++count === 3)
|
||||
tab.close(done);
|
||||
checkDone(++count, tab, srv, done);
|
||||
},
|
||||
onLoading: () => assert.fail("onLoading should not be called with 'start'."),
|
||||
onInteractive: () => assert.fail("onInteractive should not be called with 'start'."),
|
||||
@ -772,8 +761,7 @@ exports.testContentScriptWhenOnTabLoad = function(assert, done) {
|
||||
handleReadyState(url, 'ready', {
|
||||
onComplete: () => {
|
||||
assert.pass("PageMod is attached when document is complete");
|
||||
if (++count === 3)
|
||||
tab.close(done);
|
||||
checkDone(++count, tab, srv, done);
|
||||
},
|
||||
onLoading: () => assert.fail("onLoading should not be called with 'ready'."),
|
||||
onInteractive: () => assert.fail("onInteractive should not be called with 'ready'."),
|
||||
@ -782,8 +770,7 @@ exports.testContentScriptWhenOnTabLoad = function(assert, done) {
|
||||
handleReadyState(url, 'end', {
|
||||
onComplete: () => {
|
||||
assert.pass("PageMod is attached when document is complete");
|
||||
if (++count === 3)
|
||||
tab.close(done);
|
||||
checkDone(++count, tab, srv, done);
|
||||
},
|
||||
onLoading: () => assert.fail("onLoading should not be called with 'end'."),
|
||||
onInteractive: () => assert.fail("onInteractive should not be called with 'end'."),
|
||||
@ -793,6 +780,11 @@ exports.testContentScriptWhenOnTabLoad = function(assert, done) {
|
||||
});
|
||||
}
|
||||
|
||||
function checkDone(count, tab, srv, done) {
|
||||
if (count === 3)
|
||||
tab.close(_ => srv.stop(done));
|
||||
}
|
||||
|
||||
exports.testTabWorkerOnMessage = function(assert, done) {
|
||||
let { browserWindows } = require("sdk/windows");
|
||||
let tabs = require("sdk/tabs");
|
||||
@ -1063,7 +1055,7 @@ exports.testPageModCss = function(assert, done) {
|
||||
|
||||
assert.equal(div.clientHeight, 100,
|
||||
"PageMod contentStyle worked");
|
||||
|
||||
|
||||
assert.equal(div.offsetHeight, 120,
|
||||
"PageMod contentStyleFile worked");
|
||||
|
||||
@ -1144,6 +1136,7 @@ exports.testPageModCssDestroy = function(assert, done) {
|
||||
);
|
||||
|
||||
pageMod.destroy();
|
||||
|
||||
assert.equal(
|
||||
style.width,
|
||||
"200px",
|
||||
@ -1151,7 +1144,6 @@ exports.testPageModCssDestroy = function(assert, done) {
|
||||
);
|
||||
|
||||
done();
|
||||
|
||||
}
|
||||
);
|
||||
};
|
||||
@ -1194,7 +1186,6 @@ exports.testPageModCssAutomaticDestroy = function(assert, done) {
|
||||
};
|
||||
|
||||
exports.testPageModContentScriptFile = function(assert, done) {
|
||||
|
||||
testPageMod(assert, done, "about:license", [{
|
||||
include: "about:*",
|
||||
contentScriptWhen: "start",
|
||||
@ -1377,7 +1368,7 @@ exports.testIFramePostMessage = function(assert, done) {
|
||||
exports.testEvents = function(assert, done) {
|
||||
let content = "<script>\n new " + function DocumentScope() {
|
||||
window.addEventListener("ContentScriptEvent", function () {
|
||||
window.receivedEvent = true;
|
||||
window.document.body.setAttribute("receivedEvent", true);
|
||||
}, false);
|
||||
} + "\n</script>";
|
||||
let url = "data:text/html;charset=utf-8," + encodeURIComponent(content);
|
||||
@ -1391,11 +1382,12 @@ exports.testEvents = function(assert, done) {
|
||||
}],
|
||||
function(win, done) {
|
||||
assert.ok(
|
||||
win.receivedEvent,
|
||||
win.document.body.getAttribute("receivedEvent"),
|
||||
"Content script sent an event and document received it"
|
||||
);
|
||||
done();
|
||||
}
|
||||
},
|
||||
100
|
||||
);
|
||||
};
|
||||
|
||||
@ -1648,14 +1640,16 @@ exports.testDetachOnUnload = function(assert, done) {
|
||||
exports.testConsole = function(assert, done) {
|
||||
let innerID;
|
||||
const TEST_URL = 'data:text/html;charset=utf-8,console';
|
||||
const { loader } = LoaderWithHookedConsole(module, onMessage);
|
||||
const { PageMod } = loader.require('sdk/page-mod');
|
||||
const system = require("sdk/system/events");
|
||||
|
||||
let seenMessage = false;
|
||||
function onMessage(type, msg, msgID) {
|
||||
|
||||
system.on('console-api-log-event', onMessage);
|
||||
|
||||
function onMessage({ subject: { wrappedJSObject: msg }}) {
|
||||
if (msg.arguments[0] !== "Hello from the page mod")
|
||||
return;
|
||||
seenMessage = true;
|
||||
innerID = msgID;
|
||||
innerID = msg.innerID;
|
||||
}
|
||||
|
||||
let mod = PageMod({
|
||||
@ -1671,6 +1665,9 @@ exports.testConsole = function(assert, done) {
|
||||
let id = getInnerId(window);
|
||||
assert.ok(seenMessage, "Should have seen the console message");
|
||||
assert.equal(innerID, id, "Should have seen the right inner ID");
|
||||
|
||||
system.off('console-api-log-event', onMessage);
|
||||
mod.destroy();
|
||||
closeTab(tab);
|
||||
done();
|
||||
});
|
||||
@ -1704,7 +1701,8 @@ exports.testSyntaxErrorInContentScript = function(assert, done) {
|
||||
if (hitError)
|
||||
assert.equal(hitError.name, "SyntaxError", "The error thrown should be a SyntaxError");
|
||||
done();
|
||||
}
|
||||
},
|
||||
300
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -675,7 +675,7 @@ exports["test console.log in Panel"] = function(assert, done) {
|
||||
}
|
||||
};
|
||||
|
||||
if (isWindowPBSupported) {
|
||||
/*if (isWindowPBSupported) {
|
||||
exports.testPanelDoesNotShowInPrivateWindowNoAnchor = function(assert, done) {
|
||||
let { loader } = LoaderWithHookedConsole(module, ignorePassingDOMNodeWarning);
|
||||
let { Panel } = loader.require("sdk/panel");
|
||||
@ -783,7 +783,7 @@ if (isWindowPBSupported) {
|
||||
then(testShowPanel.bind(null, assert, panel)).
|
||||
then(done, assert.fail.bind(assert));
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
function testShowPanel(assert, panel) {
|
||||
let { promise, resolve } = defer();
|
||||
|
@ -221,7 +221,7 @@ exports['test promised with promise args'] = function(assert, done) {
|
||||
deferred.resolve(24);
|
||||
};
|
||||
|
||||
exports['test promised error handleing'] = function(assert, done) {
|
||||
exports['test promised error handling'] = function(assert, done) {
|
||||
let expected = Error('boom');
|
||||
let f = promised(function() {
|
||||
throw expected;
|
||||
@ -293,6 +293,18 @@ exports['test promised are not greedy'] = function(assert, done) {
|
||||
assert.equal(runs, 0, 'promised does not run task right away');
|
||||
};
|
||||
|
||||
exports['test promised does not flatten arrays'] = function(assert, done) {
|
||||
let p = promised(function(empty, one, two, nested) {
|
||||
assert.equal(empty.length, 0, "first argument is empty");
|
||||
assert.deepEqual(one, ['one'], "second has one");
|
||||
assert.deepEqual(two, ['two', 'more'], "third has two more");
|
||||
assert.deepEqual(nested, [[]], "forth is properly nested");
|
||||
done();
|
||||
});
|
||||
|
||||
p([], ['one'], ['two', 'more'], [[]]);
|
||||
};
|
||||
|
||||
exports['test arrays should not flatten'] = function(assert, done) {
|
||||
let a = defer();
|
||||
let b = defer();
|
||||
|
@ -5,7 +5,9 @@
|
||||
|
||||
const xulApp = require("sdk/system/xul-app");
|
||||
const self = require("sdk/self");
|
||||
const { Loader, main, unload } = require("toolkit/loader");
|
||||
const { Loader, main, unload, override } = require("toolkit/loader");
|
||||
const { PlainTextConsole } = require("sdk/console/plain-text");
|
||||
const { Loader: CustomLoader } = require("sdk/test/loader");
|
||||
const loaderOptions = require("@loader/options");
|
||||
|
||||
exports.testSelf = function(assert) {
|
||||
@ -52,4 +54,26 @@ exports.testSelfHandlesLackingLoaderOptions = function (assert) {
|
||||
unload(loader);
|
||||
};
|
||||
|
||||
exports.testPreferencesBranch = function (assert) {
|
||||
let options = override(loaderOptions, {
|
||||
preferencesBranch: 'human-readable',
|
||||
});
|
||||
let loader = CustomLoader(module, { }, options);
|
||||
let { preferencesBranch } = loader.require('sdk/self');
|
||||
assert.equal(preferencesBranch, 'human-readable',
|
||||
'preferencesBranch is human-readable');
|
||||
}
|
||||
|
||||
exports.testInvalidPreferencesBranch = function (assert) {
|
||||
let console = new PlainTextConsole(_ => void _);
|
||||
let options = override(loaderOptions, {
|
||||
preferencesBranch: 'invalid^branch*name',
|
||||
id: 'simple@jetpack'
|
||||
});
|
||||
let loader = CustomLoader(module, { console }, options);
|
||||
let { preferencesBranch } = loader.require('sdk/self');
|
||||
assert.equal(preferencesBranch, 'simple@jetpack',
|
||||
'invalid preferencesBranch value ignored');
|
||||
}
|
||||
|
||||
require("sdk/test").run(exports);
|
||||
|
35
addon-sdk/source/test/test-shared-require.js
Normal file
35
addon-sdk/source/test/test-shared-require.js
Normal file
@ -0,0 +1,35 @@
|
||||
"use strict";
|
||||
|
||||
const { Cu } = require("chrome");
|
||||
|
||||
const requireURI = require.resolve("toolkit/require.js");
|
||||
|
||||
|
||||
|
||||
const jsm = Cu.import(requireURI, {});
|
||||
|
||||
exports.testRequire = assert => {
|
||||
assert.equal(typeof(jsm.require), "function",
|
||||
"require is a function");
|
||||
assert.equal(typeof(jsm.require.resolve), "function",
|
||||
"require.resolve is a function");
|
||||
|
||||
assert.equal(typeof(jsm.require("method/core")), "function",
|
||||
"can import modules that aren't under sdk");
|
||||
|
||||
assert.equal(typeof(jsm.require("sdk/base64").encode), "function",
|
||||
"can import module from sdk");
|
||||
};
|
||||
|
||||
const required = require("toolkit/require")
|
||||
|
||||
exports.testSameRequire = (assert) => {
|
||||
assert.equal(jsm.require("method/core"),
|
||||
required.require("method/core"),
|
||||
"jsm and module return same instance");
|
||||
|
||||
assert.equal(jsm.require, required.require,
|
||||
"require function is same in both contexts");
|
||||
};
|
||||
|
||||
require("test").run(exports)
|
@ -50,13 +50,10 @@ exports.testTabRelativePath = function(assert, done) {
|
||||
const { merge } = require("sdk/util/object");
|
||||
const self = require("sdk/self");
|
||||
|
||||
let loader = Loader(module, null, null, {
|
||||
modules: {
|
||||
"sdk/self": merge({}, self, {
|
||||
data: merge({}, self.data, fixtures)
|
||||
})
|
||||
}
|
||||
});
|
||||
const options = merge({}, require('@loader/options'),
|
||||
{ prefixURI: require('./fixtures').url() });
|
||||
|
||||
let loader = Loader(module, null, options);
|
||||
|
||||
let tabs = loader.require("sdk/tabs");
|
||||
|
||||
@ -73,6 +70,7 @@ exports.testTabRelativePath = function(assert, done) {
|
||||
"Tab attach a contentScriptFile with relative path worked");
|
||||
|
||||
tab.close(done);
|
||||
loader.unload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -6,9 +6,13 @@
|
||||
const ERR_CONFLICT = 'Remaining conflicting property: ',
|
||||
ERR_REQUIRED = 'Missing required property: ';
|
||||
|
||||
const getOwnIdentifiers = x => [...Object.getOwnPropertyNames(x),
|
||||
...Object.getOwnPropertySymbols(x)];
|
||||
|
||||
|
||||
function assertSametrait(assert, trait1, trait2) {
|
||||
let names1 = Object.getOwnPropertyNames(trait1),
|
||||
names2 = Object.getOwnPropertyNames(trait2);
|
||||
let names1 = getOwnIdentifiers(trait1),
|
||||
names2 = getOwnIdentifiers(trait2);
|
||||
|
||||
assert.equal(
|
||||
names1.length,
|
||||
@ -738,7 +742,7 @@ exports['test:create simple'] = function(assert) {
|
||||
assert.equal(1, o1.b(), 'o1.b()');
|
||||
assert.equal(
|
||||
2,
|
||||
Object.getOwnPropertyNames(o1).length,
|
||||
getOwnIdentifiers(o1).length,
|
||||
'Object.keys(o1).length === 2'
|
||||
);
|
||||
};
|
||||
|
@ -16,11 +16,17 @@ const { open, focus, close } = require('sdk/window/helpers');
|
||||
const { setTimeout } = require('sdk/timers');
|
||||
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||
const { partial } = require('sdk/lang/functional');
|
||||
const { wait } = require('./event/helpers');
|
||||
const { gc } = require('sdk/test/memory');
|
||||
|
||||
const openBrowserWindow = partial(open, null, {features: {toolbar: true}});
|
||||
const openPrivateBrowserWindow = partial(open, null,
|
||||
{features: {toolbar: true, private: true}});
|
||||
|
||||
const badgeNodeFor = (node) =>
|
||||
node.ownerDocument.getAnonymousElementByAttribute(node,
|
||||
'class', 'toolbarbutton-badge');
|
||||
|
||||
function getWidget(buttonId, window = getMostRecentBrowserWindow()) {
|
||||
const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
|
||||
const { AREA_NAVBAR } = CustomizableUI;
|
||||
@ -107,6 +113,16 @@ exports['test basic constructor validation'] = function(assert) {
|
||||
/^The option "icon"/,
|
||||
'throws on no valid icon given');
|
||||
|
||||
assert.throws(
|
||||
() => ActionButton({ id: 'my-button', label: 'button', icon: './i.png', badge: true}),
|
||||
/^The option "badge"/,
|
||||
'throws on no valid badge given');
|
||||
|
||||
assert.throws(
|
||||
() => ActionButton({ id: 'my-button', label: 'button', icon: './i.png', badgeColor: true}),
|
||||
/^The option "badgeColor"/,
|
||||
'throws on no valid badge given');
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
@ -137,6 +153,9 @@ exports['test button added'] = function(assert) {
|
||||
assert.equal(data.url(button.icon.substr(2)), node.getAttribute('image'),
|
||||
'icon is set');
|
||||
|
||||
assert.equal("", node.getAttribute('badge'),
|
||||
'badge attribute is empty');
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
@ -242,7 +261,7 @@ exports['test button global state updated'] = function(assert) {
|
||||
let button = ActionButton({
|
||||
id: 'my-button-4',
|
||||
label: 'my button',
|
||||
icon: './icon.png'
|
||||
icon: './icon.png',
|
||||
});
|
||||
|
||||
// Tried to use `getWidgetIdsInArea` but seems undefined, not sure if it
|
||||
@ -283,6 +302,19 @@ exports['test button global state updated'] = function(assert) {
|
||||
assert.equal(node.getAttribute('disabled'), 'true',
|
||||
'node disabled is updated');
|
||||
|
||||
button.badge = '+2';
|
||||
button.badgeColor = 'blue';
|
||||
|
||||
assert.equal(button.badge, '+2',
|
||||
'badge is updated');
|
||||
assert.equal(node.getAttribute('bagde'), '',
|
||||
'node badge is updated');
|
||||
|
||||
assert.equal(button.badgeColor, 'blue',
|
||||
'badgeColor is updated');
|
||||
assert.equal(badgeNodeFor(node).style.backgroundColor, 'blue',
|
||||
'badge color is updated');
|
||||
|
||||
// TODO: test validation on update
|
||||
|
||||
loader.unload();
|
||||
@ -312,7 +344,9 @@ exports['test button global state set and get with state method'] = function(ass
|
||||
button.state(button, {
|
||||
label: 'New label',
|
||||
icon: './new-icon.png',
|
||||
disabled: true
|
||||
disabled: true,
|
||||
badge: '+2',
|
||||
badgeColor: 'blue'
|
||||
});
|
||||
|
||||
assert.equal(button.label, 'New label',
|
||||
@ -321,11 +355,15 @@ exports['test button global state set and get with state method'] = function(ass
|
||||
'icon is updated');
|
||||
assert.equal(button.disabled, true,
|
||||
'disabled is updated');
|
||||
assert.equal(button.badge, '+2',
|
||||
'badge is updated');
|
||||
assert.equal(button.badgeColor, 'blue',
|
||||
'badgeColor is updated');
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test button global state updated on multiple windows'] = function(assert, done) {
|
||||
exports['test button global state updated on multiple windows'] = function*(assert) {
|
||||
let loader = Loader(module);
|
||||
let { ActionButton } = loader.require('sdk/ui');
|
||||
|
||||
@ -337,38 +375,48 @@ exports['test button global state updated on multiple windows'] = function(asser
|
||||
|
||||
let nodes = [getWidget(button.id).node];
|
||||
|
||||
openBrowserWindow().then(window => {
|
||||
nodes.push(getWidget(button.id, window).node);
|
||||
let window = yield openBrowserWindow();
|
||||
|
||||
button.label = 'New label';
|
||||
button.icon = './new-icon.png';
|
||||
button.disabled = true;
|
||||
nodes.push(getWidget(button.id, window).node);
|
||||
|
||||
for (let node of nodes) {
|
||||
assert.equal(node.getAttribute('label'), 'New label',
|
||||
'node label is updated');
|
||||
assert.equal(node.getAttribute('tooltiptext'), 'New label',
|
||||
'node tooltip is updated');
|
||||
button.label = 'New label';
|
||||
button.icon = './new-icon.png';
|
||||
button.disabled = true;
|
||||
button.badge = '+10';
|
||||
button.badgeColor = 'green';
|
||||
|
||||
assert.equal(button.icon, './new-icon.png',
|
||||
'icon is updated');
|
||||
assert.equal(node.getAttribute('image'), data.url('new-icon.png'),
|
||||
'node image is updated');
|
||||
for (let node of nodes) {
|
||||
assert.equal(node.getAttribute('label'), 'New label',
|
||||
'node label is updated');
|
||||
assert.equal(node.getAttribute('tooltiptext'), 'New label',
|
||||
'node tooltip is updated');
|
||||
|
||||
assert.equal(button.disabled, true,
|
||||
'disabled is updated');
|
||||
assert.equal(node.getAttribute('disabled'), 'true',
|
||||
'node disabled is updated');
|
||||
};
|
||||
assert.equal(button.icon, './new-icon.png',
|
||||
'icon is updated');
|
||||
assert.equal(node.getAttribute('image'), data.url('new-icon.png'),
|
||||
'node image is updated');
|
||||
|
||||
return window;
|
||||
}).
|
||||
then(close).
|
||||
then(loader.unload).
|
||||
then(done, assert.fail);
|
||||
assert.equal(button.disabled, true,
|
||||
'disabled is updated');
|
||||
assert.equal(node.getAttribute('disabled'), 'true',
|
||||
'node disabled is updated');
|
||||
|
||||
assert.equal(button.badge, '+10',
|
||||
'badge is updated')
|
||||
assert.equal(button.badgeColor, 'green',
|
||||
'badgeColor is updated')
|
||||
assert.equal(node.getAttribute('badge'), '+10',
|
||||
'node badge is updated')
|
||||
assert.equal(badgeNodeFor(node).style.backgroundColor, 'green',
|
||||
'node badge color is updated')
|
||||
};
|
||||
|
||||
yield close(window);
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
exports['test button window state'] = function(assert, done) {
|
||||
exports['test button window state'] = function*(assert) {
|
||||
let loader = Loader(module);
|
||||
let { ActionButton } = loader.require('sdk/ui');
|
||||
let { browserWindows } = loader.require('sdk/windows');
|
||||
@ -376,107 +424,145 @@ exports['test button window state'] = function(assert, done) {
|
||||
let button = ActionButton({
|
||||
id: 'my-button-6',
|
||||
label: 'my button',
|
||||
icon: './icon.png'
|
||||
icon: './icon.png',
|
||||
badge: '+1',
|
||||
badgeColor: 'red'
|
||||
});
|
||||
|
||||
let mainWindow = browserWindows.activeWindow;
|
||||
let nodes = [getWidget(button.id).node];
|
||||
|
||||
openBrowserWindow().then(focus).then(window => {
|
||||
let node;
|
||||
let state;
|
||||
let window = yield openBrowserWindow().then(focus);
|
||||
|
||||
nodes.push(getWidget(button.id, window).node);
|
||||
nodes.push(getWidget(button.id, window).node);
|
||||
|
||||
let { activeWindow } = browserWindows;
|
||||
let { activeWindow } = browserWindows;
|
||||
|
||||
button.state(activeWindow, {
|
||||
label: 'New label',
|
||||
icon: './new-icon.png',
|
||||
disabled: true
|
||||
});
|
||||
button.state(activeWindow, {
|
||||
label: 'New label',
|
||||
icon: './new-icon.png',
|
||||
disabled: true,
|
||||
badge: '+2',
|
||||
badgeColor : 'green'
|
||||
});
|
||||
|
||||
// check the states
|
||||
// check the states
|
||||
|
||||
assert.equal(button.label, 'my button',
|
||||
'global label unchanged');
|
||||
assert.equal(button.icon, './icon.png',
|
||||
'global icon unchanged');
|
||||
assert.equal(button.disabled, false,
|
||||
'global disabled unchanged');
|
||||
assert.equal(button.label, 'my button',
|
||||
'global label unchanged');
|
||||
assert.equal(button.icon, './icon.png',
|
||||
'global icon unchanged');
|
||||
assert.equal(button.disabled, false,
|
||||
'global disabled unchanged');
|
||||
assert.equal(button.badge, '+1',
|
||||
'global badge unchanged');
|
||||
assert.equal(button.badgeColor, 'red',
|
||||
'global badgeColor unchanged');
|
||||
|
||||
state = button.state(mainWindow);
|
||||
let state = button.state(mainWindow);
|
||||
|
||||
assert.equal(state.label, 'my button',
|
||||
'previous window label unchanged');
|
||||
assert.equal(state.icon, './icon.png',
|
||||
'previous window icon unchanged');
|
||||
assert.equal(state.disabled, false,
|
||||
'previous window disabled unchanged');
|
||||
assert.equal(state.label, 'my button',
|
||||
'previous window label unchanged');
|
||||
assert.equal(state.icon, './icon.png',
|
||||
'previous window icon unchanged');
|
||||
assert.equal(state.disabled, false,
|
||||
'previous window disabled unchanged');
|
||||
assert.deepEqual(button.badge, '+1',
|
||||
'previouswindow badge unchanged');
|
||||
assert.deepEqual(button.badgeColor, 'red',
|
||||
'previous window badgeColor unchanged');
|
||||
|
||||
state = button.state(activeWindow);
|
||||
state = button.state(activeWindow);
|
||||
|
||||
assert.equal(state.label, 'New label',
|
||||
'active window label updated');
|
||||
assert.equal(state.icon, './new-icon.png',
|
||||
'active window icon updated');
|
||||
assert.equal(state.disabled, true,
|
||||
'active disabled updated');
|
||||
assert.equal(state.label, 'New label',
|
||||
'active window label updated');
|
||||
assert.equal(state.icon, './new-icon.png',
|
||||
'active window icon updated');
|
||||
assert.equal(state.disabled, true,
|
||||
'active disabled updated');
|
||||
assert.equal(state.badge, '+2',
|
||||
'active badge updated');
|
||||
assert.equal(state.badgeColor, 'green',
|
||||
'active badgeColor updated');
|
||||
|
||||
// change the global state, only the windows without a state are affected
|
||||
// change the global state, only the windows without a state are affected
|
||||
|
||||
button.label = 'A good label';
|
||||
button.label = 'A good label';
|
||||
button.badge = '+3';
|
||||
|
||||
assert.equal(button.label, 'A good label',
|
||||
'global label updated');
|
||||
assert.equal(button.state(mainWindow).label, 'A good label',
|
||||
'previous window label updated');
|
||||
assert.equal(button.state(activeWindow).label, 'New label',
|
||||
'active window label unchanged');
|
||||
assert.equal(button.label, 'A good label',
|
||||
'global label updated');
|
||||
assert.equal(button.state(mainWindow).label, 'A good label',
|
||||
'previous window label updated');
|
||||
assert.equal(button.state(activeWindow).label, 'New label',
|
||||
'active window label unchanged');
|
||||
assert.equal(button.state(activeWindow).badge, '+2',
|
||||
'active badge unchanged');
|
||||
assert.equal(button.state(activeWindow).badgeColor, 'green',
|
||||
'active badgeColor unchanged');
|
||||
assert.equal(button.state(mainWindow).badge, '+3',
|
||||
'previous window badge updated');
|
||||
assert.equal(button.state(mainWindow).badgeColor, 'red',
|
||||
'previous window badgeColor unchanged');
|
||||
|
||||
// delete the window state will inherits the global state again
|
||||
// delete the window state will inherits the global state again
|
||||
|
||||
button.state(activeWindow, null);
|
||||
button.state(activeWindow, null);
|
||||
|
||||
assert.equal(button.state(activeWindow).label, 'A good label',
|
||||
'active window label inherited');
|
||||
state = button.state(activeWindow);
|
||||
|
||||
// check the nodes properties
|
||||
node = nodes[0];
|
||||
state = button.state(mainWindow);
|
||||
assert.equal(state.label, 'A good label',
|
||||
'active window label inherited');
|
||||
assert.equal(state.badge, '+3',
|
||||
'previous window badge inherited');
|
||||
assert.equal(button.badgeColor, 'red',
|
||||
'previous window badgeColor inherited');
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
// check the nodes properties
|
||||
let node = nodes[0];
|
||||
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
state = button.state(mainWindow);
|
||||
|
||||
node = nodes[1];
|
||||
state = button.state(activeWindow);
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
assert.equal(node.getAttribute("badge"), state.badge,
|
||||
'badge is correct');
|
||||
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
assert.equal(badgeNodeFor(node).style.backgroundColor, state.badgeColor,
|
||||
'badge color is correct');
|
||||
|
||||
return window;
|
||||
}).
|
||||
then(close).
|
||||
then(loader.unload).
|
||||
then(done, assert.fail);
|
||||
node = nodes[1];
|
||||
state = button.state(activeWindow);
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
assert.equal(node.getAttribute('badge'), state.badge,
|
||||
'badge is correct');
|
||||
|
||||
assert.equal(badgeNodeFor(node).style.backgroundColor, state.badgeColor,
|
||||
'badge color is correct');
|
||||
|
||||
yield close(window);
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
|
||||
exports['test button tab state'] = function(assert, done) {
|
||||
exports['test button tab state'] = function*(assert) {
|
||||
let loader = Loader(module);
|
||||
let { ActionButton } = loader.require('sdk/ui');
|
||||
let { browserWindows } = loader.require('sdk/windows');
|
||||
@ -491,119 +577,146 @@ exports['test button tab state'] = function(assert, done) {
|
||||
let mainTab = tabs.activeTab;
|
||||
let node = getWidget(button.id).node;
|
||||
|
||||
tabs.open({
|
||||
url: 'about:blank',
|
||||
onActivate: function onActivate(tab) {
|
||||
tab.removeListener('activate', onActivate);
|
||||
tabs.open('about:blank');
|
||||
|
||||
let { activeWindow } = browserWindows;
|
||||
// set window state
|
||||
button.state(activeWindow, {
|
||||
label: 'Window label',
|
||||
icon: './window-icon.png'
|
||||
});
|
||||
yield wait(tabs, 'ready');
|
||||
|
||||
// set previous active tab state
|
||||
button.state(mainTab, {
|
||||
label: 'Tab label',
|
||||
icon: './tab-icon.png',
|
||||
});
|
||||
let tab = tabs.activeTab;
|
||||
let { activeWindow } = browserWindows;
|
||||
|
||||
// set current active tab state
|
||||
button.state(tab, {
|
||||
icon: './another-tab-icon.png',
|
||||
disabled: true
|
||||
});
|
||||
|
||||
// check the states
|
||||
|
||||
Cu.schedulePreciseGC(() => {
|
||||
let state;
|
||||
|
||||
assert.equal(button.label, 'my button',
|
||||
'global label unchanged');
|
||||
assert.equal(button.icon, './icon.png',
|
||||
'global icon unchanged');
|
||||
assert.equal(button.disabled, false,
|
||||
'global disabled unchanged');
|
||||
|
||||
state = button.state(mainTab);
|
||||
|
||||
assert.equal(state.label, 'Tab label',
|
||||
'previous tab label updated');
|
||||
assert.equal(state.icon, './tab-icon.png',
|
||||
'previous tab icon updated');
|
||||
assert.equal(state.disabled, false,
|
||||
'previous tab disabled unchanged');
|
||||
|
||||
state = button.state(tab);
|
||||
|
||||
assert.equal(state.label, 'Window label',
|
||||
'active tab inherited from window state');
|
||||
assert.equal(state.icon, './another-tab-icon.png',
|
||||
'active tab icon updated');
|
||||
assert.equal(state.disabled, true,
|
||||
'active disabled updated');
|
||||
|
||||
// change the global state
|
||||
button.icon = './good-icon.png';
|
||||
|
||||
// delete the tab state
|
||||
button.state(tab, null);
|
||||
|
||||
assert.equal(button.icon, './good-icon.png',
|
||||
'global icon updated');
|
||||
assert.equal(button.state(mainTab).icon, './tab-icon.png',
|
||||
'previous tab icon unchanged');
|
||||
assert.equal(button.state(tab).icon, './window-icon.png',
|
||||
'tab icon inherited from window');
|
||||
|
||||
// delete the window state
|
||||
button.state(activeWindow, null);
|
||||
|
||||
assert.equal(button.state(tab).icon, './good-icon.png',
|
||||
'tab icon inherited from global');
|
||||
|
||||
// check the node properties
|
||||
|
||||
state = button.state(tabs.activeTab);
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
|
||||
tabs.once('activate', () => {
|
||||
// This is made in order to avoid to check the node before it
|
||||
// is updated, need a better check
|
||||
setTimeout(() => {
|
||||
let state = button.state(mainTab);
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
|
||||
tab.close(() => {
|
||||
loader.unload();
|
||||
done();
|
||||
});
|
||||
}, 500);
|
||||
});
|
||||
|
||||
mainTab.activate();
|
||||
});
|
||||
}
|
||||
// set window state
|
||||
button.state(activeWindow, {
|
||||
label: 'Window label',
|
||||
icon: './window-icon.png',
|
||||
badge: 'win',
|
||||
badgeColor: 'blue'
|
||||
});
|
||||
|
||||
// set previous active tab state
|
||||
button.state(mainTab, {
|
||||
label: 'Tab label',
|
||||
icon: './tab-icon.png',
|
||||
badge: 'tab',
|
||||
badgeColor: 'red'
|
||||
});
|
||||
|
||||
// set current active tab state
|
||||
button.state(tab, {
|
||||
icon: './another-tab-icon.png',
|
||||
disabled: true,
|
||||
badge: 't1',
|
||||
badgeColor: 'green'
|
||||
});
|
||||
|
||||
// check the states, be sure they won't be gc'ed
|
||||
yield gc();
|
||||
|
||||
assert.equal(button.label, 'my button',
|
||||
'global label unchanged');
|
||||
assert.equal(button.icon, './icon.png',
|
||||
'global icon unchanged');
|
||||
assert.equal(button.disabled, false,
|
||||
'global disabled unchanged');
|
||||
assert.equal(button.badge, undefined,
|
||||
'global badge unchanged')
|
||||
|
||||
let state = button.state(mainTab);
|
||||
|
||||
assert.equal(state.label, 'Tab label',
|
||||
'previous tab label updated');
|
||||
assert.equal(state.icon, './tab-icon.png',
|
||||
'previous tab icon updated');
|
||||
assert.equal(state.disabled, false,
|
||||
'previous tab disabled unchanged');
|
||||
assert.equal(state.badge, 'tab',
|
||||
'previous tab badge unchanged')
|
||||
assert.equal(state.badgeColor, 'red',
|
||||
'previous tab badgeColor unchanged')
|
||||
|
||||
state = button.state(tab);
|
||||
|
||||
assert.equal(state.label, 'Window label',
|
||||
'active tab inherited from window state');
|
||||
assert.equal(state.icon, './another-tab-icon.png',
|
||||
'active tab icon updated');
|
||||
assert.equal(state.disabled, true,
|
||||
'active disabled updated');
|
||||
assert.equal(state.badge, 't1',
|
||||
'active badge updated');
|
||||
assert.equal(state.badgeColor, 'green',
|
||||
'active badgeColor updated');
|
||||
|
||||
// change the global state
|
||||
button.icon = './good-icon.png';
|
||||
|
||||
// delete the tab state
|
||||
button.state(tab, null);
|
||||
|
||||
assert.equal(button.icon, './good-icon.png',
|
||||
'global icon updated');
|
||||
assert.equal(button.state(mainTab).icon, './tab-icon.png',
|
||||
'previous tab icon unchanged');
|
||||
assert.equal(button.state(tab).icon, './window-icon.png',
|
||||
'tab icon inherited from window');
|
||||
assert.equal(button.state(mainTab).badge, 'tab',
|
||||
'previous tab badge is unchaged');
|
||||
assert.equal(button.state(tab).badge, 'win',
|
||||
'tab badge is inherited from window');
|
||||
|
||||
// delete the window state
|
||||
button.state(activeWindow, null);
|
||||
|
||||
state = button.state(tab);
|
||||
|
||||
assert.equal(state.icon, './good-icon.png',
|
||||
'tab icon inherited from global');
|
||||
assert.equal(state.badge, undefined,
|
||||
'tab badge inherited from global');
|
||||
assert.equal(state.badgeColor, undefined,
|
||||
'tab badgeColor inherited from global');
|
||||
|
||||
// check the node properties
|
||||
yield wait();
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'node disabled is correct');
|
||||
assert.equal(node.getAttribute('badge'), '',
|
||||
'badge text is correct');
|
||||
assert.equal(badgeNodeFor(node).style.backgroundColor, '',
|
||||
'badge color is correct');
|
||||
|
||||
mainTab.activate();
|
||||
|
||||
yield wait(tabs, 'activate');
|
||||
|
||||
// This is made in order to avoid to check the node before it
|
||||
// is updated, need a better check
|
||||
yield wait();
|
||||
|
||||
state = button.state(mainTab);
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
assert.equal(node.getAttribute('badge'), state.badge,
|
||||
'badge text is correct');
|
||||
assert.equal(badgeNodeFor(node).style.backgroundColor, state.badgeColor,
|
||||
'badge color is correct');
|
||||
|
||||
tab.close(loader.unload);
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
exports['test button click'] = function*(assert) {
|
||||
@ -644,7 +757,6 @@ exports['test button click'] = function*(assert) {
|
||||
}
|
||||
|
||||
exports['test button icon set'] = function(assert) {
|
||||
let size;
|
||||
const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
|
||||
let loader = Loader(module);
|
||||
let { ActionButton } = loader.require('sdk/ui');
|
||||
@ -675,7 +787,7 @@ exports['test button icon set'] = function(assert) {
|
||||
let { node, id: widgetId } = getWidget(button.id);
|
||||
let { devicePixelRatio } = node.ownerDocument.defaultView;
|
||||
|
||||
size = 16 * devicePixelRatio;
|
||||
let size = 16 * devicePixelRatio;
|
||||
|
||||
assert.equal(node.getAttribute('image'), data.url(button.icon[size].substr(2)),
|
||||
'the icon is set properly in navbar');
|
||||
@ -697,7 +809,7 @@ exports['test button icon set'] = function(assert) {
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test button icon se with only one option'] = function(assert) {
|
||||
exports['test button icon set with only one option'] = function(assert) {
|
||||
const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
|
||||
let loader = Loader(module);
|
||||
let { ActionButton } = loader.require('sdk/ui');
|
||||
@ -760,6 +872,11 @@ exports['test button state validation'] = function(assert) {
|
||||
/^The option "icon"/,
|
||||
'throws on remote icon given');
|
||||
|
||||
assert.throws(
|
||||
() => button.state(button, { badge: true } ),
|
||||
/^The option "badge"/,
|
||||
'throws on wrong badge value given');
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
@ -939,4 +1056,77 @@ exports['test button after destroy'] = function(assert) {
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
exports['test button badge property'] = function(assert) {
|
||||
let loader = Loader(module);
|
||||
let { ActionButton } = loader.require('sdk/ui');
|
||||
|
||||
let button = ActionButton({
|
||||
id: 'my-button-18',
|
||||
label: 'my button',
|
||||
icon: './icon.png',
|
||||
badge: 123456
|
||||
});
|
||||
|
||||
assert.equal(button.badge, 123456,
|
||||
'badge is set');
|
||||
|
||||
assert.equal(button.badgeColor, undefined,
|
||||
'badge color is not set');
|
||||
|
||||
let { node } = getWidget(button.id);
|
||||
let { getComputedStyle } = node.ownerDocument.defaultView;
|
||||
let badgeNode = badgeNodeFor(node);
|
||||
|
||||
assert.equal('1234', node.getAttribute('badge'),
|
||||
'badge text is displayed up to four characters');
|
||||
|
||||
assert.equal(getComputedStyle(badgeNode).backgroundColor, 'rgb(217, 0, 0)',
|
||||
'badge color is the default one');
|
||||
|
||||
button.badge = '危機';
|
||||
|
||||
assert.equal(button.badge, '危機',
|
||||
'badge is properly set');
|
||||
|
||||
assert.equal('危機', node.getAttribute('badge'),
|
||||
'badge text is displayed');
|
||||
|
||||
button.badge = '🐶🐰🐹';
|
||||
|
||||
assert.equal(button.badge, '🐶🐰🐹',
|
||||
'badge is properly set');
|
||||
|
||||
assert.equal('🐶🐰🐹', node.getAttribute('badge'),
|
||||
'badge text is displayed');
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
exports['test button badge color'] = function(assert) {
|
||||
let loader = Loader(module);
|
||||
let { ActionButton } = loader.require('sdk/ui');
|
||||
|
||||
let button = ActionButton({
|
||||
id: 'my-button-19',
|
||||
label: 'my button',
|
||||
icon: './icon.png',
|
||||
badge: '+1',
|
||||
badgeColor: 'blue'
|
||||
});
|
||||
|
||||
assert.equal(button.badgeColor, 'blue',
|
||||
'badge color is set');
|
||||
|
||||
let { node } = getWidget(button.id);
|
||||
let { getComputedStyle } = node.ownerDocument.defaultView;
|
||||
let badgeNode = badgeNodeFor(node);
|
||||
|
||||
assert.equal(badgeNodeFor(node).style.backgroundColor, 'blue',
|
||||
'badge color is displayed properly');
|
||||
assert.equal(getComputedStyle(badgeNode).backgroundColor, 'rgb(0, 0, 255)',
|
||||
'badge color overrides the default one');
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
|
||||
require('sdk/test').run(exports);
|
||||
|
@ -16,11 +16,17 @@ const { open, focus, close } = require('sdk/window/helpers');
|
||||
const { setTimeout } = require('sdk/timers');
|
||||
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||
const { partial } = require('sdk/lang/functional');
|
||||
const { wait } = require('./event/helpers');
|
||||
const { gc } = require('sdk/test/memory');
|
||||
|
||||
const openBrowserWindow = partial(open, null, {features: {toolbar: true}});
|
||||
const openPrivateBrowserWindow = partial(open, null,
|
||||
{features: {toolbar: true, private: true}});
|
||||
|
||||
const badgeNodeFor = (node) =>
|
||||
node.ownerDocument.getAnonymousElementByAttribute(node,
|
||||
'class', 'toolbarbutton-badge');
|
||||
|
||||
function getWidget(buttonId, window = getMostRecentBrowserWindow()) {
|
||||
const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
|
||||
const { AREA_NAVBAR } = CustomizableUI;
|
||||
@ -107,12 +113,15 @@ exports['test basic constructor validation'] = function(assert) {
|
||||
/^The option "icon"/,
|
||||
'throws on no valid icon given');
|
||||
|
||||
// Test wrong checked
|
||||
assert.throws(
|
||||
() => ToggleButton({
|
||||
id: 'my-button', label: 'my button', icon: './icon.png', checked: 'yes'}),
|
||||
/^The option "checked"/,
|
||||
'throws on no valid checked value given');
|
||||
() => ToggleButton({ id: 'my-button', label: 'button', icon: './i.png', badge: true}),
|
||||
/^The option "badge"/,
|
||||
'throws on no valid badge given');
|
||||
|
||||
assert.throws(
|
||||
() => ToggleButton({ id: 'my-button', label: 'button', icon: './i.png', badgeColor: true}),
|
||||
/^The option "badgeColor"/,
|
||||
'throws on no valid badge given');
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
@ -128,9 +137,6 @@ exports['test button added'] = function(assert) {
|
||||
});
|
||||
|
||||
// check defaults
|
||||
assert.equal(button.checked, false,
|
||||
'checked is set to default `false` value');
|
||||
|
||||
assert.equal(button.disabled, false,
|
||||
'disabled is set to default `false` value');
|
||||
|
||||
@ -147,6 +153,9 @@ exports['test button added'] = function(assert) {
|
||||
assert.equal(data.url(button.icon.substr(2)), node.getAttribute('image'),
|
||||
'icon is set');
|
||||
|
||||
assert.equal("", node.getAttribute('badge'),
|
||||
'badge attribute is empty');
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
@ -252,7 +261,7 @@ exports['test button global state updated'] = function(assert) {
|
||||
let button = ToggleButton({
|
||||
id: 'my-button-4',
|
||||
label: 'my button',
|
||||
icon: './icon.png'
|
||||
icon: './icon.png',
|
||||
});
|
||||
|
||||
// Tried to use `getWidgetIdsInArea` but seems undefined, not sure if it
|
||||
@ -293,13 +302,25 @@ exports['test button global state updated'] = function(assert) {
|
||||
assert.equal(node.getAttribute('disabled'), 'true',
|
||||
'node disabled is updated');
|
||||
|
||||
button.badge = '+2';
|
||||
button.badgeColor = 'blue';
|
||||
|
||||
assert.equal(button.badge, '+2',
|
||||
'badge is updated');
|
||||
assert.equal(node.getAttribute('bagde'), '',
|
||||
'node badge is updated');
|
||||
|
||||
assert.equal(button.badgeColor, 'blue',
|
||||
'badgeColor is updated');
|
||||
assert.equal(badgeNodeFor(node).style.backgroundColor, 'blue',
|
||||
'badge color is updated');
|
||||
|
||||
// TODO: test validation on update
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test button global state set and get with state method'] = function(assert) {
|
||||
let state;
|
||||
let loader = Loader(module);
|
||||
let { ToggleButton } = loader.require('sdk/ui');
|
||||
|
||||
@ -310,7 +331,7 @@ exports['test button global state set and get with state method'] = function(ass
|
||||
});
|
||||
|
||||
// read the button's state
|
||||
state = button.state(button);
|
||||
let state = button.state(button);
|
||||
|
||||
assert.equal(state.label, 'my button',
|
||||
'label is correct');
|
||||
@ -323,7 +344,9 @@ exports['test button global state set and get with state method'] = function(ass
|
||||
button.state(button, {
|
||||
label: 'New label',
|
||||
icon: './new-icon.png',
|
||||
disabled: true
|
||||
disabled: true,
|
||||
badge: '+2',
|
||||
badgeColor: 'blue'
|
||||
});
|
||||
|
||||
assert.equal(button.label, 'New label',
|
||||
@ -332,11 +355,15 @@ exports['test button global state set and get with state method'] = function(ass
|
||||
'icon is updated');
|
||||
assert.equal(button.disabled, true,
|
||||
'disabled is updated');
|
||||
assert.equal(button.badge, '+2',
|
||||
'badge is updated');
|
||||
assert.equal(button.badgeColor, 'blue',
|
||||
'badgeColor is updated');
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
}
|
||||
|
||||
exports['test button global state updated on multiple windows'] = function(assert, done) {
|
||||
exports['test button global state updated on multiple windows'] = function*(assert) {
|
||||
let loader = Loader(module);
|
||||
let { ToggleButton } = loader.require('sdk/ui');
|
||||
|
||||
@ -348,39 +375,48 @@ exports['test button global state updated on multiple windows'] = function(asser
|
||||
|
||||
let nodes = [getWidget(button.id).node];
|
||||
|
||||
openBrowserWindow().then(window => {
|
||||
nodes.push(getWidget(button.id, window).node);
|
||||
let window = yield openBrowserWindow();
|
||||
|
||||
button.label = 'New label';
|
||||
button.icon = './new-icon.png';
|
||||
button.disabled = true;
|
||||
nodes.push(getWidget(button.id, window).node);
|
||||
|
||||
for (let node of nodes) {
|
||||
assert.equal(node.getAttribute('label'), 'New label',
|
||||
'node label is updated');
|
||||
assert.equal(node.getAttribute('tooltiptext'), 'New label',
|
||||
'node tooltip is updated');
|
||||
button.label = 'New label';
|
||||
button.icon = './new-icon.png';
|
||||
button.disabled = true;
|
||||
button.badge = '+10';
|
||||
button.badgeColor = 'green';
|
||||
|
||||
assert.equal(button.icon, './new-icon.png',
|
||||
'icon is updated');
|
||||
assert.equal(node.getAttribute('image'), data.url('new-icon.png'),
|
||||
'node image is updated');
|
||||
for (let node of nodes) {
|
||||
assert.equal(node.getAttribute('label'), 'New label',
|
||||
'node label is updated');
|
||||
assert.equal(node.getAttribute('tooltiptext'), 'New label',
|
||||
'node tooltip is updated');
|
||||
|
||||
assert.equal(button.disabled, true,
|
||||
'disabled is updated');
|
||||
assert.equal(node.getAttribute('disabled'), 'true',
|
||||
'node disabled is updated');
|
||||
};
|
||||
assert.equal(button.icon, './new-icon.png',
|
||||
'icon is updated');
|
||||
assert.equal(node.getAttribute('image'), data.url('new-icon.png'),
|
||||
'node image is updated');
|
||||
|
||||
return window;
|
||||
}).
|
||||
then(close).
|
||||
then(loader.unload).
|
||||
then(done, assert.fail);
|
||||
assert.equal(button.disabled, true,
|
||||
'disabled is updated');
|
||||
assert.equal(node.getAttribute('disabled'), 'true',
|
||||
'node disabled is updated');
|
||||
|
||||
assert.equal(button.badge, '+10',
|
||||
'badge is updated')
|
||||
assert.equal(button.badgeColor, 'green',
|
||||
'badgeColor is updated')
|
||||
assert.equal(node.getAttribute('badge'), '+10',
|
||||
'node badge is updated')
|
||||
assert.equal(badgeNodeFor(node).style.backgroundColor, 'green',
|
||||
'node badge color is updated')
|
||||
};
|
||||
|
||||
yield close(window);
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
exports['test button window state'] = function(assert, done) {
|
||||
let state;
|
||||
exports['test button window state'] = function*(assert) {
|
||||
let loader = Loader(module);
|
||||
let { ToggleButton } = loader.require('sdk/ui');
|
||||
let { browserWindows } = loader.require('sdk/windows');
|
||||
@ -388,104 +424,145 @@ exports['test button window state'] = function(assert, done) {
|
||||
let button = ToggleButton({
|
||||
id: 'my-button-6',
|
||||
label: 'my button',
|
||||
icon: './icon.png'
|
||||
icon: './icon.png',
|
||||
badge: '+1',
|
||||
badgeColor: 'red'
|
||||
});
|
||||
|
||||
let mainWindow = browserWindows.activeWindow;
|
||||
let nodes = [getWidget(button.id).node];
|
||||
|
||||
openBrowserWindow().then(focus).then(window => {
|
||||
nodes.push(getWidget(button.id, window).node);
|
||||
let window = yield openBrowserWindow().then(focus);
|
||||
|
||||
let { activeWindow } = browserWindows;
|
||||
nodes.push(getWidget(button.id, window).node);
|
||||
|
||||
button.state(activeWindow, {
|
||||
label: 'New label',
|
||||
icon: './new-icon.png',
|
||||
disabled: true
|
||||
});
|
||||
let { activeWindow } = browserWindows;
|
||||
|
||||
// check the states
|
||||
button.state(activeWindow, {
|
||||
label: 'New label',
|
||||
icon: './new-icon.png',
|
||||
disabled: true,
|
||||
badge: '+2',
|
||||
badgeColor : 'green'
|
||||
});
|
||||
|
||||
assert.equal(button.label, 'my button',
|
||||
'global label unchanged');
|
||||
assert.equal(button.icon, './icon.png',
|
||||
'global icon unchanged');
|
||||
assert.equal(button.disabled, false,
|
||||
'global disabled unchanged');
|
||||
// check the states
|
||||
|
||||
state = button.state(mainWindow);
|
||||
assert.equal(button.label, 'my button',
|
||||
'global label unchanged');
|
||||
assert.equal(button.icon, './icon.png',
|
||||
'global icon unchanged');
|
||||
assert.equal(button.disabled, false,
|
||||
'global disabled unchanged');
|
||||
assert.equal(button.badge, '+1',
|
||||
'global badge unchanged');
|
||||
assert.equal(button.badgeColor, 'red',
|
||||
'global badgeColor unchanged');
|
||||
|
||||
assert.equal(state.label, 'my button',
|
||||
'previous window label unchanged');
|
||||
assert.equal(state.icon, './icon.png',
|
||||
'previous window icon unchanged');
|
||||
assert.equal(state.disabled, false,
|
||||
'previous window disabled unchanged');
|
||||
let state = button.state(mainWindow);
|
||||
|
||||
state = button.state(activeWindow);
|
||||
assert.equal(state.label, 'my button',
|
||||
'previous window label unchanged');
|
||||
assert.equal(state.icon, './icon.png',
|
||||
'previous window icon unchanged');
|
||||
assert.equal(state.disabled, false,
|
||||
'previous window disabled unchanged');
|
||||
assert.deepEqual(button.badge, '+1',
|
||||
'previouswindow badge unchanged');
|
||||
assert.deepEqual(button.badgeColor, 'red',
|
||||
'previous window badgeColor unchanged');
|
||||
|
||||
assert.equal(state.label, 'New label',
|
||||
'active window label updated');
|
||||
assert.equal(state.icon, './new-icon.png',
|
||||
'active window icon updated');
|
||||
assert.equal(state.disabled, true,
|
||||
'active disabled updated');
|
||||
state = button.state(activeWindow);
|
||||
|
||||
// change the global state, only the windows without a state are affected
|
||||
assert.equal(state.label, 'New label',
|
||||
'active window label updated');
|
||||
assert.equal(state.icon, './new-icon.png',
|
||||
'active window icon updated');
|
||||
assert.equal(state.disabled, true,
|
||||
'active disabled updated');
|
||||
assert.equal(state.badge, '+2',
|
||||
'active badge updated');
|
||||
assert.equal(state.badgeColor, 'green',
|
||||
'active badgeColor updated');
|
||||
|
||||
button.label = 'A good label';
|
||||
// change the global state, only the windows without a state are affected
|
||||
|
||||
assert.equal(button.label, 'A good label',
|
||||
'global label updated');
|
||||
assert.equal(button.state(mainWindow).label, 'A good label',
|
||||
'previous window label updated');
|
||||
assert.equal(button.state(activeWindow).label, 'New label',
|
||||
'active window label unchanged');
|
||||
button.label = 'A good label';
|
||||
button.badge = '+3';
|
||||
|
||||
// delete the window state will inherits the global state again
|
||||
assert.equal(button.label, 'A good label',
|
||||
'global label updated');
|
||||
assert.equal(button.state(mainWindow).label, 'A good label',
|
||||
'previous window label updated');
|
||||
assert.equal(button.state(activeWindow).label, 'New label',
|
||||
'active window label unchanged');
|
||||
assert.equal(button.state(activeWindow).badge, '+2',
|
||||
'active badge unchanged');
|
||||
assert.equal(button.state(activeWindow).badgeColor, 'green',
|
||||
'active badgeColor unchanged');
|
||||
assert.equal(button.state(mainWindow).badge, '+3',
|
||||
'previous window badge updated');
|
||||
assert.equal(button.state(mainWindow).badgeColor, 'red',
|
||||
'previous window badgeColor unchanged');
|
||||
|
||||
button.state(activeWindow, null);
|
||||
// delete the window state will inherits the global state again
|
||||
|
||||
assert.equal(button.state(activeWindow).label, 'A good label',
|
||||
'active window label inherited');
|
||||
button.state(activeWindow, null);
|
||||
|
||||
// check the nodes properties
|
||||
let node = nodes[0];
|
||||
state = button.state(mainWindow);
|
||||
state = button.state(activeWindow);
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
assert.equal(state.label, 'A good label',
|
||||
'active window label inherited');
|
||||
assert.equal(state.badge, '+3',
|
||||
'previous window badge inherited');
|
||||
assert.equal(button.badgeColor, 'red',
|
||||
'previous window badgeColor inherited');
|
||||
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
// check the nodes properties
|
||||
let node = nodes[0];
|
||||
|
||||
node = nodes[1];
|
||||
state = button.state(activeWindow);
|
||||
state = button.state(mainWindow);
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
assert.equal(node.getAttribute("badge"), state.badge,
|
||||
'badge is correct');
|
||||
|
||||
return window;
|
||||
}).
|
||||
then(close).
|
||||
then(loader.unload).
|
||||
then(done, assert.fail);
|
||||
assert.equal(badgeNodeFor(node).style.backgroundColor, state.badgeColor,
|
||||
'badge color is correct');
|
||||
|
||||
node = nodes[1];
|
||||
state = button.state(activeWindow);
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
assert.equal(node.getAttribute('badge'), state.badge,
|
||||
'badge is correct');
|
||||
|
||||
assert.equal(badgeNodeFor(node).style.backgroundColor, state.badgeColor,
|
||||
'badge color is correct');
|
||||
|
||||
yield close(window);
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
|
||||
exports['test button tab state'] = function(assert, done) {
|
||||
exports['test button tab state'] = function*(assert) {
|
||||
let loader = Loader(module);
|
||||
let { ToggleButton } = loader.require('sdk/ui');
|
||||
let { browserWindows } = loader.require('sdk/windows');
|
||||
@ -500,122 +577,149 @@ exports['test button tab state'] = function(assert, done) {
|
||||
let mainTab = tabs.activeTab;
|
||||
let node = getWidget(button.id).node;
|
||||
|
||||
tabs.open({
|
||||
url: 'about:blank',
|
||||
onActivate: function onActivate(tab) {
|
||||
tab.removeListener('activate', onActivate);
|
||||
tabs.open('about:blank');
|
||||
|
||||
let { activeWindow } = browserWindows;
|
||||
// set window state
|
||||
button.state(activeWindow, {
|
||||
label: 'Window label',
|
||||
icon: './window-icon.png'
|
||||
});
|
||||
yield wait(tabs, 'ready');
|
||||
|
||||
// set previous active tab state
|
||||
button.state(mainTab, {
|
||||
label: 'Tab label',
|
||||
icon: './tab-icon.png',
|
||||
});
|
||||
let tab = tabs.activeTab;
|
||||
let { activeWindow } = browserWindows;
|
||||
|
||||
// set current active tab state
|
||||
button.state(tab, {
|
||||
icon: './another-tab-icon.png',
|
||||
disabled: true
|
||||
});
|
||||
|
||||
// check the states
|
||||
|
||||
Cu.schedulePreciseGC(() => {
|
||||
let state;
|
||||
|
||||
assert.equal(button.label, 'my button',
|
||||
'global label unchanged');
|
||||
assert.equal(button.icon, './icon.png',
|
||||
'global icon unchanged');
|
||||
assert.equal(button.disabled, false,
|
||||
'global disabled unchanged');
|
||||
|
||||
state = button.state(mainTab);
|
||||
|
||||
assert.equal(state.label, 'Tab label',
|
||||
'previous tab label updated');
|
||||
assert.equal(state.icon, './tab-icon.png',
|
||||
'previous tab icon updated');
|
||||
assert.equal(state.disabled, false,
|
||||
'previous tab disabled unchanged');
|
||||
|
||||
state = button.state(tab);
|
||||
|
||||
assert.equal(state.label, 'Window label',
|
||||
'active tab inherited from window state');
|
||||
assert.equal(state.icon, './another-tab-icon.png',
|
||||
'active tab icon updated');
|
||||
assert.equal(state.disabled, true,
|
||||
'active disabled updated');
|
||||
|
||||
// change the global state
|
||||
button.icon = './good-icon.png';
|
||||
|
||||
// delete the tab state
|
||||
button.state(tab, null);
|
||||
|
||||
assert.equal(button.icon, './good-icon.png',
|
||||
'global icon updated');
|
||||
assert.equal(button.state(mainTab).icon, './tab-icon.png',
|
||||
'previous tab icon unchanged');
|
||||
assert.equal(button.state(tab).icon, './window-icon.png',
|
||||
'tab icon inherited from window');
|
||||
|
||||
// delete the window state
|
||||
button.state(activeWindow, null);
|
||||
|
||||
assert.equal(button.state(tab).icon, './good-icon.png',
|
||||
'tab icon inherited from global');
|
||||
|
||||
// check the node properties
|
||||
|
||||
state = button.state(tabs.activeTab);
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
|
||||
tabs.once('activate', () => {
|
||||
// This is made in order to avoid to check the node before it
|
||||
// is updated, need a better check
|
||||
setTimeout(() => {
|
||||
let state = button.state(mainTab);
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
|
||||
tab.close(() => {
|
||||
loader.unload();
|
||||
done();
|
||||
});
|
||||
}, 500);
|
||||
});
|
||||
|
||||
mainTab.activate();
|
||||
});
|
||||
}
|
||||
// set window state
|
||||
button.state(activeWindow, {
|
||||
label: 'Window label',
|
||||
icon: './window-icon.png',
|
||||
badge: 'win',
|
||||
badgeColor: 'blue'
|
||||
});
|
||||
|
||||
// set previous active tab state
|
||||
button.state(mainTab, {
|
||||
label: 'Tab label',
|
||||
icon: './tab-icon.png',
|
||||
badge: 'tab',
|
||||
badgeColor: 'red'
|
||||
});
|
||||
|
||||
// set current active tab state
|
||||
button.state(tab, {
|
||||
icon: './another-tab-icon.png',
|
||||
disabled: true,
|
||||
badge: 't1',
|
||||
badgeColor: 'green'
|
||||
});
|
||||
|
||||
// check the states, be sure they won't be gc'ed
|
||||
yield gc();
|
||||
|
||||
assert.equal(button.label, 'my button',
|
||||
'global label unchanged');
|
||||
assert.equal(button.icon, './icon.png',
|
||||
'global icon unchanged');
|
||||
assert.equal(button.disabled, false,
|
||||
'global disabled unchanged');
|
||||
assert.equal(button.badge, undefined,
|
||||
'global badge unchanged')
|
||||
|
||||
let state = button.state(mainTab);
|
||||
|
||||
assert.equal(state.label, 'Tab label',
|
||||
'previous tab label updated');
|
||||
assert.equal(state.icon, './tab-icon.png',
|
||||
'previous tab icon updated');
|
||||
assert.equal(state.disabled, false,
|
||||
'previous tab disabled unchanged');
|
||||
assert.equal(state.badge, 'tab',
|
||||
'previous tab badge unchanged')
|
||||
assert.equal(state.badgeColor, 'red',
|
||||
'previous tab badgeColor unchanged')
|
||||
|
||||
state = button.state(tab);
|
||||
|
||||
assert.equal(state.label, 'Window label',
|
||||
'active tab inherited from window state');
|
||||
assert.equal(state.icon, './another-tab-icon.png',
|
||||
'active tab icon updated');
|
||||
assert.equal(state.disabled, true,
|
||||
'active disabled updated');
|
||||
assert.equal(state.badge, 't1',
|
||||
'active badge updated');
|
||||
assert.equal(state.badgeColor, 'green',
|
||||
'active badgeColor updated');
|
||||
|
||||
// change the global state
|
||||
button.icon = './good-icon.png';
|
||||
|
||||
// delete the tab state
|
||||
button.state(tab, null);
|
||||
|
||||
assert.equal(button.icon, './good-icon.png',
|
||||
'global icon updated');
|
||||
assert.equal(button.state(mainTab).icon, './tab-icon.png',
|
||||
'previous tab icon unchanged');
|
||||
assert.equal(button.state(tab).icon, './window-icon.png',
|
||||
'tab icon inherited from window');
|
||||
assert.equal(button.state(mainTab).badge, 'tab',
|
||||
'previous tab badge is unchaged');
|
||||
assert.equal(button.state(tab).badge, 'win',
|
||||
'tab badge is inherited from window');
|
||||
|
||||
// delete the window state
|
||||
button.state(activeWindow, null);
|
||||
|
||||
state = button.state(tab);
|
||||
|
||||
assert.equal(state.icon, './good-icon.png',
|
||||
'tab icon inherited from global');
|
||||
assert.equal(state.badge, undefined,
|
||||
'tab badge inherited from global');
|
||||
assert.equal(state.badgeColor, undefined,
|
||||
'tab badgeColor inherited from global');
|
||||
|
||||
// check the node properties
|
||||
yield wait();
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'node disabled is correct');
|
||||
assert.equal(node.getAttribute('badge'), '',
|
||||
'badge text is correct');
|
||||
assert.equal(badgeNodeFor(node).style.backgroundColor, '',
|
||||
'badge color is correct');
|
||||
|
||||
mainTab.activate();
|
||||
|
||||
yield wait(tabs, 'activate');
|
||||
|
||||
// This is made in order to avoid to check the node before it
|
||||
// is updated, need a better check
|
||||
yield wait();
|
||||
|
||||
state = button.state(mainTab);
|
||||
|
||||
assert.equal(node.getAttribute('label'), state.label,
|
||||
'node label is correct');
|
||||
assert.equal(node.getAttribute('tooltiptext'), state.label,
|
||||
'node tooltip is correct');
|
||||
assert.equal(node.getAttribute('image'), data.url(state.icon.substr(2)),
|
||||
'node image is correct');
|
||||
assert.equal(node.hasAttribute('disabled'), state.disabled,
|
||||
'disabled is correct');
|
||||
assert.equal(node.getAttribute('badge'), state.badge,
|
||||
'badge text is correct');
|
||||
assert.equal(badgeNodeFor(node).style.backgroundColor, state.badgeColor,
|
||||
'badge color is correct');
|
||||
|
||||
tab.close(loader.unload);
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
exports['test button click'] = function(assert, done) {
|
||||
exports['test button click'] = function*(assert) {
|
||||
let loader = Loader(module);
|
||||
let { ToggleButton } = loader.require('sdk/ui');
|
||||
let { browserWindows } = loader.require('sdk/windows');
|
||||
@ -632,24 +736,24 @@ exports['test button click'] = function(assert, done) {
|
||||
let mainWindow = browserWindows.activeWindow;
|
||||
let chromeWindow = getMostRecentBrowserWindow();
|
||||
|
||||
openBrowserWindow().then(focus).then(window => {
|
||||
button.state(mainWindow, { label: 'nothing' });
|
||||
button.state(mainWindow.tabs.activeTab, { label: 'foo'})
|
||||
button.state(browserWindows.activeWindow, { label: 'bar' });
|
||||
let window = yield openBrowserWindow().then(focus);
|
||||
|
||||
button.click();
|
||||
button.state(mainWindow, { label: 'nothing' });
|
||||
button.state(mainWindow.tabs.activeTab, { label: 'foo'})
|
||||
button.state(browserWindows.activeWindow, { label: 'bar' });
|
||||
|
||||
focus(chromeWindow).then(() => {
|
||||
button.click();
|
||||
button.click();
|
||||
|
||||
assert.deepEqual(labels, ['bar', 'foo'],
|
||||
'button click works');
|
||||
yield focus(chromeWindow);
|
||||
|
||||
close(window).
|
||||
then(loader.unload).
|
||||
then(done, assert.fail);
|
||||
});
|
||||
}).then(null, assert.fail);
|
||||
button.click();
|
||||
|
||||
assert.deepEqual(labels, ['bar', 'foo'],
|
||||
'button click works');
|
||||
|
||||
yield close(window);
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test button icon set'] = function(assert) {
|
||||
@ -705,7 +809,7 @@ exports['test button icon set'] = function(assert) {
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test button icon se with only one option'] = function(assert) {
|
||||
exports['test button icon set with only one option'] = function(assert) {
|
||||
const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {});
|
||||
let loader = Loader(module);
|
||||
let { ToggleButton } = loader.require('sdk/ui');
|
||||
@ -768,6 +872,11 @@ exports['test button state validation'] = function(assert) {
|
||||
/^The option "icon"/,
|
||||
'throws on remote icon given');
|
||||
|
||||
assert.throws(
|
||||
() => button.state(button, { badge: true } ),
|
||||
/^The option "badge"/,
|
||||
'throws on wrong badge value given');
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
@ -947,6 +1056,79 @@ exports['test button after destroy'] = function(assert) {
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
exports['test button badge property'] = function(assert) {
|
||||
let loader = Loader(module);
|
||||
let { ToggleButton } = loader.require('sdk/ui');
|
||||
|
||||
let button = ToggleButton({
|
||||
id: 'my-button-18',
|
||||
label: 'my button',
|
||||
icon: './icon.png',
|
||||
badge: 123456
|
||||
});
|
||||
|
||||
assert.equal(button.badge, 123456,
|
||||
'badge is set');
|
||||
|
||||
assert.equal(button.badgeColor, undefined,
|
||||
'badge color is not set');
|
||||
|
||||
let { node } = getWidget(button.id);
|
||||
let { getComputedStyle } = node.ownerDocument.defaultView;
|
||||
let badgeNode = badgeNodeFor(node);
|
||||
|
||||
assert.equal('1234', node.getAttribute('badge'),
|
||||
'badge text is displayed up to four characters');
|
||||
|
||||
assert.equal(getComputedStyle(badgeNode).backgroundColor, 'rgb(217, 0, 0)',
|
||||
'badge color is the default one');
|
||||
|
||||
button.badge = '危機';
|
||||
|
||||
assert.equal(button.badge, '危機',
|
||||
'badge is properly set');
|
||||
|
||||
assert.equal('危機', node.getAttribute('badge'),
|
||||
'badge text is displayed');
|
||||
|
||||
button.badge = '🐶🐰🐹';
|
||||
|
||||
assert.equal(button.badge, '🐶🐰🐹',
|
||||
'badge is properly set');
|
||||
|
||||
assert.equal('🐶🐰🐹', node.getAttribute('badge'),
|
||||
'badge text is displayed');
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
exports['test button badge color'] = function(assert) {
|
||||
let loader = Loader(module);
|
||||
let { ToggleButton } = loader.require('sdk/ui');
|
||||
|
||||
let button = ToggleButton({
|
||||
id: 'my-button-19',
|
||||
label: 'my button',
|
||||
icon: './icon.png',
|
||||
badge: '+1',
|
||||
badgeColor: 'blue'
|
||||
});
|
||||
|
||||
assert.equal(button.badgeColor, 'blue',
|
||||
'badge color is set');
|
||||
|
||||
let { node } = getWidget(button.id);
|
||||
let { getComputedStyle } = node.ownerDocument.defaultView;
|
||||
let badgeNode = badgeNodeFor(node);
|
||||
|
||||
assert.equal(badgeNodeFor(node).style.backgroundColor, 'blue',
|
||||
'badge color is displayed properly');
|
||||
assert.equal(getComputedStyle(badgeNode).backgroundColor, 'rgb(0, 0, 255)',
|
||||
'badge color overrides the default one');
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
// toggle button only
|
||||
exports['test button checked'] = function(assert, done) {
|
||||
let loader = Loader(module);
|
||||
let { ToggleButton } = loader.require('sdk/ui');
|
||||
|
@ -6,6 +6,9 @@
|
||||
|
||||
var BaseAssert = require("sdk/test/assert").Assert;
|
||||
|
||||
const getOwnIdentifiers = x => [...Object.getOwnPropertyNames(x),
|
||||
...Object.getOwnPropertySymbols(x)];
|
||||
|
||||
/**
|
||||
* Whether or not given property descriptors are equivalent. They are
|
||||
* equivalent either if both are marked as "conflict" or "required" property
|
||||
@ -52,7 +55,7 @@ function equivalentSets(source, target) {
|
||||
*/
|
||||
function findNonEquivalentPropertyName(source, target) {
|
||||
var value = null;
|
||||
Object.getOwnPropertyNames(source).some(function(key) {
|
||||
getOwnIdentifiers(source).some(function(key) {
|
||||
var areEquivalent = false;
|
||||
if (!equivalentDescriptors(source[key], target[key])) {
|
||||
value = key;
|
||||
@ -67,8 +70,8 @@ var AssertDescriptor = {
|
||||
equalTraits: {
|
||||
value: function equivalentTraits(actual, expected, message) {
|
||||
var difference;
|
||||
var actualKeys = Object.getOwnPropertyNames(actual);
|
||||
var expectedKeys = Object.getOwnPropertyNames(expected);
|
||||
var actualKeys = getOwnIdentifiers(actual);
|
||||
var expectedKeys = getOwnIdentifiers(expected);
|
||||
|
||||
if (equivalentSets(actualKeys, expectedKeys)) {
|
||||
this.fail({
|
||||
|
@ -319,6 +319,11 @@ pref("dom.indexedDB.warningQuota", 5);
|
||||
pref("media.preload.default", 1); // default to preload none
|
||||
pref("media.preload.auto", 2); // preload metadata if preload=auto
|
||||
pref("media.cache_size", 4096); // 4MB media cache
|
||||
// Try to save battery by not resuming reading from a connection until we fall
|
||||
// below 10s of buffered data.
|
||||
pref("media.cache_resume_threshold", 10);
|
||||
pref("media.cache_readahead_limit", 30);
|
||||
|
||||
#ifdef MOZ_FMP4
|
||||
// Enable/Disable Gonk Decoder Module
|
||||
pref("media.fragmented-mp4.gonk.enabled", false);
|
||||
|
@ -8,13 +8,11 @@ let SocialUI,
|
||||
SocialMarks,
|
||||
SocialShare,
|
||||
SocialSidebar,
|
||||
SocialStatus;
|
||||
SocialStatus,
|
||||
SocialActivationListener;
|
||||
|
||||
(function() {
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SharedFrame",
|
||||
"resource:///modules/SharedFrame.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PanelFrame",
|
||||
"resource:///modules/PanelFrame.jsm");
|
||||
|
||||
@ -73,8 +71,8 @@ SocialUI = {
|
||||
|
||||
Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
|
||||
|
||||
gBrowser.addEventListener("ActivateSocialFeature", this._activationEventHandler.bind(this), true, true);
|
||||
CustomizableUI.addListener(this);
|
||||
SocialActivationListener.init();
|
||||
|
||||
// menupopups that list social providers. we only populate them when shown,
|
||||
// and if it has not been done already.
|
||||
@ -108,6 +106,7 @@ SocialUI = {
|
||||
|
||||
Services.prefs.removeObserver("social.toast-notifications.enabled", this);
|
||||
CustomizableUI.removeListener(this);
|
||||
SocialActivationListener.uninit();
|
||||
|
||||
document.getElementById("viewSidebarMenu").removeEventListener("popupshowing", SocialSidebar.populateSidebarMenu, true);
|
||||
document.getElementById("social-statusarea-popup").removeEventListener("popupshowing", SocialSidebar.populateSidebarMenu, true);
|
||||
@ -174,95 +173,6 @@ SocialUI = {
|
||||
SocialMarks.populateToolbarPalette();
|
||||
},
|
||||
|
||||
// This handles "ActivateSocialFeature" events fired against content documents
|
||||
// in this window. If this activation happens from within Firefox, such as
|
||||
// about:home or the share panel, we bypass the enable prompt. Any website
|
||||
// activation, such as from the activations directory or a providers website
|
||||
// will still get the prompt.
|
||||
_activationEventHandler: function SocialUI_activationHandler(e, options={}) {
|
||||
let targetDoc;
|
||||
let node;
|
||||
if (e.target instanceof HTMLDocument) {
|
||||
// version 0 support
|
||||
targetDoc = e.target;
|
||||
node = targetDoc.documentElement
|
||||
} else {
|
||||
targetDoc = e.target.ownerDocument;
|
||||
node = e.target;
|
||||
}
|
||||
if (!(targetDoc instanceof HTMLDocument))
|
||||
return;
|
||||
|
||||
// The share panel iframe will not match "content" so it passes a bypass
|
||||
// flag
|
||||
if (!options.bypassContentCheck && targetDoc.defaultView != content)
|
||||
return;
|
||||
|
||||
// If we are in PB mode, we silently do nothing (bug 829404 exists to
|
||||
// do something sensible here...)
|
||||
if (PrivateBrowsingUtils.isWindowPrivate(window))
|
||||
return;
|
||||
|
||||
// If the last event was received < 1s ago, ignore this one
|
||||
let now = Date.now();
|
||||
if (now - Social.lastEventReceived < 1000)
|
||||
return;
|
||||
Social.lastEventReceived = now;
|
||||
|
||||
// We only want to activate if it is as a result of user input.
|
||||
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
if (!dwu.isHandlingUserInput) {
|
||||
Cu.reportError("attempt to activate provider without user input from " + targetDoc.nodePrincipal.origin);
|
||||
return;
|
||||
}
|
||||
|
||||
let data = node.getAttribute("data-service");
|
||||
if (data) {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(e) {
|
||||
Cu.reportError("Social Service manifest parse error: "+e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Social.installProvider(targetDoc, data, function(manifest) {
|
||||
Social.activateFromOrigin(manifest.origin, function(provider) {
|
||||
if (provider.sidebarURL) {
|
||||
SocialSidebar.show(provider.origin);
|
||||
}
|
||||
if (provider.shareURL) {
|
||||
// Ensure that the share button is somewhere usable.
|
||||
// SocialShare.shareButton may return null if it is in the menu-panel
|
||||
// and has never been visible, so we check the widget directly. If
|
||||
// there is no area for the widget we move it into the toolbar.
|
||||
let widget = CustomizableUI.getWidget("social-share-button");
|
||||
if (!widget.areaType) {
|
||||
CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
|
||||
// ensure correct state
|
||||
SocialUI.onCustomizeEnd(window);
|
||||
}
|
||||
|
||||
// make this new provider the selected provider. If the panel hasn't
|
||||
// been opened, we need to make the frame first.
|
||||
SocialShare._createFrame();
|
||||
SocialShare.iframe.setAttribute('src', 'data:text/plain;charset=utf8,');
|
||||
SocialShare.iframe.setAttribute('origin', provider.origin);
|
||||
// get the right button selected
|
||||
SocialShare.populateProviderMenu();
|
||||
if (SocialShare.panel.state == "open") {
|
||||
SocialShare.sharePage(provider.origin);
|
||||
}
|
||||
}
|
||||
if (provider.postActivationURL) {
|
||||
// if activated from an open share panel, we load the landing page in
|
||||
// a background tab
|
||||
gBrowser.loadOneTab(provider.postActivationURL, {inBackground: SocialShare.panel.state == "open"});
|
||||
}
|
||||
});
|
||||
}, options);
|
||||
},
|
||||
|
||||
showLearnMore: function() {
|
||||
let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "social-api";
|
||||
openUILinkIn(url, "tab");
|
||||
@ -356,6 +266,68 @@ SocialUI = {
|
||||
}
|
||||
}
|
||||
|
||||
// message manager handlers
|
||||
SocialActivationListener = {
|
||||
init: function() {
|
||||
messageManager.addMessageListener("Social:Activation", this);
|
||||
},
|
||||
uninit: function() {
|
||||
messageManager.removeMessageListener("Social:Activation", this);
|
||||
},
|
||||
receiveMessage: function(aMessage) {
|
||||
let data = aMessage.json;
|
||||
let browser = aMessage.target;
|
||||
data.window = window;
|
||||
// if the source if the message is the share panel, we do a one-click
|
||||
// installation. The source of activations is controlled by the
|
||||
// social.directories preference
|
||||
let options;
|
||||
if (browser == SocialShare.iframe && Services.prefs.getBoolPref("social.share.activationPanelEnabled")) {
|
||||
options = { bypassContentCheck: true, bypassInstallPanel: true };
|
||||
}
|
||||
|
||||
// If we are in PB mode, we silently do nothing (bug 829404 exists to
|
||||
// do something sensible here...)
|
||||
if (PrivateBrowsingUtils.isWindowPrivate(window))
|
||||
return;
|
||||
Social.installProvider(data, function(manifest) {
|
||||
Social.activateFromOrigin(manifest.origin, function(provider) {
|
||||
if (provider.sidebarURL) {
|
||||
SocialSidebar.show(provider.origin);
|
||||
}
|
||||
if (provider.shareURL) {
|
||||
// Ensure that the share button is somewhere usable.
|
||||
// SocialShare.shareButton may return null if it is in the menu-panel
|
||||
// and has never been visible, so we check the widget directly. If
|
||||
// there is no area for the widget we move it into the toolbar.
|
||||
let widget = CustomizableUI.getWidget("social-share-button");
|
||||
if (!widget.areaType) {
|
||||
CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
|
||||
// ensure correct state
|
||||
SocialUI.onCustomizeEnd(window);
|
||||
}
|
||||
|
||||
// make this new provider the selected provider. If the panel hasn't
|
||||
// been opened, we need to make the frame first.
|
||||
SocialShare._createFrame();
|
||||
SocialShare.iframe.setAttribute('src', 'data:text/plain;charset=utf8,');
|
||||
SocialShare.iframe.setAttribute('origin', provider.origin);
|
||||
// get the right button selected
|
||||
SocialShare.populateProviderMenu();
|
||||
if (SocialShare.panel.state == "open") {
|
||||
SocialShare.sharePage(provider.origin);
|
||||
}
|
||||
}
|
||||
if (provider.postActivationURL) {
|
||||
// if activated from an open share panel, we load the landing page in
|
||||
// a background tab
|
||||
gBrowser.loadOneTab(provider.postActivationURL, {inBackground: SocialShare.panel.state == "open"});
|
||||
}
|
||||
});
|
||||
}, options);
|
||||
}
|
||||
}
|
||||
|
||||
SocialFlyout = {
|
||||
get panel() {
|
||||
return document.getElementById("social-flyout-panel");
|
||||
@ -517,15 +489,8 @@ SocialShare = {
|
||||
return this.panel.lastChild;
|
||||
},
|
||||
|
||||
_activationHandler: function(event) {
|
||||
if (!Services.prefs.getBoolPref("social.share.activationPanelEnabled"))
|
||||
return;
|
||||
SocialUI._activationEventHandler(event, { bypassContentCheck: true, bypassInstallPanel: true });
|
||||
},
|
||||
|
||||
uninit: function () {
|
||||
if (this.iframe) {
|
||||
this.iframe.removeEventListener("ActivateSocialFeature", this._activationHandler, true, true);
|
||||
this.iframe.remove();
|
||||
}
|
||||
},
|
||||
@ -544,7 +509,10 @@ SocialShare = {
|
||||
iframe.setAttribute("disableglobalhistory", "true");
|
||||
iframe.setAttribute("flex", "1");
|
||||
panel.appendChild(iframe);
|
||||
this.iframe.addEventListener("ActivateSocialFeature", this._activationHandler, true, true);
|
||||
iframe.addEventListener("DOMContentLoaded", function _firstload() {
|
||||
iframe.removeEventListener("DOMContentLoaded", _firstload, true);
|
||||
iframe.messageManager.loadFrameScript("chrome://browser/content/content.js", true);
|
||||
}, true);
|
||||
this.populateProviderMenu();
|
||||
},
|
||||
|
||||
@ -671,18 +639,31 @@ SocialShare = {
|
||||
// endpoints (e.g. oexchange) that do not support additional
|
||||
// socialapi functionality. One tweak is that we shoot an event
|
||||
// containing the open graph data.
|
||||
let _dataFn;
|
||||
if (!pageData || sharedURI == gBrowser.currentURI) {
|
||||
pageData = OpenGraphBuilder.getData(gBrowser);
|
||||
if (graphData) {
|
||||
// overwrite data retreived from page with data given to us as a param
|
||||
for (let p in graphData) {
|
||||
pageData[p] = graphData[p];
|
||||
messageManager.addMessageListener("Social:PageDataResult", _dataFn = (msg) => {
|
||||
messageManager.removeMessageListener("Social:PageDataResult", _dataFn);
|
||||
let pageData = msg.json;
|
||||
if (graphData) {
|
||||
// overwrite data retreived from page with data given to us as a param
|
||||
for (let p in graphData) {
|
||||
pageData[p] = graphData[p];
|
||||
}
|
||||
}
|
||||
}
|
||||
this.sharePage(providerOrigin, pageData, target);
|
||||
});
|
||||
gBrowser.selectedBrowser.messageManager.sendAsyncMessage("Social:GetPageData");
|
||||
return;
|
||||
}
|
||||
// if this is a share of a selected item, get any microdata
|
||||
if (!pageData.microdata && target) {
|
||||
pageData.microdata = OpenGraphBuilder.getMicrodata(gBrowser, target);
|
||||
messageManager.addMessageListener("Social:PageDataResult", _dataFn = (msg) => {
|
||||
messageManager.removeMessageListener("Social:PageDataResult", _dataFn);
|
||||
pageData.microdata = msg.data;
|
||||
this.sharePage(providerOrigin, pageData, target);
|
||||
});
|
||||
gBrowser.selectedBrowser.messageManager.sendAsyncMessage("Social:GetMicrodata", null, target);
|
||||
return;
|
||||
}
|
||||
this.currentShare = pageData;
|
||||
|
||||
@ -1267,7 +1248,6 @@ SocialStatus = {
|
||||
let notificationFrameId = "social-status-" + origin;
|
||||
let frame = document.getElementById(notificationFrameId);
|
||||
if (frame) {
|
||||
SharedFrame.forgetGroup(frame.id);
|
||||
frame.parentNode.removeChild(frame);
|
||||
}
|
||||
},
|
||||
|
@ -3972,7 +3972,7 @@ var XULBrowserWindow = {
|
||||
.chromeEventHandler;
|
||||
|
||||
// Ignore loads that aren't in the main tabbrowser
|
||||
if (browser.localName != "browser" || browser.getTabBrowser() != gBrowser)
|
||||
if (browser.localName != "browser" || !browser.getTabBrowser || browser.getTabBrowser() != gBrowser)
|
||||
return true;
|
||||
|
||||
if (!E10SUtils.shouldLoadURI(aDocShell, aURI, aReferrer)) {
|
||||
|
@ -837,6 +837,66 @@ addEventListener("pageshow", function(event) {
|
||||
}
|
||||
});
|
||||
|
||||
let SocialMessenger = {
|
||||
init: function() {
|
||||
addMessageListener("Social:GetPageData", this);
|
||||
addMessageListener("Social:GetMicrodata", this);
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "og", function() {
|
||||
let tmp = {};
|
||||
Cu.import("resource:///modules/Social.jsm", tmp);
|
||||
return tmp.OpenGraphBuilder;
|
||||
});
|
||||
},
|
||||
receiveMessage: function(aMessage) {
|
||||
switch(aMessage.name) {
|
||||
case "Social:GetPageData":
|
||||
sendAsyncMessage("Social:PageDataResult", this.og.getData(content.document));
|
||||
break;
|
||||
case "Social:GetMicrodata":
|
||||
let target = aMessage.objects;
|
||||
sendAsyncMessage("Social:PageDataResult", this.og.getMicrodata(content.document, target));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
SocialMessenger.init();
|
||||
|
||||
addEventListener("ActivateSocialFeature", function (aEvent) {
|
||||
let document = content.document;
|
||||
if (PrivateBrowsingUtils.isContentWindowPrivate(content)) {
|
||||
Cu.reportError("cannot use social providers in private windows");
|
||||
return;
|
||||
}
|
||||
let dwu = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
if (!dwu.isHandlingUserInput) {
|
||||
Cu.reportError("attempt to activate provider without user input from " + document.nodePrincipal.origin);
|
||||
return;
|
||||
}
|
||||
|
||||
let node = aEvent.target;
|
||||
let ownerDocument = node.ownerDocument;
|
||||
let data = node.getAttribute("data-service");
|
||||
if (data) {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(e) {
|
||||
Cu.reportError("Social Service manifest parse error: " + e);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Cu.reportError("Social Service manifest not available");
|
||||
return;
|
||||
}
|
||||
|
||||
sendAsyncMessage("Social:Activation", {
|
||||
url: ownerDocument.location.href,
|
||||
origin: ownerDocument.nodePrincipal.origin,
|
||||
manifest: data
|
||||
});
|
||||
}, true, true);
|
||||
|
||||
addMessageListener("ContextMenu:SaveVideoFrameAsImage", (message) => {
|
||||
let video = message.objects.target;
|
||||
let canvas = content.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
||||
|
@ -148,10 +148,24 @@
|
||||
panel.appendChild(this.content);
|
||||
|
||||
let URLTemplate = provider.markURL;
|
||||
pageData = pageData || OpenGraphBuilder.getData(gBrowser);
|
||||
let _dataFn;
|
||||
if (!pageData) {
|
||||
messageManager.addMessageListener("Social:PageDataResult", _dataFn = (msg) => {
|
||||
messageManager.removeMessageListener("Social:PageDataResult", _dataFn);
|
||||
this.loadPanel(msg.json, target);
|
||||
});
|
||||
gBrowser.selectedBrowser.messageManager.sendAsyncMessage("Social:GetPageData");
|
||||
return;
|
||||
}
|
||||
// if this is a share of a selected item, get any microdata
|
||||
if (!pageData.microdata && target) {
|
||||
pageData.microdata = OpenGraphBuilder.getMicrodata(gBrowser, target);
|
||||
messageManager.addMessageListener("Social:PageDataResult", _dataFn = (msg) => {
|
||||
messageManager.removeMessageListener("Social:PageDataResult", _dataFn);
|
||||
pageData.microdata = msg.data;
|
||||
this.loadPanel(pageData, target);
|
||||
});
|
||||
gBrowser.selectedBrowser.messageManager.sendAsyncMessage("Social:GetMicrodata", null, target);
|
||||
return;
|
||||
}
|
||||
this.pageData = pageData;
|
||||
|
||||
|
@ -57,6 +57,8 @@ function test() {
|
||||
|
||||
testVal("http://localhost");
|
||||
testVal("http://someotherhostwithnodots");
|
||||
testVal("http://localhost/ foo bar baz");
|
||||
testVal("http://localhost.localdomain/ foo bar baz", "localhost.localdomain/ foo bar baz");
|
||||
|
||||
Services.prefs.setBoolPref(prefname, false);
|
||||
|
||||
|
@ -27,34 +27,29 @@ support-files =
|
||||
unchecked.jpg
|
||||
|
||||
[browser_aboutHome_activation.js]
|
||||
skip-if = e10s # Bug 915547 (social providers don't install)
|
||||
skip-if = e10s # Bug 1053965 "cw.ensureSnippetsMapThen is not a function", also see general/browser.ini about:home comments
|
||||
[browser_addons.js]
|
||||
skip-if = e10s # Bug 915547 (social providers don't install)
|
||||
[browser_blocklist.js]
|
||||
skip-if = e10s # Bug 915547 (social providers don't install)
|
||||
[browser_share.js]
|
||||
skip-if = e10s # Bug 915547 (social providers don't install)
|
||||
[browser_social_activation.js]
|
||||
skip-if = e10s # Bug 915547 (social providers don't install)
|
||||
skip-if = e10s # Bug 933103 synthesizeMouseAtCenter not e10s friendly
|
||||
[browser_social_chatwindow.js]
|
||||
skip-if = e10s # Bug 915547 (social providers don't install)
|
||||
[browser_social_chatwindow_resize.js]
|
||||
skip-if = e10s # Bug 915547 (social providers don't install)
|
||||
[browser_social_chatwindowfocus.js]
|
||||
skip-if = e10s # Bug 915547 (social providers don't install)
|
||||
skip-if = e10s # tab crash on data url used in this test
|
||||
[browser_social_contextmenu.js]
|
||||
skip-if = e10s # Bug 1072669 context menu relies on target element
|
||||
[browser_social_errorPage.js]
|
||||
[browser_social_flyout.js]
|
||||
skip-if = e10s # when we backed out bug 1047603, this test broke.
|
||||
[browser_social_isVisible.js]
|
||||
[browser_social_marks.js]
|
||||
skip-if = e10s # Bug 915547 (social providers don't install)
|
||||
[browser_social_multiprovider.js]
|
||||
skip-if = e10s # Bug 1069162 - lots of orange
|
||||
[browser_social_multiworker.js]
|
||||
[browser_social_perwindowPB.js]
|
||||
[browser_social_sidebar.js]
|
||||
[browser_social_status.js]
|
||||
skip-if = e10s # Bug 915547 (social providers don't install)
|
||||
[browser_social_window.js]
|
||||
[browser_social_workercrash.js]
|
||||
#skip-if = !crashreporter
|
||||
|
@ -193,7 +193,13 @@ var tests = {
|
||||
let installFrom = doc.nodePrincipal.origin;
|
||||
Services.prefs.setCharPref("social.whitelist", "");
|
||||
is(SocialService.getOriginActivationType(installFrom), "foreign", "testing foriegn install");
|
||||
Social.installProvider(doc, manifest2, function(addonManifest) {
|
||||
let data = {
|
||||
origin: doc.nodePrincipal.origin,
|
||||
url: doc.location.href,
|
||||
manifest: manifest2,
|
||||
window: window
|
||||
}
|
||||
Social.installProvider(data, function(addonManifest) {
|
||||
Services.prefs.clearUserPref("social.whitelist");
|
||||
SocialService.enableProvider(addonManifest.origin, function(provider) {
|
||||
Social.uninstallProvider(addonManifest.origin);
|
||||
@ -217,7 +223,13 @@ var tests = {
|
||||
let installFrom = doc.nodePrincipal.origin;
|
||||
Services.prefs.setCharPref("social.directories", installFrom);
|
||||
is(SocialService.getOriginActivationType(installFrom), "directory", "testing directory install");
|
||||
Social.installProvider(doc, manifest2, function(addonManifest) {
|
||||
let data = {
|
||||
origin: installFrom,
|
||||
url: doc.location.href,
|
||||
manifest: manifest2,
|
||||
window: window
|
||||
}
|
||||
Social.installProvider(data, function(addonManifest) {
|
||||
Services.prefs.clearUserPref("social.directories");
|
||||
SocialService.enableProvider(addonManifest.origin, function(provider) {
|
||||
Social.uninstallProvider(addonManifest.origin);
|
||||
@ -241,7 +253,13 @@ var tests = {
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
let installFrom = doc.nodePrincipal.origin;
|
||||
Services.prefs.setCharPref("social.whitelist", installFrom);
|
||||
Social.installProvider(doc, manifest2, function(addonManifest) {
|
||||
let data = {
|
||||
origin: installFrom,
|
||||
url: doc.location.href,
|
||||
manifest: manifest2,
|
||||
window: window
|
||||
}
|
||||
Social.installProvider(data, function(addonManifest) {
|
||||
SocialService.enableProvider(addonManifest.origin, function(provider) {
|
||||
is(provider.manifest.version, 1, "manifest version is 1");
|
||||
|
||||
|
@ -113,7 +113,13 @@ var tests = {
|
||||
try {
|
||||
// expecting an exception when attempting to install a hard blocked
|
||||
// provider
|
||||
Social.installProvider(doc, manifest_bad, function(addonManifest) {
|
||||
let data = {
|
||||
origin: doc.nodePrincipal.origin,
|
||||
url: doc.location.href,
|
||||
manifest: manifest_bad,
|
||||
window: window
|
||||
}
|
||||
Social.installProvider(data, function(addonManifest) {
|
||||
gBrowser.removeTab(tab);
|
||||
finishTest(false);
|
||||
});
|
||||
|
@ -130,7 +130,7 @@ var tests = {
|
||||
testShareDisabledOnActivation: function(next) {
|
||||
// starting on about:blank page, share should be visible but disabled when
|
||||
// adding provider
|
||||
is(gBrowser.contentDocument.location.href, "about:blank");
|
||||
is(gBrowser.currentURI.spec, "about:blank");
|
||||
|
||||
// initialize the button into the navbar
|
||||
CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
|
||||
|
@ -86,9 +86,6 @@ function activateIFrameProvider(domain, callback) {
|
||||
}
|
||||
|
||||
function waitForProviderLoad(cb) {
|
||||
Services.obs.addObserver(function providerSet(subject, topic, data) {
|
||||
Services.obs.removeObserver(providerSet, "social:provider-enabled");
|
||||
info("social:provider-enabled observer was notified");
|
||||
waitForCondition(function() {
|
||||
let sbrowser = document.getElementById("social-sidebar-browser");
|
||||
let provider = SocialSidebar.provider;
|
||||
@ -104,7 +101,6 @@ function waitForProviderLoad(cb) {
|
||||
executeSoon(cb);
|
||||
},
|
||||
"waitForProviderLoad: provider profile was not set");
|
||||
}, "social:provider-enabled", false);
|
||||
}
|
||||
|
||||
|
||||
@ -149,14 +145,15 @@ function activateOneProvider(manifest, finishActivation, aCallback) {
|
||||
let panel = document.getElementById("servicesInstall-notification");
|
||||
PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
|
||||
PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
|
||||
info("servicesInstall-notification panel opened");
|
||||
ok(!panel.hidden, "servicesInstall-notification panel opened");
|
||||
if (finishActivation)
|
||||
panel.button.click();
|
||||
else
|
||||
panel.closebutton.click();
|
||||
});
|
||||
|
||||
activateProvider(manifest.origin, function() {
|
||||
PopupNotifications.panel.addEventListener("popuphidden", function _hidden() {
|
||||
PopupNotifications.panel.removeEventListener("popuphidden", _hidden);
|
||||
ok(panel.hidden, "servicesInstall-notification panel hidden");
|
||||
if (!finishActivation) {
|
||||
ok(panel.hidden, "activation panel is not showing");
|
||||
executeSoon(aCallback);
|
||||
@ -169,6 +166,11 @@ function activateOneProvider(manifest, finishActivation, aCallback) {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// the test will continue as the popup events fire...
|
||||
activateProvider(manifest.origin, function() {
|
||||
info("waiting on activation panel to open/close...");
|
||||
});
|
||||
}
|
||||
|
||||
let gTestDomains = ["https://example.com", "https://test1.example.com", "https://test2.example.com"];
|
||||
|
@ -0,0 +1,80 @@
|
||||
/* 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 SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
|
||||
|
||||
let manifest = { // used for testing install
|
||||
name: "provider test1",
|
||||
origin: "https://test1.example.com",
|
||||
workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||
markURL: "https://test1.example.com/browser/browser/base/content/test/social/social_mark.html?url=%{url}",
|
||||
markedIcon: "https://test1.example.com/browser/browser/base/content/test/social/unchecked.jpg",
|
||||
unmarkedIcon: "https://test1.example.com/browser/browser/base/content/test/social/checked.jpg",
|
||||
|
||||
iconURL: "https://test1.example.com/browser/browser/base/content/test/general/moz.png",
|
||||
version: 1
|
||||
};
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
runSocialTestWithProvider(manifest, function (finishcb) {
|
||||
runSocialTests(tests, undefined, undefined, function () {
|
||||
finishcb();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var tests = {
|
||||
testMarkMicrodata: function(next) {
|
||||
// emulates context menu action using target element, calling SocialMarks.markLink
|
||||
let provider = Social._getProviderFromOrigin(manifest.origin);
|
||||
let port = provider.getWorkerPort();
|
||||
let target, testTab;
|
||||
|
||||
// browser_share tests microdata on the full page, this is testing a
|
||||
// specific target element.
|
||||
let expecting = JSON.stringify({
|
||||
"url": "https://example.com/browser/browser/base/content/test/social/microdata.html",
|
||||
"microdata": {
|
||||
"items": [{
|
||||
"types": ["http://schema.org/UserComments"],
|
||||
"properties": {
|
||||
"url": ["https://example.com/browser/browser/base/content/test/social/microdata.html#c2"],
|
||||
"creator": [{
|
||||
"types": ["http://schema.org/Person"],
|
||||
"properties": {
|
||||
"name": ["Charlotte"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"commentTime": ["2013-08-29"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
port.onmessage = function (e) {
|
||||
let topic = e.data.topic;
|
||||
switch (topic) {
|
||||
case "got-share-data-message":
|
||||
is(JSON.stringify(e.data.result), expecting, "microdata data ok");
|
||||
gBrowser.removeTab(testTab);
|
||||
port.close();
|
||||
next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
port.postMessage({topic: "test-init"});
|
||||
|
||||
let url = "https://example.com/browser/browser/base/content/test/social/microdata.html"
|
||||
addTab(url, function(tab) {
|
||||
testTab = tab;
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
target = doc.getElementById("test-comment");
|
||||
SocialMarks.markLink(manifest.origin, url, target);
|
||||
});
|
||||
}
|
||||
}
|
@ -43,15 +43,6 @@ function makeMarkProvider(origin) {
|
||||
}
|
||||
}
|
||||
|
||||
function openWindowAndWaitForInit(callback) {
|
||||
let topic = "browser-delayed-startup-finished";
|
||||
let w = OpenBrowserWindow();
|
||||
Services.obs.addObserver(function providerSet(subject, topic, data) {
|
||||
Services.obs.removeObserver(providerSet, topic);
|
||||
executeSoon(() => callback(w));
|
||||
}, topic, false);
|
||||
}
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
@ -105,7 +96,14 @@ var tests = {
|
||||
let activationURL = manifest3.origin + "/browser/browser/base/content/test/social/social_activate.html"
|
||||
addTab(activationURL, function(tab) {
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
Social.installProvider(doc, manifest3, function(addonManifest) {
|
||||
let data = {
|
||||
origin: doc.nodePrincipal.origin,
|
||||
url: doc.location.href,
|
||||
manifest: manifest3,
|
||||
window: window
|
||||
}
|
||||
|
||||
Social.installProvider(data, function(addonManifest) {
|
||||
// enable the provider so we know the button would have appeared
|
||||
SocialService.enableProvider(manifest3.origin, function(provider) {
|
||||
is(provider.origin, manifest3.origin, "provider is installed");
|
||||
@ -133,7 +131,14 @@ var tests = {
|
||||
let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
|
||||
addTab(activationURL, function(tab) {
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
Social.installProvider(doc, manifest2, function(addonManifest) {
|
||||
let data = {
|
||||
origin: doc.nodePrincipal.origin,
|
||||
url: doc.location.href,
|
||||
manifest: manifest2,
|
||||
window: window
|
||||
}
|
||||
|
||||
Social.installProvider(data, function(addonManifest) {
|
||||
SocialService.enableProvider(manifest2.origin, function(provider) {
|
||||
is(provider.origin, manifest2.origin, "provider is installed");
|
||||
let id = SocialMarks._toolbarHelper.idFromOrigin(manifest2.origin);
|
||||
@ -280,56 +285,6 @@ var tests = {
|
||||
});
|
||||
},
|
||||
|
||||
testMarkMicrodata: function(next) {
|
||||
let provider = Social._getProviderFromOrigin(manifest2.origin);
|
||||
let port = provider.getWorkerPort();
|
||||
let target, testTab;
|
||||
|
||||
// browser_share tests microdata on the full page, this is testing a
|
||||
// specific target element.
|
||||
let expecting = JSON.stringify({
|
||||
"url": "https://example.com/browser/browser/base/content/test/social/microdata.html",
|
||||
"microdata": {
|
||||
"items": [{
|
||||
"types": ["http://schema.org/UserComments"],
|
||||
"properties": {
|
||||
"url": ["https://example.com/browser/browser/base/content/test/social/microdata.html#c2"],
|
||||
"creator": [{
|
||||
"types": ["http://schema.org/Person"],
|
||||
"properties": {
|
||||
"name": ["Charlotte"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"commentTime": ["2013-08-29"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
port.onmessage = function (e) {
|
||||
let topic = e.data.topic;
|
||||
switch (topic) {
|
||||
case "got-share-data-message":
|
||||
is(JSON.stringify(e.data.result), expecting, "microdata data ok");
|
||||
gBrowser.removeTab(testTab);
|
||||
port.close();
|
||||
next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
port.postMessage({topic: "test-init"});
|
||||
|
||||
let url = "https://example.com/browser/browser/base/content/test/social/microdata.html"
|
||||
addTab(url, function(tab) {
|
||||
testTab = tab;
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
target = doc.getElementById("test-comment");
|
||||
SocialMarks.markLink(manifest2.origin, url, target);
|
||||
});
|
||||
},
|
||||
|
||||
testButtonOnDisable: function(next) {
|
||||
// enable the provider now
|
||||
let provider = Social._getProviderFromOrigin(manifest2.origin);
|
||||
@ -379,7 +334,14 @@ var tests = {
|
||||
let toolbar = document.getElementById("nav-bar");
|
||||
addTab(activationURL, function(tab) {
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
Social.installProvider(doc, manifest, function(addonManifest) {
|
||||
let data = {
|
||||
origin: doc.nodePrincipal.origin,
|
||||
url: doc.location.href,
|
||||
manifest: manifest,
|
||||
window: window
|
||||
}
|
||||
|
||||
Social.installProvider(data, function(addonManifest) {
|
||||
// enable the provider so we know the button would have appeared
|
||||
SocialService.enableProvider(manifest.origin, function(provider) {
|
||||
waitForCondition(function() { return CustomizableUI.getWidget(id) },
|
||||
|
@ -66,7 +66,13 @@ var tests = {
|
||||
let activationURL = manifest3.origin + "/browser/browser/base/content/test/social/social_activate.html"
|
||||
addTab(activationURL, function(tab) {
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
Social.installProvider(doc, manifest3, function(addonManifest) {
|
||||
let data = {
|
||||
origin: doc.nodePrincipal.origin,
|
||||
url: doc.location.href,
|
||||
manifest: manifest3,
|
||||
window: window
|
||||
}
|
||||
Social.installProvider(data, function(addonManifest) {
|
||||
// enable the provider so we know the button would have appeared
|
||||
SocialService.enableProvider(manifest3.origin, function(provider) {
|
||||
is(provider.origin, manifest3.origin, "provider is installed");
|
||||
@ -93,7 +99,14 @@ var tests = {
|
||||
let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
|
||||
addTab(activationURL, function(tab) {
|
||||
let doc = tab.linkedBrowser.contentDocument;
|
||||
Social.installProvider(doc, manifest2, function(addonManifest) {
|
||||
let data = {
|
||||
origin: doc.nodePrincipal.origin,
|
||||
url: doc.location.href,
|
||||
manifest: manifest2,
|
||||
window: window
|
||||
}
|
||||
|
||||
Social.installProvider(data, function(addonManifest) {
|
||||
SocialService.enableProvider(manifest2.origin, function(provider) {
|
||||
is(provider.origin, manifest2.origin, "provider is installed");
|
||||
let id = SocialStatus._toolbarHelper.idFromOrigin(manifest2.origin);
|
||||
|
@ -31,16 +31,11 @@ function activate(node) {
|
||||
node.dispatchEvent(event);
|
||||
}
|
||||
|
||||
function oldActivate(node) {
|
||||
var event = new CustomEvent("ActivateSocialFeature");
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
</script>
|
||||
<body>
|
||||
|
||||
nothing to see here
|
||||
|
||||
<button id="activation-old" onclick="oldActivate(this)">Activate The Demo Provider</button>
|
||||
<button id="activation" onclick="activate(this)">Activate The Demo Provider</button>
|
||||
|
||||
</body>
|
||||
|
@ -373,6 +373,7 @@ function injectLoopAPI(targetWindow) {
|
||||
* Callback parameters:
|
||||
* - err null on successful registration, non-null otherwise.
|
||||
*
|
||||
* @param {LOOP_SESSION_TYPE} sessionType
|
||||
* @param {Function} callback Will be called once registration is complete,
|
||||
* or straight away if registration has already
|
||||
* happened.
|
||||
@ -380,10 +381,10 @@ function injectLoopAPI(targetWindow) {
|
||||
ensureRegistered: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function(callback) {
|
||||
value: function(sessionType, callback) {
|
||||
// We translate from a promise to a callback, as we can't pass promises from
|
||||
// Promise.jsm across the priv versus unpriv boundary.
|
||||
MozLoopService.promiseRegisteredWithServers().then(() => {
|
||||
MozLoopService.promiseRegisteredWithServers(sessionType).then(() => {
|
||||
callback(null);
|
||||
}, err => {
|
||||
callback(cloneValueInto(err, targetWindow));
|
||||
|
@ -106,10 +106,6 @@ function getJSONPref(aName) {
|
||||
return !!value ? JSON.parse(value) : null;
|
||||
}
|
||||
|
||||
// The current deferred for the registration process. This is set if in progress
|
||||
// or the registration was successful. This is null if a registration attempt was
|
||||
// unsuccessful.
|
||||
let gRegisteredDeferred = null;
|
||||
let gHawkClient = null;
|
||||
let gLocalizedStrings = null;
|
||||
let gFxAEnabled = true;
|
||||
@ -132,6 +128,13 @@ let MozLoopServiceInternal = {
|
||||
webSocket: undefined,
|
||||
},
|
||||
|
||||
/**
|
||||
* The current deferreds for the registration processes. This is set if in progress
|
||||
* or the registration was successful. This is null if a registration attempt was
|
||||
* unsuccessful.
|
||||
*/
|
||||
deferredRegistrations: new Map(),
|
||||
|
||||
get pushHandler() this.mocks.pushHandler || MozLoopPushHandler,
|
||||
|
||||
// The uri of the Loop server.
|
||||
@ -310,35 +313,37 @@ let MozLoopServiceInternal = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts registration of Loop with the push server, and then will register
|
||||
* with the Loop server as a GUEST. It will return early if already registered.
|
||||
* Get endpoints with the push server and register for notifications.
|
||||
* For now we register as both a Guest and FxA user and all must succeed.
|
||||
*
|
||||
* @returns {Promise} a promise that is resolved with no params on completion, or
|
||||
* rejected with an error code or string.
|
||||
* @return {Promise} resolves with all push endpoints
|
||||
* rejects if any of the push registrations failed
|
||||
*/
|
||||
promiseRegisteredWithServers: function() {
|
||||
if (gRegisteredDeferred) {
|
||||
return gRegisteredDeferred.promise;
|
||||
}
|
||||
|
||||
promiseRegisteredWithPushServer: function() {
|
||||
// Wrap push notification registration call-back in a Promise.
|
||||
let registerForNotification = function(channelID, onNotification) {
|
||||
function registerForNotification(channelID, onNotification) {
|
||||
log.debug("registerForNotification", channelID);
|
||||
return new Promise((resolve, reject) => {
|
||||
let onRegistered = (error, pushUrl) => {
|
||||
function onRegistered(error, pushUrl) {
|
||||
log.debug("registerForNotification onRegistered:", error, pushUrl);
|
||||
if (error) {
|
||||
reject(Error(error));
|
||||
} else {
|
||||
resolve(pushUrl);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// If we're already registered, resolve with the existing push URL
|
||||
let pushURL = MozLoopServiceInternal.pushHandler.registeredChannels[channelID];
|
||||
if (pushURL) {
|
||||
log.debug("Using the existing push endpoint for channelID:", channelID);
|
||||
resolve(pushURL);
|
||||
return;
|
||||
}
|
||||
|
||||
MozLoopServiceInternal.pushHandler.register(channelID, onRegistered, onNotification);
|
||||
});
|
||||
};
|
||||
|
||||
gRegisteredDeferred = Promise.defer();
|
||||
// We grab the promise early in case .initialize or its results sets
|
||||
// it back to null on error.
|
||||
let result = gRegisteredDeferred.promise;
|
||||
}
|
||||
|
||||
let options = this.mocks.webSocket ? { mockWebSocket: this.mocks.webSocket } : {};
|
||||
this.pushHandler.initialize(options);
|
||||
@ -355,27 +360,42 @@ let MozLoopServiceInternal = {
|
||||
let roomsRegFxA = registerForNotification(MozLoopService.channelIDs.roomsFxA,
|
||||
roomsPushNotification);
|
||||
|
||||
Promise.all([callsRegGuest, roomsRegGuest, callsRegFxA, roomsRegFxA])
|
||||
.then((pushUrls) => {
|
||||
return this.registerWithLoopServer(LOOP_SESSION_TYPE.GUEST,{
|
||||
calls: pushUrls[0],
|
||||
rooms: pushUrls[1],
|
||||
});
|
||||
return Promise.all([callsRegGuest, roomsRegGuest, callsRegFxA, roomsRegFxA]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts registration of Loop with the push server, and then will register
|
||||
* with the Loop server. It will return early if already registered.
|
||||
*
|
||||
* @param {LOOP_SESSION_TYPE} sessionType
|
||||
* @returns {Promise} a promise that is resolved with no params on completion, or
|
||||
* rejected with an error code or string.
|
||||
*/
|
||||
promiseRegisteredWithServers: function(sessionType = LOOP_SESSION_TYPE.GUEST) {
|
||||
if (this.deferredRegistrations.has(sessionType)) {
|
||||
log.debug("promiseRegisteredWithServers: registration already completed or in progress:", sessionType);
|
||||
return this.deferredRegistrations.get(sessionType).promise;
|
||||
}
|
||||
|
||||
let result = null;
|
||||
let deferred = Promise.defer();
|
||||
log.debug("assigning to deferredRegistrations for sessionType:", sessionType);
|
||||
this.deferredRegistrations.set(sessionType, deferred);
|
||||
|
||||
// We grab the promise early in case one of the callers below delete it from the map.
|
||||
result = deferred.promise;
|
||||
|
||||
this.promiseRegisteredWithPushServer().then(() => {
|
||||
return this.registerWithLoopServer(sessionType);
|
||||
}).then(() => {
|
||||
// storeSessionToken could have rejected and nulled the promise if the token was malformed.
|
||||
if (!gRegisteredDeferred) {
|
||||
return;
|
||||
}
|
||||
gRegisteredDeferred.resolve("registered to guest status");
|
||||
deferred.resolve("registered to status:" + sessionType);
|
||||
// No need to clear the promise here, everything was good, so we don't need
|
||||
// to re-register.
|
||||
}, error => {
|
||||
log.error("Failed to register with Loop server: ", error);
|
||||
// registerWithLoopServer may have already made this null.
|
||||
if (gRegisteredDeferred) {
|
||||
gRegisteredDeferred.reject(error);
|
||||
}
|
||||
gRegisteredDeferred = null;
|
||||
log.error("Failed to register with Loop server with sessionType " + sessionType, error);
|
||||
deferred.reject(error);
|
||||
this.deferredRegistrations.delete(sessionType);
|
||||
log.debug("Cleared deferredRegistration for sessionType:", sessionType);
|
||||
});
|
||||
|
||||
return result;
|
||||
@ -397,6 +417,7 @@ let MozLoopServiceInternal = {
|
||||
* rejected with this JSON-parsed response.
|
||||
*/
|
||||
hawkRequest: function(sessionType, path, method, payloadObj) {
|
||||
log.debug("hawkRequest: " + path, sessionType);
|
||||
if (!gHawkClient) {
|
||||
gHawkClient = new HawkClient(this.loopServerUri);
|
||||
}
|
||||
@ -488,15 +509,12 @@ let MozLoopServiceInternal = {
|
||||
} else {
|
||||
// XXX Bubble the precise details up to the UI somehow (bug 1013248).
|
||||
log.warn("Loop server sent an invalid session token");
|
||||
gRegisteredDeferred.reject("session-token-wrong-size");
|
||||
gRegisteredDeferred = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Clear the loop session token so we don't use it for Hawk Requests anymore.
|
||||
*
|
||||
@ -512,45 +530,62 @@ let MozLoopServiceInternal = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Registers with the Loop server either as a guest or a FxA user.
|
||||
* Registers with the Loop server either as a guest or a FxA user. This method should only be
|
||||
* called by promiseRegisteredWithServers since it prevents calling this while a registration is
|
||||
* already in progress.
|
||||
*
|
||||
* @private
|
||||
* @param {LOOP_SESSION_TYPE} sessionType The type of session e.g. guest or FxA
|
||||
* @param {String} pushUrls The push url given by the push server.
|
||||
* @param {Boolean} [retry=true] Whether to retry if authentication fails.
|
||||
* @return {Promise}
|
||||
*/
|
||||
registerWithLoopServer: function(sessionType, pushUrls, retry = true) {
|
||||
registerWithLoopServer: function(sessionType, retry = true) {
|
||||
log.debug("registerWithLoopServer with sessionType:", sessionType);
|
||||
|
||||
let callsPushURL, roomsPushURL;
|
||||
if (sessionType == LOOP_SESSION_TYPE.FXA) {
|
||||
callsPushURL = this.pushHandler.registeredChannels[MozLoopService.channelIDs.callsFxA];
|
||||
roomsPushURL = this.pushHandler.registeredChannels[MozLoopService.channelIDs.roomsFxA];
|
||||
} else if (sessionType == LOOP_SESSION_TYPE.GUEST) {
|
||||
callsPushURL = this.pushHandler.registeredChannels[MozLoopService.channelIDs.callsGuest];
|
||||
roomsPushURL = this.pushHandler.registeredChannels[MozLoopService.channelIDs.roomsGuest];
|
||||
}
|
||||
|
||||
if (!callsPushURL || !roomsPushURL) {
|
||||
return Promise.reject("Invalid sessionType or missing push URLs for registerWithLoopServer: " + sessionType);
|
||||
}
|
||||
|
||||
// create a registration payload with a backwards compatible attribute (simplePushURL)
|
||||
// that will register only the calls notification.
|
||||
let msg = {
|
||||
simplePushURL: pushUrls.calls,
|
||||
simplePushURLs: pushUrls
|
||||
simplePushURL: callsPushURL,
|
||||
simplePushURLs: {
|
||||
calls: callsPushURL,
|
||||
rooms: roomsPushURL,
|
||||
},
|
||||
};
|
||||
return this.hawkRequest(sessionType, "/registration", "POST", msg)
|
||||
.then((response) => {
|
||||
// If this failed we got an invalid token. storeSessionToken rejects
|
||||
// the gRegisteredDeferred promise for us, so here we just need to
|
||||
// early return.
|
||||
// If this failed we got an invalid token.
|
||||
if (!this.storeSessionToken(sessionType, response.headers)) {
|
||||
return;
|
||||
return Promise.reject("session-token-wrong-size");
|
||||
}
|
||||
|
||||
log.debug("Successfully registered with server for sessionType", sessionType);
|
||||
this.clearError("registration");
|
||||
return undefined;
|
||||
}, (error) => {
|
||||
// There's other errors than invalid auth token, but we should only do the reset
|
||||
// as a last resort.
|
||||
if (error.code === 401) {
|
||||
// Authorization failed, invalid token, we need to try again with a new token.
|
||||
if (retry) {
|
||||
return this.registerWithLoopServer(sessionType, pushUrls, false);
|
||||
return this.registerWithLoopServer(sessionType, false);
|
||||
}
|
||||
}
|
||||
|
||||
log.error("Failed to register with the loop server. Error: ", error);
|
||||
this.setError("registration", error);
|
||||
gRegisteredDeferred.reject(error);
|
||||
gRegisteredDeferred = null;
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
@ -567,7 +602,7 @@ let MozLoopServiceInternal = {
|
||||
* Guest or FxA have been unregistered with the LoopServer.
|
||||
*
|
||||
* @param {LOOP_SESSION_TYPE} sessionType The type of session e.g. guest or FxA
|
||||
* @param {String} pushURLs The push URL previously given by the push server.
|
||||
* @param {String} pushURL The push URL previously given by the push server.
|
||||
* This may not be necessary to unregister in the future.
|
||||
* @return {Promise} resolving when the unregistration request finishes
|
||||
*/
|
||||
@ -970,28 +1005,27 @@ this.MozLoopService = {
|
||||
});
|
||||
|
||||
try {
|
||||
yield this.promiseRegisteredWithServers();
|
||||
if (MozLoopServiceInternal.urlExpiryTimeIsInFuture()) {
|
||||
yield this.promiseRegisteredWithServers(LOOP_SESSION_TYPE.GUEST);
|
||||
} else {
|
||||
log.debug("delayedInitialize: URL expiry time isn't in the future so not registering as a guest");
|
||||
}
|
||||
} catch (ex) {
|
||||
log.debug("MozLoopService: Failure of initial registration", ex);
|
||||
log.debug("MozLoopService: Failure of guest registration", ex);
|
||||
deferredInitialization.reject(ex);
|
||||
yield completedPromise;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MozLoopServiceInternal.fxAOAuthTokenData) {
|
||||
log.debug("MozLoopService: Initialized without an already logged-in account");
|
||||
deferredInitialization.resolve("initialized to guest status");
|
||||
log.debug("delayedInitialize: Initialized without an already logged-in account");
|
||||
deferredInitialization.resolve("initialized without FxA status");
|
||||
yield completedPromise;
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("MozLoopService: Initializing with already logged-in account");
|
||||
let pushURLs = {
|
||||
calls: MozLoopServiceInternal.pushHandler.registeredChannels[this.channelIDs.callsFxA],
|
||||
rooms: MozLoopServiceInternal.pushHandler.registeredChannels[this.channelIDs.roomsFxA]
|
||||
};
|
||||
|
||||
MozLoopServiceInternal.registerWithLoopServer(LOOP_SESSION_TYPE.FXA, pushURLs).then(() => {
|
||||
MozLoopServiceInternal.promiseRegisteredWithServers(LOOP_SESSION_TYPE.FXA).then(() => {
|
||||
deferredInitialization.resolve("initialized to logged-in status");
|
||||
}, error => {
|
||||
log.debug("MozLoopService: error logging in using cached auth token");
|
||||
@ -1113,8 +1147,8 @@ this.MozLoopService = {
|
||||
/**
|
||||
* @see MozLoopServiceInternal.promiseRegisteredWithServers
|
||||
*/
|
||||
promiseRegisteredWithServers: function() {
|
||||
return MozLoopServiceInternal.promiseRegisteredWithServers();
|
||||
promiseRegisteredWithServers: function(sessionType = LOOP_SESSION_TYPE.GUEST) {
|
||||
return MozLoopServiceInternal.promiseRegisteredWithServers(sessionType);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1310,19 +1344,11 @@ this.MozLoopService = {
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = tokenData;
|
||||
return tokenData;
|
||||
}).then(tokenData => {
|
||||
return gRegisteredDeferred.promise.then(Task.async(function*() {
|
||||
let callsUrl = MozLoopServiceInternal.pushHandler.registeredChannels[MozLoopService.channelIDs.callsFxA],
|
||||
roomsUrl = MozLoopServiceInternal.pushHandler.registeredChannels[MozLoopService.channelIDs.roomsFxA];
|
||||
if (callsUrl && roomsUrl) {
|
||||
yield MozLoopServiceInternal.registerWithLoopServer(
|
||||
LOOP_SESSION_TYPE.FXA, {calls: callsUrl, rooms: roomsUrl});
|
||||
} else {
|
||||
throw new Error("No pushUrls for FxA registration");
|
||||
}
|
||||
return MozLoopServiceInternal.promiseRegisteredWithServers(LOOP_SESSION_TYPE.FXA).then(() => {
|
||||
MozLoopServiceInternal.clearError("login");
|
||||
MozLoopServiceInternal.clearError("profile");
|
||||
return MozLoopServiceInternal.fxAOAuthTokenData;
|
||||
}));
|
||||
});
|
||||
}).then(tokenData => {
|
||||
let client = new FxAccountsProfileClient({
|
||||
serverURL: gFxAOAuthClient.parameters.profile_uri,
|
||||
@ -1341,6 +1367,7 @@ this.MozLoopService = {
|
||||
}).catch(error => {
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = null;
|
||||
MozLoopServiceInternal.fxAOAuthProfile = null;
|
||||
MozLoopServiceInternal.deferredRegistrations.delete(LOOP_SESSION_TYPE.FXA);
|
||||
throw error;
|
||||
}).catch((error) => {
|
||||
MozLoopServiceInternal.setError("login", error);
|
||||
@ -1372,21 +1399,22 @@ this.MozLoopService = {
|
||||
throw error;
|
||||
} finally {
|
||||
MozLoopServiceInternal.clearSessionToken(LOOP_SESSION_TYPE.FXA);
|
||||
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = null;
|
||||
MozLoopServiceInternal.fxAOAuthProfile = null;
|
||||
MozLoopServiceInternal.deferredRegistrations.delete(LOOP_SESSION_TYPE.FXA);
|
||||
|
||||
// Reset the client since the initial promiseFxAOAuthParameters() call is
|
||||
// what creates a new session.
|
||||
gFxAOAuthClient = null;
|
||||
gFxAOAuthClientPromise = null;
|
||||
|
||||
// clearError calls notifyStatusChanged so should be done last when the
|
||||
// state is clean.
|
||||
MozLoopServiceInternal.clearError("registration");
|
||||
MozLoopServiceInternal.clearError("login");
|
||||
MozLoopServiceInternal.clearError("profile");
|
||||
}
|
||||
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = null;
|
||||
MozLoopServiceInternal.fxAOAuthProfile = null;
|
||||
|
||||
// Reset the client since the initial promiseFxAOAuthParameters() call is
|
||||
// what creates a new session.
|
||||
gFxAOAuthClient = null;
|
||||
gFxAOAuthClientPromise = null;
|
||||
|
||||
// clearError calls notifyStatusChanged so should be done last when the
|
||||
// state is clean.
|
||||
MozLoopServiceInternal.clearError("registration");
|
||||
MozLoopServiceInternal.clearError("login");
|
||||
MozLoopServiceInternal.clearError("profile");
|
||||
}),
|
||||
|
||||
openFxASettings: Task.async(function() {
|
||||
|
@ -87,10 +87,11 @@ loop.Client = (function($) {
|
||||
* Callback parameters:
|
||||
* - err null on successful registration, non-null otherwise.
|
||||
*
|
||||
* @param {LOOP_SESSION_TYPE} sessionType Guest or FxA
|
||||
* @param {Function} cb Callback(err)
|
||||
*/
|
||||
_ensureRegistered: function(cb) {
|
||||
this.mozLoop.ensureRegistered(function(error) {
|
||||
_ensureRegistered: function(sessionType, cb) {
|
||||
this.mozLoop.ensureRegistered(sessionType, function(error) {
|
||||
if (error) {
|
||||
console.log("Error registering with Loop server, code: " + error);
|
||||
cb(error);
|
||||
@ -110,17 +111,11 @@ loop.Client = (function($) {
|
||||
* -- callUrl: The url of the call
|
||||
* -- expiresAt: The amount of hours until expiry of the url
|
||||
*
|
||||
* @param {LOOP_SESSION_TYPE} sessionType
|
||||
* @param {string} nickname the nickname of the future caller
|
||||
* @param {Function} cb Callback(err, callUrlData)
|
||||
*/
|
||||
_requestCallUrlInternal: function(nickname, cb) {
|
||||
var sessionType;
|
||||
if (this.mozLoop.userProfile) {
|
||||
sessionType = this.mozLoop.LOOP_SESSION_TYPE.FXA;
|
||||
} else {
|
||||
sessionType = this.mozLoop.LOOP_SESSION_TYPE.GUEST;
|
||||
}
|
||||
|
||||
_requestCallUrlInternal: function(sessionType, nickname, cb) {
|
||||
this.mozLoop.hawkRequest(sessionType, "/call-url/", "POST",
|
||||
{callerId: nickname},
|
||||
function (error, responseText) {
|
||||
@ -159,7 +154,7 @@ loop.Client = (function($) {
|
||||
* it does not make sense to display an error.
|
||||
**/
|
||||
deleteCallUrl: function(token, sessionType, cb) {
|
||||
this._ensureRegistered(function(err) {
|
||||
this._ensureRegistered(sessionType, function(err) {
|
||||
if (err) {
|
||||
cb(err);
|
||||
return;
|
||||
@ -206,13 +201,20 @@ loop.Client = (function($) {
|
||||
* @param {Function} cb Callback(err, callUrlData)
|
||||
*/
|
||||
requestCallUrl: function(nickname, cb) {
|
||||
this._ensureRegistered(function(err) {
|
||||
var sessionType;
|
||||
if (this.mozLoop.userProfile) {
|
||||
sessionType = this.mozLoop.LOOP_SESSION_TYPE.FXA;
|
||||
} else {
|
||||
sessionType = this.mozLoop.LOOP_SESSION_TYPE.GUEST;
|
||||
}
|
||||
|
||||
this._ensureRegistered(sessionType, function(err) {
|
||||
if (err) {
|
||||
cb(err);
|
||||
return;
|
||||
}
|
||||
|
||||
this._requestCallUrlInternal(nickname, cb);
|
||||
this._requestCallUrlInternal(sessionType, nickname, cb);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
|
@ -27,7 +27,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
*/
|
||||
var HomeView = React.createClass({displayName: 'HomeView',
|
||||
render: function() {
|
||||
loop.standaloneMedia.multiplexGum.reset();
|
||||
multiplexGum.reset();
|
||||
return (
|
||||
React.DOM.p(null, mozL10n.get("welcome", {clientShortname: mozL10n.get("clientShortname2")}))
|
||||
);
|
||||
@ -291,7 +291,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
},
|
||||
|
||||
_cancelOutgoingCall: function() {
|
||||
loop.standaloneMedia.multiplexGum.reset();
|
||||
multiplexGum.reset();
|
||||
this.props.websocket.cancel();
|
||||
},
|
||||
|
||||
@ -852,6 +852,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
* timeout, cancel, media-fail, user-unknown, closed)
|
||||
*/
|
||||
_handleCallTerminated: function(reason) {
|
||||
multiplexGum.reset();
|
||||
|
||||
if (reason === "cancel") {
|
||||
this.setState({callStatus: "start"});
|
||||
return;
|
||||
|
@ -27,7 +27,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
*/
|
||||
var HomeView = React.createClass({
|
||||
render: function() {
|
||||
loop.standaloneMedia.multiplexGum.reset();
|
||||
multiplexGum.reset();
|
||||
return (
|
||||
<p>{mozL10n.get("welcome", {clientShortname: mozL10n.get("clientShortname2")})}</p>
|
||||
);
|
||||
@ -291,7 +291,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
},
|
||||
|
||||
_cancelOutgoingCall: function() {
|
||||
loop.standaloneMedia.multiplexGum.reset();
|
||||
multiplexGum.reset();
|
||||
this.props.websocket.cancel();
|
||||
},
|
||||
|
||||
@ -852,6 +852,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
* timeout, cancel, media-fail, user-unknown, closed)
|
||||
*/
|
||||
_handleCallTerminated: function(reason) {
|
||||
multiplexGum.reset();
|
||||
|
||||
if (reason === "cancel") {
|
||||
this.setState({callStatus: "start"});
|
||||
return;
|
||||
|
@ -32,7 +32,7 @@ describe("loop.Client", function() {
|
||||
.returns(null)
|
||||
.withArgs("hawk-session-token")
|
||||
.returns(fakeToken),
|
||||
ensureRegistered: sinon.stub().callsArgWith(0, null),
|
||||
ensureRegistered: sinon.stub().callsArgWith(1, null),
|
||||
noteCallUrlExpiry: sinon.spy(),
|
||||
hawkRequest: sinon.stub(),
|
||||
LOOP_SESSION_TYPE: {
|
||||
@ -62,7 +62,7 @@ describe("loop.Client", function() {
|
||||
});
|
||||
|
||||
it("should send an error when registration fails", function() {
|
||||
mozLoop.ensureRegistered.callsArgWith(0, "offline");
|
||||
mozLoop.ensureRegistered.callsArgWith(1, "offline");
|
||||
|
||||
client.deleteCallUrl("fakeToken", mozLoop.LOOP_SESSION_TYPE.FXA, callback);
|
||||
|
||||
@ -113,7 +113,7 @@ describe("loop.Client", function() {
|
||||
});
|
||||
|
||||
it("should send an error when registration fails", function() {
|
||||
mozLoop.ensureRegistered.callsArgWith(0, "offline");
|
||||
mozLoop.ensureRegistered.callsArgWith(1, "offline");
|
||||
|
||||
client.requestCallUrl("foo", callback);
|
||||
|
||||
|
@ -44,6 +44,10 @@ describe("loop.conversation", function() {
|
||||
calls: {
|
||||
clearCallInProgress: sinon.stub()
|
||||
},
|
||||
LOOP_SESSION_TYPE: {
|
||||
GUEST: 1,
|
||||
FXA: 2
|
||||
},
|
||||
startAlerting: sinon.stub(),
|
||||
stopAlerting: sinon.stub(),
|
||||
ensureRegistered: sinon.stub(),
|
||||
|
@ -342,7 +342,7 @@ add_task(function* logoutWithIncorrectPushURL() {
|
||||
const fxASessionPref = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
|
||||
Services.prefs.setCharPref(fxASessionPref, "X".repeat(HAWK_TOKEN_LENGTH));
|
||||
|
||||
yield MozLoopServiceInternal.registerWithLoopServer(LOOP_SESSION_TYPE.FXA, {calls: pushURL});
|
||||
yield MozLoopServiceInternal.registerWithLoopServer(LOOP_SESSION_TYPE.FXA);
|
||||
let registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
|
||||
ise(registrationResponse.response.simplePushURLs.calls, pushURL, "Check registered push URL");
|
||||
mockPushHandler.registeredChannels[MozLoopService.channelIDs.callsFxA] = "http://www.example.com/invalid";
|
||||
@ -365,7 +365,7 @@ add_task(function* logoutWithNoPushURL() {
|
||||
const fxASessionPref = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
|
||||
Services.prefs.setCharPref(fxASessionPref, "X".repeat(HAWK_TOKEN_LENGTH));
|
||||
|
||||
yield MozLoopServiceInternal.registerWithLoopServer(LOOP_SESSION_TYPE.FXA, {calls: pushURL});
|
||||
yield MozLoopServiceInternal.registerWithLoopServer(LOOP_SESSION_TYPE.FXA);
|
||||
let registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
|
||||
ise(registrationResponse.response.simplePushURLs.calls, pushURL, "Check registered push URL");
|
||||
mockPushHandler.registeredChannels[MozLoopService.channelIDs.callsFxA] = null;
|
||||
|
@ -120,7 +120,7 @@ function* resetFxA() {
|
||||
global.gHawkClient = null;
|
||||
global.gFxAOAuthClientPromise = null;
|
||||
global.gFxAOAuthClient = null;
|
||||
global.gRegisteredDeferred = null;
|
||||
MozLoopServiceInternal.deferredRegistrations.delete(LOOP_SESSION_TYPE.FXA);
|
||||
MozLoopServiceInternal.fxAOAuthProfile = null;
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = null;
|
||||
const fxASessionPref = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
|
||||
|
@ -231,6 +231,20 @@ describe("loop.webapp", function() {
|
||||
loop.webapp.FailedConversationView);
|
||||
});
|
||||
|
||||
it("should reset multiplexGum when a call is rejected",
|
||||
function() {
|
||||
var multiplexGum = new standaloneMedia._MultiplexGum();
|
||||
standaloneMedia.setSingleton(multiplexGum);
|
||||
sandbox.stub(standaloneMedia._MultiplexGum.prototype, "reset");
|
||||
|
||||
ocView._websocket.trigger("progress", {
|
||||
state: "terminated",
|
||||
reason: "reject"
|
||||
});
|
||||
|
||||
sinon.assert.calledOnce(multiplexGum.reset);
|
||||
});
|
||||
|
||||
it("should display an error message if the reason is not 'cancel'",
|
||||
function() {
|
||||
ocView._websocket.trigger("progress", {
|
||||
|
@ -229,6 +229,8 @@ add_task(function* setup_server() {
|
||||
res.finish();
|
||||
});
|
||||
|
||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
||||
|
||||
yield MozLoopService.promiseRegisteredWithServers();
|
||||
});
|
||||
|
||||
|
@ -24,6 +24,8 @@ let msgHandler = function(msg) {
|
||||
add_test(function test_busy_2guest_calls() {
|
||||
actionReceived = false;
|
||||
|
||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
||||
|
||||
MozLoopService.promiseRegisteredWithServers().then(() => {
|
||||
let opened = 0;
|
||||
let windowId;
|
||||
|
@ -31,6 +31,8 @@ add_test(function test_set_do_not_disturb() {
|
||||
add_test(function test_do_not_disturb_disabled_should_open_chat_window() {
|
||||
MozLoopService.doNotDisturb = false;
|
||||
|
||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
||||
|
||||
MozLoopService.promiseRegisteredWithServers().then(() => {
|
||||
let opened = false;
|
||||
Chat.open = function() {
|
||||
|
@ -10,6 +10,8 @@ let openChatOrig = Chat.open;
|
||||
add_test(function test_openChatWindow_on_notification() {
|
||||
Services.prefs.setCharPref("loop.seenToS", "unseen");
|
||||
|
||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
||||
|
||||
MozLoopService.promiseRegisteredWithServers().then(() => {
|
||||
let opened = false;
|
||||
Chat.open = function() {
|
||||
|
@ -43,7 +43,7 @@ add_task(function test_initialize_with_urls_and_no_auth_token() {
|
||||
});
|
||||
|
||||
yield MozLoopService.initialize().then((msg) => {
|
||||
Assert.equal(msg, "initialized to guest status", "Initialize should register as a " +
|
||||
Assert.equal(msg, "initialized without FxA status", "Initialize should register as a " +
|
||||
"guest when no auth tokens but expired URLs");
|
||||
}, (error) => {
|
||||
Assert.ok(false, error, "should have resolved the promise that initialize returned");
|
||||
@ -70,7 +70,7 @@ add_task(function test_initialize_with_invalid_fxa_token() {
|
||||
Assert.ok(false, "Initializing with an invalid token should reject the promise");
|
||||
},
|
||||
(error) => {
|
||||
Assert.equal(MozLoopServiceInternal.pushHandler.pushUrl, kEndPointUrl, "Push URL should match");
|
||||
Assert.equal(MozLoopServiceInternal.pushHandler.registrationPushURL, kEndPointUrl, "Push URL should match");
|
||||
Assert.equal(Services.prefs.getCharPref(LOOP_FXA_TOKEN_PREF), "",
|
||||
"FXA pref should be cleared if token was invalid");
|
||||
Assert.equal(Services.prefs.getCharPref(LOOP_FXA_PROFILE_PREF), "",
|
||||
@ -104,7 +104,7 @@ function run_test() {
|
||||
// Note, this is just used to speed up the test.
|
||||
Services.prefs.setIntPref(LOOP_INITIAL_DELAY_PREF, 0);
|
||||
MozLoopServiceInternal.mocks.pushHandler = mockPushHandler;
|
||||
mockPushHandler.pushUrl = kEndPointUrl;
|
||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
||||
|
||||
do_register_cleanup(function() {
|
||||
MozLoopServiceInternal.mocks.pushHandler = undefined;
|
||||
|
@ -46,6 +46,8 @@ add_test(function test_registration_invalid_token() {
|
||||
function run_test() {
|
||||
setupFakeLoopServer();
|
||||
|
||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
||||
|
||||
do_register_cleanup(function() {
|
||||
Services.prefs.clearUserPref("loop.hawk-session-token");
|
||||
});
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user