Merge m-c to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2014-04-15 15:14:05 +02:00
commit dc43494b15
90 changed files with 2698 additions and 639 deletions

View File

@ -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 });
}

View File

@ -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");
};
*/

View File

@ -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 });
}

View File

@ -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);
};
*/

View File

@ -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")
});
},

View File

@ -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);

View File

@ -67,3 +67,8 @@ function load(sandbox, uri) {
}
}
exports.load = load;
/**
* Forces the given `sandbox` to be freed immediately.
*/
exports.nuke = Cu.nukeSandbox

View File

@ -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);

View File

@ -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);
});

View File

@ -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".

View File

@ -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);

View File

@ -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"
}
}
});

View File

@ -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.'

View File

@ -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);

View File

@ -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();
}
});

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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);

View File

@ -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");
};

View 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);

View File

@ -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');

View File

@ -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');

View File

@ -52,6 +52,10 @@ function checkDebuggerPort() {
// DebuggerServer.openListener detects that it isn't a file path (string),
// and starts listening on the tcp port given here as command line argument.
if (!window.arguments) {
return;
}
// Get the command line arguments that were passed to the b2g client
let args = window.arguments[0].QueryInterface(Ci.nsICommandLine);

View File

@ -5,6 +5,10 @@
let runAppObj;
window.addEventListener('load', function() {
if (!window.arguments) {
return;
}
// Get the command line arguments that were passed to the b2g client
let args = window.arguments[0].QueryInterface(Ci.nsICommandLine);
let appname;

View File

@ -57,12 +57,19 @@ window.addEventListener('ContentStart', function() {
};
// Get the command line arguments that were passed to the b2g client
let args = window.arguments[0].QueryInterface(Ci.nsICommandLine);
let screenarg;
let args;
try {
// On Firefox Mulet, we don't always have a command line argument
args = window.arguments[0].QueryInterface(Ci.nsICommandLine);
} catch(e) {}
let screenarg = null;
// Get the --screen argument from the command line
try {
screenarg = args.handleFlagWithParam('screen', false);
if (args) {
screenarg = args.handleFlagWithParam('screen', false);
}
// If there isn't one, use the default screen
if (screenarg === null)

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="553da99ab09b6b894d9f95bb06b16b6e1ddbf0a1"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3d47c0627017ef77b1adf179792c6543a349af72"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="55bcc2d7e44dc805c24b57d1e783fc26e8a2ee86"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="553da99ab09b6b894d9f95bb06b16b6e1ddbf0a1"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3d47c0627017ef77b1adf179792c6543a349af72"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="52c909ccead537f8f9dbf634f3e6639078a8b0bd">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="553da99ab09b6b894d9f95bb06b16b6e1ddbf0a1"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3d47c0627017ef77b1adf179792c6543a349af72"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="553da99ab09b6b894d9f95bb06b16b6e1ddbf0a1"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3d47c0627017ef77b1adf179792c6543a349af72"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="55bcc2d7e44dc805c24b57d1e783fc26e8a2ee86"/>

View File

@ -18,7 +18,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="553da99ab09b6b894d9f95bb06b16b6e1ddbf0a1"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3d47c0627017ef77b1adf179792c6543a349af72"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "a54e097e9e3384d885c0116d9c4ca15c1e1cd75e",
"revision": "c766bc0d49af19f18788ad8ed0542b82db81f1d8",
"repo_path": "/integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="553da99ab09b6b894d9f95bb06b16b6e1ddbf0a1"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3d47c0627017ef77b1adf179792c6543a349af72"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="553da99ab09b6b894d9f95bb06b16b6e1ddbf0a1"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3d47c0627017ef77b1adf179792c6543a349af72"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="553da99ab09b6b894d9f95bb06b16b6e1ddbf0a1"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3d47c0627017ef77b1adf179792c6543a349af72"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="553da99ab09b6b894d9f95bb06b16b6e1ddbf0a1"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3d47c0627017ef77b1adf179792c6543a349af72"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="553da99ab09b6b894d9f95bb06b16b6e1ddbf0a1"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3d47c0627017ef77b1adf179792c6543a349af72"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="553da99ab09b6b894d9f95bb06b16b6e1ddbf0a1"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3d47c0627017ef77b1adf179792c6543a349af72"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

View File

@ -183,7 +183,6 @@
@BINPATH@/components/dom_notification.xpt
@BINPATH@/components/dom_html.xpt
@BINPATH@/components/dom_indexeddb.xpt
@BINPATH@/components/dom_inputmethod.xpt
@BINPATH@/components/dom_offline.xpt
@BINPATH@/components/dom_payment.xpt
@BINPATH@/components/dom_json.xpt
@ -457,6 +456,8 @@
@BINPATH@/components/nsPlacesDBFlush.js
@BINPATH@/components/nsPlacesAutoComplete.manifest
@BINPATH@/components/nsPlacesAutoComplete.js
@BINPATH@/components/UnifiedComplete.manifest
@BINPATH@/components/UnifiedComplete.js
@BINPATH@/components/nsPlacesExpiration.js
@BINPATH@/components/PlacesProtocolHandler.js
@BINPATH@/components/PlacesCategoriesStarter.js

View File

@ -122,7 +122,6 @@ tabbrowser {
visibility: hidden;
}
.tab-close-button,
.tab-background {
/* Explicitly set the visibility to override the value (collapsed)
* we inherit from #TabsToolbar[collapsed] upon opening a browser window. */
@ -131,15 +130,28 @@ tabbrowser {
transition: visibility 0ms 25ms;
}
.tab-close-button:not([fadein]):not([pinned]),
.tab-background:not([fadein]):not([pinned]) {
visibility: hidden;
/* Closing tabs are hidden without a delay. */
transition-delay: 0ms;
}
.tab-close-button,
.tab-label {
/* Explicitly set the visibility to override the value (collapsed)
* we inherit from #TabsToolbar[collapsed] upon opening a browser window. */
visibility: visible;
transition: opacity 70ms 230ms,
visibility 0ms 230ms;
}
.tab-close-button:not([fadein]):not([pinned]),
.tab-label:not([fadein]):not([pinned]) {
visibility: collapse;
opacity: .6;
}
.tab-throbber:not([fadein]):not([pinned]),
.tab-label:not([fadein]):not([pinned]),
.tab-icon-image:not([fadein]):not([pinned]) {
display: none;
}

View File

@ -81,6 +81,10 @@
Components.classes["@mozilla.org/autocomplete/search;1?name=history"]
.getService(Components.interfaces.mozIPlacesAutoComplete);
</field>
<field name="_unifiedComplete" readonly="true">
Components.classes["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
.getService(Components.interfaces.mozIPlacesAutoComplete);
</field>
<field name="mTabBox" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
</field>
@ -744,8 +748,10 @@
}
let autocomplete = this.mTabBrowser._placesAutocomplete;
let unifiedComplete = this.mTabBrowser._unifiedComplete;
if (this.mBrowser.registeredOpenURI) {
autocomplete.unregisterOpenPage(this.mBrowser.registeredOpenURI);
unifiedComplete.unregisterOpenPage(this.mBrowser.registeredOpenURI);
delete this.mBrowser.registeredOpenURI;
}
// Tabs in private windows aren't registered as "Open" so
@ -754,6 +760,7 @@
(!PrivateBrowsingUtils.isWindowPrivate(window) ||
PrivateBrowsingUtils.permanentPrivateBrowsing)) {
autocomplete.registerOpenPage(aLocation);
unifiedComplete.registerOpenPage(aLocation);
this.mBrowser.registeredOpenURI = aLocation;
}
}
@ -1672,12 +1679,6 @@
// kick the animation off
t.setAttribute("fadein", "true");
// This call to adjustTabstrip is redundant but needed so that
// when opening a second tab, the first tab's close buttons
// appears immediately rather than when the transition ends.
if (this.tabs.length - this._removingTabs.length == 2)
this.tabContainer.adjustTabstrip();
}.bind(this));
}
@ -1863,13 +1864,6 @@
aTab.style.maxWidth = ""; // ensure that fade-out transition happens
aTab.removeAttribute("fadein");
if (this.tabs.length - this._removingTabs.length == 1) {
// The second tab just got closed and we will end up with a single
// one. Remove the first tab's close button immediately (if needed)
// rather than after the tabclose animation ends.
this.tabContainer.adjustTabstrip();
}
setTimeout(function (tab, tabbrowser) {
if (tab.parentNode &&
window.getComputedStyle(tab).maxWidth == "0.1px") {
@ -1969,6 +1963,7 @@
if (browser.registeredOpenURI && !aTabWillBeMoved) {
this._placesAutocomplete.unregisterOpenPage(browser.registeredOpenURI);
this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI);
delete browser.registeredOpenURI;
}
@ -2295,6 +2290,7 @@
// If the current URI is registered as open remove it from the list.
if (aOurBrowser.registeredOpenURI) {
this._placesAutocomplete.unregisterOpenPage(aOurBrowser.registeredOpenURI);
this._unifiedComplete.unregisterOpenPage(aOurBrowser.registeredOpenURI);
delete aOurBrowser.registeredOpenURI;
}
@ -3104,6 +3100,7 @@
let browser = this.getBrowserAtIndex(i);
if (browser.registeredOpenURI) {
this._placesAutocomplete.unregisterOpenPage(browser.registeredOpenURI);
this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI);
delete browser.registeredOpenURI;
}
browser.webProgress.removeProgressListener(this.mTabFilters[i]);

View File

@ -68,6 +68,12 @@
this.inputField.addEventListener("overflow", this, false);
this.inputField.addEventListener("underflow", this, false);
try {
if (this._prefs.getBoolPref("unifiedcomplete")) {
this.setAttribute("autocompletesearch", "unifiedcomplete");
}
} catch (ex) {}
const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
var textBox = document.getAnonymousElementByAttribute(this,
"anonid", "textbox-input-box");
@ -599,6 +605,14 @@
case "trimURLs":
this._mayTrimURLs = this._prefs.getBoolPref(aData);
break;
case "unifiedcomplete":
let useUnifiedComplete = false;
try {
useUnifiedComplete = this._prefs.getBoolPref(aData);
} catch (ex) {}
this.setAttribute("autocompletesearch",
useUnifiedComplete ? "unifiedcomplete"
: "urlinline history");
}
}
]]></body>

View File

@ -386,6 +386,13 @@ let CustomizableUIInternal = {
gPlacements.set(aName, placements);
}
gFuturePlacements.delete(aName);
let existingAreaNodes = gBuildAreas.get(aName);
if (existingAreaNodes) {
for (let areaNode of existingAreaNodes) {
this.notifyListeners("onAreaNodeUnregistered", aName, areaNode.customizationTarget,
CustomizableUI.REASON_AREA_UNREGISTERED);
}
}
gBuildAreas.delete(aName);
} finally {
this.endBatchUpdate(true);
@ -458,6 +465,7 @@ let CustomizableUIInternal = {
if (gDirtyAreaCache.has(area)) {
this.buildArea(area, placements, aToolbar);
}
this.notifyListeners("onAreaNodeRegistered", area, aToolbar.customizationTarget);
aToolbar.setAttribute("currentset", placements.join(","));
} finally {
this.endBatchUpdate();
@ -699,6 +707,8 @@ let CustomizableUIInternal = {
let placements = gPlacements.get(CustomizableUI.AREA_PANEL);
this.buildArea(CustomizableUI.AREA_PANEL, placements, aPanelContents);
this.notifyListeners("onAreaNodeRegistered", CustomizableUI.AREA_PANEL, aPanelContents);
for (let child of aPanelContents.children) {
if (child.localName != "toolbarbutton") {
if (child.localName == "toolbaritem") {
@ -839,6 +849,8 @@ let CustomizableUIInternal = {
let areaProperties = gAreas.get(areaId);
for (let node of areaNodes) {
if (node.ownerDocument == document) {
this.notifyListeners("onAreaNodeUnregistered", areaId, node.customizationTarget,
CustomizableUI.REASON_WINDOW_CLOSED);
if (areaProperties.has("overflowable")) {
node.overflowable.uninit();
node.overflowable = null;
@ -2502,6 +2514,17 @@ this.CustomizableUI = {
*/
get PANEL_COLUMN_COUNT() 3,
/**
* Constant indicating the reason the event was fired was a window closing
*/
get REASON_WINDOW_CLOSED() "window-closed",
/**
* Constant indicating the reason the event was fired was an area being
* unregistered separately from window closing mechanics.
*/
get REASON_AREA_UNREGISTERED() "area-unregistered",
/**
* An iteratable property of windows managed by CustomizableUI.
* Note that this can *only* be used as an iterator. ie:
@ -2603,6 +2626,15 @@ this.CustomizableUI = {
* - onWindowClosed(aWindow)
* Fired when a window that has been managed by CustomizableUI has been
* closed.
* - onAreaNodeRegistered(aArea, aContainer)
* Fired after an area node is first built when it is registered. This
* is often when the window has opened, but in the case of add-ons,
* could fire when the node has just been registered with CustomizableUI
* after an add-on update or disable/enable sequence.
* - onAreaNodeUnregistered(aArea, aContainer, aReason)
* Fired when an area node is explicitly unregistered by an API caller,
* or by a window closing. The aReason parameter indicates which of
* these is the case.
*/
addListener: function(aListener) {
CustomizableUIInternal.addListener(aListener);

View File

@ -236,11 +236,7 @@ CustomizeMode.prototype = {
yield this._wrapToolbarItems();
this.populatePalette();
this.visiblePalette.addEventListener("dragstart", this, true);
this.visiblePalette.addEventListener("dragover", this, true);
this.visiblePalette.addEventListener("dragexit", this, true);
this.visiblePalette.addEventListener("drop", this, true);
this.visiblePalette.addEventListener("dragend", this, true);
this._addDragHandlers(this.visiblePalette);
window.gNavToolbox.addEventListener("toolbarvisibilitychange", this);
@ -402,11 +398,7 @@ CustomizeMode.prototype = {
window.gNavToolbox.removeEventListener("toolbarvisibilitychange", this);
DragPositionManager.stop();
this.visiblePalette.removeEventListener("dragstart", this, true);
this.visiblePalette.removeEventListener("dragover", this, true);
this.visiblePalette.removeEventListener("dragexit", this, true);
this.visiblePalette.removeEventListener("drop", this, true);
this.visiblePalette.removeEventListener("dragend", this, true);
this._removeDragHandlers(this.visiblePalette);
yield this._unwrapToolbarItems();
@ -900,11 +892,7 @@ CustomizeMode.prototype = {
this.areas = [];
for (let area of CustomizableUI.areas) {
let target = CustomizableUI.getCustomizeTargetForArea(area, window);
target.addEventListener("dragstart", this, true);
target.addEventListener("dragover", this, true);
target.addEventListener("dragexit", this, true);
target.addEventListener("drop", this, true);
target.addEventListener("dragend", this, true);
this._addDragHandlers(target);
for (let child of target.children) {
if (this.isCustomizableItem(child)) {
yield this.deferredWrapToolbarItem(child, CustomizableUI.getPlaceForItem(child));
@ -915,6 +903,14 @@ CustomizeMode.prototype = {
}.bind(this)).then(null, ERROR);
},
_addDragHandlers: function(aTarget) {
aTarget.addEventListener("dragstart", this, true);
aTarget.addEventListener("dragover", this, true);
aTarget.addEventListener("dragexit", this, true);
aTarget.addEventListener("drop", this, true);
aTarget.addEventListener("dragend", this, true);
},
_wrapItemsInArea: function(target) {
for (let child of target.children) {
if (this.isCustomizableItem(child)) {
@ -923,6 +919,14 @@ CustomizeMode.prototype = {
}
},
_removeDragHandlers: function(aTarget) {
aTarget.removeEventListener("dragstart", this, true);
aTarget.removeEventListener("dragover", this, true);
aTarget.removeEventListener("dragexit", this, true);
aTarget.removeEventListener("drop", this, true);
aTarget.removeEventListener("dragend", this, true);
},
_unwrapItemsInArea: function(target) {
for (let toolbarItem of target.children) {
if (this.isWrappedToolbarItem(toolbarItem)) {
@ -939,11 +943,7 @@ CustomizeMode.prototype = {
yield this.deferredUnwrapToolbarItem(toolbarItem);
}
}
target.removeEventListener("dragstart", this, true);
target.removeEventListener("dragover", this, true);
target.removeEventListener("dragexit", this, true);
target.removeEventListener("drop", this, true);
target.removeEventListener("dragend", this, true);
this._removeDragHandlers(target);
}
}.bind(this)).then(null, ERROR);
},
@ -1118,6 +1118,25 @@ CustomizeMode.prototype = {
}
},
onAreaNodeRegistered: function(aArea, aContainer) {
if (aContainer.ownerDocument == this.document) {
this._wrapItemsInArea(aContainer);
this._addDragHandlers(aContainer);
DragPositionManager.add(this.window, aArea, aContainer);
this.areas.push(aContainer);
}
},
onAreaNodeUnregistered: function(aArea, aContainer, aReason) {
if (aContainer.ownerDocument == this.document && aReason == CustomizableUI.REASON_AREA_UNREGISTERED) {
this._unwrapItemsInArea(aContainer);
this._removeDragHandlers(aContainer);
DragPositionManager.remove(this.window, aArea, aContainer);
let index = this.areas.indexOf(aContainer);
this.areas.splice(index, 1);
}
},
_onUIChange: function() {
this._changed = true;
if (!this.resetting) {

View File

@ -393,6 +393,22 @@ let DragPositionManager = {
}
},
add: function(aWindow, aArea, aContainer) {
if (CustomizableUI.getAreaType(aArea) != "toolbar") {
return;
}
gManagers.set(aContainer, new AreaPositionManager(aContainer));
},
remove: function(aWindow, aArea, aContainer) {
if (CustomizableUI.getAreaType(aArea) != "toolbar") {
return;
}
gManagers.delete(aContainer);
},
stop: function() {
gManagers.clear();
},

View File

@ -104,5 +104,6 @@ skip-if = os == "linux"
[browser_987492_window_api.js]
[browser_992747_toggle_noncustomizable_toolbar.js]
[browser_993322_widget_notoolbar.js]
[browser_995164_registerArea_during_customize_mode.js]
[browser_bootstrapped_custom_toolbar.js]
[browser_panel_toggle.js]

View File

@ -31,4 +31,6 @@ add_task(function*() {
CustomizableUI.destroyWidget(BUTTONID);
CustomizableUI.unregisterArea(TOOLBARID, true);
toolbar.remove();
gAddedToolbars.clear();
});

View File

@ -0,0 +1,113 @@
/* 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 TOOLBARID = "test-toolbar-added-during-customize-mode";
add_task(function*() {
yield startCustomizing();
let toolbar = createToolbarWithPlacements(TOOLBARID, []);
CustomizableUI.addWidgetToArea("sync-button", TOOLBARID);
let syncButton = document.getElementById("sync-button");
ok(syncButton, "Sync button should exist.");
is(syncButton.parentNode.localName, "toolbarpaletteitem", "Sync button's parent node should be a wrapper.");
simulateItemDrag(syncButton, gNavToolbox.palette);
ok(!CustomizableUI.getPlacementOfWidget("sync-button"), "Button moved to the palette");
ok(gNavToolbox.palette.querySelector("#sync-button"), "Sync button really is in palette.");
simulateItemDrag(syncButton, toolbar);
ok(CustomizableUI.getPlacementOfWidget("sync-button"), "Button moved out of palette");
is(CustomizableUI.getPlacementOfWidget("sync-button").area, TOOLBARID, "Button's back on toolbar");
ok(toolbar.querySelector("#sync-button"), "Sync button really is on toolbar.");
yield endCustomizing();
isnot(syncButton.parentNode.localName, "toolbarpaletteitem", "Sync button's parent node should not be a wrapper outside customize mode.");
yield startCustomizing();
is(syncButton.parentNode.localName, "toolbarpaletteitem", "Sync button's parent node should be a wrapper back in customize mode.");
simulateItemDrag(syncButton, gNavToolbox.palette);
ok(!CustomizableUI.getPlacementOfWidget("sync-button"), "Button moved to the palette");
ok(gNavToolbox.palette.querySelector("#sync-button"), "Sync button really is in palette.");
ok(!CustomizableUI.inDefaultState, "Not in default state while toolbar is not collapsed yet.");
setToolbarVisibility(toolbar, false);
ok(CustomizableUI.inDefaultState, "In default state while toolbar is collapsed.");
setToolbarVisibility(toolbar, true);
info("Check that removing the area registration from within customize mode works");
CustomizableUI.unregisterArea(TOOLBARID);
ok(CustomizableUI.inDefaultState, "Now that the toolbar is no longer registered, should be in default state.");
ok(!(new Set(gCustomizeMode.areas)).has(toolbar), "Toolbar shouldn't be known to customize mode.");
CustomizableUI.registerArea(TOOLBARID, {legacy: true, defaultPlacements: []});
CustomizableUI.registerToolbarNode(toolbar, []);
ok(!CustomizableUI.inDefaultState, "Now that the toolbar is registered again, should no longer be in default state.");
ok((new Set(gCustomizeMode.areas)).has(toolbar), "Toolbar should be known to customize mode again.");
simulateItemDrag(syncButton, toolbar);
ok(CustomizableUI.getPlacementOfWidget("sync-button"), "Button moved out of palette");
is(CustomizableUI.getPlacementOfWidget("sync-button").area, TOOLBARID, "Button's back on toolbar");
ok(toolbar.querySelector("#sync-button"), "Sync button really is on toolbar.");
let otherWin = yield openAndLoadWindow({}, true);
let otherTB = otherWin.document.createElementNS(kNSXUL, "toolbar");
otherTB.id = TOOLBARID;
otherTB.setAttribute("customizable", "true");
otherWin.gNavToolbox.appendChild(otherTB);
ok(otherTB.querySelector("#sync-button"), "Sync button is on other toolbar, too.");
simulateItemDrag(syncButton, gNavToolbox.palette);
ok(!CustomizableUI.getPlacementOfWidget("sync-button"), "Button moved to the palette");
ok(gNavToolbox.palette.querySelector("#sync-button"), "Sync button really is in palette.");
ok(!otherTB.querySelector("#sync-button"), "Sync button is in palette in other window, too.");
simulateItemDrag(syncButton, toolbar);
ok(CustomizableUI.getPlacementOfWidget("sync-button"), "Button moved out of palette");
is(CustomizableUI.getPlacementOfWidget("sync-button").area, TOOLBARID, "Button's back on toolbar");
ok(toolbar.querySelector("#sync-button"), "Sync button really is on toolbar.");
ok(otherTB.querySelector("#sync-button"), "Sync button is on other toolbar, too.");
let wasInformedCorrectlyOfAreaDisappearing = false;
let listener = {
onAreaNodeUnregistered: function(aArea, aNode, aReason) {
if (aArea == TOOLBARID) {
is(aNode, otherTB, "Should be informed about other toolbar");
is(aReason, CustomizableUI.REASON_WINDOW_CLOSED, "Reason should be correct.");
wasInformedCorrectlyOfAreaDisappearing = (aReason === CustomizableUI.REASON_WINDOW_CLOSED);
}
}
};
CustomizableUI.addListener(listener);
yield promiseWindowClosed(otherWin);
ok(wasInformedCorrectlyOfAreaDisappearing, "Should be told about window closing.");
CustomizableUI.removeListener(listener);
// Closing the other window should not be counted against this window's customize mode:
is(syncButton.parentNode.localName, "toolbarpaletteitem", "Sync button's parent node should still be a wrapper.");
isnot(gCustomizeMode.areas.indexOf(toolbar), -1, "Toolbar should still be a customizable area for this customize mode instance.");
yield gCustomizeMode.reset();
yield endCustomizing();
wasInformedCorrectlyOfAreaDisappearing = false;
listener = {
onAreaNodeUnregistered: function(aArea, aNode, aReason) {
if (aArea == TOOLBARID) {
is(aNode, toolbar, "Should be informed about this window's toolbar");
is(aReason, CustomizableUI.REASON_AREA_UNREGISTERED, "Reason for final removal should be correct.");
wasInformedCorrectlyOfAreaDisappearing = (aReason === CustomizableUI.REASON_AREA_UNREGISTERED);
}
},
}
CustomizableUI.addListener(listener);
removeCustomToolbars();
ok(wasInformedCorrectlyOfAreaDisappearing, "Should be told about area being unregistered.");
CustomizableUI.removeListener(listener);
ok(CustomizableUI.inDefaultState, "Should be fine after exiting customize mode.");
});

View File

@ -145,6 +145,8 @@ skip-if = true
[browser_590268.js]
[browser_590563.js]
[browser_595601-restore_hidden.js]
[browser_597071.js]
skip-if = true # Needs to be rewritten as Marionette test, bug 995916
[browser_599909.js]
[browser_600545.js]
[browser_601955.js]
@ -184,8 +186,6 @@ skip-if = true
skip-if = true
# Disabled on OS X:
[browser_597071.js]
skip-if = os == "mac" || e10s
[browser_625016.js]
skip-if = os == "mac" || e10s

View File

@ -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 stateBackup = ss.getBrowserState();
const stateBackup = JSON.parse(ss.getBrowserState());
const testState = {
windows: [{
tabs: [
@ -76,8 +76,7 @@ function runNextTest() {
});
}
else {
ss.setBrowserState(stateBackup);
finish();
waitForBrowserState(stateBackup, finish);
}
}

View File

@ -203,7 +203,6 @@
@BINPATH@/components/dom_notification.xpt
@BINPATH@/components/dom_html.xpt
@BINPATH@/components/dom_indexeddb.xpt
@BINPATH@/components/dom_inputmethod.xpt
@BINPATH@/components/dom_offline.xpt
@BINPATH@/components/dom_json.xpt
@BINPATH@/components/dom_power.xpt
@ -430,6 +429,8 @@
@BINPATH@/components/nsTaggingService.js
@BINPATH@/components/nsPlacesAutoComplete.manifest
@BINPATH@/components/nsPlacesAutoComplete.js
@BINPATH@/components/UnifiedComplete.manifest
@BINPATH@/components/UnifiedComplete.js
@BINPATH@/components/nsPlacesExpiration.js
@BINPATH@/browser/components/PlacesProtocolHandler.js
@BINPATH@/components/PlacesCategoriesStarter.js

View File

@ -88,7 +88,7 @@
margin-top: @windowButtonMarginTop@;
}
#main-window[customizing] > #titlebar {
#main-window[customize-entered] > #titlebar {
-moz-appearance: none;
}
@ -2847,6 +2847,16 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
padding: 6px 0 4px;
}
/* Background tabs:
*
* Decrease the height of the hoverable region of background tabs whenever the tabs are at the top
* of the window (e.g. no menubar, tabs in titlebar, etc.) to make it easier to drag the window by
* the titlebar. We don't need this in fullscreen since window dragging is not an issue there.
*/
#main-window[tabsintitlebar]:not([inFullscreen]) .tab-background-middle:not([selected=true]) {
clip-path: url(chrome://browser/content/browser.xul#tab-hover-clip-path);
}
/**
* Tab Drag and Drop
*/

View File

@ -226,20 +226,6 @@
/* End selected tab */
/* Background tabs */
/* Decrease the height of the hoverable region of background tabs whenever the tabs are at the top
of the window (e.g. no menubar, tabs in titlebar, etc.) to make it easier to drag the window by
the titlebar. We don't need this in fullscreen since window dragging is not an issue there. */
%ifdef XP_MACOSX
#main-window[tabsintitlebar][sizemode="maximized"] .tab-background-middle:not([selected=true]),
%endif
#main-window[tabsintitlebar]:not([sizemode="maximized"]):not([inFullscreen]) #toolbar-menubar:-moz-any([autohide="true"][inactive], :not([autohide])) + #TabsToolbar .tab-background-middle:not([selected=true]) {
clip-path: url(chrome://browser/content/browser.xul#tab-hover-clip-path);
}
/* End background tabs */
/* new tab button border and gradient on hover */
.tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]),
.tabs-newtab-button:hover {

View File

@ -1902,6 +1902,16 @@ toolbarbutton[type="socialmark"] > .toolbarbutton-icon {
outline: 1px dotted;
}
/* Background tabs:
*
* Decrease the height of the hoverable region of background tabs whenever the tabs are at the top
* of the window (e.g. no menubar, tabs in titlebar, etc.) to make it easier to drag the window by
* the titlebar. We don't need this in fullscreen since window dragging is not an issue there.
*/
#main-window[tabsintitlebar][sizemode=normal] #toolbar-menubar[autohide="true"][inactive] + #TabsToolbar .tab-background-middle:not([selected=true]) {
clip-path: url(chrome://browser/content/browser.xul#tab-hover-clip-path);
}
/* Tab DnD indicator */
.tab-drop-indicator {
list-style-image: url(chrome://browser/skin/tabbrowser/tabDragIndicator.png);

View File

@ -57,6 +57,25 @@ extern bool gBluetoothDebugFlag;
#define BT_WARNING(msg, ...) printf("%s: " msg, __FUNCTION__, ##__VA_ARGS__))
#endif
/**
* Wrap literal name and value into a BluetoothNamedValue
* and append it to the array.
*/
#define BT_APPEND_NAMED_VALUE(array, name, value) \
array.AppendElement(BluetoothNamedValue(NS_LITERAL_STRING(name), value))
/**
* Ensure success of system message broadcast with void return.
*/
#define BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters) \
do { \
if (!BroadcastSystemMessage(type, parameters)) { \
BT_WARNING("Failed to broadcast [%s]", \
NS_ConvertUTF16toUTF8(type).get()); \
return; \
} \
} while(0)
#define BEGIN_BLUETOOTH_NAMESPACE \
namespace mozilla { namespace dom { namespace bluetooth {
#define END_BLUETOOTH_NAMESPACE \

View File

@ -241,18 +241,10 @@ BluetoothHidManager::NotifyStatusChanged()
NS_NAMED_LITERAL_STRING(type, BLUETOOTH_HID_STATUS_CHANGED_ID);
InfallibleTArray<BluetoothNamedValue> parameters;
BluetoothValue v = mConnected;
parameters.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("connected"), v));
BT_APPEND_NAMED_VALUE(parameters, "connected", mConnected);
BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress);
v = mDeviceAddress;
parameters.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("address"), v));
if (!BroadcastSystemMessage(type, parameters)) {
BT_WARNING("Failed to broadcast system message to settings");
return;
}
BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
}
void

View File

@ -266,8 +266,7 @@ A2dpConnectionStateCallback(btav_connection_state_t aState,
AvStatusToSinkString(aState, a2dpState);
InfallibleTArray<BluetoothNamedValue> props;
props.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("State"), a2dpState));
BT_APPEND_NAMED_VALUE(props, "State", a2dpState);
BluetoothSignal signal(NS_LITERAL_STRING("AudioSink"),
remoteDeviceBdAddress, props);
@ -296,8 +295,7 @@ A2dpAudioStateCallback(btav_audio_state_t aState,
}
InfallibleTArray<BluetoothNamedValue> props;
props.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("State"), a2dpState));
BT_APPEND_NAMED_VALUE(props, "State", a2dpState);
BluetoothSignal signal(NS_LITERAL_STRING("AudioSink"),
remoteDeviceBdAddress, props);

View File

@ -1265,39 +1265,17 @@ BluetoothOppManager::FileTransferComplete()
return;
}
nsString type, name;
BluetoothValue v;
NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-transfer-complete");
InfallibleTArray<BluetoothNamedValue> parameters;
type.AssignLiteral("bluetooth-opp-transfer-complete");
name.AssignLiteral("address");
v = mDeviceAddress;
parameters.AppendElement(BluetoothNamedValue(name, v));
BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress);
BT_APPEND_NAMED_VALUE(parameters, "success", mSuccessFlag);
BT_APPEND_NAMED_VALUE(parameters, "received", mIsServer);
BT_APPEND_NAMED_VALUE(parameters, "fileName", mFileName);
BT_APPEND_NAMED_VALUE(parameters, "fileLength", mSentFileLength);
BT_APPEND_NAMED_VALUE(parameters, "contentType", mContentType);
name.AssignLiteral("success");
v = mSuccessFlag;
parameters.AppendElement(BluetoothNamedValue(name, v));
name.AssignLiteral("received");
v = mIsServer;
parameters.AppendElement(BluetoothNamedValue(name, v));
name.AssignLiteral("fileName");
v = mFileName;
parameters.AppendElement(BluetoothNamedValue(name, v));
name.AssignLiteral("fileLength");
v = mSentFileLength;
parameters.AppendElement(BluetoothNamedValue(name, v));
name.AssignLiteral("contentType");
v = mContentType;
parameters.AppendElement(BluetoothNamedValue(name, v));
if (!BroadcastSystemMessage(type, parameters)) {
BT_WARNING("Failed to broadcast [bluetooth-opp-transfer-complete]");
return;
}
BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
mSendTransferCompleteFlag = true;
}
@ -1305,35 +1283,16 @@ BluetoothOppManager::FileTransferComplete()
void
BluetoothOppManager::StartFileTransfer()
{
nsString type, name;
BluetoothValue v;
NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-transfer-start");
InfallibleTArray<BluetoothNamedValue> parameters;
type.AssignLiteral("bluetooth-opp-transfer-start");
name.AssignLiteral("address");
v = mDeviceAddress;
parameters.AppendElement(BluetoothNamedValue(name, v));
BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress);
BT_APPEND_NAMED_VALUE(parameters, "received", mIsServer);
BT_APPEND_NAMED_VALUE(parameters, "fileName", mFileName);
BT_APPEND_NAMED_VALUE(parameters, "fileLength", mFileLength);
BT_APPEND_NAMED_VALUE(parameters, "contentType", mContentType);
name.AssignLiteral("received");
v = mIsServer;
parameters.AppendElement(BluetoothNamedValue(name, v));
name.AssignLiteral("fileName");
v = mFileName;
parameters.AppendElement(BluetoothNamedValue(name, v));
name.AssignLiteral("fileLength");
v = mFileLength;
parameters.AppendElement(BluetoothNamedValue(name, v));
name.AssignLiteral("contentType");
v = mContentType;
parameters.AppendElement(BluetoothNamedValue(name, v));
if (!BroadcastSystemMessage(type, parameters)) {
BT_WARNING("Failed to broadcast [bluetooth-opp-transfer-start]");
return;
}
BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
mSendTransferCompleteFlag = false;
}
@ -1341,61 +1300,29 @@ BluetoothOppManager::StartFileTransfer()
void
BluetoothOppManager::UpdateProgress()
{
nsString type, name;
BluetoothValue v;
NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-update-progress");
InfallibleTArray<BluetoothNamedValue> parameters;
type.AssignLiteral("bluetooth-opp-update-progress");
name.AssignLiteral("address");
v = mDeviceAddress;
parameters.AppendElement(BluetoothNamedValue(name, v));
BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress);
BT_APPEND_NAMED_VALUE(parameters, "received", mIsServer);
BT_APPEND_NAMED_VALUE(parameters, "processedLength", mSentFileLength);
BT_APPEND_NAMED_VALUE(parameters, "fileLength", mFileLength);
name.AssignLiteral("received");
v = mIsServer;
parameters.AppendElement(BluetoothNamedValue(name, v));
name.AssignLiteral("processedLength");
v = mSentFileLength;
parameters.AppendElement(BluetoothNamedValue(name, v));
name.AssignLiteral("fileLength");
v = mFileLength;
parameters.AppendElement(BluetoothNamedValue(name, v));
if (!BroadcastSystemMessage(type, parameters)) {
BT_WARNING("Failed to broadcast [bluetooth-opp-update-progress]");
return;
}
BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
}
void
BluetoothOppManager::ReceivingFileConfirmation()
{
nsString type, name;
BluetoothValue v;
NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-receiving-file-confirmation");
InfallibleTArray<BluetoothNamedValue> parameters;
type.AssignLiteral("bluetooth-opp-receiving-file-confirmation");
name.AssignLiteral("address");
v = mDeviceAddress;
parameters.AppendElement(BluetoothNamedValue(name, v));
BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress);
BT_APPEND_NAMED_VALUE(parameters, "fileName", mFileName);
BT_APPEND_NAMED_VALUE(parameters, "fileLength", mFileLength);
BT_APPEND_NAMED_VALUE(parameters, "contentType", mContentType);
name.AssignLiteral("fileName");
v = mFileName;
parameters.AppendElement(BluetoothNamedValue(name, v));
name.AssignLiteral("fileLength");
v = mFileLength;
parameters.AppendElement(BluetoothNamedValue(name, v));
name.AssignLiteral("contentType");
v = mContentType;
parameters.AppendElement(BluetoothNamedValue(name, v));
if (!BroadcastSystemMessage(type, parameters)) {
BT_WARNING("Failed to send [bluetooth-opp-receiving-file-confirmation]");
return;
}
BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
}
void

View File

@ -294,15 +294,13 @@ AdapterPropertiesCallback(bt_status_t aStatus, int aNumProperties,
if (p.type == BT_PROPERTY_BDADDR) {
BdAddressTypeToString((bt_bdaddr_t*)p.val, sAdapterBdAddress);
propertyValue = sAdapterBdAddress;
props.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Address"), propertyValue));
BT_APPEND_NAMED_VALUE(props, "Address", propertyValue);
} else if (p.type == BT_PROPERTY_BDNAME) {
// Construct nsCString here because Bd name returned from bluedroid
// is missing a null terminated character after SetProperty.
propertyValue = sAdapterBdName = NS_ConvertUTF8toUTF16(
nsCString((char*)p.val, p.len));
props.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Name"), propertyValue));
BT_APPEND_NAMED_VALUE(props, "Name", propertyValue);
} else if (p.type == BT_PROPERTY_ADAPTER_SCAN_MODE) {
bt_scan_mode_t newMode = *(bt_scan_mode_t*)p.val;
@ -312,13 +310,10 @@ AdapterPropertiesCallback(bt_status_t aStatus, int aNumProperties,
propertyValue = sAdapterDiscoverable = false;
}
props.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Discoverable"), propertyValue));
BT_APPEND_NAMED_VALUE(props, "Discoverable", propertyValue);
} else if (p.type == BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT) {
propertyValue = sAdapterDiscoverableTimeout = *(uint32_t*)p.val;
props.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("DiscoverableTimeout"),
propertyValue));
BT_APPEND_NAMED_VALUE(props, "DiscoverableTimeout", propertyValue);
} else if (p.type == BT_PROPERTY_ADAPTER_BONDED_DEVICES) {
// We have to cache addresses of bonded devices. Unlike BlueZ,
// bluedroid would not send an another BT_PROPERTY_ADAPTER_BONDED_DEVICES
@ -337,8 +332,7 @@ AdapterPropertiesCallback(bt_status_t aStatus, int aNumProperties,
}
propertyValue = sAdapterBondedAddressArray;
props.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Devices"), propertyValue));
BT_APPEND_NAMED_VALUE(props, "Devices", propertyValue);
} else if (p.type == BT_PROPERTY_UUIDS) {
//FIXME: This will be implemented in the later patchset
continue;
@ -389,25 +383,21 @@ RemoteDevicePropertiesCallback(bt_status_t aStatus, bt_bdaddr_t *aBdAddress,
nsString remoteDeviceBdAddress;
BdAddressTypeToString(aBdAddress, remoteDeviceBdAddress);
props.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Address"), remoteDeviceBdAddress));
BT_APPEND_NAMED_VALUE(props, "Address", remoteDeviceBdAddress);
for (int i = 0; i < aNumProperties; ++i) {
bt_property_t p = aProperties[i];
if (p.type == BT_PROPERTY_BDNAME) {
BluetoothValue propertyValue = NS_ConvertUTF8toUTF16((char*)p.val);
props.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Name"), propertyValue));
BT_APPEND_NAMED_VALUE(props, "Name", propertyValue);
} else if (p.type == BT_PROPERTY_CLASS_OF_DEVICE) {
uint32_t cod = *(uint32_t*)p.val;
props.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Class"), BluetoothValue(cod)));
BT_APPEND_NAMED_VALUE(props, "Class", cod);
nsString icon;
ClassToIcon(cod, icon);
props.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Icon"), BluetoothValue(icon)));
BT_APPEND_NAMED_VALUE(props, "Icon", icon);
} else {
BT_LOGD("Other non-handled device properties. Type: %d", p.type);
}
@ -460,22 +450,20 @@ DeviceFoundCallback(int aNumProperties, bt_property_t *aProperties)
nsString remoteDeviceBdAddress;
BdAddressTypeToString((bt_bdaddr_t*)p.val, remoteDeviceBdAddress);
propertyValue = remoteDeviceBdAddress;
propertiesArray.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Address"), propertyValue));
BT_APPEND_NAMED_VALUE(propertiesArray, "Address", propertyValue);
} else if (p.type == BT_PROPERTY_BDNAME) {
propertyValue = NS_ConvertUTF8toUTF16((char*)p.val);
propertiesArray.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Name"), propertyValue));
BT_APPEND_NAMED_VALUE(propertiesArray, "Name", propertyValue);
} else if (p.type == BT_PROPERTY_CLASS_OF_DEVICE) {
uint32_t cod = *(uint32_t*)p.val;
propertyValue = cod;
propertiesArray.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Class"), propertyValue));
BT_APPEND_NAMED_VALUE(propertiesArray, "Class", propertyValue);
nsString icon;
ClassToIcon(cod, icon);
propertyValue = icon;
propertiesArray.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Icon"), propertyValue));
BT_APPEND_NAMED_VALUE(propertiesArray, "Icon", propertyValue);
} else {
BT_LOGD("Not handled remote device property: %d", p.type);
}
@ -515,15 +503,12 @@ PinRequestCallback(bt_bdaddr_t* aRemoteBdAddress,
nsAutoString remoteAddress;
BdAddressTypeToString(aRemoteBdAddress, remoteAddress);
propertiesArray.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("address"), remoteAddress));
propertiesArray.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("method"),
NS_LITERAL_STRING("pincode")));
propertiesArray.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("name"),
BT_APPEND_NAMED_VALUE(propertiesArray, "address", remoteAddress);
BT_APPEND_NAMED_VALUE(propertiesArray, "method",
NS_LITERAL_STRING("pincode"));
BT_APPEND_NAMED_VALUE(propertiesArray, "name",
NS_ConvertUTF8toUTF16(
(const char*)aRemoteBdName->name)));
(const char*)aRemoteBdName->name));
BluetoothValue value = propertiesArray;
BluetoothSignal signal(NS_LITERAL_STRING("RequestPinCode"),
@ -546,17 +531,13 @@ SspRequestCallback(bt_bdaddr_t* aRemoteBdAddress, bt_bdname_t* aRemoteBdName,
nsAutoString remoteAddress;
BdAddressTypeToString(aRemoteBdAddress, remoteAddress);
propertiesArray.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("address"), remoteAddress));
propertiesArray.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("method"),
NS_LITERAL_STRING("confirmation")));
propertiesArray.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("name"),
BT_APPEND_NAMED_VALUE(propertiesArray, "address", remoteAddress);
BT_APPEND_NAMED_VALUE(propertiesArray, "method",
NS_LITERAL_STRING("confirmation"));
BT_APPEND_NAMED_VALUE(propertiesArray, "name",
NS_ConvertUTF8toUTF16(
(const char*)aRemoteBdName->name)));
propertiesArray.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("passkey"), aPasskey));
(const char*)aRemoteBdName->name));
BT_APPEND_NAMED_VALUE(propertiesArray, "passkey", aPasskey);
BluetoothValue value = propertiesArray;
BluetoothSignal signal(NS_LITERAL_STRING("RequestConfirmation"),
@ -592,9 +573,9 @@ BondStateChangedCallback(bt_status_t aStatus, bt_bdaddr_t* aRemoteBdAddress,
// Update bonded address list to BluetoothAdapter
InfallibleTArray<BluetoothNamedValue> propertiesChangeArray;
propertiesChangeArray.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Devices"),
sAdapterBondedAddressArray));
BT_APPEND_NAMED_VALUE(propertiesChangeArray, "Devices",
sAdapterBondedAddressArray);
BluetoothValue value(propertiesChangeArray);
BluetoothSignal signal(NS_LITERAL_STRING("PropertyChanged"),
NS_LITERAL_STRING(KEY_ADAPTER),
@ -603,10 +584,9 @@ BondStateChangedCallback(bt_status_t aStatus, bt_bdaddr_t* aRemoteBdAddress,
// Update bonding status to gaia
InfallibleTArray<BluetoothNamedValue> propertiesArray;
propertiesArray.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("address"), remoteAddress));
propertiesArray.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("status"), bonded));
BT_APPEND_NAMED_VALUE(propertiesArray, "address", remoteAddress);
BT_APPEND_NAMED_VALUE(propertiesArray, "status", bonded);
BluetoothSignal newSignal(NS_LITERAL_STRING(PAIRED_STATUS_CHANGED_ID),
NS_LITERAL_STRING(KEY_ADAPTER),
BluetoothValue(propertiesArray));
@ -796,23 +776,20 @@ BluetoothServiceBluedroid::GetDefaultAdapterPathInternal(
BluetoothValue v = InfallibleTArray<BluetoothNamedValue>();
v.get_ArrayOfBluetoothNamedValue().AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Address"), sAdapterBdAddress));
BT_APPEND_NAMED_VALUE(v.get_ArrayOfBluetoothNamedValue(),
"Address", sAdapterBdAddress);
v.get_ArrayOfBluetoothNamedValue().AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Name"), sAdapterBdName));
BT_APPEND_NAMED_VALUE(v.get_ArrayOfBluetoothNamedValue(),
"Name", sAdapterBdName);
v.get_ArrayOfBluetoothNamedValue().AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Discoverable"),
sAdapterDiscoverable));
BT_APPEND_NAMED_VALUE(v.get_ArrayOfBluetoothNamedValue(),
"Discoverable", sAdapterDiscoverable);
v.get_ArrayOfBluetoothNamedValue().AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("DiscoverableTimeout"),
sAdapterDiscoverableTimeout));
BT_APPEND_NAMED_VALUE(v.get_ArrayOfBluetoothNamedValue(),
"DiscoverableTimeout", sAdapterDiscoverableTimeout);
v.get_ArrayOfBluetoothNamedValue().AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("Devices"),
sAdapterBondedAddressArray));
BT_APPEND_NAMED_VALUE(v.get_ArrayOfBluetoothNamedValue(),
"Devices", sAdapterBondedAddressArray);
nsAutoString replyError;
DispatchBluetoothReply(runnable.get(), v, replyError);

View File

@ -176,10 +176,8 @@ DispatchStatusChangedEvent(const nsAString& aType,
MOZ_ASSERT(NS_IsMainThread());
InfallibleTArray<BluetoothNamedValue> data;
data.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("address"), nsString(aAddress)));
data.AppendElement(
BluetoothNamedValue(NS_LITERAL_STRING("status"), aStatus));
BT_APPEND_NAMED_VALUE(data, "address", nsString(aAddress));
BT_APPEND_NAMED_VALUE(data, "status", aStatus);
BluetoothSignal signal(nsString(aType), NS_LITERAL_STRING(KEY_ADAPTER), data);

View File

@ -811,18 +811,12 @@ BluetoothHfpManager::NotifyConnectionStateChanged(const nsAString& aType)
void
BluetoothHfpManager::NotifyDialer(const nsAString& aCommand)
{
BluetoothValue v;
NS_NAMED_LITERAL_STRING(type, "bluetooth-dialer-command");
InfallibleTArray<BluetoothNamedValue> parameters;
NS_NAMED_LITERAL_STRING(type, "bluetooth-dialer-command");
NS_NAMED_LITERAL_STRING(name, "command");
BT_APPEND_NAMED_VALUE(parameters, "command", nsString(aCommand));
v = nsString(aCommand);
parameters.AppendElement(BluetoothNamedValue(name, v));
if (!BroadcastSystemMessage(type, parameters)) {
BT_WARNING("Failed to broadcast system message to dialer");
}
BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
}
void

View File

@ -1,6 +1,2 @@
component {397a7fdf-2254-47be-b74e-76625a1a66d5} MozKeyboard.js
contract @mozilla.org/b2g-keyboard;1 {397a7fdf-2254-47be-b74e-76625a1a66d5}
category JavaScript-navigator-property mozKeyboard @mozilla.org/b2g-keyboard;1
component {4607330d-e7d2-40a4-9eb8-43967eae0142} MozKeyboard.js
contract @mozilla.org/b2g-inputmethod;1 {4607330d-e7d2-40a4-9eb8-43967eae0142}

View File

@ -27,7 +27,6 @@ this.Keyboard = {
],
_messageNames: [
'SetValue', 'RemoveFocus', 'SetSelectedOption', 'SetSelectedOptions',
'SetSelectionRange', 'ReplaceSurroundingText', 'ShowInputMethodPicker',
'SwitchToNextInputMethod', 'HideInputMethod',
'GetText', 'SendKey', 'GetContext',
@ -170,7 +169,6 @@ this.Keyboard = {
this.forwardEvent(name, msg);
break;
case 'Keyboard:SetValue':
case 'System:SetValue':
this.setValue(msg);
break;
@ -178,11 +176,9 @@ this.Keyboard = {
case 'System:RemoveFocus':
this.removeFocus();
break;
case 'Keyboard:SetSelectedOption':
case 'System:SetSelectedOption':
this.setSelectedOption(msg);
break;
case 'Keyboard:SetSelectedOptions':
case 'System:SetSelectedOptions':
this.setSelectedOption(msg);
break;

View File

@ -18,187 +18,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
XPCOMUtils.defineLazyServiceGetter(this, "tm",
"@mozilla.org/thread-manager;1", "nsIThreadManager");
// -----------------------------------------------------------------------
// MozKeyboard
// -----------------------------------------------------------------------
function MozKeyboard() { }
MozKeyboard.prototype = {
classID: Components.ID("{397a7fdf-2254-47be-b74e-76625a1a66d5}"),
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIB2GKeyboard, Ci.nsIDOMGlobalPropertyInitializer, Ci.nsIObserver
]),
classInfo: XPCOMUtils.generateCI({
"classID": Components.ID("{397a7fdf-2254-47be-b74e-76625a1a66d5}"),
"contractID": "@mozilla.org/b2g-keyboard;1",
"interfaces": [Ci.nsIB2GKeyboard],
"flags": Ci.nsIClassInfo.DOM_OBJECT,
"classDescription": "B2G Virtual Keyboard"
}),
init: function mozKeyboardInit(win) {
let principal = win.document.nodePrincipal;
// Limited the deprecated mozKeyboard API to certified apps only
let perm = Services.perms.testExactPermissionFromPrincipal(principal,
"input-manage");
if (perm != Ci.nsIPermissionManager.ALLOW_ACTION) {
dump("No permission to use the keyboard API for " +
principal.origin + "\n");
return null;
}
Services.obs.addObserver(this, "inner-window-destroyed", false);
cpmm.addMessageListener('Keyboard:FocusChange', this);
cpmm.addMessageListener('Keyboard:SelectionChange', this);
this._window = win;
this._utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
this.innerWindowID = this._utils.currentInnerWindowID;
this._focusHandler = null;
this._selectionHandler = null;
this._selectionStart = -1;
this._selectionEnd = -1;
},
uninit: function mozKeyboardUninit() {
Services.obs.removeObserver(this, "inner-window-destroyed");
cpmm.removeMessageListener('Keyboard:FocusChange', this);
cpmm.removeMessageListener('Keyboard:SelectionChange', this);
this._window = null;
this._utils = null;
this._focusHandler = null;
this._selectionHandler = null;
},
sendKey: function mozKeyboardSendKey(keyCode, charCode) {
charCode = (charCode == undefined) ? keyCode : charCode;
let mainThread = tm.mainThread;
let utils = this._utils;
function send(type) {
mainThread.dispatch(function() {
utils.sendKeyEvent(type, keyCode, charCode, null);
}, mainThread.DISPATCH_NORMAL);
}
send("keydown");
send("keypress");
send("keyup");
},
setSelectedOption: function mozKeyboardSetSelectedOption(index) {
cpmm.sendAsyncMessage('Keyboard:SetSelectedOption', {
'index': index
});
},
setValue: function mozKeyboardSetValue(value) {
cpmm.sendAsyncMessage('Keyboard:SetValue', {
'value': value
});
},
setSelectedOptions: function mozKeyboardSetSelectedOptions(indexes) {
cpmm.sendAsyncMessage('Keyboard:SetSelectedOptions', {
'indexes': indexes
});
},
set onselectionchange(val) {
this._selectionHandler = val;
},
get onselectionchange() {
return this._selectionHandler;
},
get selectionStart() {
return this._selectionStart;
},
get selectionEnd() {
return this._selectionEnd;
},
setSelectionRange: function mozKeyboardSetSelectionRange(start, end) {
cpmm.sendAsyncMessage('Keyboard:SetSelectionRange', {
'selectionStart': start,
'selectionEnd': end
});
},
removeFocus: function mozKeyboardRemoveFocus() {
cpmm.sendAsyncMessage('Keyboard:RemoveFocus', {});
},
set onfocuschange(val) {
this._focusHandler = val;
},
get onfocuschange() {
return this._focusHandler;
},
replaceSurroundingText: function mozKeyboardReplaceSurroundingText(
text, beforeLength, afterLength) {
cpmm.sendAsyncMessage('Keyboard:ReplaceSurroundingText', {
'text': text || '',
'beforeLength': (typeof beforeLength === 'number' ? beforeLength : 0),
'afterLength': (typeof afterLength === 'number' ? afterLength: 0)
});
},
receiveMessage: function mozKeyboardReceiveMessage(msg) {
if (msg.name == "Keyboard:FocusChange") {
let msgJson = msg.json;
if (msgJson.type != "blur") {
this._selectionStart = msgJson.selectionStart;
this._selectionEnd = msgJson.selectionEnd;
} else {
this._selectionStart = 0;
this._selectionEnd = 0;
}
let handler = this._focusHandler;
if (!handler || !(handler instanceof Ci.nsIDOMEventListener))
return;
let detail = {
"detail": msgJson
};
let evt = new this._window.CustomEvent("focuschanged",
Cu.cloneInto(detail, this._window));
handler.handleEvent(evt);
} else if (msg.name == "Keyboard:SelectionChange") {
let msgJson = msg.json;
this._selectionStart = msgJson.selectionStart;
this._selectionEnd = msgJson.selectionEnd;
let handler = this._selectionHandler;
if (!handler || !(handler instanceof Ci.nsIDOMEventListener))
return;
let evt = new this._window.CustomEvent("selectionchange",
Cu.cloneInto({}, this._window));
handler.handleEvent(evt);
}
},
observe: function mozKeyboardObserve(subject, topic, data) {
let wId = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
if (wId == this.innerWindowID)
this.uninit();
}
};
/*
* A WeakMap to map input method iframe window to its active status.
*/
@ -288,6 +107,7 @@ MozInputMethod.prototype = {
_layouts: {},
_window: null,
_isSystem: false,
_isKeyboard: true,
classID: Components.ID("{4607330d-e7d2-40a4-9eb8-43967eae0142}"),
@ -304,6 +124,8 @@ MozInputMethod.prototype = {
.getInterface(Ci.nsIDOMWindowUtils)
.currentInnerWindowID;
Services.obs.addObserver(this, "inner-window-destroyed", false);
let principal = win.document.nodePrincipal;
let perm = Services.perms.testExactPermissionFromPrincipal(principal,
"input-manage");
@ -311,7 +133,18 @@ MozInputMethod.prototype = {
this._isSystem = true;
}
Services.obs.addObserver(this, "inner-window-destroyed", false);
// Check if we can use keyboard related APIs.
let testing = false;
try {
testing = Services.prefs.getBoolPref("dom.mozInputMethod.testing");
} catch (e) {
}
perm = Services.perms.testExactPermissionFromPrincipal(principal, "input");
if (!testing && perm !== Ci.nsIPermissionManager.ALLOW_ACTION) {
this._isKeyboard = false;
return;
}
cpmm.addWeakMessageListener('Keyboard:FocusChange', this);
cpmm.addWeakMessageListener('Keyboard:SelectionChange', this);
cpmm.addWeakMessageListener('Keyboard:GetContext:Result:OK', this);
@ -319,15 +152,18 @@ MozInputMethod.prototype = {
},
uninit: function mozInputMethodUninit() {
this.setActive(false);
this._window = null;
this._mgmt = null;
Services.obs.removeObserver(this, "inner-window-destroyed");
if (!this._isKeyboard) {
return;
}
cpmm.removeWeakMessageListener('Keyboard:FocusChange', this);
cpmm.removeWeakMessageListener('Keyboard:SelectionChange', this);
cpmm.removeWeakMessageListener('Keyboard:GetContext:Result:OK', this);
cpmm.removeWeakMessageListener('Keyboard:LayoutsChange', this);
this._window = null;
this._mgmt = null;
this.setActive(false);
},
receiveMessage: function mozInputMethodReceiveMsg(msg) {
@ -769,5 +605,4 @@ MozInputContext.prototype = {
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(
[MozKeyboard, MozInputMethod]);
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MozInputMethod]);

View File

@ -6,12 +6,6 @@
TEST_DIRS += ['mochitest']
XPIDL_SOURCES += [
'nsIB2GKeyboard.idl',
]
XPIDL_MODULE = 'dom_inputmethod'
EXTRA_COMPONENTS += [
'InputMethod.manifest',
'MozKeyboard.js',
@ -21,4 +15,4 @@ EXTRA_JS_MODULES += [
'Keyboard.jsm',
]
JAR_MANIFESTS += ['jar.mn']
JAR_MANIFESTS += ['jar.mn']

View File

@ -1,71 +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/. */
#include "domstubs.idl"
[scriptable, uuid(40ad96b2-9efa-41fb-84c7-fbcec9b153f0)]
interface nsIB2GKeyboard : nsISupports
{
void sendKey(in long keyCode, in long charCode);
// Select the <select> option specified by index.
// If this method is called on a <select> that support multiple
// selection, then the option specified by index will be added to
// the selection.
// If this method is called for a select that does not support multiple
// selection the previous element will be unselected.
void setSelectedOption(in jsval index);
// Select the <select> options specified by indexes. All other options
// will be deselected.
// If this method is called for a <select> that does not support multiple
// selection, then the last index specified in indexes will be selected.
void setSelectedOptions(in jsval indexes);
// Set the value on the currently focused element. This has to be used
// for special situations where the value had to be chosen amongst a
// list (type=month) or a widget (type=date, time, etc.).
// If the value passed in parameter isn't valid (in the term of HTML5
// Forms Validation), the value will simply be ignored by the element.
void setValue(in jsval value);
void removeFocus();
attribute nsIDOMEventListener onfocuschange;
// Fires when user moves the cursor, changes the selection, or alters the
// composing text length
attribute nsIDOMEventListener onselectionchange;
// The start position of the selection.
readonly attribute long selectionStart;
// The stop position of the selection.
readonly attribute long selectionEnd;
/*
* Set the selection range of the the editable text.
*
* @param start The beginning of the selected text.
* @param end The end of the selected text.
*
* Note that the start position should be less or equal to the end position.
* To move the cursor, set the start and end position to the same value.
*/
void setSelectionRange(in long start, in long end);
/*
* Replace text around the beginning of the current selection range of the
* editable text.
*
* @param text The string to be replaced with.
* @param beforeLength The number of characters to be deleted before the
* beginning of the current selection range. Defaults to 0.
* @param afterLength The number of characters to be deleted after the
* beginning of the current selection range. Defaults to 0.
*/
void replaceSurroundingText(in DOMString text,
[optional] in long beforeLength,
[optional] in long afterLength);
};

View File

@ -166,8 +166,9 @@ mozNfc.prototype = {
this._window = aWindow;
},
// Only System Process can call the following interfaces
// 'checkP2PRegistration' , 'notifyUserAcceptedP2P' , 'notifySendFileStatus'
// Only apps which have nfc-manager permission can call the following interfaces
// 'checkP2PRegistration' , 'notifyUserAcceptedP2P' , 'notifySendFileStatus',
// 'startPoll', 'stopPoll', and 'powerOff'.
checkP2PRegistration: function checkP2PRegistration(manifestUrl) {
// Get the AppID and pass it to ContentHelper
let appID = appsService.getAppLocalIdByManifestURL(manifestUrl);
@ -185,6 +186,18 @@ mozNfc.prototype = {
status, requestId);
},
startPoll: function startPoll() {
return this._nfcContentHelper.startPoll(this._window);
},
stopPoll: function stopPoll() {
return this._nfcContentHelper.stopPoll(this._window);
},
powerOff: function powerOff() {
return this._nfcContentHelper.powerOff(this._window);
},
getNFCTag: function getNFCTag(sessionToken) {
let obj = new MozNFCTag();
let nfcTag = this._window.MozNFCTag._create(this._window, obj);

View File

@ -3,40 +3,35 @@
let pendingEmulatorCmdCount = 0;
SpecialPowers.addPermission("nfc-manager", true, document);
function toggleNFC(enabled, callback) {
isnot(callback, null);
var settings = window.navigator.mozSettings;
isnot(settings, null);
ok(settings instanceof SettingsManager,
'settings instanceof ' + settings.constructor +
', expected SettingsManager');
let req = settings.createLock().get('nfc.enabled');
let nfc = window.navigator.mozNfc;
let req;
if (enabled) {
req = nfc.startPoll();
} else {
req = nfc.powerOff();
}
req.onsuccess = function() {
if (req.result['nfc.enabled'] === enabled) {
callback();
} else {
let req = settings.createLock().set({'nfc.enabled': enabled});
req.onsuccess = function() {
window.setTimeout(callback, 5000); // give emulator time to toggle NFC
};
req.onerror = function() {
ok(false,
'Setting \'nfc.enabled\' to \'' + enabled +
'\' failed, error ' + req.error.name);
finish();
};
}
callback();
};
req.onerror = function() {
ok(false, 'Getting \'nfc.enabled\' failed, error ' + req.error.name);
ok(false, 'operation failed, error ' + req.error.name);
finish();
};
}
function cleanUp() {
log('Cleaning up');
waitFor(finish(),
waitFor(function() {
SpecialPowers.removePermission("nfc-manager", document);
finish()
},
function() {
return pendingEmulatorCmdCount === 0;
});

View File

@ -33,5 +33,4 @@ let tests = [
testConstructNDEF
];
SpecialPowers.pushPermissions(
[{'type': 'settings', 'allow': true, 'context': document}], runTests);
runTests();

View File

@ -4,20 +4,52 @@
MARIONETTE_TIMEOUT = 30000;
MARIONETTE_HEAD_JS = 'head.js';
let nfc = window.navigator.mozNfc;
function testEnableNFC() {
log('Running \'testEnableNFC\'');
toggleNFC(true, runNextTest);
let req = nfc.startPoll();
req.onsuccess = function () {
ok(true);
runNextTest();
};
req.onerror = function () {
ok(false, "startPoll failed");
runNextTest();
};
}
function testDisableNFC() {
log('Running \'testDisableNFC\'');
toggleNFC(false, runNextTest);
let req = nfc.powerOff();
req.onsuccess = function () {
ok(true);
runNextTest();
};
req.onerror = function () {
ok(false, "powerOff failed");
runNextTest();
};
}
function testStopPollNFC() {
log('Running \'testStopPollNFC\'');
let req = nfc.stopPoll();
req.onsuccess = function () {
ok(true);
runNextTest();
};
req.onerror = function () {
ok(false, "stopPoll failed");
runNextTest();
};
}
let tests = [
testEnableNFC,
testStopPollNFC,
testDisableNFC
];
SpecialPowers.pushPermissions(
[{'type': 'settings', 'allow': true, 'context': document}], runTests);
[{'type': 'nfc-manager', 'allow': true, 'context': document}],
runTests);

View File

@ -33,5 +33,4 @@ let tests = [
];
SpecialPowers.pushPermissions(
[{'type': 'nfc-manager', 'allow': true, context: document},
{'type': 'settings', 'allow': true, context: document}], runTests);
[{'type': 'nfc-manager', 'allow': true, context: document}], runTests);

View File

@ -20,6 +20,8 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' # b2g(https not working, bug
[test_camera.html]
disabled = disabled until bug 859593 is fixed
[test_keyboard.html]
skip-if = buildapp != 'b2g'
skip-if = toolkit == 'android'
[test_input-manage.html]
skip-if = toolkit == 'android'
[test_wifi-manage.html]
skip-if = (buildapp != 'b2g') || (buildapp == 'b2g' && toolkit != 'gonk') #b2g-desktop(Bug 931116, b2g desktop specific, initial triage)

View File

@ -0,0 +1,68 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=920977
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 920977 </title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=920977">Mozilla Bug 920977 </a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
<script type="application/javascript;version=1.8" src="file_framework.js"></script>
<script type="application/javascript;version=1.8">
function verifier(success, failure) {
try {
if (!this.getObj()) {
failure("Did not receive proper object");
return;
}
} catch (e) {
failure("Received exception!: " + e);
return;
}
try {
this.getObj().removeFocus();
} catch (e) {
failure("Received exception!: " + e);
return;
}
var iframe = document.createElement("iframe");
iframe.setAttribute("mozbrowser", true);
iframe.src = "http://example.org/";
iframe.addEventListener("load", function() {
iframe.removeEventListener("load", arguments.callee);
if (iframe.setInputMethodActive &&
typeof iframe.setInputMethodActive == "function") {
success("Got setInputMethodActive");
} else {
failure("Didn't get setInputMethodActive") ;
}
});
document.getElementById('content').appendChild(iframe);
}
var gData = [
{
perm: ["input-manage", "browser"],
needParentPerm: true,
obj: "mozInputMethod",
webidl: "MozInputMethod",
settings: [["dom.mozInputMethod.enabled", true],
["dom.mozBrowserFramesEnabled", true]],
verifier: verifier.toSource()
}
]
</script>
</pre>
</body>
</html>

View File

@ -16,17 +16,32 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=920977
<pre id="test">
<script type="application/javascript;version=1.8" src="file_framework.js"></script>
<script type="application/javascript;version=1.8">
function verifier(success, failure) {
try {
if (!this.getObj()) {
failure("Did not receive proper object");
return;
}
} catch (e) {
failure("Received exception!: " + e);
return;
}
try {
this.getObj().removeFocus();
failure("Should receive exception when accessing system only method.!");
} catch (e) {
success(this.perm);
}
}
var gData = [
{
perm: ["input"],
obj: "mozInputMethod",
webidl: "MozInputMethod",
settings: [["dom.mozInputMethod.enabled", true]],
},
{
perm: ["input-manage"],
obj: "mozKeyboard",
idl: "nsIB2GKeyboard",
verifier: verifier.toSource()
}
]
</script>

View File

@ -66,7 +66,10 @@ const NFC_IPC_WRITE_PERM_MSG_NAMES = [
const NFC_IPC_MANAGER_PERM_MSG_NAMES = [
"NFC:CheckP2PRegistration",
"NFC:NotifyUserAcceptedP2P",
"NFC:NotifySendFileStatus"
"NFC:NotifySendFileStatus",
"NFC:StartPoll",
"NFC:StopPoll",
"NFC:PowerOff"
];
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
@ -420,6 +423,7 @@ function Nfc() {
lock.get(NFC.SETTING_NFC_ENABLED, this);
// Maps sessionId (that are generated from nfcd) with a unique guid : 'SessionToken'
this.sessionTokenMap = {};
this.targetsByRequestId = {};
gSystemWorkerManager.registerNfcWorker(this.worker);
}
@ -516,7 +520,14 @@ Nfc.prototype = {
this.currentPeerAppId = null;
break;
case "ConfigResponse":
gSystemMessenger.broadcastMessage("nfc-powerlevel-change", message);
let target = this.targetsByRequestId[message.requestId];
if (!target) {
debug("No target for requestId: " + message.requestId);
return;
}
delete this.targetsByRequestId[message.requestId];
target.sendAsyncMessage("NFC:ConfigResponse", message);
break;
case "ConnectResponse": // Fall through.
case "CloseResponse":
@ -539,12 +550,32 @@ Nfc.prototype = {
sessionTokenMap: null,
targetsByRequestId: null,
/**
* Process a message from the content process.
*/
receiveMessage: function receiveMessage(message) {
debug("Received '" + JSON.stringify(message) + "' message from content process");
// Handle messages without sessionToken.
if (message.name == "NFC:StartPoll") {
this.targetsByRequestId[message.json.requestId] = message.target;
this.setConfig({powerLevel: NFC.NFC_POWER_LEVEL_ENABLED,
requestId: message.json.requestId});
return null;
} else if (message.name == "NFC:StopPoll") {
this.targetsByRequestId[message.json.requestId] = message.target;
this.setConfig({powerLevel: NFC.NFC_POWER_LEVEL_LOW,
requestId: message.json.requestId});
return null;
} else if (message.name == "NFC:PowerOff") {
this.targetsByRequestId[message.json.requestId] = message.target;
this.setConfig({powerLevel: NFC.NFC_POWER_LEVEL_DISABLED,
requestId: message.json.requestId});
return null;
}
if (!this._enabled) {
debug("NFC is not enabled.");
this.sendNfcErrorResponse(message);

View File

@ -54,7 +54,7 @@ const NFC_IPC_MSG_NAMES = [
"NFC:CheckP2PRegistrationResponse",
"NFC:PeerEvent",
"NFC:NotifySendFileStatusResponse",
"NFC:SendFileResponse"
"NFC:ConfigResponse"
];
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
@ -314,6 +314,51 @@ NfcContentHelper.prototype = {
});
},
startPoll: function startPoll(window) {
if (window == null) {
throw Components.Exception("Can't get window object",
Cr.NS_ERROR_UNEXPECTED);
}
let request = Services.DOMRequest.createRequest(window);
let requestId = btoa(this.getRequestId(request));
this._requestMap[requestId] = window;
cpmm.sendAsyncMessage("NFC:StartPoll",
{requestId: requestId});
return request;
},
stopPoll: function stopPoll(window) {
if (window == null) {
throw Components.Exception("Can't get window object",
Cr.NS_ERROR_UNEXPECTED);
}
let request = Services.DOMRequest.createRequest(window);
let requestId = btoa(this.getRequestId(request));
this._requestMap[requestId] = window;
cpmm.sendAsyncMessage("NFC:StopPoll",
{requestId: requestId});
return request;
},
powerOff: function powerOff(window) {
if (window == null) {
throw Components.Exception("Can't get window object",
Cr.NS_ERROR_UNEXPECTED);
}
let request = Services.DOMRequest.createRequest(window);
let requestId = btoa(this.getRequestId(request));
this._requestMap[requestId] = window;
cpmm.sendAsyncMessage("NFC:PowerOff",
{requestId: requestId});
return request;
},
// nsIObserver
observe: function observe(subject, topic, data) {
if (topic == "xpcom-shutdown") {
@ -368,6 +413,7 @@ NfcContentHelper.prototype = {
case "NFC:MakeReadOnlyNDEFResponse":
case "NFC:CheckP2PRegistrationResponse":
case "NFC:NotifySendFileStatusResponse":
case "NFC:ConfigResponse":
if (result.status !== NFC.GECKO_NFC_ERROR_SUCCESS) {
this.fireRequestError(atob(result.requestId), result.status);
} else {

View File

@ -24,7 +24,7 @@ interface nsINfcPeerCallback : nsISupports
in DOMString sessionToken);
};
[scriptable, uuid(70cac000-7e3c-11e3-baa7-0800200c9a66)]
[scriptable, uuid(10b2eb1b-3fe0-4c98-9c67-9e4c2274cd78)]
interface nsINfcContentHelper : nsISupports
{
const long NFC_EVENT_PEER_READY = 0x01;
@ -135,4 +135,19 @@ interface nsINfcContentHelper : nsISupports
void notifySendFileStatus(in nsIDOMWindow window,
in octet status,
in DOMString requestId);
/**
* Power on the NFC hardware and start polling for NFC tags or devices.
*/
nsIDOMDOMRequest startPoll(in nsIDOMWindow window);
/**
* Stop polling for NFC tags or devices. i.e. enter low power mode.
*/
nsIDOMDOMRequest stopPoll(in nsIDOMWindow window);
/**
* Power off the NFC hardware.
*/
nsIDOMDOMRequest powerOff(in nsIDOMWindow window);
};

View File

@ -25,6 +25,21 @@ interface MozNfcManager {
* Notify the status of sendFile operation
*/
void notifySendFileStatus(octet status, DOMString requestId);
/**
* Power on the NFC hardware and start polling for NFC tags or devices.
*/
DOMRequest startPoll();
/**
* Stop polling for NFC tags or devices. i.e. enter low power mode.
*/
DOMRequest stopPoll();
/**
* Power off the NFC hardware.
*/
DOMRequest powerOff();
};
[JSImplementation="@mozilla.org/navigatorNfc;1",

View File

@ -126,7 +126,7 @@ this.PriorityUrlProvider = Object.freeze({
matches.delete(token);
},
getMatchingSpec: function (searchToken) {
getMatch: function (searchToken) {
return Task.spawn(function* () {
yield promiseInitialized();
for (let [token, match] of matches.entries()) {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
component {f964a319-397a-4d21-8be6-5cdd1ee3e3ae} UnifiedComplete.js
contract @mozilla.org/autocomplete/search;1?name=unifiedcomplete {f964a319-397a-4d21-8be6-5cdd1ee3e3ae}

View File

@ -87,6 +87,8 @@ if CONFIG['MOZ_PLACES']:
EXTRA_COMPONENTS += [
'nsPlacesAutoComplete.js',
'nsPlacesAutoComplete.manifest',
'UnifiedComplete.js',
'UnifiedComplete.manifest',
]
FINAL_LIBRARY = 'xul'

View File

@ -11,7 +11,7 @@ function run_test() {
add_task(function* search_engine_match() {
let engine = yield promiseDefaultSearchEngine();
let token = engine.getResultDomain();
let match = yield PriorityUrlProvider.getMatchingSpec(token.substr(0, 1));
let match = yield PriorityUrlProvider.getMatch(token.substr(0, 1));
do_check_eq(match.url, engine.searchForm);
do_check_eq(match.title, engine.name);
do_check_eq(match.iconUrl, engine.iconURI ? engine.iconURI.spec : null);
@ -19,7 +19,7 @@ add_task(function* search_engine_match() {
});
add_task(function* no_match() {
do_check_eq(null, yield PriorityUrlProvider.getMatchingSpec("test"));
do_check_eq(null, yield PriorityUrlProvider.getMatch("test"));
});
add_task(function* hide_search_engine_nomatch() {
@ -29,16 +29,16 @@ add_task(function* hide_search_engine_nomatch() {
Services.search.removeEngine(engine);
yield promiseTopic;
do_check_true(engine.hidden);
do_check_eq(null, yield PriorityUrlProvider.getMatchingSpec(token.substr(0, 1)));
do_check_eq(null, yield PriorityUrlProvider.getMatch(token.substr(0, 1)));
});
add_task(function* add_search_engine_match() {
let promiseTopic = promiseSearchTopic("engine-added");
do_check_eq(null, yield PriorityUrlProvider.getMatchingSpec("bacon"));
do_check_eq(null, yield PriorityUrlProvider.getMatch("bacon"));
Services.search.addEngineWithDetails("bacon", "", "bacon", "Search Bacon",
"GET", "http://www.bacon.moz/?search={searchTerms}");
yield promiseSearchTopic;
let match = yield PriorityUrlProvider.getMatchingSpec("bacon");
let match = yield PriorityUrlProvider.getMatch("bacon");
do_check_eq(match.url, "http://www.bacon.moz");
do_check_eq(match.title, "bacon");
do_check_eq(match.iconUrl, null);
@ -50,7 +50,7 @@ add_task(function* remove_search_engine_nomatch() {
let promiseTopic = promiseSearchTopic("engine-removed");
Services.search.removeEngine(engine);
yield promiseTopic;
do_check_eq(null, yield PriorityUrlProvider.getMatchingSpec("bacon"));
do_check_eq(null, yield PriorityUrlProvider.getMatch("bacon"));
});
function promiseDefaultSearchEngine() {

View File

@ -45,6 +45,12 @@ const UNKNOWN_XPCOM_ABI = "unknownABI";
const UPDATE_REQUEST_VERSION = 2;
const CATEGORY_UPDATE_PARAMS = "extension-update-params";
const XMLURI_BLOCKLIST = "http://www.mozilla.org/2006/addons-blocklist";
const KEY_PROFILEDIR = "ProfD";
const KEY_APPDIR = "XCurProcD";
const FILE_BLOCKLIST = "blocklist.xml";
const BRANCH_REGEXP = /^([^\.]+\.[0-9]+[a-z]*).*/gi;
const PREF_EM_CHECK_COMPATIBILITY_BASE = "extensions.checkCompatibility";
#ifdef MOZ_COMPATIBILITY_NIGHTLY
@ -65,6 +71,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
"resource://gre/modules/addons/AddonRepository.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "CertUtils", function certUtilsLazyGetter() {
let certUtils = {};
@ -521,6 +529,88 @@ var AddonManagerInternal = {
this.TelemetryTimestamps.add(name, value);
},
validateBlocklist: function AMI_validateBlocklist() {
let appBlocklist = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
// If there is no application shipped blocklist then there is nothing to do
if (!appBlocklist.exists())
return;
let profileBlocklist = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
// If there is no blocklist in the profile then copy the application shipped
// one there
if (!profileBlocklist.exists()) {
try {
appBlocklist.copyTo(profileBlocklist.parent, FILE_BLOCKLIST);
}
catch (e) {
logger.warn("Failed to copy the application shipped blocklist to the profile", e);
}
return;
}
let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
try {
let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"].
createInstance(Ci.nsIConverterInputStream);
fileStream.init(appBlocklist, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
cstream.init(fileStream, "UTF-8", 0, 0);
let data = "";
let str = {};
let read = 0;
do {
read = cstream.readString(0xffffffff, str);
data += str.value;
} while (read != 0);
let parser = Cc["@mozilla.org/xmlextras/domparser;1"].
createInstance(Ci.nsIDOMParser);
var doc = parser.parseFromString(data, "text/xml");
}
catch (e) {
logger.warn("Application shipped blocklist could not be loaded", e);
return;
}
finally {
try {
fileStream.close();
}
catch (e) {
logger.warn("Unable to close blocklist file stream", e);
}
}
// If the namespace is incorrect then ignore the application shipped
// blocklist
if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) {
logger.warn("Application shipped blocklist has an unexpected namespace (" +
doc.documentElement.namespaceURI + ")");
return;
}
// If there is no lastupdate information then ignore the application shipped
// blocklist
if (!doc.documentElement.hasAttribute("lastupdate"))
return;
// If the application shipped blocklist is older than the profile blocklist
// then do nothing
if (doc.documentElement.getAttribute("lastupdate") <=
profileBlocklist.lastModifiedTime)
return;
// Otherwise copy the application shipped blocklist to the profile
try {
appBlocklist.copyTo(profileBlocklist.parent, FILE_BLOCKLIST);
}
catch (e) {
logger.warn("Failed to copy the application shipped blocklist to the profile", e);
}
},
/**
* Initializes the AddonManager, loading any known providers and initializing
* them.
@ -559,6 +649,7 @@ var AddonManagerInternal = {
Services.appinfo.platformVersion);
Services.prefs.setIntPref(PREF_BLOCKLIST_PINGCOUNTVERSION,
(appChanged === undefined ? 0 : -1));
this.validateBlocklist();
}
#ifndef MOZ_COMPATIBILITY_NIGHTLY

View File

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
<emItems>
<emItem blockID="i454" id="ancient@tests.mozilla.org">
<versionRange minVersion="0" maxVersion="*" severity="3"/>
</emItem>
</emItems>
</blocklist>

View File

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1396046918000">
<emItems>
<emItem blockID="i454" id="new@tests.mozilla.org">
<versionRange minVersion="0" maxVersion="*" severity="3"/>
</emItem>
</emItems>
</blocklist>

View File

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1296046918000">
<emItems>
<emItem blockID="i454" id="old@tests.mozilla.org">
<versionRange minVersion="0" maxVersion="*" severity="3"/>
</emItem>
</emItems>
</blocklist>

View File

@ -0,0 +1,200 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
const KEY_PROFILEDIR = "ProfD";
const KEY_APPDIR = "XCurProcD";
const FILE_BLOCKLIST = "blocklist.xml";
const PREF_BLOCKLIST_ENABLED = "extensions.blocklist.enabled";
const OLD = do_get_file("data/test_overrideblocklist/old.xml");
const NEW = do_get_file("data/test_overrideblocklist/new.xml");
const ANCIENT = do_get_file("data/test_overrideblocklist/ancient.xml");
const OLD_TSTAMP = 1296046918000;
const NEW_TSTAMP = 1396046918000;
const gAppDir = FileUtils.getFile(KEY_APPDIR, []);
let oldAddon = {
id: "old@tests.mozilla.org",
version: 1
}
let newAddon = {
id: "new@tests.mozilla.org",
version: 1
}
let ancientAddon = {
id: "ancient@tests.mozilla.org",
version: 1
}
let invalidAddon = {
id: "invalid@tests.mozilla.org",
version: 1
}
function incrementAppVersion() {
gAppInfo.version = "" + (parseInt(gAppInfo.version) + 1);
}
function clearBlocklists() {
let blocklist = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
if (blocklist.exists())
blocklist.remove(true);
blocklist = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
if (blocklist.exists())
blocklist.remove(true);
}
function reloadBlocklist() {
Services.prefs.setBoolPref(PREF_BLOCKLIST_ENABLED, false);
Services.prefs.setBoolPref(PREF_BLOCKLIST_ENABLED, true);
}
function copyToApp(file) {
file.clone().copyTo(gAppDir, FILE_BLOCKLIST);
}
function copyToProfile(file, tstamp) {
file = file.clone();
file.copyTo(gProfD, FILE_BLOCKLIST);
file = gProfD.clone();
file.append(FILE_BLOCKLIST);
file.lastModifiedTime = tstamp;
}
function run_test() {
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
let appBlocklist = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
if (appBlocklist.exists()) {
try {
appBlocklist.moveTo(gAppDir, "blocklist.old");
}
catch (e) {
todo(false, "Aborting test due to unmovable blocklist file: " + e);
return;
}
do_register_cleanup(function() {
clearBlocklists();
appBlocklist.moveTo(gAppDir, FILE_BLOCKLIST);
});
}
run_next_test();
}
// On first run whataver is in the app dir should get copied to the profile
add_test(function test_copy() {
clearBlocklists();
copyToApp(OLD);
incrementAppVersion();
startupManager();
reloadBlocklist();
let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
getService(AM_Ci.nsIBlocklistService);
do_check_false(blocklist.isAddonBlocklisted(invalidAddon));
do_check_false(blocklist.isAddonBlocklisted(ancientAddon));
do_check_true(blocklist.isAddonBlocklisted(oldAddon));
do_check_false(blocklist.isAddonBlocklisted(newAddon));
shutdownManager();
run_next_test();
});
// An ancient blocklist should be ignored
add_test(function test_ancient() {
clearBlocklists();
copyToApp(ANCIENT);
copyToProfile(OLD, OLD_TSTAMP);
incrementAppVersion();
startupManager();
reloadBlocklist();
let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
getService(AM_Ci.nsIBlocklistService);
do_check_false(blocklist.isAddonBlocklisted(invalidAddon));
do_check_false(blocklist.isAddonBlocklisted(ancientAddon));
do_check_true(blocklist.isAddonBlocklisted(oldAddon));
do_check_false(blocklist.isAddonBlocklisted(newAddon));
shutdownManager();
run_next_test();
});
// A new blocklist should override an old blocklist
add_test(function test_override() {
clearBlocklists();
copyToApp(NEW);
copyToProfile(OLD, OLD_TSTAMP);
incrementAppVersion();
startupManager();
reloadBlocklist();
let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
getService(AM_Ci.nsIBlocklistService);
do_check_false(blocklist.isAddonBlocklisted(invalidAddon));
do_check_false(blocklist.isAddonBlocklisted(ancientAddon));
do_check_false(blocklist.isAddonBlocklisted(oldAddon));
do_check_true(blocklist.isAddonBlocklisted(newAddon));
shutdownManager();
run_next_test();
});
// An old blocklist shouldn't override a new blocklist
add_test(function test_retain() {
clearBlocklists();
copyToApp(OLD);
copyToProfile(NEW, NEW_TSTAMP);
incrementAppVersion();
startupManager();
reloadBlocklist();
let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
getService(AM_Ci.nsIBlocklistService);
do_check_false(blocklist.isAddonBlocklisted(invalidAddon));
do_check_false(blocklist.isAddonBlocklisted(ancientAddon));
do_check_false(blocklist.isAddonBlocklisted(oldAddon));
do_check_true(blocklist.isAddonBlocklisted(newAddon));
shutdownManager();
run_next_test();
});
// A missing blocklist in the profile should still load an app-shipped blocklist
add_test(function test_missing() {
clearBlocklists();
copyToApp(OLD);
copyToProfile(NEW, NEW_TSTAMP);
incrementAppVersion();
startupManager();
shutdownManager();
let blocklist = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
blocklist.remove(true);
startupManager(false);
reloadBlocklist();
let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
getService(AM_Ci.nsIBlocklistService);
do_check_false(blocklist.isAddonBlocklisted(invalidAddon));
do_check_false(blocklist.isAddonBlocklisted(ancientAddon));
do_check_true(blocklist.isAddonBlocklisted(oldAddon));
do_check_false(blocklist.isAddonBlocklisted(newAddon));
shutdownManager();
run_next_test();
});

View File

@ -262,4 +262,6 @@ run-sequentially = Uses global XCurProcD dir.
# Bug 676992: test consistently hangs on Android
skip-if = os == "android"
run-sequentially = Uses global XCurProcD dir.
[test_overrideblocklist.js]
run-sequentially = Uses global XCurProcD dir.
[test_sourceURI.js]