Merge m-c to inbound a=merge CLOSED TREE

This commit is contained in:
Wes Kocher 2015-03-24 18:35:30 -07:00
Родитель 82959e5df3 a2b428efea
Коммит af7c4c6606
119 изменённых файлов: 3195 добавлений и 1281 удалений

1
addon-sdk/source/app-extension/bootstrap.js поставляемый
Просмотреть файл

@ -354,7 +354,6 @@ function nukeModules() {
// the addon is unload.
unloadSandbox(cuddlefishSandbox.loaderSandbox);
unloadSandbox(cuddlefishSandbox.xulappSandbox);
// Bug 764840: We need to unload cuddlefish otherwise it will stay alive
// and keep a reference to this compartment.

Просмотреть файл

@ -3,8 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var readParam = require("./node-scripts/utils").readParam;
var path = require("path");
var Promise = require("promise");
var Mocha = require("mocha");
var mocha = new Mocha({
ui: "bdd",
@ -12,16 +11,19 @@ var mocha = new Mocha({
timeout: 900000
});
var type = readParam("type");
exports.run = function(type) {
return new Promise(function(resolve) {
type = type || "";
[
(/^(modules)?$/.test(type)) && require.resolve("../bin/node-scripts/test.modules"),
(/^(addons)?$/.test(type)) && require.resolve("../bin/node-scripts/test.addons"),
(/^(examples)?$/.test(type)) && require.resolve("../bin/node-scripts/test.examples"),
].sort().forEach(function(filepath) {
filepath && mocha.addFile(filepath);
})
[
(!type || type == "modules") && require.resolve("../bin/node-scripts/test.modules"),
(!type || type == "addons") && require.resolve("../bin/node-scripts/test.addons"),
(!type || type == "examples") && require.resolve("../bin/node-scripts/test.examples"),
].sort().forEach(function(filepath) {
filepath && mocha.addFile(filepath);
})
mocha.run(function (failures) {
process.exit(failures);
});
mocha.run(function(failures) {
resolve(failures);
});
});
}

Просмотреть файл

@ -202,7 +202,7 @@ var Connection = Class({
},
poolFor: function(id) {
for (let pool of this.pools.values()) {
if (pool.has(id))
if pool.has(id)
return pool;
}
},
@ -797,7 +797,7 @@ var Tab = Client.from({
"storageActor": "storage",
"gcliActor": "gcli",
"memoryActor": "memory",
"eventLoopLag": "eventLoopLag",
"eventLoopLag": "eventLoopLag"
"trace": "trace", // missing
}

Просмотреть файл

@ -0,0 +1,23 @@
/* 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 gulp = require('gulp');
gulp.task('test', function(done) {
require("./bin/jpm-test").run().then(done);
});
gulp.task('test:addons', function(done) {
require("./bin/jpm-test").run("addons").then(done);
});
gulp.task('test:examples', function(done) {
require("./bin/jpm-test").run("examples").then(done);
});
gulp.task('test:modules', function(done) {
require("./bin/jpm-test").run("modules").then(done);
});

Просмотреть файл

@ -16,6 +16,31 @@ const assetsURI = require('../self').data.url();
const hideSheetUri = "data:text/css,:root {visibility: hidden !important;}";
function translateElementAttributes(element) {
// Translateable attributes
const attrList = ['title', 'accesskey', 'alt', 'label', 'placeholder'];
const ariaAttrMap = {
'ariaLabel': 'aria-label',
'ariaValueText': 'aria-valuetext',
'ariaMozHint': 'aria-moz-hint'
};
const attrSeparator = '.';
// Try to translate each of the attributes
for (let attribute of attrList) {
const data = core.get(element.dataset.l10nId + attrSeparator + attribute);
if (data)
element.setAttribute(attribute, data);
}
// Look for the aria attribute translations that match fxOS's aliases
for (let attrAlias in ariaAttrMap) {
const data = core.get(element.dataset.l10nId + attrSeparator + attrAlias);
if (data)
element.setAttribute(ariaAttrMap[attrAlias], data);
}
}
// Taken from Gaia:
// https://github.com/andreasgal/gaia/blob/04fde2640a7f40314643016a5a6c98bf3755f5fd/webapi.js#L1470
function translateElement(element) {
@ -32,6 +57,8 @@ function translateElement(element) {
var data = core.get(key);
if (data)
child.textContent = data;
translateElementAttributes(child);
}
}
exports.translateElement = translateElement;

Просмотреть файл

@ -320,6 +320,8 @@ TestRunner.prototype = {
});
PromiseDebugging.flushUncaughtErrors();
PromiseDebugging.removeUncaughtErrorObserver(this._uncaughtErrorObserver);
return all(winPromises).then(() => {
let browserWins = wins.filter(isBrowser);
@ -537,7 +539,8 @@ TestRunner.prototype = {
this.test.errors = {};
this.test.last = 'START';
PromiseDebugging.clearUncaughtErrorObservers();
PromiseDebugging.addUncaughtErrorObserver(this._uncaughtErrorObserver.bind(this));
this._uncaughtErrorObserver = this._uncaughtErrorObserver.bind(this);
PromiseDebugging.addUncaughtErrorObserver(this._uncaughtErrorObserver);
this.isDone = false;
this.onDone = function(self) {

Просмотреть файл

@ -21,6 +21,10 @@ let getWindowFrom = x =>
null;
function removeFromListeners() {
this.removeEventListener("DOMWindowClose", removeFromListeners);
for (let cleaner of listeners.get(this))
cleaner();
listeners.delete(this);
}
@ -45,26 +49,25 @@ function open(target, type, options) {
if (!window)
throw new Error("Unable to obtain the owner window from the target given.");
let cleaners = listeners.get(window) || [];
let cleaners = listeners.get(window);
if (!cleaners) {
cleaners = [];
listeners.set(window, cleaners);
// We need to remove from our map the `window` once is closed, to prevent
// memory leak
window.addEventListener("DOMWindowClose", removeFromListeners);
}
cleaners.push(() => target.removeEventListener(type, listener, capture));
listeners.set(window, cleaners);
// We need to remove from our map the `window` once is closed, to prevent
// memory leak
window.addEventListener("DOMWindowClose", removeFromListeners);
target.addEventListener(type, listener, capture);
return output;
}
unload(() => {
for (let [window, cleaners] of listeners) {
cleaners.forEach(callback => callback())
}
listeners.clear();
for (let window of listeners.keys())
removeFromListeners.call(window);
});
exports.open = open;

Просмотреть файл

@ -692,8 +692,6 @@ function writeSync(fd, buffer, offset, length, position) {
else if (length + offset !== buffer.length) {
buffer = buffer.slice(offset, offset + length);
}
let writeStream = new WriteStream(fd, { position: position,
length: length });
let output = BinaryOutputStream(nsIFileOutputStream(fd));
nsIBinaryOutputStream(fd, output);

Просмотреть файл

@ -23,6 +23,7 @@ const historyService = Cc['@mozilla.org/browser/nav-history-service;1']
const { mapBookmarkItemType } = require('./utils');
const { EventTarget } = require('../event/target');
const { emit } = require('../event/core');
const { when } = require('../system/unload');
const emitter = EventTarget();
@ -119,4 +120,9 @@ historyService.addObserver(historyObserver, false);
let bookmarkObserver = createObserverInstance(BOOKMARK_EVENTS, BOOKMARK_ARGS);
bookmarkService.addObserver(bookmarkObserver, false);
when(() => {
historyService.removeObserver(historyObserver);
bookmarkService.removeObserver(bookmarkObserver);
});
exports.events = emitter;

Просмотреть файл

@ -169,6 +169,15 @@ function serializeStack(frames) {
Loader.serializeStack = serializeStack;
function readURI(uri) {
let nsURI = NetUtil.newURI(uri);
if (nsURI.scheme == "resource") {
// Resolve to a real URI, this will catch any obvious bad paths without
// logging assertions in debug builds, see bug 1135219
let proto = Cc["@mozilla.org/network/protocol;1?name=resource"].
getService(Ci.nsIResProtocolHandler);
uri = proto.resolveURI(nsURI);
}
let stream = NetUtil.newChannel2(uri,
'UTF-8',
null,
@ -420,6 +429,10 @@ const nodeResolve = iced(function nodeResolve(id, requirer, { rootURI }) {
// Resolve again
id = Loader.resolve(id, requirer);
// If this is already an absolute URI then there is no resolution to do
if (isAbsoluteURI(id))
return void 0;
// we assume that extensions are correct, i.e., a directory doesnt't have '.js'
// and a js file isn't named 'file.json.js'
let fullId = join(rootURI, id);
@ -431,9 +444,14 @@ const nodeResolve = iced(function nodeResolve(id, requirer, { rootURI }) {
if ((resolvedPath = loadAsDirectory(fullId)))
return stripBase(rootURI, resolvedPath);
// If the requirer is an absolute URI then the node module resolution below
// won't work correctly as we prefix everything with rootURI
if (isAbsoluteURI(requirer))
return void 0;
// If manifest has dependencies, attempt to look up node modules
// in the `dependencies` list
let dirs = getNodeModulePaths(dirname(join(rootURI, requirer))).map(dir => join(dir, id));
let dirs = getNodeModulePaths(dirname(requirer)).map(dir => join(rootURI, dir, id));
for (let i = 0; i < dirs.length; i++) {
if ((resolvedPath = loadAsFile(dirs[i])))
return stripBase(rootURI, resolvedPath);
@ -509,6 +527,7 @@ function getNodeModulePaths (start) {
let dir = join(parts.slice(0, i + 1).join('/'), moduleDir);
dirs.push(dir);
}
dirs.push(moduleDir);
return dirs;
}

Просмотреть файл

@ -8,10 +8,7 @@
"license": "MPL 2.0",
"unpack": true,
"scripts": {
"test": "node ./bin/jpm-test.js",
"modules": "node ./bin/jpm-test.js --type modules",
"addons": "node ./bin/jpm-test.js --type addons",
"examples": "node ./bin/jpm-test.js --type examples"
"test": "gulp test"
},
"homepage": "https://github.com/mozilla/addon-sdk",
"repository": {
@ -25,6 +22,7 @@
"async": "0.9.0",
"chai": "2.1.1",
"glob": "4.4.2",
"gulp": "3.8.11",
"jpm": "0.0.29",
"lodash": "3.3.1",
"mocha": "2.1.0",

Просмотреть файл

@ -56,6 +56,7 @@ DEFAULT_NO_CONNECTIONS_PREFS = {
'browser.aboutHomeSnippets.updateUrl': 'https://localhost/snippet-dummy',
'browser.newtab.url' : 'about:blank',
'browser.search.update': False,
'browser.search.suggest.enabled' : False,
'browser.safebrowsing.enabled' : False,
'browser.safebrowsing.updateURL': 'http://localhost/safebrowsing-dummy/update',
'browser.safebrowsing.gethashURL': 'http://localhost/safebrowsing-dummy/gethash',

Просмотреть файл

@ -20,5 +20,10 @@
<div data-l10n-id="Translated">
A data-l10n-id value can be used in multiple elements
</div>
<a data-l10n-id="link-attributes" title="Certain whitelisted attributes get translated too" alt="No" accesskey="A"></a>
<input data-l10n-id="input" type="text" placeholder="Form placeholders are translateable">
<menu>
<menuitem data-l10n-id="contextitem" label="Labels of select options and context menus are translateable">
</menu>
</body>
</html

Просмотреть файл

@ -26,3 +26,13 @@ first_identifier[one]=first entry is %s and the second one is %s.
first_identifier=the entries are %s and %s.
second_identifier[other]=first entry is %s and the second one is %s.
third_identifier=first entry is %s and the second one is %s.
# bug 824489 allow translation of element attributes
link-attributes.title=Yes
link-attributes.alt=Yes
link-attributes.accesskey=B
input.placeholder=Yes
contextitem.label=Yes
link-attributes.ariaLabel=Yes
link-attributes.ariaValueText=Value
link-attributes.ariaMozHint=Hint

Просмотреть файл

@ -105,7 +105,15 @@ exports.testHtmlLocalizationPageWorker = createTest("en-GB", function(assert, lo
self.postMessage([nodes[0].innerHTML,
nodes[1].innerHTML,
nodes[2].innerHTML,
nodes[3].innerHTML]);
nodes[3].innerHTML,
nodes[4].title,
nodes[4].getAttribute("alt"),
nodes[4].getAttribute("accesskey"),
nodes[4].getAttribute("aria-label"),
nodes[4].getAttribute("aria-valuetext"),
nodes[4].getAttribute("aria-moz-hint"),
nodes[5].placeholder,
nodes[6].label]);
},
onMessage: function (data) {
assert.equal(
@ -121,6 +129,19 @@ exports.testHtmlLocalizationPageWorker = createTest("en-GB", function(assert, lo
);
assert.equal(data[3], "Yes", "Multiple elements with same data-l10n-id are accepted.");
// Attribute translation tests
assert.equal(data[4], "Yes", "Title attributes gets translated.");
assert.equal(data[5], "Yes", "Alt attributes gets translated.");
assert.equal(data[6], "B", "Accesskey gets translated.");
assert.equal(data[7], "Yes", "Aria-Label gets translated.");
assert.equal(data[8], "Value", "Aria-valuetext gets translated.");
assert.equal(data[9], "Hint", "Aria-moz-hint gets translated.");
assert.equal(data[10], "Yes", "Form placeholders are translateable.");
assert.equal(data[11], "Yes", "Labels of select options and context menus are translateable.");
done();
}
});
@ -144,7 +165,15 @@ exports.testHtmlLocalization = createTest("en-GB", function(assert, loader, done
self.postMessage([nodes[0].innerHTML,
nodes[1].innerHTML,
nodes[2].innerHTML,
nodes[3].innerHTML]);
nodes[3].innerHTML,
nodes[4].title,
nodes[4].getAttribute("alt"),
nodes[4].getAttribute("accesskey"),
nodes[4].getAttribute("aria-label"),
nodes[4].getAttribute("aria-valuetext"),
nodes[4].getAttribute("aria-moz-hint"),
nodes[5].placeholder,
nodes[6].label]);
},
onMessage: function (data) {
assert.equal(
@ -160,6 +189,19 @@ exports.testHtmlLocalization = createTest("en-GB", function(assert, loader, done
);
assert.equal(data[3], "Yes", "Multiple elements with same data-l10n-id are accepted.");
// Attribute translation tests
assert.equal(data[4], "Yes", "Title attributes gets translated.");
assert.equal(data[5], "Yes", "Alt attributes gets translated.");
assert.equal(data[6], "B", "Accesskey gets translated.");
assert.equal(data[7], "Yes", "Aria-Label gets translated.");
assert.equal(data[8], "Value", "Aria-valuetext gets translated.");
assert.equal(data[9], "Hint", "Aria-moz-hint gets translated.");
assert.equal(data[10], "Yes", "Form placeholders are translateable.");
assert.equal(data[11], "Yes", "Labels of select options and context menus are translateable.");
tab.close(done);
}
});

Просмотреть файл

@ -5,7 +5,7 @@
"use strict";
const LOCAL_URI = "about:robots";
const REMOTE_URI = "about:home";
const REMOTE_URI = "data:text/html;charset=utf-8,remote";
const { Loader } = require('sdk/test/loader');
const { getTabs, openTab, closeTab, setTabURL, getBrowserForTab, getURI } = require('sdk/tabs/utils');
@ -21,6 +21,19 @@ const { set } = require('sdk/preferences/service');
// The hidden preload browser messes up our frame counts
set('browser.newtab.preload', false);
function promiseTabFrameAttach(frames) {
return new Promise(resolve => {
let listener = function(frame, ...args) {
if (!frame.isTab)
return;
frames.off("attach", listener);
resolve([frame, ...args]);
}
frames.on("attach", listener);
});
}
// Check that we see a process stop and start
exports["test process restart"] = function*(assert) {
if (!isE10S) {
@ -44,7 +57,7 @@ exports["test process restart"] = function*(assert) {
// Switch the remote tab to a local URI which should kill the remote process
let frameDetach = promiseEventOnItemAndContainer(assert, remoteFrame, frames, 'detach');
let frameAttach = promiseEvent(frames, 'attach');
let frameAttach = promiseTabFrameAttach(frames);
let processDetach = promiseEventOnItemAndContainer(assert, remoteProcess, processes, 'detach');
setTabURL(tab, LOCAL_URI);
// The load should kill the remote frame
@ -57,7 +70,7 @@ exports["test process restart"] = function*(assert) {
frameDetach = promiseEventOnItemAndContainer(assert, newFrame, frames, 'detach');
let processAttach = promiseEvent(processes, 'attach');
frameAttach = promiseEvent(frames, 'attach');
frameAttach = promiseTabFrameAttach(frames);
setTabURL(tab, REMOTE_URI);
// The load should kill the remote frame
yield frameDetach;
@ -149,7 +162,7 @@ exports["test frame list"] = function*(assert) {
assert.equal(browserFrames(frames), getTabs(window).length, "Should be the right number of browser frames.");
assert.equal((yield getChildFrameCount(processes)), frames.length, "Child processes should have the right number of frames");
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab1 = openTab(window, LOCAL_URI);
let [frame1] = yield promise;
assert.ok(!!frame1, "Should have seen the new frame");
@ -158,7 +171,7 @@ exports["test frame list"] = function*(assert) {
assert.equal(browserFrames(frames), getTabs(window).length, "Should be the right number of browser frames.");
assert.equal((yield getChildFrameCount(processes)), frames.length, "Child processes should have the right number of frames");
promise = promiseEvent(frames, 'attach');
promise = promiseTabFrameAttach(frames);
let tab2 = openTab(window, REMOTE_URI);
let [frame2] = yield promise;
assert.ok(!!frame2, "Should have seen the new frame");
@ -256,7 +269,7 @@ exports["test unload"] = function*(assert) {
let loader = new Loader(module);
let { frames } = yield waitForProcesses(loader);
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab = openTab(window, "data:,<html/>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -280,7 +293,7 @@ exports["test frame detach on unload"] = function*(assert) {
let loader = new Loader(module);
let { frames } = yield waitForProcesses(loader);
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab = openTab(window, "data:,<html/>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -304,7 +317,7 @@ exports["test frame event listeners"] = function*(assert) {
let loader = new Loader(module);
let { frames } = yield waitForProcesses(loader);
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab = openTab(window, "data:text/html,<html></html>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -339,7 +352,7 @@ exports["test frames event listeners"] = function*(assert) {
let loader = new Loader(module);
let { frames } = yield waitForProcesses(loader);
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab = openTab(window, "data:text/html,<html></html>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -377,8 +390,8 @@ exports["test unload removes frame event listeners"] = function*(assert) {
let loader2 = new Loader(module);
let { frames: frames2 } = yield waitForProcesses(loader2);
let promise = promiseEvent(frames, 'attach');
let promise2 = promiseEvent(frames2, 'attach');
let promise = promiseTabFrameAttach(frames);
let promise2 = promiseTabFrameAttach(frames2);
let tab = openTab(window, "data:text/html,<html></html>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -418,8 +431,8 @@ exports["test unload removes frames event listeners"] = function*(assert) {
let loader2 = new Loader(module);
let { frames: frames2 } = yield waitForProcesses(loader2);
let promise = promiseEvent(frames, 'attach');
let promise2 = promiseEvent(frames2, 'attach');
let promise = promiseTabFrameAttach(frames);
let promise2 = promiseTabFrameAttach(frames2);
let tab = openTab(window, "data:text/html,<html></html>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);

Просмотреть файл

@ -20,5 +20,10 @@
<div data-l10n-id="Translated">
A data-l10n-id value can be used in multiple elements
</div>
<a data-l10n-id="link-attributes" title="Certain whitelisted attributes get translated too" alt="No" accesskey="A"></a>
<input data-l10n-id="input" type="text" placeholder="Form placeholders are translateable">
<menu>
<menuitem data-l10n-id="contextitem" label="Labels of select options and context menus are translateable">
</menu>
</body>
</html

Просмотреть файл

@ -26,3 +26,13 @@ first_identifier[one]=first entry is %s and the second one is %s.
first_identifier=the entries are %s and %s.
second_identifier[other]=first entry is %s and the second one is %s.
third_identifier=first entry is %s and the second one is %s.
# bug 824489 allow translation of element attributes
link-attributes.title=Yes
link-attributes.alt=Yes
link-attributes.accesskey=B
input.placeholder=Yes
contextitem.label=Yes
link-attributes.ariaLabel=Yes
link-attributes.ariaValueText=Value
link-attributes.ariaMozHint=Hint

Просмотреть файл

@ -105,7 +105,15 @@ exports.testHtmlLocalizationPageWorker = createTest("en-GB", function(assert, lo
self.postMessage([nodes[0].innerHTML,
nodes[1].innerHTML,
nodes[2].innerHTML,
nodes[3].innerHTML]);
nodes[3].innerHTML,
nodes[4].title,
nodes[4].getAttribute("alt"),
nodes[4].getAttribute("accesskey"),
nodes[4].getAttribute("aria-label"),
nodes[4].getAttribute("aria-valuetext"),
nodes[4].getAttribute("aria-moz-hint"),
nodes[5].placeholder,
nodes[6].label]);
},
onMessage: function (data) {
assert.equal(
@ -120,6 +128,19 @@ exports.testHtmlLocalizationPageWorker = createTest("en-GB", function(assert, lo
"Content from .properties is text content; HTML can't be injected."
);
assert.equal(data[3], "Yes", "Multiple elements with same data-l10n-id are accepted.");
// Attribute translation tests
assert.equal(data[4], "Yes", "Title attributes gets translated.");
assert.equal(data[5], "Yes", "Alt attributes gets translated.");
assert.equal(data[6], "B", "Accesskey gets translated.");
assert.equal(data[7], "Yes", "Aria-Label gets translated.");
assert.equal(data[8], "Value", "Aria-valuetext gets translated.");
assert.equal(data[9], "Hint", "Aria-moz-hint gets translated.");
assert.equal(data[10], "Yes", "Form placeholders are translateable.");
assert.equal(data[11], "Yes", "Labels of select options and context menus are translateable.");
done();
}
@ -144,7 +165,15 @@ exports.testHtmlLocalization = createTest("en-GB", function(assert, loader, done
self.postMessage([nodes[0].innerHTML,
nodes[1].innerHTML,
nodes[2].innerHTML,
nodes[3].innerHTML]);
nodes[3].innerHTML,
nodes[4].title,
nodes[4].getAttribute("alt"),
nodes[4].getAttribute("accesskey"),
nodes[4].getAttribute("aria-label"),
nodes[4].getAttribute("aria-valuetext"),
nodes[4].getAttribute("aria-moz-hint"),
nodes[5].placeholder,
nodes[6].label]);
},
onMessage: function (data) {
assert.equal(
@ -160,6 +189,19 @@ exports.testHtmlLocalization = createTest("en-GB", function(assert, loader, done
);
assert.equal(data[3], "Yes", "Multiple elements with same data-l10n-id are accepted.");
// Attribute translation tests
assert.equal(data[4], "Yes", "Title attributes gets translated.");
assert.equal(data[5], "Yes", "Alt attributes gets translated.");
assert.equal(data[6], "B", "Accesskey gets translated.");
assert.equal(data[7], "Yes", "Aria-Label gets translated.");
assert.equal(data[8], "Value", "Aria-valuetext gets translated.");
assert.equal(data[9], "Hint", "Aria-moz-hint gets translated.");
assert.equal(data[10], "Yes", "Form placeholders are translateable.");
assert.equal(data[11], "Yes", "Labels of select options and context menus are translateable.");
tab.close(done);
}
});

Просмотреть файл

@ -5,7 +5,7 @@
"use strict";
const LOCAL_URI = "about:robots";
const REMOTE_URI = "about:home";
const REMOTE_URI = "data:text/html;charset=utf-8,remote";
const { Loader } = require('sdk/test/loader');
const { getTabs, openTab, closeTab, setTabURL, getBrowserForTab, getURI } = require('sdk/tabs/utils');
@ -21,6 +21,19 @@ const { set } = require('sdk/preferences/service');
// The hidden preload browser messes up our frame counts
set('browser.newtab.preload', false);
function promiseTabFrameAttach(frames) {
return new Promise(resolve => {
let listener = function(frame, ...args) {
if (!frame.isTab)
return;
frames.off("attach", listener);
resolve([frame, ...args]);
}
frames.on("attach", listener);
});
}
// Check that we see a process stop and start
exports["test process restart"] = function*(assert) {
if (!isE10S) {
@ -44,7 +57,7 @@ exports["test process restart"] = function*(assert) {
// Switch the remote tab to a local URI which should kill the remote process
let frameDetach = promiseEventOnItemAndContainer(assert, remoteFrame, frames, 'detach');
let frameAttach = promiseEvent(frames, 'attach');
let frameAttach = promiseTabFrameAttach(frames);
let processDetach = promiseEventOnItemAndContainer(assert, remoteProcess, processes, 'detach');
setTabURL(tab, LOCAL_URI);
// The load should kill the remote frame
@ -57,7 +70,7 @@ exports["test process restart"] = function*(assert) {
frameDetach = promiseEventOnItemAndContainer(assert, newFrame, frames, 'detach');
let processAttach = promiseEvent(processes, 'attach');
frameAttach = promiseEvent(frames, 'attach');
frameAttach = promiseTabFrameAttach(frames);
setTabURL(tab, REMOTE_URI);
// The load should kill the remote frame
yield frameDetach;
@ -149,7 +162,7 @@ exports["test frame list"] = function*(assert) {
assert.equal(browserFrames(frames), getTabs(window).length, "Should be the right number of browser frames.");
assert.equal((yield getChildFrameCount(processes)), frames.length, "Child processes should have the right number of frames");
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab1 = openTab(window, LOCAL_URI);
let [frame1] = yield promise;
assert.ok(!!frame1, "Should have seen the new frame");
@ -158,7 +171,7 @@ exports["test frame list"] = function*(assert) {
assert.equal(browserFrames(frames), getTabs(window).length, "Should be the right number of browser frames.");
assert.equal((yield getChildFrameCount(processes)), frames.length, "Child processes should have the right number of frames");
promise = promiseEvent(frames, 'attach');
promise = promiseTabFrameAttach(frames);
let tab2 = openTab(window, REMOTE_URI);
let [frame2] = yield promise;
assert.ok(!!frame2, "Should have seen the new frame");
@ -256,7 +269,7 @@ exports["test unload"] = function*(assert) {
let loader = new Loader(module);
let { frames } = yield waitForProcesses(loader);
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab = openTab(window, "data:,<html/>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -280,7 +293,7 @@ exports["test frame detach on unload"] = function*(assert) {
let loader = new Loader(module);
let { frames } = yield waitForProcesses(loader);
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab = openTab(window, "data:,<html/>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -304,7 +317,7 @@ exports["test frame event listeners"] = function*(assert) {
let loader = new Loader(module);
let { frames } = yield waitForProcesses(loader);
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab = openTab(window, "data:text/html,<html></html>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -339,7 +352,7 @@ exports["test frames event listeners"] = function*(assert) {
let loader = new Loader(module);
let { frames } = yield waitForProcesses(loader);
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab = openTab(window, "data:text/html,<html></html>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -377,8 +390,8 @@ exports["test unload removes frame event listeners"] = function*(assert) {
let loader2 = new Loader(module);
let { frames: frames2 } = yield waitForProcesses(loader2);
let promise = promiseEvent(frames, 'attach');
let promise2 = promiseEvent(frames2, 'attach');
let promise = promiseTabFrameAttach(frames);
let promise2 = promiseTabFrameAttach(frames2);
let tab = openTab(window, "data:text/html,<html></html>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -418,8 +431,8 @@ exports["test unload removes frames event listeners"] = function*(assert) {
let loader2 = new Loader(module);
let { frames: frames2 } = yield waitForProcesses(loader2);
let promise = promiseEvent(frames, 'attach');
let promise2 = promiseEvent(frames2, 'attach');
let promise = promiseTabFrameAttach(frames);
let promise2 = promiseTabFrameAttach(frames2);
let tab = openTab(window, "data:text/html,<html></html>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);

Просмотреть файл

@ -62,6 +62,7 @@ skip-if = true
[test-environment.js]
[test-errors.js]
[test-event-core.js]
[test-event-dom.js]
[test-event-target.js]
[test-event-utils.js]
[test-events.js]

Просмотреть файл

@ -13,6 +13,7 @@
"browser.aboutHomeSnippets.updateUrl": "https://localhost/snippet-dummy",
"browser.newtab.url": "about:blank",
"browser.search.update": false,
"browser.search.suggest.enabled": false,
"browser.safebrowsing.enabled": false,
"browser.safebrowsing.updateURL": "http://localhost/safebrowsing-dummy/update",
"browser.safebrowsing.gethashURL": "http://localhost/safebrowsing-dummy/gethash",

Просмотреть файл

@ -10,6 +10,7 @@ const { getMode } = require('sdk/private-browsing/utils');
const { browserWindows: windows } = require('sdk/windows');
const { defer } = require('sdk/core/promise');
const tabs = require('sdk/tabs');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
// test openDialog() from window/utils with private option
// test isActive state in pwpb case
@ -80,27 +81,22 @@ exports.testIsPrivateOnWindowOpenFromPrivate = function(assert, done) {
};
exports.testOpenTabWithPrivateWindow = function*(assert) {
let { promise, resolve } = defer();
let window = getMostRecentBrowserWindow().OpenBrowserWindow({ private: true });
let window = yield openPromise(null, {
features: {
private: true,
toolbar: true
}
});
yield focus(window);
assert.pass("loading new private window");
yield promise(window, 'load').then(focus);
assert.equal(isPrivate(window), true, 'the focused window is private');
tabs.open({
yield new Promise(resolve => tabs.open({
url: 'about:blank',
onOpen: (tab) => {
assert.equal(isPrivate(tab), false, 'the opened tab is not private');
tab.close(resolve);
}
});
}));
yield promise;
yield close(window);
};

Просмотреть файл

@ -0,0 +1,24 @@
/* 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 { openTab: makeTab, getTabContentWindow } = require("sdk/tabs/utils");
function openTab(rawWindow, url) {
return new Promise(resolve => {
let tab = makeTab(rawWindow, url);
let window = getTabContentWindow(tab);
if (window.document.readyState == "complete") {
return resolve();
}
window.addEventListener("load", function onLoad() {
window.removeEventListener("load", onLoad, true);
resolve();
}, true);
return null;
})
}
exports.openTab = openTab;

Просмотреть файл

@ -0,0 +1,92 @@
/* 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 { openWindow, closeWindow } = require('./util');
const { Loader } = require('sdk/test/loader');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
const { Cc, Ci } = require('chrome');
const els = Cc["@mozilla.org/eventlistenerservice;1"].
getService(Ci.nsIEventListenerService);
function countListeners(target, type) {
let listeners = els.getListenerInfoFor(target, {});
return listeners.filter(listener => listener.type == type).length;
}
exports['test window close clears listeners'] = function(assert) {
let window = yield openWindow();
let loader = Loader(module);
// Any element will do here
let gBrowser = window.gBrowser;
// Other parts of the app may be listening for this
let windowListeners = countListeners(window, "DOMWindowClose");
// We can assume we're the only ones using the test events
assert.equal(countListeners(gBrowser, "TestEvent1"), 0, "Should be no listener for test event 1");
assert.equal(countListeners(gBrowser, "TestEvent2"), 0, "Should be no listener for test event 2");
let { open } = loader.require('sdk/event/dom');
open(gBrowser, "TestEvent1");
assert.equal(countListeners(window, "DOMWindowClose"), windowListeners + 1,
"Should have added a DOMWindowClose listener");
assert.equal(countListeners(gBrowser, "TestEvent1"), 1, "Should be a listener for test event 1");
assert.equal(countListeners(gBrowser, "TestEvent2"), 0, "Should be no listener for test event 2");
open(gBrowser, "TestEvent2");
assert.equal(countListeners(window, "DOMWindowClose"), windowListeners + 1,
"Should not have added another DOMWindowClose listener");
assert.equal(countListeners(gBrowser, "TestEvent1"), 1, "Should be a listener for test event 1");
assert.equal(countListeners(gBrowser, "TestEvent2"), 1, "Should be a listener for test event 2");
window = yield closeWindow(window);
assert.equal(countListeners(window, "DOMWindowClose"), windowListeners,
"Should have removed a DOMWindowClose listener");
assert.equal(countListeners(gBrowser, "TestEvent1"), 0, "Should be no listener for test event 1");
assert.equal(countListeners(gBrowser, "TestEvent2"), 0, "Should be no listener for test event 2");
loader.unload();
};
exports['test unload clears listeners'] = function(assert) {
let window = getMostRecentBrowserWindow();
let loader = Loader(module);
// Any element will do here
let gBrowser = window.gBrowser;
// Other parts of the app may be listening for this
let windowListeners = countListeners(window, "DOMWindowClose");
// We can assume we're the only ones using the test events
assert.equal(countListeners(gBrowser, "TestEvent1"), 0, "Should be no listener for test event 1");
assert.equal(countListeners(gBrowser, "TestEvent2"), 0, "Should be no listener for test event 2");
let { open } = loader.require('sdk/event/dom');
open(gBrowser, "TestEvent1");
assert.equal(countListeners(window, "DOMWindowClose"), windowListeners + 1,
"Should have added a DOMWindowClose listener");
assert.equal(countListeners(gBrowser, "TestEvent1"), 1, "Should be a listener for test event 1");
assert.equal(countListeners(gBrowser, "TestEvent2"), 0, "Should be no listener for test event 2");
open(gBrowser, "TestEvent2");
assert.equal(countListeners(window, "DOMWindowClose"), windowListeners + 1,
"Should not have added another DOMWindowClose listener");
assert.equal(countListeners(gBrowser, "TestEvent1"), 1, "Should be a listener for test event 1");
assert.equal(countListeners(gBrowser, "TestEvent2"), 1, "Should be a listener for test event 2");
loader.unload();
assert.equal(countListeners(window, "DOMWindowClose"), windowListeners,
"Should have removed a DOMWindowClose listener");
assert.equal(countListeners(gBrowser, "TestEvent1"), 0, "Should be no listener for test event 1");
assert.equal(countListeners(gBrowser, "TestEvent2"), 0, "Should be no listener for test event 2");
};
require('sdk/test').run(exports);

Просмотреть файл

@ -48,6 +48,14 @@ exports['test nodeResolve'] = function (assert) {
'./node_modules/test-math/node_modules/test-add/index.js',
'Dependencies\' dependencies can be found');
resolveTest('resource://gre/modules/commonjs/sdk/tabs.js', './index.js', undefined,
'correctly ignores absolute URIs.');
resolveTest('../tabs', 'resource://gre/modules/commonjs/sdk/addon/bootstrap.js', undefined,
'correctly ignores attempts to resolve from a module at an absolute URI.');
resolveTest('sdk/tabs', 'resource://gre/modules/commonjs/sdk/addon/bootstrap.js', undefined,
'correctly ignores attempts to resolve from a module at an absolute URI.');
function resolveTest (id, requirer, expected, msg) {
let result = nodeResolve(id, requirer, { manifest: manifest, rootURI: rootURI });

Просмотреть файл

@ -1491,12 +1491,17 @@ exports.testShowHideRawWindowArg = function*(assert) {
const { Sidebar } = require('sdk/ui/sidebar');
let testName = 'testShowHideRawWindowArg';
assert.pass("Creating sidebar");
let sidebar = Sidebar({
id: testName,
title: testName,
url: 'data:text/html;charset=utf-8,' + testName
});
assert.pass("Created sidebar");
let mainWindow = getMostRecentBrowserWindow();
let newWindow = yield windowPromise(mainWindow.OpenBrowserWindow(), 'load');
assert.pass("Created the new window");
@ -1504,21 +1509,26 @@ exports.testShowHideRawWindowArg = function*(assert) {
yield focus(newWindow);
assert.pass("Focused the new window");
yield focus(mainWindow);
assert.pass("Focused the old window");
let newWindow2 = yield windowPromise(mainWindow.OpenBrowserWindow(), 'load');
assert.pass("Created the second new window");
yield focus(newWindow2);
assert.pass("Focused the second new window");
yield sidebar.show(newWindow);
assert.pass('the sidebar was shown');
assert.equal(isSidebarShowing(mainWindow), false, 'sidebar is not showing in main window');
assert.equal(isSidebarShowing(newWindow2), false, 'sidebar is not showing in second window');
assert.equal(isSidebarShowing(newWindow), true, 'sidebar is showing in new window');
assert.ok(isFocused(mainWindow), 'main window is still focused');
assert.ok(isFocused(newWindow2), 'main window is still focused');
yield sidebar.hide(newWindow);
assert.equal(isFocused(mainWindow), true, 'main window is still focused');
assert.equal(isFocused(newWindow2), true, 'second window is still focused');
assert.equal(isSidebarShowing(mainWindow), false, 'sidebar is not showing in main window');
assert.equal(isSidebarShowing(newWindow2), false, 'sidebar is not showing in second window');
assert.equal(isSidebarShowing(newWindow), false, 'sidebar is not showing in new window');
sidebar.destroy();

Просмотреть файл

@ -31,7 +31,7 @@ const openWindow = () => {
exports.openWindow = openWindow;
const closeWindow = (window) => {
const closed = when(window, "unload", true).then(_target);
const closed = when(window, "unload", true).then(_ => window);
window.close();
return closed;
};

Просмотреть файл

@ -7,7 +7,7 @@ const { Cc, Ci } = require('chrome');
const { setTimeout } = require('sdk/timers');
const { Loader } = require('sdk/test/loader');
const { onFocus, getMostRecentWindow, windows, isBrowser, getWindowTitle, isFocused } = require('sdk/window/utils');
const { open, close, focus } = require('sdk/window/helpers');
const { open, close, focus, promise: windowPromise } = require('sdk/window/helpers');
const { browserWindows } = require("sdk/windows");
const tabs = require("sdk/tabs");
const winUtils = require("sdk/deprecated/window-utils");
@ -17,6 +17,9 @@ const { viewFor } = require("sdk/view/core");
const { defer } = require("sdk/lang/functional");
const { cleanUI } = require("sdk/test/utils");
const { after } = require("sdk/test/utils");
const { merge } = require("sdk/util/object");
const self = require("sdk/self");
const { openTab } = require("../tabs/utils");
// TEST: open & close window
exports.testOpenAndCloseWindow = function(assert, done) {
@ -59,12 +62,9 @@ exports.testNeWindowIsFocused = function(assert, done) {
});
}
exports.testOpenRelativePathWindow = function(assert, done) {
exports.testOpenRelativePathWindow = function*(assert) {
assert.equal(browserWindows.length, 1, "Only one window open");
const { merge } = require("sdk/util/object");
const self = require("sdk/self");
let loader = Loader(module, null, null, {
modules: {
"sdk/self": merge({}, self, {
@ -72,17 +72,31 @@ exports.testOpenRelativePathWindow = function(assert, done) {
})
}
});
assert.pass("Created a new loader");
loader.require("sdk/windows").browserWindows.open({
url: "./test.html",
onOpen: (window) => {
window.tabs.activeTab.once("ready", (tab) => {
assert.equal(tab.title, "foo",
"tab opened a document with relative path");
done();
});
}
})
let tabReady = new Promise(resolve => {
loader.require("sdk/tabs").on("ready", (tab) => {
if (!/test\.html$/.test(tab.url))
return;
assert.equal(tab.title, "foo",
"tab opened a document with relative path");
resolve();
});
});
yield new Promise(resolve => {
loader.require("sdk/windows").browserWindows.open({
url: "./test.html",
onOpen: (window) => {
assert.pass("Created a new window");
resolve();
}
})
});
yield tabReady;
loader.unload();
}
exports.testAutomaticDestroy = function(assert, done) {
@ -218,52 +232,26 @@ exports.testOnOpenOnCloseListeners = function(assert, done) {
exports.testActiveWindow = function*(assert) {
let windows = browserWindows;
// API window objects
let window2, window3;
let window = getMostRecentWindow();
// Raw window objects
let rawWindow2, rawWindow3;
let rawWindow2 = yield windowPromise(window.OpenBrowserWindow(), "load").then(focus);
assert.pass("Window 2 was created");
yield new Promise(resolve => {
windows.open({
url: "data:text/html;charset=utf-8,<title>window 2</title>",
onOpen: (window) => {
assert.pass('window 2 open');
// open a tab in window 2
yield openTab(rawWindow2, "data:text/html;charset=utf-8,<title>window 2</title>");
window.tabs.activeTab.once('ready', () => {
assert.pass('window 2 tab activated');
assert.equal(rawWindow2.content.document.title, "window 2", "Got correct raw window 2");
assert.equal(rawWindow2.document.title, windows[1].title, "Saw correct title on window 2");
window2 = window;
rawWindow2 = viewFor(window);
let rawWindow3 = yield windowPromise(window.OpenBrowserWindow(), "load").then(focus);;
assert.pass("Window 3 was created");
assert.equal(rawWindow2.content.document.title, "window 2", "Got correct raw window 2");
assert.equal(rawWindow2.document.title, window2.title, "Saw correct title on window 2");
// open a tab in window 3
yield openTab(rawWindow3, "data:text/html;charset=utf-8,<title>window 3</title>");
windows.open({
url: "data:text/html;charset=utf-8,<title>window 3</title>",
onOpen: (window) => {
assert.pass('window 3 open');
window.tabs.activeTab.once('ready', () => {
assert.pass('window 3 tab activated');
window3 = window;
rawWindow3 = viewFor(window);
assert.equal(rawWindow3.content.document.title, "window 3", "Got correct raw window 3");
assert.equal(rawWindow3.document.title, window3.title, "Saw correct title on window 3");
resolve();
});
}
});
});
}
});
});
yield focus(rawWindow3);
assert.equal(rawWindow3.content.document.title, "window 3", "Got correct raw window 3");
assert.equal(rawWindow3.document.title, windows[2].title, "Saw correct title on window 3");
assert.equal(windows.length, 3, "Correct number of browser windows");
@ -272,11 +260,13 @@ exports.testActiveWindow = function*(assert) {
count++;
}
assert.equal(count, 3, "Correct number of windows returned by iterator");
assert.equal(windows.activeWindow.title, window3.title, "Correct active window title - 3");
assert.equal(windows.activeWindow.title, windows[2].title, "Correct active window title - 3");
let window3 = windows[2];
yield focus(rawWindow2);
assert.equal(windows.activeWindow.title, window2.title, "Correct active window title - 2");
assert.equal(windows.activeWindow.title, windows[1].title, "Correct active window title - 2");
let window2 = windows[1];
yield new Promise(resolve => {
onFocus(rawWindow2).then(resolve);
@ -284,7 +274,7 @@ exports.testActiveWindow = function*(assert) {
assert.pass("activating window2");
});
assert.equal(windows.activeWindow.title, window2.title, "Correct active window - 2");
assert.equal(windows.activeWindow.title, windows[1].title, "Correct active window - 2");
yield new Promise(resolve => {
onFocus(rawWindow3).then(resolve);
@ -391,21 +381,32 @@ exports.testTrackWindows = function(assert, done) {
}
// test that it is not possible to open a private window by default
exports.testWindowOpenPrivateDefault = function(assert, done) {
browserWindows.open({
url: 'about:mozilla',
exports.testWindowOpenPrivateDefault = function*(assert) {
const TITLE = "yo";
const URL = "data:text/html,<title>" + TITLE + "</title>";
let tabReady = new Promise(resolve => {
tabs.on('ready', function onTabReady(tab) {
if (tab.url != URL)
return;
tabs.removeListener('ready', onTabReady);
assert.equal(tab.title, TITLE, 'opened correct tab');
assert.equal(isPrivate(tab), false, 'tab is not private');
resolve();
});
})
yield new Promise(resolve => browserWindows.open({
url: URL,
isPrivate: true,
onOpen: function(window) {
let tab = window.tabs[0];
tab.once('ready', function() {
assert.equal(tab.url, 'about:mozilla', 'opened correct tab');
assert.equal(isPrivate(tab), false, 'tab is not private');
done();
});
assert.pass("the new window was opened");
resolve();
}
});
}));
yield tabReady;
}
// test that it is not possible to find a private window in

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="00d50b78a7a99de5b0b55309a467807c677e2a66"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="aebfbd998041e960cea0468533c0b5041b504850"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -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="00d50b78a7a99de5b0b55309a467807c677e2a66"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="aebfbd998041e960cea0468533c0b5041b504850"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>

Просмотреть файл

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="00d50b78a7a99de5b0b55309a467807c677e2a66"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="aebfbd998041e960cea0468533c0b5041b504850"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="00d50b78a7a99de5b0b55309a467807c677e2a66"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="aebfbd998041e960cea0468533c0b5041b504850"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="00d50b78a7a99de5b0b55309a467807c677e2a66"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="aebfbd998041e960cea0468533c0b5041b504850"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -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="00d50b78a7a99de5b0b55309a467807c677e2a66"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="aebfbd998041e960cea0468533c0b5041b504850"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="00d50b78a7a99de5b0b55309a467807c677e2a66"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="aebfbd998041e960cea0468533c0b5041b504850"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="00d50b78a7a99de5b0b55309a467807c677e2a66"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="aebfbd998041e960cea0468533c0b5041b504850"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>

Просмотреть файл

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "00d50b78a7a99de5b0b55309a467807c677e2a66",
"git_revision": "aebfbd998041e960cea0468533c0b5041b504850",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "c1db04cf683e85135ca587f1f414d4456b20cfa0",
"revision": "26282fe3ad19972a8d84cdc7eee85f73b6cfcc4e",
"repo_path": "integration/gaia-central"
}

Просмотреть файл

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="00d50b78a7a99de5b0b55309a467807c677e2a66"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="aebfbd998041e960cea0468533c0b5041b504850"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="00d50b78a7a99de5b0b55309a467807c677e2a66"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="aebfbd998041e960cea0468533c0b5041b504850"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -490,7 +490,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "PanelFrame", "resource:///modules/Panel
let wasVisible = false;
// Hide the infobar from the previous tab.
if (event.detail.previousTabfromTab) {
if (event.detail.previousTab) {
wasVisible = this._hideBrowserSharingInfoBar(false, event.detail.previousTab.linkedBrowser);
}

Просмотреть файл

@ -62,7 +62,7 @@
<popupnotification id="password-notification" hidden="true">
<popupnotificationcontent orient="vertical">
<textbox id="password-notification-username" disabled="true"/>
<textbox id="password-notification-username"/>
<textbox id="password-notification-password" type="password"
disabled="true"/>
</popupnotificationcontent>

Просмотреть файл

@ -14,6 +14,6 @@ function startMiddleClickTestCase(aTestNumber) {
}
function test() {
requestLongerTimeout(5); // slowwww shutdown on e10s
requestLongerTimeout(10); // slowwww shutdown on e10s
startReferrerTest(startMiddleClickTestCase);
}

Просмотреть файл

@ -17,6 +17,6 @@ function startNewPrivateWindowTestCase(aTestNumber) {
}
function test() {
requestLongerTimeout(5); // slowwww shutdown on e10s
requestLongerTimeout(10); // slowwww shutdown on e10s
startReferrerTest(startNewPrivateWindowTestCase);
}

Просмотреть файл

@ -16,6 +16,6 @@ function startNewTabTestCase(aTestNumber) {
}
function test() {
requestLongerTimeout(5); // slowwww shutdown on e10s
requestLongerTimeout(10); // slowwww shutdown on e10s
startReferrerTest(startNewTabTestCase);
}

Просмотреть файл

@ -17,6 +17,6 @@ function startNewWindowTestCase(aTestNumber) {
}
function test() {
requestLongerTimeout(5); // slowwww shutdown on e10s
requestLongerTimeout(10); // slowwww shutdown on e10s
startReferrerTest(startNewWindowTestCase);
}

Просмотреть файл

@ -13,6 +13,6 @@ function startSimpleClickTestCase(aTestNumber) {
};
function test() {
requestLongerTimeout(5); // slowwww shutdown on e10s
requestLongerTimeout(10); // slowwww shutdown on e10s
startReferrerTest(startSimpleClickTestCase);
}

Просмотреть файл

@ -90,7 +90,7 @@
<hbox align="center">
<label control="defaultFont" accesskey="&defaultFont.accesskey;">&defaultFont.label;</label>
<menulist id="defaultFont" flex="1"/>
<label control="defaultFontSize" accesskey="&defaultSize.accesskey;">&defaultSize.label;</label>
<label id="defaultFontSizeLabel" control="defaultFontSize" accesskey="&defaultSize.accesskey;">&defaultSize.label;</label>
<menulist id="defaultFontSize">
<menupopup>
<menuitem value="9" label="9"/>

Просмотреть файл

@ -72,14 +72,9 @@ let SessionMigrationInternal = {
win._closedTabs = [];
return win;
});
let wrappedState = {
url: "about:welcomeback",
formdata: {
id: {"sessionData": state},
xpath: {}
}
};
return {windows: [{tabs: [{entries: [wrappedState]}]}]};
let url = "about:welcomeback";
let formdata = {id: {sessionData: state}, url};
return {windows: [{tabs: [{entries: [{url}], formdata}]}]};
},
/**
* Asynchronously read session restore state (JSON) from a path

Просмотреть файл

@ -470,14 +470,9 @@ let SessionStoreInternal = {
if (this._needsRestorePage(state, this._recentCrashes)) {
// replace the crashed session with a restore-page-only session
let pageData = {
url: "about:sessionrestore",
formdata: {
id: { "sessionData": state },
xpath: {}
}
};
state = { windows: [{ tabs: [{ entries: [pageData] }] }] };
let url = "about:sessionrestore";
let formdata = {id: {sessionData: state}, url};
state = { windows: [{ tabs: [{ entries: [{url}], formdata }] }] };
} else if (this._hasSingleTabWithURL(state.windows,
"about:welcomeback")) {
// On a single about:welcomeback URL that crashed, replace about:welcomeback

Просмотреть файл

@ -3,6 +3,8 @@
"use strict";
let HiddenFrame = Cu.import("resource:///modules/HiddenFrame.jsm", {}).HiddenFrame;
const HTML_NS = "http://www.w3.org/1999/xhtml";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
@ -14,33 +16,25 @@ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
* The URL to open in the browser.
**/
function createHiddenBrowser(aURL) {
let deferred = Promise.defer();
let hiddenDoc = Services.appShell.hiddenDOMWindow.document;
let frame = new HiddenFrame();
return new Promise(resolve =>
frame.get().then(aFrame => {
let doc = aFrame.document;
let browser = doc.createElementNS(XUL_NS, "browser");
browser.setAttribute("type", "content");
browser.setAttribute("disableglobalhistory", "true");
browser.setAttribute("src", aURL);
// Create a HTML iframe with a chrome URL, then this can host the browser.
let iframe = hiddenDoc.createElementNS(HTML_NS, "iframe");
iframe.setAttribute("src", "chrome://global/content/mozilla.xhtml");
iframe.addEventListener("load", function onLoad() {
iframe.removeEventListener("load", onLoad, true);
let browser = iframe.contentDocument.createElementNS(XUL_NS, "browser");
browser.setAttribute("type", "content");
browser.setAttribute("disableglobalhistory", "true");
browser.setAttribute("src", aURL);
iframe.contentDocument.documentElement.appendChild(browser);
deferred.resolve({frame: iframe, browser: browser});
}, true);
hiddenDoc.documentElement.appendChild(iframe);
return deferred.promise;
};
doc.documentElement.appendChild(browser);
resolve({frame: frame, browser: browser});
}));
}
/**
* Remove the browser and the iframe.
* Remove the browser and the HiddenFrame.
*
* @param aFrame
* The iframe to dismiss.
* The HiddenFrame to dismiss.
* @param aBrowser
* The browser to dismiss.
*/
@ -49,9 +43,7 @@ function destroyHiddenBrowser(aFrame, aBrowser) {
aBrowser.remove();
// Take care of the frame holding our invisible browser.
if (!Cu.isDeadWrapper(aFrame)) {
aFrame.remove();
}
aFrame.destroy();
};
/**

Просмотреть файл

@ -496,10 +496,22 @@ ThreadState.prototype = {
// Ignore "interrupted" events, to avoid UI flicker. These are generated
// by the slow script dialog and internal events such as setting
// breakpoints. Pressing the resume button does need to be shown, though.
if (aEvent == "paused" &&
aPacket.why.type == "interrupted" &&
!this.interruptedByResumeButton) {
return;
if (aEvent == "paused") {
if (aPacket.why.type == "interrupted" &&
!this.interruptedByResumeButton) {
return;
} else if (aPacket.why.type == "breakpointConditionThrown" && aPacket.why.message) {
let where = aPacket.frame.where;
let aLocation = {
line: where.line,
column: where.column,
actor: where.source ? where.source.actor : null
};
DebuggerView.Sources.showBreakpointConditionThrownMessage(
aLocation,
aPacket.why.message
);
}
}
this.interruptedByResumeButton = false;
@ -590,6 +602,10 @@ StackFrames.prototype = {
case "breakpoint":
this._currentBreakpointLocation = aPacket.frame.where;
break;
case "breakpointConditionThrown":
this._currentBreakpointLocation = aPacket.frame.where;
this._conditionThrowMessage = aPacket.why.message;
break;
// If paused by a client evaluation, store the evaluated value.
case "clientEvaluated":
this._currentEvaluation = aPacket.why.frameFinished;

Просмотреть файл

@ -450,6 +450,19 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
this._unselectBreakpoint();
},
/**
* Display the message thrown on breakpoint condition
*/
showBreakpointConditionThrownMessage: function(aLocation, aMessage = "") {
let breakpointItem = this.getBreakpoint(aLocation);
if (!breakpointItem) {
return;
}
let attachment = breakpointItem.attachment;
attachment.view.container.classList.add("dbg-breakpoint-condition-thrown");
attachment.view.message.setAttribute("value", aMessage);
},
/**
* Update the checked/unchecked and enabled/disabled states of the buttons in
* the sources toolbar based on the currently selected source's state.
@ -689,12 +702,13 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
* - location: the breakpoint's source location and line number
* - disabled: the breakpoint's disabled state, boolean
* - text: the breakpoint's line text to be displayed
* - message: thrown string when the breakpoint condition throws,
* @return object
* An object containing the breakpoint container, checkbox,
* line number and line text nodes.
*/
_createBreakpointView: function(aOptions) {
let { location, disabled, text } = aOptions;
let { location, disabled, text, message } = aOptions;
let identifier = DebuggerController.Breakpoints.getIdentifier(location);
let checkbox = document.createElement("checkbox");
@ -714,6 +728,26 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
let tooltip = text ? text.substr(0, BREAKPOINT_LINE_TOOLTIP_MAX_LENGTH) : "";
lineTextNode.setAttribute("tooltiptext", tooltip);
let thrownNode = document.createElement("label");
thrownNode.className = "plain dbg-breakpoint-condition-thrown-message dbg-breakpoint-text";
thrownNode.setAttribute("value", message);
thrownNode.setAttribute("crop", "end");
thrownNode.setAttribute("flex", "1");
let bpLineContainer = document.createElement("hbox");
bpLineContainer.className = "plain dbg-breakpoint-line-container";
bpLineContainer.setAttribute("flex", "1");
bpLineContainer.appendChild(lineNumberNode);
bpLineContainer.appendChild(lineTextNode);
let bpDetailContainer = document.createElement("vbox");
bpDetailContainer.className = "plain dbg-breakpoint-detail-container";
bpDetailContainer.setAttribute("flex", "1");
bpDetailContainer.appendChild(bpLineContainer);
bpDetailContainer.appendChild(thrownNode);
let container = document.createElement("hbox");
container.id = "breakpoint-" + identifier;
container.className = "dbg-breakpoint side-menu-widget-item-other";
@ -725,14 +759,14 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
checkbox.addEventListener("click", this._onBreakpointCheckboxClick, false);
container.appendChild(checkbox);
container.appendChild(lineNumberNode);
container.appendChild(lineTextNode);
container.appendChild(bpDetailContainer);
return {
container: container,
checkbox: checkbox,
lineNumber: lineNumberNode,
lineText: lineTextNode
lineText: lineTextNode,
message: thrownNode
};
},

Просмотреть файл

@ -48,3 +48,13 @@
#body[layout=vertical] #stackframes {
visibility: hidden;
}
#source-progress-container {
display: flex;
flex-flow: column;
justify-content: center;
}
#source-progress {
flex: none;
}

Просмотреть файл

@ -15,6 +15,7 @@
<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
macanimationtype="document"
fullscreenbutton="true"
screenX="4" screenY="4"
@ -421,12 +422,12 @@
label="&debuggerUI.blackBoxMessage.unBlackBoxButton;"
command="unBlackBoxCommand"/>
</vbox>
<vbox id="source-progress-container"
align="center"
pack="center">
<progressmeter id="source-progress"
mode="undetermined"/>
</vbox>
<html:div id="source-progress-container"
align="center">
<html:div id="hbox">
<html:progress id="source-progress"></html:progress>
</html:div>
</html:div>
</deck>
<splitter id="editor-and-instruments-splitter"
class="devtools-side-splitter"/>

Просмотреть файл

@ -143,6 +143,8 @@ skip-if = e10s # TODO
skip-if = e10s # Bug 1093535
[browser_dbg_breakpoints-button-01.js]
[browser_dbg_breakpoints-button-02.js]
[browser_dbg_breakpoints-condition-thrown-message.js]
skip-if = e10s && debug
[browser_dbg_breakpoints-contextmenu-add.js]
[browser_dbg_breakpoints-contextmenu.js]
[browser_dbg_breakpoints-disabled-reload.js]

Просмотреть файл

@ -0,0 +1,106 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Make sure that the message which breakpoint condition throws
* could be displayed on UI correctly
*/
const TAB_URL = EXAMPLE_URL + "doc_conditional-breakpoints.html";
function test() {
let gTab, gPanel, gDebugger, gEditor;
let gSources, gLocation;
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gEditor = gDebugger.DebuggerView.editor;
gSources = gDebugger.DebuggerView.Sources;
waitForSourceAndCaretAndScopes(gPanel, ".html", 17)
.then(addBreakpoints)
.then(() => resumeAndTestThrownMessage(18))
.then(() => resumeAndTestNoThrownMessage(19))
.then(() => resumeAndTestThrownMessage(22))
.then(() => resumeAndFinishTest())
.then(() => closeDebuggerAndFinish(gPanel))
.then(null, aError => {
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
callInTab(gTab, "ermahgerd");
});
function resumeAndTestThrownMessage(aLine) {
EventUtils.sendMouseEvent({ type: "mousedown" },
gDebugger.document.getElementById("resume"),
gDebugger);
let finished = waitForCaretUpdated(gPanel, aLine).then(() => {
//test that the thrown message is correctly shown
let attachment = gSources.getBreakpoint({ actor: gSources.values[0], line: aLine}).attachment;
ok(attachment.view.container.classList.contains('dbg-breakpoint-condition-thrown'),
"Message on line " + aLine + " should be shown when condition throws.");
});
return finished;
}
function resumeAndTestNoThrownMessage(aLine) {
EventUtils.sendMouseEvent({ type: "mousedown" },
gDebugger.document.getElementById("resume"),
gDebugger);
let finished = waitForCaretUpdated(gPanel, aLine).then(() => {
//test that the thrown message is correctly shown
let attachment = gSources.getBreakpoint({ actor: gSources.values[0], line: aLine}).attachment;
ok(!attachment.view.container.classList.contains("dbg-breakpoint-condition-thrown"),
"Message on line " + aLine + " should be hidden if condition doesn't throw.");
});
return finished;
}
function resumeAndFinishTest() {
let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.AFTER_FRAMES_CLEARED)
gDebugger.gThreadClient.resume();
return finished;
}
function addBreakpoints() {
return promise.resolve(null)
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue,
line: 18,
condition: " 1a"}))
.then(() => initialCheck(18))
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue,
line: 19,
condition: "true"}))
.then(() => initialCheck(19))
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue,
line: 20,
condition: "false"}))
.then(() => initialCheck(20))
.then(() => gPanel.addBreakpoint({ actor: gSources.selectedValue,
line: 22,
condition: "randomVar"}))
.then(() => initialCheck(22));
}
function initialCheck(aCaretLine) {
let bp = gSources.getBreakpoint({ actor: gSources.values[0], line: aCaretLine})
let attachment = bp.attachment;
ok(attachment,
"There should be an item for line " + aCaretLine + " in the sources pane.");
let thrownNode = attachment.view.container.querySelector(".dbg-breakpoint-condition-thrown-message");
ok(thrownNode,
"The breakpoint item should contain a thrown message node.")
ok(!attachment.view.container.classList.contains("dbg-breakpoint-condition-thrown"),
"The thrown message on line " + aCaretLine + " should be hidden when condition has not been evaluated.")
}
}

Просмотреть файл

@ -435,11 +435,8 @@ function Rule(aElementStyle, aOptions) {
this.keyframes = aOptions.keyframes || null;
this._modificationDepth = 0;
if (this.domRule) {
let parentRule = this.domRule.parentRule;
if (parentRule && parentRule.type == Ci.nsIDOMCSSRule.MEDIA_RULE) {
this.mediaText = parentRule.mediaText;
}
if (this.domRule && this.domRule.mediaText) {
this.mediaText = this.domRule.mediaText;
}
// Populate the text properties with the style's current cssText
@ -507,7 +504,7 @@ Rule.prototype = {
* The rule's line within a stylesheet
*/
get ruleLine() {
return this.domRule ? this.domRule.line : null;
return this.domRule ? this.domRule.line : "";
},
/**
@ -529,10 +526,12 @@ Rule.prototype = {
if (this._originalSourceStrings) {
return promise.resolve(this._originalSourceStrings);
}
return this.domRule.getOriginalLocation().then(({href, line}) => {
return this.domRule.getOriginalLocation().then(({href, line, mediaText}) => {
let mediaString = mediaText ? " @" + mediaText : "";
let sourceStrings = {
full: href + ":" + line,
short: CssLogic.shortSource({href: href}) + ":" + line
full: (href || CssLogic.l10n("rule.sourceInline")) + ":" + line + mediaString,
short: CssLogic.shortSource({href: href}) + ":" + line + mediaString
};
this._originalSourceStrings = sourceStrings;

Просмотреть файл

@ -12,8 +12,10 @@ add_task(function*() {
info("Creating the test document");
let style = "" +
"#testid {" +
" background-color: blue;" +
"@media screen and (min-width: 10px) {" +
" #testid {" +
" background-color: blue;" +
" }" +
"}" +
".testclass, .unmatched {" +
" background-color: green;" +
@ -35,6 +37,15 @@ function* testContentAfterNodeSelection(inspector, ruleView) {
"After highlighting null, has a no-results element again.");
yield selectNode("#testid", inspector);
let linkText = getRuleViewLinkTextByIndex(ruleView, 1);
is(linkText, "inline:1 @screen and (min-width: 10px)",
"link text at index 1 contains media query text.");
linkText = getRuleViewLinkTextByIndex(ruleView, 2);
is(linkText, "inline:1",
"link text at index 2 contains no media query text.");
let classEditor = getRuleViewRuleEditor(ruleView, 2);
is(classEditor.selectorText.querySelector(".ruleview-selector-matched").textContent,
".testclass", ".textclass should be matched.");

Просмотреть файл

@ -693,6 +693,17 @@ function getRuleViewLinkByIndex(view, index) {
return links[index];
}
/**
* Get rule-link text from the rule-view given its index
* @param {CssRuleView} view The instance of the rule-view panel
* @param {Number} index The index of the link to get
* @return {String} The string at this index
*/
function getRuleViewLinkTextByIndex(view, index) {
let link = getRuleViewLinkByIndex(view, index);
return link.querySelector(".source-link-label").value;
}
/**
* Get the rule editor from the rule-view given its index
* @param {CssRuleView} view The instance of the rule-view panel

Просмотреть файл

@ -302,9 +302,8 @@ let UI = {
},
busyUntil: function(promise, operationDescription) {
// Freeze the UI until the promise is resolved. A 30s timeout
// will unfreeze the UI, just in case the promise never gets
// resolved.
// Freeze the UI until the promise is resolved. A timeout will unfreeze the
// UI, just in case the promise never gets resolved.
this._busyPromise = promise;
this._busyOperationDescription = operationDescription;
this.setupBusyTimeout();
@ -469,7 +468,13 @@ let UI = {
// |busyUntil| will listen for rejections.
// Bug 1121100 may find a better way to silence these.
});
return this.busyUntil(promise, "Connecting to " + name);
promise = this.busyUntil(promise, "Connecting to " + name);
// Stop busy timeout for runtimes that take unknown or long amounts of time
// to connect.
if (runtime.prolongedConnection) {
this.cancelBusyTimeout();
}
return promise;
},
updateRuntimeButton: function() {

Просмотреть файл

@ -66,6 +66,10 @@ const Strings = Services.strings.createBundle("chrome://browser/locale/devtools/
* |name| field
* A user-visible label to identify the runtime that will be displayed in a
* runtime list.
* |prolongedConnection| field
* A boolean value which should be |true| if the connection process is
* expected to take a unknown or large amount of time. A UI may use this as a
* hint to skip timeouts or other time-based code paths.
* connect()
* Configure the passed |connection| object with any settings need to
* successfully connect to the runtime, and call the |connection|'s connect()
@ -446,6 +450,8 @@ function WiFiRuntime(deviceName) {
WiFiRuntime.prototype = {
type: RuntimeTypes.WIFI,
// Mark runtime as taking a long time to connect
prolongedConnection: true,
connect: function(connection) {
let service = discovery.getRemoteService("devtools", this.deviceName);
if (!service) {

Просмотреть файл

@ -37,6 +37,7 @@ SimpleTest.registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.webide.autoinstallADBHelper");
Services.prefs.clearUserPref("devtools.webide.autoinstallFxdtAdapters");
Services.prefs.clearUserPref("devtools.webide.sidebars");
Services.prefs.clearUserPref("devtools.webide.busyTimeout");
});
function openWebIDE(autoInstallAddons) {

Просмотреть файл

@ -62,6 +62,30 @@
}
});
win.AppManager.runtimeList.usb.push({
connect: function(connection) {
let deferred = promise.defer();
return deferred.promise;
},
get name() {
return "infiniteRuntime";
}
});
win.AppManager.runtimeList.usb.push({
connect: function(connection) {
let deferred = promise.defer();
return deferred.promise;
},
prolongedConnection: true,
get name() {
return "prolongedRuntime";
}
});
win.AppManager.update("runtimelist");
let packagedAppLocation = getTestFilePath("app");
@ -71,7 +95,7 @@
let panelNode = win.document.querySelector("#runtime-panel");
let items = panelNode.querySelectorAll(".runtime-panel-item-usb");
is(items.length, 1, "Found one runtime button");
is(items.length, 3, "Found 3 runtime buttons");
let deferred = promise.defer();
win.AppManager.connection.once(
@ -104,7 +128,6 @@
ok(isPlayActive(), "play button is enabled 3");
ok(!isStopActive(), "stop button is disabled 3");
yield win.Cmds.disconnectRuntime();
is(Object.keys(DebuggerServer._connections).length, 0, "Disconnected");
@ -137,6 +160,39 @@
yield win.Cmds.disconnectRuntime();
Services.prefs.setIntPref("devtools.webide.busyTimeout", 100);
// Wait for error message since connection never completes
let errorDeferred = promise.defer();
win.UI.reportError = errorName => {
if (errorName === "error_operationTimeout") {
errorDeferred.resolve();
}
};
// Click the infinite runtime
items[1].click();
ok(win.document.querySelector("window").className, "busy", "UI is busy");
yield errorDeferred.promise;
// Check for unexpected error message since this is prolonged
let noErrorDeferred = promise.defer();
win.UI.reportError = errorName => {
if (errorName === "error_operationTimeout") {
noErrorDeferred.reject();
}
};
// Click the prolonged runtime
items[2].click();
ok(win.document.querySelector("window").className, "busy", "UI is busy");
setTimeout(() => {
noErrorDeferred.resolve();
}, 1000);
yield noErrorDeferred.promise;
SimpleTest.finish();
});

Просмотреть файл

@ -13,7 +13,7 @@ pref("devtools.webide.enableLocalRuntime", false);
pref("devtools.webide.addonsURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/index.json");
pref("devtools.webide.simulatorAddonsURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/#VERSION#/#OS#/fxos_#SLASHED_VERSION#_simulator-#OS#-latest.xpi");
pref("devtools.webide.simulatorAddonID", "fxos_#SLASHED_VERSION#_simulator@mozilla.org");
pref("devtools.webide.simulatorAddonRegExp", "fxos_(.*)_simulator@mozilla\.org$");
pref("devtools.webide.simulatorAddonRegExp", "fxos_(.*)_simulator@mozilla\\.org$");
pref("devtools.webide.adbAddonURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/adb-helper/#OS#/adbhelper-#OS#-latest.xpi");
pref("devtools.webide.adbAddonID", "adbhelper@mozilla.org");
pref("devtools.webide.adaptersAddonURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxdt-adapters/#OS#/fxdt-adapters-#OS#-latest.xpi");

Просмотреть файл

@ -45,6 +45,16 @@
margin: 2px;
}
.dbg-breakpoint-condition-thrown-message {
display: none;
color: var(--theme-highlight-red);
}
.dbg-breakpoint.dbg-breakpoint-condition-thrown .dbg-breakpoint-condition-thrown-message {
display: block;
-moz-padding-start: 0;
}
/* Sources toolbar */
#sources-toolbar > .devtools-toolbarbutton,

Просмотреть файл

@ -151,6 +151,11 @@ treecol {
margin-right: 4px !important;
}
#defaultFontSizeLabel {
/* !important needed to override common !important rule */
-moz-margin-start: 4px !important;
}
/* Applications Pane Styles */
#applicationsContent {

Просмотреть файл

@ -164,7 +164,8 @@ public:
return GetOwner();
}
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
virtual void DisconnectFromOwner() override;
/**

Просмотреть файл

@ -95,7 +95,8 @@ BluetoothClassOfDevice::Create(nsPIDOMWindow* aOwner)
}
JSObject*
BluetoothClassOfDevice::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
BluetoothClassOfDevice::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return BluetoothClassOfDeviceBinding::Wrap(aCx, this, aGivenProto);
}

Просмотреть файл

@ -18,8 +18,8 @@ struct JSContext;
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothClassOfDevice final : public nsISupports,
public nsWrapperCache
class BluetoothClassOfDevice final : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@ -69,7 +69,8 @@ public:
{
return mOwnerWindow;
}
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
private:
BluetoothClassOfDevice(nsPIDOMWindow* aOwner);

Просмотреть файл

@ -320,7 +320,8 @@ BluetoothDevice::GetGatt()
}
JSObject*
BluetoothDevice::WrapObject(JSContext* aContext, JS::Handle<JSObject*> aGivenProto)
BluetoothDevice::WrapObject(JSContext* aContext,
JS::Handle<JSObject*> aGivenProto)
{
return BluetoothDeviceBinding::Wrap(aContext, this, aGivenProto);
}

Просмотреть файл

@ -30,7 +30,7 @@ class BluetoothSignal;
class BluetoothSocket;
class BluetoothDevice final : public DOMEventTargetHelper
, public BluetoothSignalObserver
, public BluetoothSignalObserver
{
public:
NS_DECL_ISUPPORTS_INHERITED
@ -94,7 +94,8 @@ public:
return GetOwner();
}
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
virtual void DisconnectFromOwner() override;
private:

Просмотреть файл

@ -58,7 +58,8 @@ BluetoothDiscoveryHandle::DispatchDeviceEvent(BluetoothDevice* aDevice)
}
JSObject*
BluetoothDiscoveryHandle::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
BluetoothDiscoveryHandle::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return BluetoothDiscoveryHandleBinding::Wrap(aCx, this, aGivenProto);
}

Просмотреть файл

@ -30,7 +30,8 @@ public:
IMPL_EVENT_HANDLER(devicefound);
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
private:
BluetoothDiscoveryHandle(nsPIDOMWindow* aWindow);

Просмотреть файл

@ -314,7 +314,8 @@ BluetoothGatt::Notify(const BluetoothSignal& aData)
}
JSObject*
BluetoothGatt::WrapObject(JSContext* aContext, JS::Handle<JSObject*> aGivenProto)
BluetoothGatt::WrapObject(JSContext* aContext,
JS::Handle<JSObject*> aGivenProto)
{
return BluetoothGattBinding::Wrap(aContext, this, aGivenProto);
}

Просмотреть файл

@ -28,7 +28,7 @@ class BluetoothSignal;
class BluetoothValue;
class BluetoothGatt final : public DOMEventTargetHelper
, public BluetoothSignalObserver
, public BluetoothSignalObserver
{
public:
NS_DECL_ISUPPORTS_INHERITED
@ -70,7 +70,8 @@ public:
return GetOwner();
}
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
virtual void DisconnectFromOwner() override;
BluetoothGatt(nsPIDOMWindow* aOwner,

Просмотреть файл

@ -20,8 +20,8 @@ BEGIN_BLUETOOTH_NAMESPACE
class BluetoothAdapter;
class BluetoothValue;
class BluetoothManager : public DOMEventTargetHelper
, public BluetoothSignalObserver
class BluetoothManager final : public DOMEventTargetHelper
, public BluetoothSignalObserver
{
public:
NS_DECL_ISUPPORTS_INHERITED
@ -63,7 +63,8 @@ public:
return GetOwner();
}
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
virtual void DisconnectFromOwner() override;
/**

Просмотреть файл

@ -121,7 +121,8 @@ BluetoothPairingHandle::SetPairingConfirmation(bool aConfirm, ErrorResult& aRv)
}
JSObject*
BluetoothPairingHandle::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
BluetoothPairingHandle::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return BluetoothPairingHandleBinding::Wrap(aCx, this, aGivenProto);
}

Просмотреть файл

@ -21,8 +21,8 @@ BEGIN_BLUETOOTH_NAMESPACE
class BluetoothDevice;
class BluetoothPairingHandle final : public nsISupports,
public nsWrapperCache
class BluetoothPairingHandle final : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@ -39,7 +39,8 @@ public:
return mOwner;
}
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
void GetPasskey(nsString& aPasskey) const
{

Просмотреть файл

@ -108,7 +108,8 @@ BluetoothPairingListener::Notify(const BluetoothSignal& aData)
}
JSObject*
BluetoothPairingListener::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
BluetoothPairingListener::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return BluetoothPairingListenerBinding::Wrap(aCx, this, aGivenProto);
}

Просмотреть файл

@ -17,7 +17,7 @@ class BluetoothDevice;
class BluetoothSignal;
class BluetoothPairingListener final : public DOMEventTargetHelper
, public BluetoothSignalObserver
, public BluetoothSignalObserver
{
public:
NS_DECL_ISUPPORTS_INHERITED
@ -37,7 +37,8 @@ public:
return GetOwner();
}
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
virtual void DisconnectFromOwner() override;
virtual void EventListenerAdded(nsIAtom* aType) override;

Просмотреть файл

@ -17,7 +17,7 @@ class BluetoothGattClient;
class BluetoothReplyRunnable;
class BluetoothGattManager final : public nsIObserver
, public BluetoothGattNotificationHandler
, public BluetoothGattNotificationHandler
{
public:
NS_DECL_ISUPPORTS

Просмотреть файл

@ -17,6 +17,7 @@
#include "nsIRadioVisitor.h"
#include "nsIPhonetic.h"
#include "mozilla/Telemetry.h"
#include "nsIControllers.h"
#include "nsIStringBundle.h"
#include "nsFocusManager.h"
@ -4497,6 +4498,12 @@ HTMLInputElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
// And now make sure our state is up to date
UpdateState(false);
#ifdef EARLY_BETA_OR_EARLIER
if (mType == NS_FORM_INPUT_PASSWORD) {
Telemetry::Accumulate(Telemetry::PWMGR_PASSWORD_INPUT_IN_FORM, !!mForm);
}
#endif
return rv;
}

Просмотреть файл

@ -115,6 +115,11 @@ this.Keyboard = {
// The application has been closed unexpectingly. Let's tell the
// keyboard app that the focus has been lost.
this.sendToKeyboard('Keyboard:FocusChange', { 'type': 'blur' });
// Notify system app to hide keyboard.
SystemAppProxy.dispatchEvent({
type: 'inputmethod-contextchange',
inputType: 'blur'
});
}
} else {
// Ignore notifications that aren't from a BrowserOrApp

Просмотреть файл

@ -2988,6 +2988,7 @@ public class BrowserApp extends GeckoApp
bookmark.setCheckable(true);
bookmark.setChecked(tab.isBookmark());
bookmark.setIcon(resolveBookmarkIconID(tab.isBookmark()));
bookmark.setTitle(resolveBookmarkTitleID(tab.isBookmark()));
reader.setEnabled(isAboutReader || !AboutPages.isAboutPage(tab.getURL()));
reader.setVisible(!inGuestMode);
@ -3116,6 +3117,10 @@ public class BrowserApp extends GeckoApp
}
}
private int resolveBookmarkTitleID(final boolean isBookmark) {
return (isBookmark ? R.string.bookmark_remove : R.string.bookmark);
}
private int resolveReadingListIconID(final boolean isInReadingList) {
return (isInReadingList ? R.drawable.ic_menu_reader_remove : R.drawable.ic_menu_reader_add);
}
@ -3146,10 +3151,12 @@ public class BrowserApp extends GeckoApp
Telemetry.sendUIEvent(TelemetryContract.Event.UNSAVE, TelemetryContract.Method.MENU, "bookmark");
tab.removeBookmark();
item.setIcon(resolveBookmarkIconID(false));
item.setTitle(resolveBookmarkTitleID(false));
} else {
Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.MENU, "bookmark");
tab.addBookmark();
item.setIcon(resolveBookmarkIconID(true));
item.setTitle(resolveBookmarkTitleID(true));
}
}
return true;

Просмотреть файл

@ -42,6 +42,7 @@
<!ENTITY url_bar_default_text2 "Search or enter address">
<!ENTITY bookmark "Bookmark">
<!ENTITY bookmark_remove "Remove bookmark">
<!ENTITY bookmark_added "Bookmark added">
<!ENTITY bookmark_removed "Bookmark removed">
<!ENTITY bookmark_updated "Bookmark updated">

Просмотреть файл

@ -78,6 +78,7 @@
<string name="quit">&quit;</string>
<string name="bookmark">&bookmark;</string>
<string name="bookmark_remove">&bookmark_remove;</string>
<string name="bookmark_added">&bookmark_added;</string>
<string name="bookmark_removed">&bookmark_removed;</string>
<string name="bookmark_updated">&bookmark_updated;</string>

Просмотреть файл

@ -33,10 +33,9 @@ public class ToolbarComponent extends BaseComponent {
private static final String URL_HTTP_PREFIX = "http://";
// We are waiting up to 60 seconds instead of the default waiting time
// because reader mode parsing can take quite some time on slower devices
// See Bug 1142699
private static final int READER_MODE_WAIT_MS = 60000;
// We are waiting up to 30 seconds instead of the default waiting time because reader mode
// parsing can take quite some time on slower devices (Bug 1142699)
private static final int READER_MODE_WAIT_MS = 30000;
public ToolbarComponent(final UITestContext testContext) {
super(testContext);

Просмотреть файл

@ -25,6 +25,14 @@ function getSelectionHandler() {
this._selectionHandler;
}
function getClipboard() {
return Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
}
function getTextValue(aElement) {
return aElement.value || aElement.textContent;
}
function todo(result, msg) {
return Messaging.sendRequestForResult({
type: TYPE_NAME,

Просмотреть файл

@ -9,8 +9,9 @@
const DIV_POINT_TEXT = "Under";
const INPUT_TEXT = "Text for select all in an <input>";
const TEXTAREA_TEXT = "Text for select all in a <textarea>";
const PASTE_TEXT = "Text for testing paste";
const READONLY_INPUT_TEXT = "readOnly text";
const TEXTAREA_TEXT = "Text for select all in a <textarea>";
/* =================================================================================
*
@ -25,6 +26,7 @@ function startTests() {
then(testReadonlyInput).
then(testCloseSelection).
then(testStartSelectionFail).
then(testPaste).
then(testAttachCaret).
then(testAttachCaretFail).
@ -351,6 +353,44 @@ function testAttachCaretFail() {
});
}
/* =================================================================================
*
* Tests to ensure we can paste text inside editable elements
*
*/
function testPaste() {
let sh = getSelectionHandler();
let clipboard = getClipboard();
clipboard.copyString(PASTE_TEXT, document);
// Add a contentEditable element to the document.
let div = document.createElement("div");
div.contentEditable = true;
div.dataset.editable = true;
document.body.appendChild(div);
let elements = document.querySelectorAll("div, input, textarea");
let promises = [];
for (var i = 0; i < elements.length; i++) {
sh.startSelection(elements[i]);
if (sh.isElementEditableText(elements[i]) && !elements[i].disabled) {
sh.actions.PASTE.action(elements[i]);
}
if (elements[i].dataset.editable) {
promises.push(is(getTextValue(elements[i]), PASTE_TEXT, "Pasted correctly"));
promises.push(ok(sh.isElementEditableText(elements[i]), "Element is editable"));
} else {
promises.push(isNot(getTextValue(elements[i]), PASTE_TEXT, "Paste failed correctly"));
}
}
document.body.removeChild(div);
div = null;
return Promise.all(promises);
}
</script>
</head>
@ -381,9 +421,9 @@ function testAttachCaretFail() {
nunc vel, fringilla turpis. Nulla lacinia, leo ut egestas hendrerit, risus
ligula interdum enim, vel varius libero sem ut ligula.</div><br>
<input id="inputNode" type="text"><br>
<input data-editable="true" id="inputNode" type="text"><br>
<textarea id="textareaNode"></textarea><br>
<textarea data-editable="true" id="textareaNode"></textarea><br>
<input id="readOnlyTextInput" type="text" readonly><br>

Просмотреть файл

@ -1,5 +1,6 @@
package org.mozilla.gecko.tests;
import org.mozilla.gecko.tests.helpers.GeckoHelper;
import org.mozilla.gecko.tests.helpers.NavigationHelper;
/**
@ -7,6 +8,8 @@ import org.mozilla.gecko.tests.helpers.NavigationHelper;
*/
public class testReaderModeTitle extends UITest {
public void testReaderModeTitle() {
GeckoHelper.blockForReady();
NavigationHelper.enterAndLoadUrl(StringHelper.ROBOCOP_READER_MODE_BASIC_ARTICLE);
mToolbar.pressReaderModeButton();

Просмотреть файл

@ -679,7 +679,8 @@ var SelectionHandler = {
order: 4,
selector: {
matches: function(aElement) {
return SelectionHandler.isElementEditableText(aElement) ?
// Disallow cut for contentEditable elements (until Bug 1112276 is fixed).
return !aElement.isContentEditable && SelectionHandler.isElementEditableText(aElement) ?
SelectionHandler.isSelectionActive() : false;
}
}
@ -711,10 +712,10 @@ var SelectionHandler = {
id: "paste_action",
icon: "drawable://ab_paste",
action: function(aElement) {
if (aElement && (aElement instanceof Ci.nsIDOMNSEditableElement)) {
let target = aElement.QueryInterface(Ci.nsIDOMNSEditableElement);
target.editor.paste(Ci.nsIClipboard.kGlobalClipboard);
target.focus();
if (aElement) {
let target = SelectionHandler._getEditor();
aElement.focus();
target.paste(Ci.nsIClipboard.kGlobalClipboard);
SelectionHandler._closeSelection();
UITelemetry.addEvent("action.1", "actionbar", null, "paste");
}
@ -895,7 +896,8 @@ var SelectionHandler = {
isElementEditableText: function (aElement) {
return (((aElement instanceof HTMLInputElement && aElement.mozIsTextField(false)) ||
(aElement instanceof HTMLTextAreaElement)) && !aElement.readOnly);
(aElement instanceof HTMLTextAreaElement)) && !aElement.readOnly) ||
aElement.isContentEditable;
},
_isNonTextInputElement: function(aElement) {
@ -963,7 +965,7 @@ var SelectionHandler = {
_moveCaret: function sh_moveCaret(aX, aY) {
// Get rect of text inside element
let range = document.createRange();
range.selectNodeContents(this._targetElement.QueryInterface(Ci.nsIDOMNSEditableElement).editor.rootElement);
range.selectNodeContents(this._getEditor().rootElement);
let textBounds = range.getBoundingClientRect();
// Get rect of editor

Просмотреть файл

@ -10,6 +10,10 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
Cu.import("resource://gre/modules/SharedPromptUtils.jsm");
const LoginInfo =
Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
"nsILoginInfo", "init");
/* Constants for password prompt telemetry.
* Mirrored in mobile/android/components/LoginManagerPrompter.js */
const PROMPT_DISPLAYED = 0;
@ -792,11 +796,13 @@ LoginManagerPrompter.prototype = {
_showLoginCaptureDoorhanger(login, type) {
let { browser } = this._getNotifyWindow();
let msgNames = type == "password-save" ? {
let saveMsgNames = {
prompt: "rememberPasswordMsgNoUsername",
buttonLabel: "notifyBarRememberPasswordButtonText",
buttonAccessKey: "notifyBarRememberPasswordButtonAccessKey",
} : {
};
let changeMsgNames = {
// We reuse the existing message, even if it expects a username, until we
// switch to the final terminology in bug 1144856.
prompt: "updatePasswordMsg",
@ -804,28 +810,94 @@ LoginManagerPrompter.prototype = {
buttonAccessKey: "notifyBarUpdateButtonAccessKey",
};
let initialMsgNames = type == "password-save" ? saveMsgNames
: changeMsgNames;
let histogramName = type == "password-save" ? "PWMGR_PROMPT_REMEMBER_ACTION"
: "PWMGR_PROMPT_UPDATE_ACTION";
let histogram = Services.telemetry.getHistogramById(histogramName);
histogram.add(PROMPT_DISPLAYED);
let chromeDoc = browser.ownerDocument;
let currentNotification;
let updateButtonLabel = () => {
let foundLogins = Services.logins.findLogins({}, login.hostname,
login.formSubmitURL,
login.httpRealm);
let logins = foundLogins.filter(l => l.username == login.username);
let msgNames = (logins.length == 0) ? saveMsgNames : changeMsgNames;
// Update the label based on whether this will be a new login or not.
let label = this._getLocalizedString(msgNames.buttonLabel);
let accessKey = this._getLocalizedString(msgNames.buttonAccessKey);
// Update the labels for the next time the panel is opened.
currentNotification.mainAction.label = label;
currentNotification.mainAction.accessKey = accessKey;
// Update the labels in real time if the notification is displayed.
let element = [...currentNotification.owner.panel.childNodes]
.find(n => n.notification == currentNotification);
if (element) {
element.setAttribute("buttonlabel", label);
element.setAttribute("buttonaccesskey", accessKey);
}
};
let writeDataToUI = () => {
chromeDoc.getElementById("password-notification-username")
.setAttribute("placeholder", usernamePlaceholder);
chromeDoc.getElementById("password-notification-username")
.setAttribute("value", login.username);
chromeDoc.getElementById("password-notification-password")
.setAttribute("value", login.password);
updateButtonLabel();
};
let readDataFromUI = () => {
login.username =
chromeDoc.getElementById("password-notification-username").value;
login.password =
chromeDoc.getElementById("password-notification-password").value;
};
let onUsernameInput = () => {
readDataFromUI();
updateButtonLabel();
};
let persistData = () => {
let foundLogins = Services.logins.findLogins({}, login.hostname,
login.formSubmitURL,
login.httpRealm);
let logins = foundLogins.filter(l => l.username == login.username);
if (logins.length == 0) {
// The original login we have been provided with might have its own
// metadata, but we don't want it propagated to the newly created one.
Services.logins.addLogin(new LoginInfo(login.hostname,
login.formSubmitURL,
login.httpRealm,
login.username,
login.password,
login.usernameField,
login.passwordField));
} else if (logins.length == 1) {
this._updateLogin(logins[0], login.password);
} else {
Cu.reportError("Unexpected match of multiple logins.");
}
};
// The main action is the "Remember" or "Update" button.
let mainAction = {
label: this._getLocalizedString(msgNames.buttonLabel),
accessKey: this._getLocalizedString(msgNames.buttonAccessKey),
label: this._getLocalizedString(initialMsgNames.buttonLabel),
accessKey: this._getLocalizedString(initialMsgNames.buttonAccessKey),
callback: () => {
histogram.add(PROMPT_ADD_OR_UPDATE);
let foundLogins = Services.logins.findLogins({}, login.hostname,
login.formSubmitURL,
login.httpRealm);
let logins = foundLogins.filter(l => l.username == login.username);
if (logins.length == 0) {
Services.logins.addLogin(login);
} else if (logins.length == 1) {
this._updateLogin(logins[0], login.password);
} else {
Cu.reportError("Unexpected match of multiple logins.");
}
readDataFromUI();
persistData();
browser.focus();
}
};
@ -847,7 +919,7 @@ LoginManagerPrompter.prototype = {
this._getPopupNote().show(
browser,
"password",
this._getLocalizedString(msgNames.prompt, [displayHost]),
this._getLocalizedString(initialMsgNames.prompt, [displayHost]),
"password-notification-icon",
mainAction,
secondaryActions,
@ -856,18 +928,23 @@ LoginManagerPrompter.prototype = {
persistWhileVisible: true,
passwordNotificationType: type,
eventCallback: function (topic) {
if (topic != "showing") {
return false;
switch (topic) {
case "showing":
currentNotification = this;
writeDataToUI();
chromeDoc.getElementById("password-notification-username")
.addEventListener("input", onUsernameInput);
break;
case "dismissed":
readDataFromUI();
// Fall through.
case "removed":
currentNotification = null;
chromeDoc.getElementById("password-notification-username")
.removeEventListener("input", onUsernameInput);
break;
}
let chromeDoc = this.browser.ownerDocument;
chromeDoc.getElementById("password-notification-username")
.setAttribute("placeholder", usernamePlaceholder);
chromeDoc.getElementById("password-notification-username")
.setAttribute("value", login.username);
chromeDoc.getElementById("password-notification-password")
.setAttribute("value", login.password);
return false;
},
}
);

Просмотреть файл

@ -81,3 +81,119 @@ add_task(function* test_save_change() {
Services.logins.removeAllLogins();
}
});
/**
* Test changing the username inside the doorhanger notification for passwords.
*
* We have to test combination of existing and non-existing logins both for
* the original one from the webpage and the final one used by the dialog.
*
* We also check switching to and from empty usernames.
*/
add_task(function* test_edit_username() {
let testCases = [{
usernameInPage: "username",
usernameChangedTo: "newUsername",
}, {
usernameInPage: "username",
usernameInPageExists: true,
usernameChangedTo: "newUsername",
}, {
usernameInPage: "username",
usernameChangedTo: "newUsername",
usernameChangedToExists: true,
}, {
usernameInPage: "username",
usernameInPageExists: true,
usernameChangedTo: "newUsername",
usernameChangedToExists: true,
}, {
usernameInPage: "",
usernameChangedTo: "newUsername",
}, {
usernameInPage: "newUsername",
usernameChangedTo: "",
}, {
usernameInPage: "",
usernameChangedTo: "newUsername",
usernameChangedToExists: true,
}, {
usernameInPage: "newUsername",
usernameChangedTo: "",
usernameChangedToExists: true,
}];
for (let testCase of testCases) {
info("Test case: " + JSON.stringify(testCase));
// Create the pre-existing logins when needed.
if (testCase.usernameInPageExists) {
Services.logins.addLogin(LoginTestUtils.testData.formLogin({
hostname: "https://example.com",
formSubmitURL: "https://example.com",
username: testCase.usernameInPage,
password: "old password",
}));
}
if (testCase.usernameChangedToExists) {
Services.logins.addLogin(LoginTestUtils.testData.formLogin({
hostname: "https://example.com",
formSubmitURL: "https://example.com",
username: testCase.usernameChangedTo,
password: "old password",
}));
}
yield BrowserTestUtils.withNewTab({
gBrowser,
url: "https://example.com/browser/toolkit/components/" +
"passwordmgr/test/browser/form_basic.html",
}, function* (browser) {
// Submit the form in the content page with the credentials from the test
// case. This will cause the doorhanger notification to be displayed.
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
"Shown");
yield ContentTask.spawn(browser, testCase.usernameInPage,
function* (usernameInPage) {
let doc = content.document;
doc.getElementById("form-basic-username").value = usernameInPage;
doc.getElementById("form-basic-password").value = "password";
doc.getElementById("form-basic").submit();
});
yield promiseShown;
// Modify the username in the dialog if requested.
if (testCase.usernameChangedTo) {
document.getElementById("password-notification-username")
.setAttribute("value", testCase.usernameChangedTo);
}
// We expect a modifyLogin notification if the final username used by the
// dialog exists in the logins database, otherwise an addLogin one.
let expectModifyLogin = testCase.usernameChangedTo
? testCase.usernameChangedToExists
: testCase.usernameInPageExists;
// Simulate the action on the notification to request the login to be
// saved, and wait for the data to be updated or saved based on the type
// of operation we expect.
let expectedNotification = expectModifyLogin ? "modifyLogin" : "addLogin";
let promiseLogin = TestUtils.topicObserved("passwordmgr-storage-changed",
(_, data) => data == expectedNotification);
let notificationElement = PopupNotifications.panel.childNodes[0];
notificationElement.button.doCommand();
let [result] = yield promiseLogin;
// Check that the values in the database match the expected values.
let login = expectModifyLogin ? result.QueryInterface(Ci.nsIArray)
.queryElementAt(1, Ci.nsILoginInfo)
: result.QueryInterface(Ci.nsILoginInfo);
Assert.equal(login.username, testCase.usernameChangedTo ||
testCase.usernameInPage);
Assert.equal(login.password, "password");
});
// Clean up the database before the next test case is executed.
Services.logins.removeAllLogins();
}
});

Просмотреть файл

@ -740,7 +740,12 @@ Database::InitSchema(bool* aDatabaseMigrated)
NS_ENSURE_SUCCESS(rv, rv);
}
// Firefox 38 uses schema version 27.
if (currentSchemaVersion < 28) {
rv = MigrateV28Up();
NS_ENSURE_SUCCESS(rv, rv);
}
// Firefox 39 uses schema version 28.
// Schema Upgrades must add migration code here.
@ -1536,8 +1541,8 @@ Database::MigrateV27Up() {
"JOIN moz_bookmarks b ON b.fk = h.id "
"JOIN moz_keywords k ON k.id = b.keyword_id "
"LEFT JOIN moz_items_annos a ON a.item_id = b.id "
"LEFT JOIN moz_anno_attributes n ON a.anno_attribute_id = n.id "
"AND n.name = 'bookmarkProperties/POSTData'"
"AND a.anno_attribute_id = (SELECT id FROM moz_anno_attributes "
"WHERE name = 'bookmarkProperties/POSTData') "
"WHERE k.place_id ISNULL "
"GROUP BY keyword"));
NS_ENSURE_SUCCESS(rv, rv);
@ -1563,6 +1568,38 @@ Database::MigrateV27Up() {
return NS_OK;
}
nsresult
Database::MigrateV28Up() {
MOZ_ASSERT(NS_IsMainThread());
// v27 migration was bogus and set some unrelated annotations as post_data for
// keywords having an annotated bookmark.
// The current v27 migration function is fixed, but we still need to handle
// users that hit the bogus version. Since we can't distinguish, we'll just
// set again all of the post data.
DebugOnly<nsresult> rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"UPDATE moz_keywords "
"SET post_data = ( "
"SELECT content FROM moz_items_annos a "
"JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id "
"JOIN moz_bookmarks b on b.id = a.item_id "
"WHERE n.name = 'bookmarkProperties/POSTData' "
"AND b.keyword_id = moz_keywords.id "
"ORDER BY b.lastModified DESC "
"LIMIT 1 "
") "
"WHERE EXISTS(SELECT 1 FROM moz_bookmarks WHERE keyword_id = moz_keywords.id) "
));
// In case the update fails a constraint, we don't want to throw away the
// whole database for just a few keywords. In rare cases the user might have
// to recreate them. Though, at this point, there shouldn't be 2 keywords
// pointing to the same url and post data, cause the previous migration step
// removed them.
MOZ_ASSERT(NS_SUCCEEDED(rv));
return NS_OK;
}
void
Database::Shutdown()
{

Просмотреть файл

@ -16,7 +16,7 @@
// This is the schema version. Update it at any schema change and add a
// corresponding migrateVxx method below.
#define DATABASE_SCHEMA_VERSION 27
#define DATABASE_SCHEMA_VERSION 28
// Fired after Places inited.
#define TOPIC_PLACES_INIT_COMPLETE "places-init-complete"
@ -275,6 +275,7 @@ protected:
nsresult MigrateV25Up();
nsresult MigrateV26Up();
nsresult MigrateV27Up();
nsresult MigrateV28Up();
nsresult UpdateBookmarkRootTitles();

Просмотреть файл

@ -3,7 +3,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 CURRENT_SCHEMA_VERSION = 27;
const CURRENT_SCHEMA_VERSION = 28;
const FIRST_UPGRADABLE_SCHEMA_VERSION = 11;
const NS_APP_USER_PROFILE_50_DIR = "ProfD";

Двоичные данные
toolkit/components/places/tests/migration/places_v28.sqlite Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -10,12 +10,15 @@ add_task(function* setup() {
yield db.execute(`INSERT INTO moz_places (url, guid)
VALUES ("http://test1.com/", "test1_______")
, ("http://test2.com/", "test2_______")
, ("http://test3.com/", "test3_______")
`);
// Add keywords.
yield db.execute(`INSERT INTO moz_keywords (keyword)
VALUES ("kw1")
, ("kw2")
, ("kw3")
, ("kw4")
, ("kw5")
`);
// Add bookmarks.
let now = Date.now() * 1000;
@ -38,15 +41,28 @@ add_task(function* setup() {
/* different uri, same keyword as 1 */
, (1, (SELECT id FROM moz_places WHERE guid = 'test1_______'), 3, ${index++}, ${now}, ${now},
(SELECT id FROM moz_keywords WHERE keyword = 'kw3'), "bookmark6___")
, (1, (SELECT id FROM moz_places WHERE guid = 'test3_______'), 3, ${index++}, ${now}, ${now},
(SELECT id FROM moz_keywords WHERE keyword = 'kw4'), "bookmark7___")
/* same uri and post_data as bookmark7, different keyword */
, (1, (SELECT id FROM moz_places WHERE guid = 'test3_______'), 3, ${index++}, ${now}, ${now},
(SELECT id FROM moz_keywords WHERE keyword = 'kw5'), "bookmark8___")
`);
// Add postData.
yield db.execute(`INSERT INTO moz_anno_attributes (name)
VALUES ("bookmarkProperties/POSTData")`);
VALUES ("bookmarkProperties/POSTData")
, ("someOtherAnno")`);
yield db.execute(`INSERT INTO moz_items_annos(anno_attribute_id, item_id, content)
VALUES ((SELECT id FROM moz_anno_attributes where name = "bookmarkProperties/POSTData"),
(SELECT id FROM moz_bookmarks WHERE guid = "bookmark3___"), "postData1")
, ((SELECT id FROM moz_anno_attributes where name = "bookmarkProperties/POSTData"),
(SELECT id FROM moz_bookmarks WHERE guid = "bookmark5___"), "postData2")`);
(SELECT id FROM moz_bookmarks WHERE guid = "bookmark5___"), "postData2")
, ((SELECT id FROM moz_anno_attributes where name = "someOtherAnno"),
(SELECT id FROM moz_bookmarks WHERE guid = "bookmark5___"), "zzzzzzzzzz")
, ((SELECT id FROM moz_anno_attributes where name = "bookmarkProperties/POSTData"),
(SELECT id FROM moz_bookmarks WHERE guid = "bookmark7___"), "postData3")
, ((SELECT id FROM moz_anno_attributes where name = "bookmarkProperties/POSTData"),
(SELECT id FROM moz_bookmarks WHERE guid = "bookmark8___"), "postData3")
`);
yield db.close();
});
@ -69,7 +85,15 @@ add_task(function* test_keywords() {
Assert.equal(postData2, "postData2");
let [ url3, postData3 ] = PlacesUtils.getURLAndPostDataForKeyword("kw3");
Assert.equal(url3, "http://test1.com/");
Assert.equal(postData3, null);
let [ url4, postData4 ] = PlacesUtils.getURLAndPostDataForKeyword("kw4");
Assert.equal(url4, null);
Assert.equal(postData4, null);
let [ url5, postData5 ] = PlacesUtils.getURLAndPostDataForKeyword("kw5");
Assert.equal(url5, "http://test3.com/");
Assert.equal(postData5, "postData3");
Assert.equal((yield foreign_count("http://test1.com/")), 5); // 4 bookmark2 + 1 keywords
Assert.equal((yield foreign_count("http://test2.com/")), 4); // 2 bookmark2 + 2 keywords
Assert.equal((yield foreign_count("http://test3.com/")), 3); // 2 bookmark2 + 1 keywords
});

Просмотреть файл

@ -0,0 +1,77 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
add_task(function* setup() {
yield setupPlacesDatabase("places_v27.sqlite");
// Setup database contents to be migrated.
let path = OS.Path.join(OS.Constants.Path.profileDir, DB_FILENAME);
let db = yield Sqlite.openConnection({ path });
// Add pages.
yield db.execute(`INSERT INTO moz_places (url, guid)
VALUES ("http://test1.com/", "test1_______")
, ("http://test2.com/", "test2_______")
`);
// Add keywords.
yield db.execute(`INSERT INTO moz_keywords (keyword, place_id, post_data)
VALUES ("kw1", (SELECT id FROM moz_places WHERE guid = "test2_______"), "broken data")
, ("kw2", (SELECT id FROM moz_places WHERE guid = "test2_______"), NULL)
, ("kw3", (SELECT id FROM moz_places WHERE guid = "test1_______"), "zzzzzzzzzz")
`);
// Add bookmarks.
let now = Date.now() * 1000;
let index = 0;
yield db.execute(`INSERT INTO moz_bookmarks (type, fk, parent, position, dateAdded, lastModified, keyword_id, guid)
VALUES (1, (SELECT id FROM moz_places WHERE guid = "test1_______"), 3, ${index++}, ${now}, ${now},
(SELECT id FROM moz_keywords WHERE keyword = "kw1"), "bookmark1___")
/* same uri, different keyword */
, (1, (SELECT id FROM moz_places WHERE guid = "test1_______"), 3, ${index++}, ${now}, ${now},
(SELECT id FROM moz_keywords WHERE keyword = "kw2"), "bookmark2___")
/* different uri, same keyword as 1 */
, (1, (SELECT id FROM moz_places WHERE guid = "test2_______"), 3, ${index++}, ${now}, ${now},
(SELECT id FROM moz_keywords WHERE keyword = "kw1"), "bookmark3___")
/* same uri, same keyword as 1 */
, (1, (SELECT id FROM moz_places WHERE guid = "test1_______"), 3, ${index++}, ${now}, ${now},
(SELECT id FROM moz_keywords WHERE keyword = "kw1"), "bookmark4___")
/* same uri, same keyword as 2 */
, (1, (SELECT id FROM moz_places WHERE guid = "test2_______"), 3, ${index++}, ${now}, ${now},
(SELECT id FROM moz_keywords WHERE keyword = "kw2"), "bookmark5___")
/* different uri, same keyword as 1 */
, (1, (SELECT id FROM moz_places WHERE guid = "test1_______"), 3, ${index++}, ${now}, ${now},
(SELECT id FROM moz_keywords WHERE keyword = "kw3"), "bookmark6___")
`);
// Add postData.
yield db.execute(`INSERT INTO moz_anno_attributes (name)
VALUES ("bookmarkProperties/POSTData")
, ("someOtherAnno")`);
yield db.execute(`INSERT INTO moz_items_annos(anno_attribute_id, item_id, content)
VALUES ((SELECT id FROM moz_anno_attributes where name = "bookmarkProperties/POSTData"),
(SELECT id FROM moz_bookmarks WHERE guid = "bookmark3___"), "postData1")
, ((SELECT id FROM moz_anno_attributes where name = "bookmarkProperties/POSTData"),
(SELECT id FROM moz_bookmarks WHERE guid = "bookmark5___"), "postData2")
, ((SELECT id FROM moz_anno_attributes where name = "someOtherAnno"),
(SELECT id FROM moz_bookmarks WHERE guid = "bookmark5___"), "zzzzzzzzzz")
`);
yield db.close();
});
add_task(function* database_is_valid() {
Assert.equal(PlacesUtils.history.databaseStatus,
PlacesUtils.history.DATABASE_STATUS_UPGRADED);
let db = yield PlacesUtils.promiseDBConnection();
Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION);
});
add_task(function* test_keywords() {
// When 2 urls have the same keyword, if one has postData it will be
// preferred.
let [ url1, postData1 ] = PlacesUtils.getURLAndPostDataForKeyword("kw1");
Assert.equal(url1, "http://test2.com/");
Assert.equal(postData1, "postData1");
let [ url2, postData2 ] = PlacesUtils.getURLAndPostDataForKeyword("kw2");
Assert.equal(url2, "http://test2.com/");
Assert.equal(postData2, "postData2");
let [ url3, postData3 ] = PlacesUtils.getURLAndPostDataForKeyword("kw3");
Assert.equal(url3, "http://test1.com/");
Assert.equal(postData3, null);
});

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше