mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 15:52:07 +00:00
Bug 993272 - Uplift Add-on SDK to Firefox
This commit is contained in:
parent
557b8e0b3e
commit
7b8ad73f76
@ -1,10 +1,11 @@
|
||||
/* 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";
|
||||
|
||||
var self = require("sdk/self");
|
||||
var panels = require("sdk/panel");
|
||||
var widgets = require("sdk/widget");
|
||||
var { Panel } = require("sdk/panel");
|
||||
var { ToggleButton } = require("sdk/ui");
|
||||
|
||||
function replaceMom(html) {
|
||||
return html.replace("World", "Mom");
|
||||
@ -21,20 +22,18 @@ exports.main = function(options, callbacks) {
|
||||
helloHTML = replaceMom(helloHTML);
|
||||
|
||||
// ... and then create a panel that displays it.
|
||||
var myPanel = panels.Panel({
|
||||
contentURL: "data:text/html," + helloHTML
|
||||
var myPanel = Panel({
|
||||
contentURL: "data:text/html," + helloHTML,
|
||||
onHide: handleHide
|
||||
});
|
||||
|
||||
// Load the URL of the sample image.
|
||||
var iconURL = self.data.url("mom.png");
|
||||
|
||||
// Create a widget that displays the image. We'll attach the panel to it.
|
||||
// When you click the widget, the panel will pop up.
|
||||
widgets.Widget({
|
||||
var button = ToggleButton({
|
||||
id: "test-widget",
|
||||
label: "Mom",
|
||||
contentURL: iconURL,
|
||||
panel: myPanel
|
||||
icon: './mom.png',
|
||||
onChange: handleChange
|
||||
});
|
||||
|
||||
// If you run cfx with --static-args='{"quitWhenDone":true}' this program
|
||||
@ -42,3 +41,13 @@ exports.main = function(options, callbacks) {
|
||||
if (options.staticArgs.quitWhenDone)
|
||||
callbacks.quit();
|
||||
}
|
||||
|
||||
function handleChange(state) {
|
||||
if (state.checked) {
|
||||
myPanel.show({ position: button });
|
||||
}
|
||||
}
|
||||
|
||||
function handleHide() {
|
||||
button.state('window', { checked: false });
|
||||
}
|
||||
|
@ -3,9 +3,6 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
// Disable tests below for now.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=987348
|
||||
/*
|
||||
var m = require("main");
|
||||
var self = require("sdk/self");
|
||||
|
||||
@ -26,4 +23,3 @@ exports.testID = function(test) {
|
||||
test.assertEqual(self.data.url("sample.html"),
|
||||
"resource://reading-data-example-at-jetpack-dot-mozillalabs-dot-com/reading-data/data/sample.html");
|
||||
};
|
||||
*/
|
||||
|
@ -1,26 +1,34 @@
|
||||
/* 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";
|
||||
|
||||
var data = require("sdk/self").data;
|
||||
var { data } = require("sdk/self");
|
||||
var { ToggleButton } = require("sdk/ui");
|
||||
|
||||
var base64png = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYA" +
|
||||
"AABzenr0AAAASUlEQVRYhe3O0QkAIAwD0eyqe3Q993AQ3cBSUKpygfsNTy" +
|
||||
"N5ugbQpK0BAADgP0BRDWXWlwEAAAAAgPsA3rzDaAAAAHgPcGrpgAnzQ2FG" +
|
||||
"bWRR9AAAAABJRU5ErkJggg%3D%3D";
|
||||
|
||||
var reddit_panel = require("sdk/panel").Panel({
|
||||
width: 240,
|
||||
height: 320,
|
||||
contentURL: "http://www.reddit.com/.mobile?keep_extension=True",
|
||||
contentScriptFile: [data.url("jquery-1.4.4.min.js"),
|
||||
data.url("panel.js")]
|
||||
data.url("panel.js")],
|
||||
onHide: handleHide
|
||||
});
|
||||
|
||||
reddit_panel.port.on("click", function(url) {
|
||||
require("sdk/tabs").open(url);
|
||||
});
|
||||
|
||||
require("sdk/widget").Widget({
|
||||
let button = ToggleButton({
|
||||
id: "open-reddit-btn",
|
||||
label: "Reddit",
|
||||
contentURL: "http://www.reddit.com/static/favicon.ico",
|
||||
panel: reddit_panel
|
||||
icon: base64png,
|
||||
onChange: handleChange
|
||||
});
|
||||
|
||||
exports.main = function(options, callbacks) {
|
||||
@ -29,3 +37,13 @@ exports.main = function(options, callbacks) {
|
||||
if (options.staticArgs.quitWhenDone)
|
||||
callbacks.quit();
|
||||
};
|
||||
|
||||
function handleChange(state) {
|
||||
if (state.checked) {
|
||||
reddit_panel.show({ position: button });
|
||||
}
|
||||
}
|
||||
|
||||
function handleHide() {
|
||||
button.state('window', { checked: false });
|
||||
}
|
||||
|
@ -3,9 +3,6 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
// Disable tests below for now.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=987348
|
||||
/*
|
||||
var m = require("main");
|
||||
var self = require("sdk/self");
|
||||
|
||||
@ -23,4 +20,3 @@ exports.testMain = function(test) {
|
||||
exports.testData = function(test) {
|
||||
test.assert(self.data.load("panel.js").length > 0);
|
||||
};
|
||||
*/
|
||||
|
@ -107,7 +107,9 @@ const ContentWorker = Object.freeze({
|
||||
error: pipe.emit.bind(null, "console", "error"),
|
||||
debug: pipe.emit.bind(null, "console", "debug"),
|
||||
exception: pipe.emit.bind(null, "console", "exception"),
|
||||
trace: pipe.emit.bind(null, "console", "trace")
|
||||
trace: pipe.emit.bind(null, "console", "trace"),
|
||||
time: pipe.emit.bind(null, "console", "time"),
|
||||
timeEnd: pipe.emit.bind(null, "console", "timeEnd")
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -31,6 +31,9 @@ let detachFrom = method("detatchFrom");
|
||||
exports.detachFrom = detachFrom;
|
||||
|
||||
function attach(modification, target) {
|
||||
if (!modification)
|
||||
return;
|
||||
|
||||
let window = getTargetWindow(target);
|
||||
|
||||
attachTo(modification, window);
|
||||
@ -42,6 +45,9 @@ function attach(modification, target) {
|
||||
exports.attach = attach;
|
||||
|
||||
function detach(modification, target) {
|
||||
if (!modification)
|
||||
return;
|
||||
|
||||
if (target) {
|
||||
let window = getTargetWindow(target);
|
||||
detachFrom(modification, window);
|
||||
|
@ -67,3 +67,8 @@ function load(sandbox, uri) {
|
||||
}
|
||||
}
|
||||
exports.load = load;
|
||||
|
||||
/**
|
||||
* Forces the given `sandbox` to be freed immediately.
|
||||
*/
|
||||
exports.nuke = Cu.nukeSandbox
|
||||
|
@ -201,6 +201,10 @@ function createWorker (mod, window) {
|
||||
contentScript: mod.contentScript,
|
||||
contentScriptFile: mod.contentScriptFile,
|
||||
contentScriptOptions: mod.contentScriptOptions,
|
||||
// Bug 980468: Syntax errors from scripts can happen before the worker
|
||||
// can set up an error handler. They are per-mod rather than per-worker
|
||||
// so are best handled at the mod level.
|
||||
onError: (e) => emit(mod, 'error', e)
|
||||
});
|
||||
workers.set(mod, worker);
|
||||
pipe(worker, mod);
|
||||
|
@ -18,7 +18,7 @@ const { isPrivateBrowsingSupported } = require('./self');
|
||||
const { isWindowPBSupported } = require('./private-browsing/utils');
|
||||
const { Class } = require("./core/heritage");
|
||||
const { merge } = require("./util/object");
|
||||
const { WorkerHost, detach, attach, destroy } = require("./content/utils");
|
||||
const { WorkerHost } = require("./content/utils");
|
||||
const { Worker } = require("./content/worker");
|
||||
const { Disposable } = require("./core/disposable");
|
||||
const { WeakReference } = require('./core/reference');
|
||||
@ -34,6 +34,8 @@ const { getNodeView, getActiveView } = require("./view/core");
|
||||
const { isNil, isObject, isNumber } = require("./lang/type");
|
||||
const { getAttachEventType } = require("./content/utils");
|
||||
const { number, boolean, object } = require('./deprecated/api-utils');
|
||||
const { Style } = require("./stylesheet/style");
|
||||
const { attach, detach } = require("./content/mod");
|
||||
|
||||
let isRect = ({top, right, bottom, left}) => [top, right, bottom, left].
|
||||
some(value => isNumber(value) && !isNaN(value));
|
||||
@ -63,7 +65,16 @@ let displayContract = contract({
|
||||
position: position
|
||||
});
|
||||
|
||||
let panelContract = contract(merge({}, displayContract.rules, loaderContract.rules));
|
||||
let panelContract = contract(merge({
|
||||
// contentStyle* / contentScript* are sharing the same validation constraints,
|
||||
// so they can be mostly reused, except for the messages.
|
||||
contentStyle: merge(Object.create(loaderContract.rules.contentScript), {
|
||||
msg: 'The `contentStyle` option must be a string or an array of strings.'
|
||||
}),
|
||||
contentStyleFile: merge(Object.create(loaderContract.rules.contentScriptFile), {
|
||||
msg: 'The `contentStyleFile` option must be a local URL or an array of URLs'
|
||||
})
|
||||
}, displayContract.rules, loaderContract.rules));
|
||||
|
||||
|
||||
function isDisposed(panel) !views.has(panel);
|
||||
@ -72,12 +83,13 @@ let panels = new WeakMap();
|
||||
let models = new WeakMap();
|
||||
let views = new WeakMap();
|
||||
let workers = new WeakMap();
|
||||
let styles = new WeakMap();
|
||||
|
||||
function viewFor(panel) views.get(panel)
|
||||
function modelFor(panel) models.get(panel)
|
||||
function panelFor(view) panels.get(view)
|
||||
function workerFor(panel) workers.get(panel)
|
||||
|
||||
const viewFor = (panel) => views.get(panel);
|
||||
const modelFor = (panel) => models.get(panel);
|
||||
const panelFor = (view) => panels.get(view);
|
||||
const workerFor = (panel) => workers.get(panel);
|
||||
const styleFor = (panel) => styles.get(panel);
|
||||
|
||||
// Utility function takes `panel` instance and makes sure it will be
|
||||
// automatically hidden as soon as other panel is shown.
|
||||
@ -125,6 +137,12 @@ const Panel = Class({
|
||||
}, panelContract(options));
|
||||
models.set(this, model);
|
||||
|
||||
if (model.contentStyle || model.contentStyleFile) {
|
||||
styles.set(this, Style({
|
||||
uri: model.contentStyleFile,
|
||||
source: model.contentStyle
|
||||
}));
|
||||
}
|
||||
|
||||
// Setup view
|
||||
let view = domPanel.make();
|
||||
@ -148,7 +166,8 @@ const Panel = Class({
|
||||
this.hide();
|
||||
off(this);
|
||||
|
||||
destroy(workerFor(this));
|
||||
workerFor(this).destroy();
|
||||
detach(styleFor(this));
|
||||
|
||||
domPanel.dispose(viewFor(this));
|
||||
|
||||
@ -177,7 +196,7 @@ const Panel = Class({
|
||||
domPanel.setURL(viewFor(this), model.contentURL);
|
||||
// Detach worker so that messages send will be queued until it's
|
||||
// reatached once panel content is ready.
|
||||
detach(workerFor(this));
|
||||
workerFor(this).detach();
|
||||
},
|
||||
|
||||
/* Public API: Panel.isShowing */
|
||||
@ -262,12 +281,25 @@ let hides = filter(panelEvents, ({type}) => type === "popuphidden");
|
||||
let ready = filter(panelEvents, ({type, target}) =>
|
||||
getAttachEventType(modelFor(panelFor(target))) === type);
|
||||
|
||||
// Styles should be always added as soon as possible, and doesn't makes them
|
||||
// depends on `contentScriptWhen`
|
||||
let start = filter(panelEvents, ({type}) => type === "document-element-inserted");
|
||||
|
||||
// Forward panel show / hide events to panel's own event listeners.
|
||||
on(shows, "data", ({target}) => emit(panelFor(target), "show"));
|
||||
|
||||
on(hides, "data", ({target}) => emit(panelFor(target), "hide"));
|
||||
|
||||
on(ready, "data", function({target}) {
|
||||
let worker = workerFor(panelFor(target));
|
||||
attach(worker, domPanel.getContentDocument(target).defaultView);
|
||||
on(ready, "data", ({target}) => {
|
||||
let panel = panelFor(target);
|
||||
let window = domPanel.getContentDocument(target).defaultView;
|
||||
|
||||
workerFor(panel).attach(window);
|
||||
});
|
||||
|
||||
on(start, "data", ({target}) => {
|
||||
let panel = panelFor(target);
|
||||
let window = domPanel.getContentDocument(target).defaultView;
|
||||
|
||||
attach(styleFor(panel), window);
|
||||
});
|
||||
|
@ -109,17 +109,21 @@ exports.pathFor = function pathFor(id) {
|
||||
*/
|
||||
exports.platform = runtime.OS.toLowerCase();
|
||||
|
||||
const [, architecture, compiler] = runtime.XPCOMABI ?
|
||||
runtime.XPCOMABI.match(/^([^-]*)-(.*)$/) :
|
||||
[, null, null];
|
||||
|
||||
/**
|
||||
* What processor architecture you're running on:
|
||||
* `'arm', 'ia32', or 'x64'`.
|
||||
*/
|
||||
exports.architecture = runtime.XPCOMABI.split('_')[0];
|
||||
exports.architecture = architecture;
|
||||
|
||||
/**
|
||||
* What compiler used for build:
|
||||
* `'msvc', 'n32', 'gcc2', 'gcc3', 'sunc', 'ibmc'...`
|
||||
*/
|
||||
exports.compiler = runtime.XPCOMABI.split('_')[1];
|
||||
exports.compiler = compiler;
|
||||
|
||||
/**
|
||||
* The application's build ID/date, for example "2004051604".
|
||||
|
@ -8,12 +8,13 @@ module.metadata = {
|
||||
'stability': 'unstable'
|
||||
};
|
||||
|
||||
const { Cc, Ci } = require('chrome');
|
||||
const { Cc, Ci, Cu } = require('chrome');
|
||||
const { Unknown } = require('../platform/xpcom');
|
||||
const { Class } = require('../core/heritage');
|
||||
const { ns } = require('../core/namespace');
|
||||
const { addObserver, removeObserver, notifyObservers } =
|
||||
Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
|
||||
const unloadSubject = require('@loader/unload');
|
||||
|
||||
const Subject = Class({
|
||||
extends: Unknown,
|
||||
@ -94,6 +95,10 @@ function on(type, listener, strong) {
|
||||
let observer = Observer(listener);
|
||||
observers[type] = observer;
|
||||
addObserver(observer, type, weak);
|
||||
// WeakRef gymnastics to remove all alive observers on unload
|
||||
let ref = Cu.getWeakReference(observer);
|
||||
weakRefs.set(observer, ref);
|
||||
stillAlive.set(ref, type);
|
||||
}
|
||||
}
|
||||
exports.on = on;
|
||||
@ -120,6 +125,31 @@ function off(type, listener) {
|
||||
let observer = observers[type];
|
||||
delete observers[type];
|
||||
removeObserver(observer, type);
|
||||
stillAlive.delete(weakRefs.get(observer));
|
||||
}
|
||||
}
|
||||
exports.off = off;
|
||||
|
||||
// must use WeakMap to keep reference to all the WeakRefs (!), see bug 986115
|
||||
let weakRefs = new WeakMap();
|
||||
|
||||
// and we're out of beta, we're releasing on time!
|
||||
let stillAlive = new Map();
|
||||
|
||||
on('sdk:loader:destroy', function onunload({ subject, data: reason }) {
|
||||
// using logic from ./unload, to avoid a circular module reference
|
||||
if (subject.wrappedJSObject === unloadSubject) {
|
||||
off('sdk:loader:destroy', onunload);
|
||||
|
||||
// don't bother
|
||||
if (reason === 'shutdown')
|
||||
return;
|
||||
|
||||
stillAlive.forEach( (type, ref) => {
|
||||
let observer = ref.get();
|
||||
if (observer)
|
||||
removeObserver(observer, type);
|
||||
})
|
||||
}
|
||||
// a strong reference
|
||||
}, true);
|
||||
|
@ -62,9 +62,11 @@ exports.LoaderWithHookedConsole = function (module, callback) {
|
||||
error: hook.bind("error"),
|
||||
debug: hook.bind("debug"),
|
||||
exception: hook.bind("exception"),
|
||||
time: hook.bind("time"),
|
||||
timeEnd: hook.bind("timeEnd"),
|
||||
__exposedProps__: {
|
||||
log: "rw", info: "rw", warn: "rw", error: "rw", debug: "rw",
|
||||
exception: "rw"
|
||||
exception: "rw", time: "rw", timeEnd: "rw"
|
||||
}
|
||||
}
|
||||
}),
|
||||
@ -105,9 +107,11 @@ exports.LoaderWithFilteredConsole = function (module, callback) {
|
||||
error: hook.bind("error"),
|
||||
debug: hook.bind("debug"),
|
||||
exception: hook.bind("exception"),
|
||||
time: hook.bind("time"),
|
||||
timeEnd: hook.bind("timeEnd"),
|
||||
__exposedProps__: {
|
||||
log: "rw", info: "rw", warn: "rw", error: "rw", debug: "rw",
|
||||
exception: "rw"
|
||||
exception: "rw", time: "rw", timeEnd: "rw"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -8,6 +8,7 @@ const { isLocalURL } = require('../../url');
|
||||
const { isNil, isObject, isString } = require('../../lang/type');
|
||||
const { required, either, string, boolean, object } = require('../../deprecated/api-utils');
|
||||
const { merge } = require('../../util/object');
|
||||
const { freeze } = Object;
|
||||
|
||||
function isIconSet(icons) {
|
||||
return Object.keys(icons).
|
||||
@ -16,6 +17,7 @@ function isIconSet(icons) {
|
||||
|
||||
let iconSet = {
|
||||
is: either(object, string),
|
||||
map: v => isObject(v) ? freeze(merge({}, v)) : v,
|
||||
ok: v => (isString(v) && isLocalURL(v)) || (isObject(v) && isIconSet(v)),
|
||||
msg: 'The option "icon" must be a local URL or an object with ' +
|
||||
'numeric keys / local URL values pair.'
|
||||
|
@ -253,9 +253,11 @@ const Sidebar = Class({
|
||||
remove(sidebars, this);
|
||||
|
||||
// stop tracking windows
|
||||
internals.tracker.unload();
|
||||
internals.tracker = null;
|
||||
if (internals.tracker) {
|
||||
internals.tracker.unload();
|
||||
}
|
||||
|
||||
internals.tracker = null;
|
||||
internals.windowNS = null;
|
||||
|
||||
views.delete(this);
|
||||
|
@ -382,8 +382,8 @@ exports["test:ensure console.xxx works in cs"] = WorkerTest(
|
||||
let calls = [];
|
||||
function onMessage(type, msg) {
|
||||
assert.equal(type, msg,
|
||||
"console.xxx(\"xxx\"), i.e. message is equal to the " +
|
||||
"console method name we are calling");
|
||||
"console.xxx(\"xxx\"), i.e. message is equal to the " +
|
||||
"console method name we are calling");
|
||||
calls.push(msg);
|
||||
}
|
||||
|
||||
@ -391,19 +391,23 @@ exports["test:ensure console.xxx works in cs"] = WorkerTest(
|
||||
let worker = loader.require("sdk/content/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("exception");
|
||||
console.timeEnd("timeEnd");
|
||||
self.postMessage();
|
||||
},
|
||||
onMessage: function() {
|
||||
// Ensure that console methods are called in the same execution order
|
||||
const EXPECTED_CALLS = ["time", "log", "info", "warn", "error",
|
||||
"debug", "exception", "timeEnd"];
|
||||
assert.equal(JSON.stringify(calls),
|
||||
JSON.stringify(["log", "info", "warn", "error", "debug", "exception"]),
|
||||
"console has been called successfully, in the expected order");
|
||||
JSON.stringify(EXPECTED_CALLS),
|
||||
"console methods have been called successfully, in expected order");
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
@ -957,7 +957,7 @@ exports.testPageModCss = function(assert, done) {
|
||||
'data:text/html;charset=utf-8,<div style="background: silver">css test</div>', [{
|
||||
include: ["*", "data:*"],
|
||||
contentStyle: "div { height: 100px; }",
|
||||
contentStyleFile: data.url("pagemod-css-include-file.css")
|
||||
contentStyleFile: data.url("css-include-file.css")
|
||||
}],
|
||||
function(win, done) {
|
||||
let div = win.document.querySelector("div");
|
||||
@ -1531,4 +1531,32 @@ exports.testDetachOnUnload = function(assert, done) {
|
||||
})
|
||||
}
|
||||
|
||||
exports.testSyntaxErrorInContentScript = function(assert, done) {
|
||||
const url = "data:text/html;charset=utf-8,testSyntaxErrorInContentScript";
|
||||
let hitError = null;
|
||||
let attached = false;
|
||||
|
||||
testPageMod(assert, done, url, [{
|
||||
include: url,
|
||||
contentScript: 'console.log(23',
|
||||
|
||||
onAttach: function() {
|
||||
attached = true;
|
||||
},
|
||||
|
||||
onError: function(e) {
|
||||
hitError = e;
|
||||
}
|
||||
}],
|
||||
|
||||
function(win, done) {
|
||||
assert.ok(attached, "The worker was attached.");
|
||||
assert.notStrictEqual(hitError, null, "The syntax error was reported.");
|
||||
if (hitError)
|
||||
assert.equal(hitError.name, "SyntaxError", "The error thrown should be a SyntaxError");
|
||||
done();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
require('sdk/test').run(exports);
|
||||
|
@ -25,6 +25,8 @@ const { URL } = require('sdk/url');
|
||||
const fixtures = require('./fixtures')
|
||||
|
||||
const SVG_URL = fixtures.url('mofo_logo.SVG');
|
||||
const CSS_URL = fixtures.url('css-include-file.css');
|
||||
|
||||
const Isolate = fn => '(' + fn + ')()';
|
||||
|
||||
function ignorePassingDOMNodeWarning(type, message) {
|
||||
@ -974,6 +976,88 @@ exports['test panel can be constructed without any arguments'] = function (asser
|
||||
assert.ok(true, "Creating a panel with no arguments does not throw");
|
||||
};
|
||||
|
||||
exports['test panel CSS'] = function(assert, done) {
|
||||
const loader = Loader(module);
|
||||
const { Panel } = loader.require('sdk/panel');
|
||||
|
||||
const { getActiveView } = loader.require('sdk/view/core');
|
||||
|
||||
const getContentWindow = panel =>
|
||||
getActiveView(panel).querySelector('iframe').contentWindow;
|
||||
|
||||
let panel = Panel({
|
||||
contentURL: 'data:text/html;charset=utf-8,' +
|
||||
'<div style="background: silver">css test</div>',
|
||||
contentStyle: 'div { height: 100px; }',
|
||||
contentStyleFile: CSS_URL,
|
||||
onShow: () => {
|
||||
ready(getContentWindow(panel)).then(({ document }) => {
|
||||
let div = document.querySelector('div');
|
||||
|
||||
assert.equal(div.clientHeight, 100, 'Panel contentStyle worked');
|
||||
assert.equal(div.offsetHeight, 120, 'Panel contentStyleFile worked');
|
||||
|
||||
loader.unload();
|
||||
done();
|
||||
}).then(null, assert.fail);
|
||||
}
|
||||
});
|
||||
|
||||
panel.show();
|
||||
};
|
||||
|
||||
exports['test panel CSS list'] = function(assert, done) {
|
||||
const loader = Loader(module);
|
||||
const { Panel } = loader.require('sdk/panel');
|
||||
|
||||
const { getActiveView } = loader.require('sdk/view/core');
|
||||
|
||||
const getContentWindow = panel =>
|
||||
getActiveView(panel).querySelector('iframe').contentWindow;
|
||||
|
||||
let panel = Panel({
|
||||
contentURL: 'data:text/html;charset=utf-8,' +
|
||||
'<div style="width:320px; max-width: 480px!important">css test</div>',
|
||||
contentStyleFile: [
|
||||
// Highlight evaluation order in this list
|
||||
"data:text/css;charset=utf-8,div { border: 1px solid black; }",
|
||||
"data:text/css;charset=utf-8,div { border: 10px solid black; }",
|
||||
// Highlight evaluation order between contentStylesheet & contentStylesheetFile
|
||||
"data:text/css;charset=utf-8s,div { height: 1000px; }",
|
||||
// Highlight precedence between the author and user style sheet
|
||||
"data:text/css;charset=utf-8,div { width: 200px; max-width: 640px!important}",
|
||||
],
|
||||
contentStyle: [
|
||||
"div { height: 10px; }",
|
||||
"div { height: 100px; }"
|
||||
],
|
||||
onShow: () => {
|
||||
ready(getContentWindow(panel)).then(({ window, document }) => {
|
||||
let div = document.querySelector('div');
|
||||
let style = window.getComputedStyle(div);
|
||||
|
||||
assert.equal(div.clientHeight, 100,
|
||||
'Panel contentStyle list is evaluated after contentStyleFile');
|
||||
|
||||
assert.equal(div.offsetHeight, 120,
|
||||
'Panel contentStyleFile list works');
|
||||
|
||||
assert.equal(style.width, '320px',
|
||||
'add-on author/page author stylesheet precedence works');
|
||||
|
||||
assert.equal(style.maxWidth, '480px',
|
||||
'add-on author/page author stylesheet !important precedence works');
|
||||
|
||||
loader.unload();
|
||||
done();
|
||||
}).then(null, assert.fail);
|
||||
}
|
||||
});
|
||||
|
||||
panel.show();
|
||||
};
|
||||
|
||||
|
||||
if (isWindowPBSupported) {
|
||||
exports.testGetWindow = function(assert, done) {
|
||||
let activeWindow = getMostRecentBrowserWindow();
|
||||
|
@ -2,7 +2,7 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { sandbox, load, evaluate } = require('sdk/loader/sandbox');
|
||||
const { sandbox, load, evaluate, nuke } = require('sdk/loader/sandbox');
|
||||
const xulApp = require("sdk/system/xul-app");
|
||||
const fixturesURI = module.uri.split('test-sandbox.js')[0] + 'fixtures/';
|
||||
|
||||
@ -137,4 +137,30 @@ exports['test metadata'] = function(assert) {
|
||||
let self = require('sdk/self');
|
||||
}
|
||||
|
||||
exports['test nuke sandbox'] = function(assert) {
|
||||
|
||||
let fixture = sandbox('http://example.com');
|
||||
fixture.foo = 'foo';
|
||||
|
||||
let ref = evaluate(fixture, 'let a = {bar: "bar"}; a');
|
||||
|
||||
nuke(fixture);
|
||||
|
||||
assert.ok(Cu.isDeadWrapper(fixture), 'sandbox should be dead');
|
||||
|
||||
assert.throws(
|
||||
() => fixture.foo,
|
||||
/can't access dead object/,
|
||||
'property of nuked sandbox should not be accessible'
|
||||
);
|
||||
|
||||
assert.ok(Cu.isDeadWrapper(ref), 'ref to object from sandbox should be dead');
|
||||
|
||||
assert.throws(
|
||||
() => ref.bar,
|
||||
/can't access dead object/,
|
||||
'object from nuked sandbox should not be alive'
|
||||
);
|
||||
}
|
||||
|
||||
require('test').run(exports);
|
||||
|
@ -119,6 +119,30 @@ exports["test listeners are GC-ed"] = function(assert, done) {
|
||||
});
|
||||
};
|
||||
|
||||
exports["test alive listeners are removed on unload"] = function(assert) {
|
||||
let receivedFromWeak = [];
|
||||
let receivedFromStrong = [];
|
||||
let loader = Loader(module);
|
||||
let events = loader.require('sdk/system/events');
|
||||
|
||||
let type = 'test-alive-listeners-are-removed';
|
||||
const handler = (event) => receivedFromStrong.push(event);
|
||||
const weakHandler = (event) => receivedFromWeak.push(event);
|
||||
|
||||
events.on(type, handler, true);
|
||||
events.on(type, weakHandler);
|
||||
|
||||
events.emit(type, { data: 1 });
|
||||
assert.equal(receivedFromStrong.length, 1, "strong listener invoked");
|
||||
assert.equal(receivedFromWeak.length, 1, "weak listener invoked");
|
||||
|
||||
loader.unload();
|
||||
events.emit(type, { data: 2 });
|
||||
|
||||
assert.equal(receivedFromWeak.length, 1, "weak listener was removed");
|
||||
assert.equal(receivedFromStrong.length, 1, "strong listener was removed");
|
||||
};
|
||||
|
||||
exports["test handle nsIObserverService notifications"] = function(assert) {
|
||||
let ios = Cc['@mozilla.org/network/io-service;1']
|
||||
.getService(Ci.nsIIOService);
|
||||
|
@ -3,7 +3,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
var runtime = require("sdk/system/runtime");
|
||||
const runtime = require("sdk/system/runtime");
|
||||
|
||||
exports["test system runtime"] = function(assert) {
|
||||
assert.equal(typeof(runtime.inSafeMode), "boolean",
|
||||
@ -14,7 +14,7 @@ exports["test system runtime"] = function(assert) {
|
||||
"runtime.processType is a number");
|
||||
assert.equal(typeof(runtime.widgetToolkit), "string",
|
||||
"runtime.widgetToolkit is string");
|
||||
var XPCOMABI = typeof(runtime.XPCOMABI);
|
||||
const XPCOMABI = runtime.XPCOMABI;
|
||||
assert.ok(XPCOMABI === null || typeof(XPCOMABI) === "string",
|
||||
"runtime.XPCOMABI is string or null if not supported by platform");
|
||||
};
|
||||
|
37
addon-sdk/source/test/test-system.js
Normal file
37
addon-sdk/source/test/test-system.js
Normal file
@ -0,0 +1,37 @@
|
||||
/* 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 runtime = require("sdk/system/runtime");
|
||||
const system = require("sdk/system");
|
||||
|
||||
exports["test system architecture and compiler"] = function(assert) {
|
||||
|
||||
if (system.architecture !== null) {
|
||||
assert.equal(
|
||||
runtime.XPCOMABI.indexOf(system.architecture), 0,
|
||||
"system.architecture is starting substring of runtime.XPCOMABI"
|
||||
);
|
||||
}
|
||||
|
||||
if (system.compiler !== null) {
|
||||
assert.equal(
|
||||
runtime.XPCOMABI.indexOf(system.compiler),
|
||||
runtime.XPCOMABI.length - system.compiler.length,
|
||||
"system.compiler is trailing substring of runtime.XPCOMABI"
|
||||
);
|
||||
}
|
||||
|
||||
assert.ok(
|
||||
system.architecture === null || typeof(system.architecture) === "string",
|
||||
"system.architecture is string or null if not supported by platform"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
system.compiler === null || typeof(system.compiler) === "string",
|
||||
"system.compiler is string or null if not supported by platform"
|
||||
);
|
||||
};
|
||||
|
||||
require("test").run(exports);
|
@ -835,6 +835,44 @@ exports['test button state are snapshot'] = function(assert) {
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test button icon object is a snapshot'] = function(assert) {
|
||||
let loader = Loader(module);
|
||||
let { ActionButton } = loader.require('sdk/ui');
|
||||
|
||||
let icon = {
|
||||
'16': './foo.png'
|
||||
};
|
||||
|
||||
let button = ActionButton({
|
||||
id: 'my-button-17',
|
||||
label: 'my button',
|
||||
icon: icon
|
||||
});
|
||||
|
||||
assert.deepEqual(button.icon, icon,
|
||||
'button.icon has the same properties of the object set in the constructor');
|
||||
|
||||
assert.notEqual(button.icon, icon,
|
||||
'button.icon is not the same object of the object set in the constructor');
|
||||
|
||||
assert.throws(
|
||||
() => button.icon[16] = './bar.png',
|
||||
/16 is read-only/,
|
||||
'properties of button.icon are ready-only'
|
||||
);
|
||||
|
||||
let newIcon = {'16': './bar.png'};
|
||||
button.icon = newIcon;
|
||||
|
||||
assert.deepEqual(button.icon, newIcon,
|
||||
'button.icon has the same properties of the object set');
|
||||
|
||||
assert.notEqual(button.icon, newIcon,
|
||||
'button.icon is not the same object of the object set');
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test button after destroy'] = function(assert) {
|
||||
let loader = Loader(module);
|
||||
let { ActionButton } = loader.require('sdk/ui');
|
||||
|
@ -844,6 +844,44 @@ exports['test button state are snapshot'] = function(assert) {
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test button icon object is a snapshot'] = function(assert) {
|
||||
let loader = Loader(module);
|
||||
let { ToggleButton } = loader.require('sdk/ui');
|
||||
|
||||
let icon = {
|
||||
'16': './foo.png'
|
||||
};
|
||||
|
||||
let button = ToggleButton({
|
||||
id: 'my-button-17',
|
||||
label: 'my button',
|
||||
icon: icon
|
||||
});
|
||||
|
||||
assert.deepEqual(button.icon, icon,
|
||||
'button.icon has the same properties of the object set in the constructor');
|
||||
|
||||
assert.notEqual(button.icon, icon,
|
||||
'button.icon is not the same object of the object set in the constructor');
|
||||
|
||||
assert.throws(
|
||||
() => button.icon[16] = './bar.png',
|
||||
/16 is read-only/,
|
||||
'properties of button.icon are ready-only'
|
||||
);
|
||||
|
||||
let newIcon = {'16': './bar.png'};
|
||||
button.icon = newIcon;
|
||||
|
||||
assert.deepEqual(button.icon, newIcon,
|
||||
'button.icon has the same properties of the object set');
|
||||
|
||||
assert.notEqual(button.icon, newIcon,
|
||||
'button.icon is not the same object of the object set');
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
exports['test button after destroy'] = function(assert) {
|
||||
let loader = Loader(module);
|
||||
let { ToggleButton } = loader.require('sdk/ui');
|
||||
|
Loading…
Reference in New Issue
Block a user