Merge m-c to inbound. a=merge
63
addon-sdk/source/CONTRIBUTING.md
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
## Overview
|
||||||
|
|
||||||
|
- Changes should follow the [design guidelines], as well as [coding style guide] for Jetpack
|
||||||
|
- All changes must be accompanied by tests
|
||||||
|
- In order to land, changes must have review from a core Jetpack developer
|
||||||
|
- Changes should have additional API review when needed
|
||||||
|
- Changes should have additional review from a Mozilla platform domain-expert when needed
|
||||||
|
|
||||||
|
If you have questions, ask in [#jetpack on IRC][jetpack irc channel] or on the [Jetpack mailing list].
|
||||||
|
|
||||||
|
## How to Make Code Contributions
|
||||||
|
|
||||||
|
If you have code that you'd like to contribute the Jetpack project, follow these steps:
|
||||||
|
|
||||||
|
1. Look for your issue in the [bugs already filed][open bugs]
|
||||||
|
2. If no bug exists, [submit one][submit bug]
|
||||||
|
3. Make your changes, per the Overview
|
||||||
|
4. Write a test ([intro][test intro], [API][test API])
|
||||||
|
5. Submit pull request with changes and a title in a form of `Bug XXX - description`
|
||||||
|
6. Copy the pull request link from GitHub and paste it in as an attachment to the bug
|
||||||
|
7. Each pull request should idealy contain only one commit, so squash the commits if necessary.
|
||||||
|
8. Flag the attachment for code review from one of the Jetpack reviewers listed below.
|
||||||
|
This step is optional, but could speed things up.
|
||||||
|
9. Address any nits (ie style changes), or other issues mentioned in the review.
|
||||||
|
10. Finally, once review is approved, a team member will do the merging
|
||||||
|
|
||||||
|
## Good First Bugs
|
||||||
|
|
||||||
|
There is a list of [good first bugs here](https://bugzilla.mozilla.org/buglist.cgi?list_id=7345714&columnlist=bug_severity%2Cpriority%2Cassigned_to%2Cbug_status%2Ctarget_milestone%2Cresolution%2Cshort_desc%2Cchangeddate&query_based_on=jetpack-good-1st-bugs&status_whiteboard_type=allwordssubstr&query_format=advanced&status_whiteboard=[good%20first%20bug]&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&bug_status=VERIFIED&product=Add-on%20SDK&known_name=jetpack-good-1st-bugs).
|
||||||
|
|
||||||
|
## Reviewers
|
||||||
|
|
||||||
|
All changes must be reviewed by someone on the Jetpack review crew:
|
||||||
|
|
||||||
|
- [@mossop]
|
||||||
|
- [@gozala]
|
||||||
|
- [@wbamberg]
|
||||||
|
- [@ZER0]
|
||||||
|
- [@erikvold]
|
||||||
|
- [@jsantell]
|
||||||
|
- [@zombie]
|
||||||
|
|
||||||
|
For review of Mozilla platform usage and best practices, ask [@autonome],
|
||||||
|
[@0c0w3], or [@mossop] to find the domain expert.
|
||||||
|
|
||||||
|
For API and developer ergonomics review, ask [@gozala].
|
||||||
|
|
||||||
|
[design guidelines]:https://wiki.mozilla.org/Labs/Jetpack/Design_Guidelines
|
||||||
|
[jetpack irc channel]:irc://irc.mozilla.org/#jetpack
|
||||||
|
[Jetpack mailing list]:http://groups.google.com/group/mozilla-labs-jetpack
|
||||||
|
[open bugs]:https://bugzilla.mozilla.org/buglist.cgi?quicksearch=product%3ASDK
|
||||||
|
[submit bug]:https://bugzilla.mozilla.org/enter_bug.cgi?product=Add-on%20SDK&component=general
|
||||||
|
[test intro]:https://jetpack.mozillalabs.com/sdk/latest/docs/#guide/implementing-reusable-module
|
||||||
|
[test API]:https://jetpack.mozillalabs.com/sdk/latest/docs/#module/api-utils/unit-test
|
||||||
|
[coding style guide]:https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide
|
||||||
|
|
||||||
|
[@mossop]:https://github.com/mossop/
|
||||||
|
[@gozala]:https://github.com/Gozala/
|
||||||
|
[@wbamberg]:https://github.com/wbamberg/
|
||||||
|
[@ZER0]:https://github.com/ZER0/
|
||||||
|
[@erikvold]:https://github.com/erikvold/
|
||||||
|
[@jsantell]:https://github.com/jsantell
|
||||||
|
[@zombie]:https://github.com/zombie
|
@ -135,9 +135,19 @@ TestFinder.prototype = {
|
|||||||
let { fileFilter, testFilter } = makeFilters({ filter: this.filter });
|
let { fileFilter, testFilter } = makeFilters({ filter: this.filter });
|
||||||
|
|
||||||
return getSuites({ id: id, filter: fileFilter }).then(suites => {
|
return getSuites({ id: id, filter: fileFilter }).then(suites => {
|
||||||
let tests = [];
|
let testsRemaining = [];
|
||||||
|
|
||||||
|
let getNextTest = () => {
|
||||||
|
if (testsRemaining.length) {
|
||||||
|
return testsRemaining.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!suites.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let suite = suites.shift();
|
||||||
|
|
||||||
suites.forEach(suite => {
|
|
||||||
// Load each test file as a main module in its own loader instance
|
// Load each test file as a main module in its own loader instance
|
||||||
// `suite` is defined by cuddlefish/manifest.py:ManifestBuilder.build
|
// `suite` is defined by cuddlefish/manifest.py:ManifestBuilder.build
|
||||||
let suiteModule;
|
let suiteModule;
|
||||||
@ -162,7 +172,7 @@ TestFinder.prototype = {
|
|||||||
if (this.testInProcess) {
|
if (this.testInProcess) {
|
||||||
for (let name of Object.keys(suiteModule).sort()) {
|
for (let name of Object.keys(suiteModule).sort()) {
|
||||||
if (NOT_TESTS.indexOf(name) === -1 && testFilter(name)) {
|
if (NOT_TESTS.indexOf(name) === -1 && testFilter(name)) {
|
||||||
tests.push({
|
testsRemaining.push({
|
||||||
setup: suiteModule.setup,
|
setup: suiteModule.setup,
|
||||||
teardown: suiteModule.teardown,
|
teardown: suiteModule.teardown,
|
||||||
testFunction: suiteModule[name],
|
testFunction: suiteModule[name],
|
||||||
@ -171,9 +181,13 @@ TestFinder.prototype = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
return tests;
|
return getNextTest();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
getNext: () => resolve(getNextTest())
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -11,8 +11,9 @@ const memory = require("./memory");
|
|||||||
const timer = require("../timers");
|
const timer = require("../timers");
|
||||||
const cfxArgs = require("../test/options");
|
const cfxArgs = require("../test/options");
|
||||||
const { getTabs, closeTab, getURI } = require("../tabs/utils");
|
const { getTabs, closeTab, getURI } = require("../tabs/utils");
|
||||||
const { windows, isBrowser } = require("../window/utils");
|
const { windows, isBrowser, getMostRecentBrowserWindow } = require("../window/utils");
|
||||||
const { defer, all, Debugging: PromiseDebugging } = require("../core/promise");
|
const { defer, all, Debugging: PromiseDebugging } = require("../core/promise");
|
||||||
|
const { getInnerId } = require("../window/utils");
|
||||||
|
|
||||||
const findAndRunTests = function findAndRunTests(options) {
|
const findAndRunTests = function findAndRunTests(options) {
|
||||||
var TestFinder = require("./unit-test-finder").TestFinder;
|
var TestFinder = require("./unit-test-finder").TestFinder;
|
||||||
@ -32,11 +33,13 @@ const findAndRunTests = function findAndRunTests(options) {
|
|||||||
};
|
};
|
||||||
exports.findAndRunTests = findAndRunTests;
|
exports.findAndRunTests = findAndRunTests;
|
||||||
|
|
||||||
|
let runnerWindows = new WeakMap();
|
||||||
|
|
||||||
const TestRunner = function TestRunner(options) {
|
const TestRunner = function TestRunner(options) {
|
||||||
if (options) {
|
options = options || {};
|
||||||
this.fs = options.fs;
|
runnerWindows.set(this, getInnerId(getMostRecentBrowserWindow()));
|
||||||
}
|
this.fs = options.fs;
|
||||||
this.console = (options && "console" in options) ? options.console : console;
|
this.console = options.console || console;
|
||||||
memory.track(this);
|
memory.track(this);
|
||||||
this.passed = 0;
|
this.passed = 0;
|
||||||
this.failed = 0;
|
this.failed = 0;
|
||||||
@ -314,7 +317,7 @@ TestRunner.prototype = {
|
|||||||
}
|
}
|
||||||
let leftover = tabs.slice(1);
|
let leftover = tabs.slice(1);
|
||||||
|
|
||||||
if (wins.length != 1)
|
if (wins.length != 1 || getInnerId(wins[0]) !== runnerWindows.get(this))
|
||||||
this.fail("Should not be any unexpected windows open");
|
this.fail("Should not be any unexpected windows open");
|
||||||
if (tabs.length != 1)
|
if (tabs.length != 1)
|
||||||
this.fail("Should not be any unexpected tabs open");
|
this.fail("Should not be any unexpected tabs open");
|
||||||
@ -483,17 +486,23 @@ TestRunner.prototype = {
|
|||||||
|
|
||||||
startMany: function startMany(options) {
|
startMany: function startMany(options) {
|
||||||
function runNextTest(self) {
|
function runNextTest(self) {
|
||||||
var test = options.tests.shift();
|
let { tests, onDone } = options;
|
||||||
if (options.stopOnError && self.test && self.test.failed) {
|
|
||||||
self.console.error("aborted: test failed and --stop-on-error was specified");
|
return tests.getNext().then((test) => {
|
||||||
options.onDone(self);
|
if (options.stopOnError && self.test && self.test.failed) {
|
||||||
} else if (test) {
|
self.console.error("aborted: test failed and --stop-on-error was specified");
|
||||||
self.start({test: test, onDone: runNextTest});
|
onDone(self);
|
||||||
} else {
|
}
|
||||||
options.onDone(self);
|
else if (test) {
|
||||||
}
|
self.start({test: test, onDone: runNextTest});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
onDone(self);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
runNextTest(this);
|
|
||||||
|
return runNextTest(this).catch(console.exception);
|
||||||
},
|
},
|
||||||
|
|
||||||
start: function start(options) {
|
start: function start(options) {
|
||||||
|
@ -10,6 +10,7 @@ module.metadata = {
|
|||||||
var { exit, stdout } = require("../system");
|
var { exit, stdout } = require("../system");
|
||||||
var cfxArgs = require("../test/options");
|
var cfxArgs = require("../test/options");
|
||||||
var events = require("../system/events");
|
var events = require("../system/events");
|
||||||
|
const { resolve } = require("../core/promise");
|
||||||
|
|
||||||
function runTests(findAndRunTests) {
|
function runTests(findAndRunTests) {
|
||||||
var harness = require("./harness");
|
var harness = require("./harness");
|
||||||
@ -120,7 +121,9 @@ exports.runTestsFromModule = function runTestsFromModule(module) {
|
|||||||
var { TestRunner } = loader.require("../deprecated/unit-test");
|
var { TestRunner } = loader.require("../deprecated/unit-test");
|
||||||
var runner = new TestRunner();
|
var runner = new TestRunner();
|
||||||
runner.startMany({
|
runner.startMany({
|
||||||
tests: tests,
|
tests: {
|
||||||
|
getNext: () => resolve(tests.shift())
|
||||||
|
},
|
||||||
stopOnError: cfxArgs.stopOnError,
|
stopOnError: cfxArgs.stopOnError,
|
||||||
onDone: nextIteration
|
onDone: nextIteration
|
||||||
});
|
});
|
||||||
|
@ -22,8 +22,8 @@ DEFAULT_ICON = 'icon.png'
|
|||||||
DEFAULT_ICON64 = 'icon64.png'
|
DEFAULT_ICON64 = 'icon64.png'
|
||||||
|
|
||||||
METADATA_PROPS = ['name', 'description', 'keywords', 'author', 'version',
|
METADATA_PROPS = ['name', 'description', 'keywords', 'author', 'version',
|
||||||
'translators', 'contributors', 'license', 'homepage', 'icon',
|
'developers', 'translators', 'contributors', 'license', 'homepage',
|
||||||
'icon64', 'main', 'directories', 'permissions', 'preferences']
|
'icon', 'icon64', 'main', 'directories', 'permissions', 'preferences']
|
||||||
|
|
||||||
RESOURCE_HOSTNAME_RE = re.compile(r'^[a-z0-9_\-]+$')
|
RESOURCE_HOSTNAME_RE = re.compile(r'^[a-z0-9_\-]+$')
|
||||||
|
|
||||||
|
@ -138,6 +138,11 @@ def gen_manifest(template_root_dir, target_cfg, jid,
|
|||||||
elem.appendChild(dom.createTextNode(translator))
|
elem.appendChild(dom.createTextNode(translator))
|
||||||
dom.documentElement.getElementsByTagName("Description")[0].appendChild(elem)
|
dom.documentElement.getElementsByTagName("Description")[0].appendChild(elem)
|
||||||
|
|
||||||
|
for developer in target_cfg.get("developers", [ ]):
|
||||||
|
elem = dom.createElement("em:developer");
|
||||||
|
elem.appendChild(dom.createTextNode(developer))
|
||||||
|
dom.documentElement.getElementsByTagName("Description")[0].appendChild(elem)
|
||||||
|
|
||||||
for contributor in target_cfg.get("contributors", [ ]):
|
for contributor in target_cfg.get("contributors", [ ]):
|
||||||
elem = dom.createElement("em:contributor");
|
elem = dom.createElement("em:contributor");
|
||||||
elem.appendChild(dom.createTextNode(contributor))
|
elem.appendChild(dom.createTextNode(contributor))
|
||||||
@ -150,7 +155,7 @@ def gen_manifest(template_root_dir, target_cfg, jid,
|
|||||||
|
|
||||||
if target_cfg.get("preferences"):
|
if target_cfg.get("preferences"):
|
||||||
manifest.set("em:optionsType", "2")
|
manifest.set("em:optionsType", "2")
|
||||||
|
|
||||||
# workaround until bug 971249 is fixed
|
# workaround until bug 971249 is fixed
|
||||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=971249
|
# https://bugzilla.mozilla.org/show_bug.cgi?id=971249
|
||||||
manifest.set("em:optionsURL", "data:text/xml,<placeholder/>")
|
manifest.set("em:optionsURL", "data:text/xml,<placeholder/>")
|
||||||
|
23
addon-sdk/source/test/addons/developers/main.js
Normal file
@ -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';
|
||||||
|
|
||||||
|
const { Cu } = require('chrome');
|
||||||
|
const { id } = require('sdk/self');
|
||||||
|
const { AddonManager } = Cu.import('resource://gre/modules/AddonManager.jsm', {});
|
||||||
|
|
||||||
|
exports.testDevelopers = function(assert, done) {
|
||||||
|
AddonManager.getAddonByID(id, (addon) => {
|
||||||
|
let count = 0;
|
||||||
|
addon.developers.forEach(({ name }) => {
|
||||||
|
count++;
|
||||||
|
assert.equal(name, count == 1 ? 'A' : 'B', 'The developers keys are correct');
|
||||||
|
});
|
||||||
|
assert.equal(count, 2, 'The key count is correct');
|
||||||
|
assert.equal(addon.developers.length, 2, 'The key length is correct');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
require('sdk/test/runner').runTestsFromModule(module);
|
6
addon-sdk/source/test/addons/developers/package.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"id": "test-developers@jetpack",
|
||||||
|
"title": "Test developers package key",
|
||||||
|
"author": "Erik Vold",
|
||||||
|
"developers": [ "A", "B" ]
|
||||||
|
}
|
@ -93,39 +93,29 @@ exports.testIsPrivateOnWindowOpenFromPrivate = function(assert, done) {
|
|||||||
then(done, assert.fail);
|
then(done, assert.fail);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.testOpenTabWithPrivateWindow = function(assert, done) {
|
exports.testOpenTabWithPrivateWindow = function*(assert) {
|
||||||
function start() {
|
let { promise, resolve } = defer();
|
||||||
openPromise(null, {
|
|
||||||
features: {
|
|
||||||
private: true,
|
|
||||||
toolbar: true
|
|
||||||
}
|
|
||||||
}).then(focus).then(function(window) {
|
|
||||||
let { promise, resolve } = defer();
|
|
||||||
assert.equal(isPrivate(window), true, 'the focused window is private');
|
|
||||||
|
|
||||||
tabs.open({
|
let window = yield openPromise(null, {
|
||||||
url: 'about:blank',
|
features: {
|
||||||
onOpen: function(tab) {
|
private: true,
|
||||||
assert.equal(isPrivate(tab), false, 'the opened tab is not private');
|
toolbar: true
|
||||||
// not closing this tab on purpose.. for now...
|
|
||||||
// we keep this tab open because we closed all windows
|
|
||||||
// and must keep a non-private window open at end of this test for next ones.
|
|
||||||
resolve(window);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
}).then(close).then(done, assert.fail);
|
|
||||||
}
|
|
||||||
|
|
||||||
(function closeWindows() {
|
|
||||||
if (windows.length > 0) {
|
|
||||||
return windows.activeWindow.close(closeWindows);
|
|
||||||
}
|
}
|
||||||
assert.pass('all pre test windows have been closed');
|
});
|
||||||
return start();
|
yield focus(window);
|
||||||
})()
|
|
||||||
|
assert.equal(isPrivate(window), true, 'the focused window is private');
|
||||||
|
|
||||||
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.testIsPrivateOnWindowOff = function(assert, done) {
|
exports.testIsPrivateOnWindowOff = function(assert, done) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
module.metadata = {
|
module.metadata = {
|
||||||
@ -25,8 +24,7 @@ const FRAME_URL = "data:text/html;charset=utf-8," + encodeURIComponent(FRAME_HTM
|
|||||||
|
|
||||||
const { defer } = require("sdk/core/promise");
|
const { defer } = require("sdk/core/promise");
|
||||||
const tabs = require("sdk/tabs");
|
const tabs = require("sdk/tabs");
|
||||||
const { setTabURL } = require("sdk/tabs/utils");
|
const { getActiveTab, getTabContentWindow, closeTab, setTabURL } = require("sdk/tabs/utils")
|
||||||
const { getActiveTab, getTabContentWindow, closeTab } = require("sdk/tabs/utils")
|
|
||||||
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
|
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
|
||||||
const { open: openNewWindow, close: closeWindow } = require("sdk/window/helpers");
|
const { open: openNewWindow, close: closeWindow } = require("sdk/window/helpers");
|
||||||
const { Loader } = require("sdk/test/loader");
|
const { Loader } = require("sdk/test/loader");
|
||||||
@ -293,403 +291,390 @@ function createEmptySelections(window) {
|
|||||||
|
|
||||||
// Test cases
|
// Test cases
|
||||||
|
|
||||||
exports["test No Selection"] = function(assert, done) {
|
exports["test No Selection"] = function*(assert) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
let window = yield open(URL);
|
||||||
|
|
||||||
open(URL).then(function() {
|
assert.equal(selection.isContiguous, false,
|
||||||
|
"selection.isContiguous without selection works.");
|
||||||
|
|
||||||
assert.equal(selection.isContiguous, false,
|
assert.strictEqual(selection.text, null,
|
||||||
"selection.isContiguous without selection works.");
|
"selection.text without selection works.");
|
||||||
|
|
||||||
assert.strictEqual(selection.text, null,
|
assert.strictEqual(selection.html, null,
|
||||||
"selection.text without selection works.");
|
"selection.html without selection works.");
|
||||||
|
|
||||||
assert.strictEqual(selection.html, null,
|
let selectionCount = 0;
|
||||||
"selection.html without selection works.");
|
for (let sel of selection)
|
||||||
|
selectionCount++;
|
||||||
|
|
||||||
let selectionCount = 0;
|
assert.equal(selectionCount, 0, "No iterable selections");
|
||||||
for each (let sel in selection)
|
|
||||||
selectionCount++;
|
|
||||||
|
|
||||||
assert.equal(selectionCount, 0,
|
yield close(window);
|
||||||
"No iterable selections");
|
loader.unload();
|
||||||
|
|
||||||
}).then(close).then(loader.unload).then(done, assert.fail);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
exports["test Single DOM Selection"] = function(assert, done) {
|
exports["test Single DOM Selection"] = function*(assert) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
let window = yield open(URL);
|
||||||
|
|
||||||
open(URL).then(selectFirstDiv).then(function() {
|
selectFirstDiv(window)
|
||||||
|
|
||||||
assert.equal(selection.isContiguous, true,
|
assert.equal(selection.isContiguous, true,
|
||||||
"selection.isContiguous with single DOM Selection works.");
|
"selection.isContiguous with single DOM Selection works.");
|
||||||
|
|
||||||
assert.equal(selection.text, "foo",
|
assert.equal(selection.text, "foo",
|
||||||
"selection.text with single DOM Selection works.");
|
"selection.text with single DOM Selection works.");
|
||||||
|
|
||||||
assert.equal(selection.html, "<div>foo</div>",
|
assert.equal(selection.html, "<div>foo</div>",
|
||||||
"selection.html with single DOM Selection works.");
|
"selection.html with single DOM Selection works.");
|
||||||
|
|
||||||
let selectionCount = 0;
|
let selectionCount = 0;
|
||||||
for each (let sel in selection) {
|
for (let sel of selection) {
|
||||||
selectionCount++;
|
selectionCount++;
|
||||||
|
|
||||||
assert.equal(sel.text, "foo",
|
assert.equal(sel.text, "foo",
|
||||||
"iterable selection.text with single DOM Selection works.");
|
"iterable selection.text with single DOM Selection works.");
|
||||||
|
|
||||||
assert.equal(sel.html, "<div>foo</div>",
|
assert.equal(sel.html, "<div>foo</div>",
|
||||||
"iterable selection.html with single DOM Selection works.");
|
"iterable selection.html with single DOM Selection works.");
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.equal(selectionCount, 1,
|
assert.equal(selectionCount, 1, "One iterable selection");
|
||||||
"One iterable selection");
|
|
||||||
|
|
||||||
}).then(close).then(loader.unload).then(done, assert.fail);
|
yield close(window);
|
||||||
|
loader.unload();
|
||||||
};
|
};
|
||||||
|
|
||||||
exports["test Multiple DOM Selection"] = function(assert, done) {
|
exports["test Multiple DOM Selection"] = function*(assert) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
let expectedText = ["foo", "and"];
|
||||||
|
let expectedHTML = ["<div>foo</div>", "<div>and</div>"];
|
||||||
|
let window = yield open(URL);
|
||||||
|
|
||||||
open(URL).then(selectAllDivs).then(function() {
|
selectAllDivs(window);
|
||||||
let expectedText = ["foo", "and"];
|
|
||||||
let expectedHTML = ["<div>foo</div>", "<div>and</div>"];
|
|
||||||
|
|
||||||
assert.equal(selection.isContiguous, false,
|
assert.equal(selection.isContiguous, false,
|
||||||
"selection.isContiguous with multiple DOM Selection works.");
|
"selection.isContiguous with multiple DOM Selection works.");
|
||||||
|
|
||||||
assert.equal(selection.text, expectedText[0],
|
assert.equal(selection.text, expectedText[0],
|
||||||
"selection.text with multiple DOM Selection works.");
|
"selection.text with multiple DOM Selection works.");
|
||||||
|
|
||||||
assert.equal(selection.html, expectedHTML[0],
|
assert.equal(selection.html, expectedHTML[0],
|
||||||
"selection.html with multiple DOM Selection works.");
|
"selection.html with multiple DOM Selection works.");
|
||||||
|
|
||||||
let selectionCount = 0;
|
let selectionCount = 0;
|
||||||
for each (let sel in selection) {
|
for (let sel of selection) {
|
||||||
assert.equal(sel.text, expectedText[selectionCount],
|
assert.equal(sel.text, expectedText[selectionCount],
|
||||||
"iterable selection.text with multiple DOM Selection works.");
|
"iterable selection.text with multiple DOM Selection works.");
|
||||||
|
|
||||||
assert.equal(sel.html, expectedHTML[selectionCount],
|
assert.equal(sel.html, expectedHTML[selectionCount],
|
||||||
"iterable selection.text with multiple DOM Selection works.");
|
"iterable selection.text with multiple DOM Selection works.");
|
||||||
|
|
||||||
selectionCount++;
|
selectionCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.equal(selectionCount, 2,
|
assert.equal(selectionCount, 2, "Two iterable selections");
|
||||||
"Two iterable selections");
|
|
||||||
|
|
||||||
}).then(close).then(loader.unload).then(done, assert.fail);
|
yield close(window);
|
||||||
|
loader.unload();
|
||||||
};
|
};
|
||||||
|
|
||||||
exports["test Textarea Selection"] = function(assert, done) {
|
exports["test Textarea Selection"] = function*(assert) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
let window = yield open(URL);
|
||||||
|
|
||||||
open(URL).then(selectTextarea).then(function() {
|
selectTextarea(window);
|
||||||
|
|
||||||
assert.equal(selection.isContiguous, true,
|
assert.equal(selection.isContiguous, true,
|
||||||
"selection.isContiguous with Textarea Selection works.");
|
"selection.isContiguous with Textarea Selection works.");
|
||||||
|
|
||||||
assert.equal(selection.text, "noodles",
|
assert.equal(selection.text, "noodles",
|
||||||
"selection.text with Textarea Selection works.");
|
"selection.text with Textarea Selection works.");
|
||||||
|
|
||||||
assert.strictEqual(selection.html, null,
|
assert.strictEqual(selection.html, null,
|
||||||
"selection.html with Textarea Selection works.");
|
"selection.html with Textarea Selection works.");
|
||||||
|
|
||||||
let selectionCount = 0;
|
let selectionCount = 0;
|
||||||
for each (let sel in selection) {
|
for (let sel of selection) {
|
||||||
selectionCount++;
|
selectionCount++;
|
||||||
|
|
||||||
assert.equal(sel.text, "noodles",
|
assert.equal(sel.text, "noodles",
|
||||||
"iterable selection.text with Textarea Selection works.");
|
"iterable selection.text with Textarea Selection works.");
|
||||||
|
|
||||||
assert.strictEqual(sel.html, null,
|
assert.strictEqual(sel.html, null,
|
||||||
"iterable selection.html with Textarea Selection works.");
|
"iterable selection.html with Textarea Selection works.");
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.equal(selectionCount, 1,
|
assert.equal(selectionCount, 1, "One iterable selection");
|
||||||
"One iterable selection");
|
|
||||||
|
|
||||||
}).then(close).then(loader.unload).then(done, assert.fail);
|
yield close(window);
|
||||||
|
loader.unload();
|
||||||
};
|
};
|
||||||
|
|
||||||
exports["test Set Text in Multiple DOM Selection"] = function(assert, done) {
|
exports["test Set Text in Multiple DOM Selection"] = function*(assert) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
let expectedText = ["bar", "and"];
|
||||||
|
let expectedHTML = ["bar", "<div>and</div>"];
|
||||||
|
let window = yield open(URL);
|
||||||
|
|
||||||
open(URL).then(selectAllDivs).then(function() {
|
selectAllDivs(window);
|
||||||
let expectedText = ["bar", "and"];
|
|
||||||
let expectedHTML = ["bar", "<div>and</div>"];
|
|
||||||
|
|
||||||
selection.text = "bar";
|
selection.text = "bar";
|
||||||
|
|
||||||
assert.equal(selection.text, expectedText[0],
|
assert.equal(selection.text, expectedText[0],
|
||||||
"set selection.text with single DOM Selection works.");
|
"set selection.text with single DOM Selection works.");
|
||||||
|
|
||||||
assert.equal(selection.html, expectedHTML[0],
|
assert.equal(selection.html, expectedHTML[0],
|
||||||
"selection.html with single DOM Selection works.");
|
"selection.html with single DOM Selection works.");
|
||||||
|
|
||||||
let selectionCount = 0;
|
let selectionCount = 0;
|
||||||
for each (let sel in selection) {
|
for (let sel of selection) {
|
||||||
|
assert.equal(sel.text, expectedText[selectionCount],
|
||||||
|
"iterable selection.text with multiple DOM Selection works.");
|
||||||
|
|
||||||
assert.equal(sel.text, expectedText[selectionCount],
|
assert.equal(sel.html, expectedHTML[selectionCount],
|
||||||
"iterable selection.text with multiple DOM Selection works.");
|
"iterable selection.html with multiple DOM Selection works.");
|
||||||
|
|
||||||
assert.equal(sel.html, expectedHTML[selectionCount],
|
selectionCount++;
|
||||||
"iterable selection.html with multiple DOM Selection works.");
|
}
|
||||||
|
|
||||||
selectionCount++;
|
assert.equal(selectionCount, 2, "Two iterable selections");
|
||||||
}
|
|
||||||
|
|
||||||
assert.equal(selectionCount, 2,
|
yield close(window);
|
||||||
"Two iterable selections");
|
loader.unload();
|
||||||
|
|
||||||
}).then(close).then(loader.unload).then(done, assert.fail);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
exports["test Set HTML in Multiple DOM Selection"] = function(assert, done) {
|
exports["test Set HTML in Multiple DOM Selection"] = function*(assert) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
let html = "<span>b<b>a</b>r</span>";
|
||||||
|
let expectedText = ["bar", "and"];
|
||||||
|
let expectedHTML = [html, "<div>and</div>"];
|
||||||
|
let window = yield open(URL);
|
||||||
|
|
||||||
open(URL).then(selectAllDivs).then(function() {
|
selectAllDivs(window);
|
||||||
let html = "<span>b<b>a</b>r</span>";
|
|
||||||
|
|
||||||
let expectedText = ["bar", "and"];
|
selection.html = html;
|
||||||
let expectedHTML = [html, "<div>and</div>"];
|
|
||||||
|
|
||||||
selection.html = html;
|
assert.equal(selection.text, expectedText[0],
|
||||||
|
"set selection.text with DOM Selection works.");
|
||||||
|
|
||||||
assert.equal(selection.text, expectedText[0],
|
assert.equal(selection.html, expectedHTML[0],
|
||||||
"set selection.text with DOM Selection works.");
|
"selection.html with DOM Selection works.");
|
||||||
|
|
||||||
assert.equal(selection.html, expectedHTML[0],
|
let selectionCount = 0;
|
||||||
"selection.html with DOM Selection works.");
|
for (let sel of selection) {
|
||||||
|
assert.equal(sel.text, expectedText[selectionCount],
|
||||||
|
"iterable selection.text with multiple DOM Selection works.");
|
||||||
|
|
||||||
let selectionCount = 0;
|
assert.equal(sel.html, expectedHTML[selectionCount],
|
||||||
for each (let sel in selection) {
|
"iterable selection.html with multiple DOM Selection works.");
|
||||||
|
|
||||||
assert.equal(sel.text, expectedText[selectionCount],
|
selectionCount++;
|
||||||
"iterable selection.text with multiple DOM Selection works.");
|
}
|
||||||
|
|
||||||
assert.equal(sel.html, expectedHTML[selectionCount],
|
assert.equal(selectionCount, 2, "Two iterable selections");
|
||||||
"iterable selection.html with multiple DOM Selection works.");
|
|
||||||
|
|
||||||
selectionCount++;
|
yield close(window);
|
||||||
}
|
loader.unload();
|
||||||
|
|
||||||
assert.equal(selectionCount, 2,
|
|
||||||
"Two iterable selections");
|
|
||||||
|
|
||||||
}).then(close).then(loader.unload).then(done, assert.fail);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
exports["test Set HTML as text in Multiple DOM Selection"] = function(assert, done) {
|
exports["test Set HTML as text in Multiple DOM Selection"] = function*(assert) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
let text = "<span>b<b>a</b>r</span>";
|
||||||
|
let html = "<span>b<b>a</b>r</span>";
|
||||||
|
let expectedText = [text, "and"];
|
||||||
|
let expectedHTML = [html, "<div>and</div>"];
|
||||||
|
let window = yield open(URL);
|
||||||
|
|
||||||
open(URL).then(selectAllDivs).then(function() {
|
selectAllDivs(window);
|
||||||
let text = "<span>b<b>a</b>r</span>";
|
|
||||||
let html = "<span>b<b>a</b>r</span>";
|
|
||||||
|
|
||||||
let expectedText = [text, "and"];
|
selection.text = text;
|
||||||
let expectedHTML = [html, "<div>and</div>"];
|
|
||||||
|
|
||||||
selection.text = text;
|
assert.equal(selection.text, expectedText[0],
|
||||||
|
"set selection.text with DOM Selection works.");
|
||||||
|
|
||||||
assert.equal(selection.text, expectedText[0],
|
assert.equal(selection.html, expectedHTML[0],
|
||||||
"set selection.text with DOM Selection works.");
|
"selection.html with DOM Selection works.");
|
||||||
|
|
||||||
assert.equal(selection.html, expectedHTML[0],
|
let selectionCount = 0;
|
||||||
"selection.html with DOM Selection works.");
|
for (let sel of selection) {
|
||||||
|
assert.equal(sel.text, expectedText[selectionCount],
|
||||||
|
"iterable selection.text with multiple DOM Selection works.");
|
||||||
|
|
||||||
let selectionCount = 0;
|
assert.equal(sel.html, expectedHTML[selectionCount],
|
||||||
for each (let sel in selection) {
|
"iterable selection.html with multiple DOM Selection works.");
|
||||||
|
|
||||||
assert.equal(sel.text, expectedText[selectionCount],
|
selectionCount++;
|
||||||
"iterable selection.text with multiple DOM Selection works.");
|
}
|
||||||
|
|
||||||
assert.equal(sel.html, expectedHTML[selectionCount],
|
assert.equal(selectionCount, 2, "Two iterable selections");
|
||||||
"iterable selection.html with multiple DOM Selection works.");
|
|
||||||
|
|
||||||
selectionCount++;
|
yield close(window);
|
||||||
}
|
loader.unload();
|
||||||
|
|
||||||
assert.equal(selectionCount, 2,
|
|
||||||
"Two iterable selections");
|
|
||||||
|
|
||||||
}).then(close).then(loader.unload).then(done, assert.fail);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
exports["test Set Text in Textarea Selection"] = function(assert, done) {
|
exports["test Set Text in Textarea Selection"] = function*(assert) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
let text = "bar";
|
||||||
|
let window = yield open(URL);
|
||||||
|
|
||||||
open(URL).then(selectTextarea).then(function() {
|
selectTextarea(window);
|
||||||
|
|
||||||
let text = "bar";
|
selection.text = text;
|
||||||
|
|
||||||
selection.text = text;
|
assert.equal(selection.text, text,
|
||||||
|
"set selection.text with Textarea Selection works.");
|
||||||
|
|
||||||
assert.equal(selection.text, text,
|
assert.strictEqual(selection.html, null,
|
||||||
"set selection.text with Textarea Selection works.");
|
"selection.html with Textarea Selection works.");
|
||||||
|
|
||||||
assert.strictEqual(selection.html, null,
|
let selectionCount = 0;
|
||||||
"selection.html with Textarea Selection works.");
|
for (let sel of selection) {
|
||||||
|
selectionCount++;
|
||||||
|
|
||||||
let selectionCount = 0;
|
assert.equal(sel.text, text,
|
||||||
for each (let sel in selection) {
|
"iterable selection.text with Textarea Selection works.");
|
||||||
selectionCount++;
|
|
||||||
|
|
||||||
assert.equal(sel.text, text,
|
assert.strictEqual(sel.html, null,
|
||||||
"iterable selection.text with Textarea Selection works.");
|
"iterable selection.html with Textarea Selection works.");
|
||||||
|
}
|
||||||
|
|
||||||
assert.strictEqual(sel.html, null,
|
assert.equal(selectionCount, 1, "One iterable selection");
|
||||||
"iterable selection.html with Textarea Selection works.");
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.equal(selectionCount, 1,
|
yield close(window);
|
||||||
"One iterable selection");
|
loader.unload();
|
||||||
|
|
||||||
}).then(close).then(loader.unload).then(done, assert.fail);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
exports["test Set HTML in Textarea Selection"] = function(assert, done) {
|
exports["test Set HTML in Textarea Selection"] = function*(assert) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
let html = "<span>b<b>a</b>r</span>";
|
||||||
|
let window = yield open(URL);
|
||||||
|
|
||||||
open(URL).then(selectTextarea).then(function() {
|
selectTextarea(window);
|
||||||
|
|
||||||
let html = "<span>b<b>a</b>r</span>";
|
// Textarea can't have HTML so set `html` property is equals to set `text`
|
||||||
|
// property
|
||||||
|
selection.html = html;
|
||||||
|
|
||||||
// Textarea can't have HTML so set `html` property is equals to set `text`
|
assert.equal(selection.text, html,
|
||||||
// property
|
"set selection.text with Textarea Selection works.");
|
||||||
selection.html = html;
|
|
||||||
|
|
||||||
assert.equal(selection.text, html,
|
assert.strictEqual(selection.html, null,
|
||||||
"set selection.text with Textarea Selection works.");
|
"selection.html with Textarea Selection works.");
|
||||||
|
|
||||||
assert.strictEqual(selection.html, null,
|
let selectionCount = 0;
|
||||||
"selection.html with Textarea Selection works.");
|
for (let sel of selection) {
|
||||||
|
selectionCount++;
|
||||||
|
|
||||||
let selectionCount = 0;
|
assert.equal(sel.text, html,
|
||||||
for each (let sel in selection) {
|
"iterable selection.text with Textarea Selection works.");
|
||||||
selectionCount++;
|
|
||||||
|
|
||||||
assert.equal(sel.text, html,
|
assert.strictEqual(sel.html, null,
|
||||||
"iterable selection.text with Textarea Selection works.");
|
"iterable selection.html with Textarea Selection works.");
|
||||||
|
}
|
||||||
|
|
||||||
assert.strictEqual(sel.html, null,
|
assert.equal(selectionCount, 1, "One iterable selection");
|
||||||
"iterable selection.html with Textarea Selection works.");
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.equal(selectionCount, 1,
|
yield close(window);
|
||||||
"One iterable selection");
|
loader.unload();
|
||||||
|
|
||||||
}).then(close).then(loader.unload).then(done, assert.fail);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
exports["test Empty Selections"] = function(assert, done) {
|
exports["test Empty Selections"] = function*(assert) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
let window = yield open(URL);
|
||||||
|
|
||||||
open(URL).then(createEmptySelections).then(function(){
|
createEmptySelections(window);
|
||||||
assert.equal(selection.isContiguous, false,
|
|
||||||
"selection.isContiguous with empty selections works.");
|
|
||||||
|
|
||||||
assert.strictEqual(selection.text, null,
|
assert.equal(selection.isContiguous, false,
|
||||||
"selection.text with empty selections works.");
|
"selection.isContiguous with empty selections works.");
|
||||||
|
|
||||||
assert.strictEqual(selection.html, null,
|
assert.strictEqual(selection.text, null,
|
||||||
"selection.html with empty selections works.");
|
"selection.text with empty selections works.");
|
||||||
|
|
||||||
let selectionCount = 0;
|
assert.strictEqual(selection.html, null,
|
||||||
for each (let sel in selection)
|
"selection.html with empty selections works.");
|
||||||
selectionCount++;
|
|
||||||
|
|
||||||
assert.equal(selectionCount, 0,
|
let selectionCount = 0;
|
||||||
"No iterable selections");
|
for (let sel of selection)
|
||||||
|
selectionCount++;
|
||||||
|
|
||||||
}).then(close).then(loader.unload).then(done, assert.fail);
|
assert.equal(selectionCount, 0, "No iterable selections");
|
||||||
|
|
||||||
|
yield close(window);
|
||||||
|
loader.unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
exports["test No Selection Exception"] = function(assert, done) {
|
exports["test No Selection Exception"] = function*(assert) {
|
||||||
const NO_SELECTION = /It isn't possible to change the selection/;
|
const NO_SELECTION = /It isn't possible to change the selection/;
|
||||||
|
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
let window = yield open(URL);
|
||||||
|
|
||||||
open(URL).then(function() {
|
// We're trying to change a selection when there is no selection
|
||||||
|
assert.throws(function() {
|
||||||
|
selection.text = "bar";
|
||||||
|
}, NO_SELECTION);
|
||||||
|
|
||||||
// We're trying to change a selection when there is no selection
|
assert.throws(function() {
|
||||||
assert.throws(function() {
|
selection.html = "bar";
|
||||||
selection.text = "bar";
|
}, NO_SELECTION);
|
||||||
}, NO_SELECTION);
|
|
||||||
|
|
||||||
assert.throws(function() {
|
yield close(window);
|
||||||
selection.html = "bar";
|
loader.unload();
|
||||||
}, NO_SELECTION);
|
|
||||||
|
|
||||||
}).then(close).then(loader.unload).then(done, assert.fail);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
exports["test for...of without selections"] = function(assert, done) {
|
exports["test for...of without selections"] = function*(assert) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
let window = yield open(URL);
|
||||||
|
let selectionCount = 0;
|
||||||
|
|
||||||
open(URL).then(function() {
|
for (let sel of selection)
|
||||||
let selectionCount = 0;
|
selectionCount++;
|
||||||
|
|
||||||
for (let sel of selection)
|
assert.equal(selectionCount, 0, "No iterable selections");
|
||||||
selectionCount++;
|
|
||||||
|
|
||||||
assert.equal(selectionCount, 0,
|
yield close(window);
|
||||||
"No iterable selections");
|
loader.unload();
|
||||||
|
|
||||||
}).then(close).then(loader.unload).then(null, function(error) {
|
|
||||||
// iterable are not supported yet in Firefox 16, for example, but
|
|
||||||
// they are in Firefox 17.
|
|
||||||
if (error.message.indexOf("is not iterable") > -1)
|
|
||||||
assert.pass("`iterable` method not supported in this application");
|
|
||||||
else
|
|
||||||
assert.fail(error);
|
|
||||||
}).then(done, assert.fail);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports["test for...of with selections"] = function(assert, done) {
|
exports["test for...of with selections"] = function*(assert) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
let expectedText = ["foo", "and"];
|
||||||
|
let expectedHTML = ["<div>foo</div>", "<div>and</div>"];
|
||||||
|
let window = yield open(URL);
|
||||||
|
|
||||||
open(URL).then(selectAllDivs).then(function(){
|
selectAllDivs(window);
|
||||||
let expectedText = ["foo", "and"];
|
|
||||||
let expectedHTML = ["<div>foo</div>", "<div>and</div>"];
|
|
||||||
|
|
||||||
let selectionCount = 0;
|
let selectionCount = 0;
|
||||||
|
|
||||||
for (let sel of selection) {
|
for (let sel of selection) {
|
||||||
assert.equal(sel.text, expectedText[selectionCount],
|
assert.equal(sel.text, expectedText[selectionCount],
|
||||||
"iterable selection.text with for...of works.");
|
"iterable selection.text with for...of works.");
|
||||||
|
|
||||||
assert.equal(sel.html, expectedHTML[selectionCount],
|
assert.equal(sel.html, expectedHTML[selectionCount],
|
||||||
"iterable selection.text with for...of works.");
|
"iterable selection.text with for...of works.");
|
||||||
|
|
||||||
selectionCount++;
|
selectionCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.equal(selectionCount, 2,
|
assert.equal(selectionCount, 2, "Two iterable selections");
|
||||||
"Two iterable selections");
|
|
||||||
|
|
||||||
}).then(close).then(loader.unload).then(null, function(error) {
|
yield close(window);
|
||||||
// iterable are not supported yet in Firefox 16, for example, but
|
loader.unload();
|
||||||
// they are in Firefox 17.
|
|
||||||
if (error.message.indexOf("is not iterable") > -1)
|
|
||||||
assert.pass("`iterable` method not supported in this application");
|
|
||||||
else
|
|
||||||
assert.fail(error);
|
|
||||||
}).then(done, assert.fail)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports["test Selection Listener"] = function(assert, done) {
|
exports["test Selection Listener"] = function(assert, done) {
|
||||||
@ -722,7 +707,7 @@ exports["test Textarea OnSelect Listener"] = function(assert, done) {
|
|||||||
then(dispatchOnSelectEvent, assert.fail);
|
then(dispatchOnSelectEvent, assert.fail);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports["test Selection listener removed on unload"] = function(assert, done) {
|
exports["test Selection listener removed on unload"] = function*(assert) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
|
||||||
@ -731,17 +716,16 @@ exports["test Selection listener removed on unload"] = function(assert, done) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
loader.unload();
|
loader.unload();
|
||||||
|
assert.pass("unload was a success");
|
||||||
|
|
||||||
assert.pass();
|
yield open(URL).
|
||||||
|
|
||||||
open(URL).
|
|
||||||
then(selectContentFirstDiv).
|
then(selectContentFirstDiv).
|
||||||
then(dispatchSelectionEvent).
|
then(dispatchSelectionEvent).
|
||||||
then(close).
|
then(close).
|
||||||
then(done, assert.fail);
|
catch(assert.fail);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports["test Textarea onSelect Listener removed on unload"] = function(assert, done) {
|
exports["test Textarea onSelect Listener removed on unload"] = function*(assert) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
|
||||||
@ -750,14 +734,13 @@ exports["test Textarea onSelect Listener removed on unload"] = function(assert,
|
|||||||
});
|
});
|
||||||
|
|
||||||
loader.unload();
|
loader.unload();
|
||||||
|
assert.pass("unload was a success");
|
||||||
|
|
||||||
assert.pass();
|
yield open(URL).
|
||||||
|
|
||||||
open(URL).
|
|
||||||
then(selectTextarea).
|
then(selectTextarea).
|
||||||
then(dispatchOnSelectEvent).
|
then(dispatchOnSelectEvent).
|
||||||
then(close).
|
then(close).
|
||||||
then(done, assert.fail);
|
catch(assert.fail);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -848,7 +831,7 @@ exports["test Selection Listener on frame"] = function(assert, done) {
|
|||||||
then(getFrameWindow).
|
then(getFrameWindow).
|
||||||
then(selectContentFirstDiv).
|
then(selectContentFirstDiv).
|
||||||
then(dispatchSelectionEvent).
|
then(dispatchSelectionEvent).
|
||||||
then(null, assert.fail);
|
catch(assert.fail);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports["test Textarea onSelect Listener on frame"] = function(assert, done) {
|
exports["test Textarea onSelect Listener on frame"] = function(assert, done) {
|
||||||
@ -867,11 +850,11 @@ exports["test Textarea onSelect Listener on frame"] = function(assert, done) {
|
|||||||
then(getFrameWindow).
|
then(getFrameWindow).
|
||||||
then(selectTextarea).
|
then(selectTextarea).
|
||||||
then(dispatchOnSelectEvent).
|
then(dispatchOnSelectEvent).
|
||||||
then(null, assert.fail);
|
catch(assert.fail);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
exports["test PBPW Selection Listener"] = function(assert, done) {
|
exports["test PBPW Selection Listener"] = function*(assert) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
|
||||||
@ -881,15 +864,16 @@ exports["test PBPW Selection Listener"] = function(assert, done) {
|
|||||||
|
|
||||||
assert.pass();
|
assert.pass();
|
||||||
|
|
||||||
open(URL, {private: true}).
|
yield open(URL, { private: true }).
|
||||||
then(selectContentFirstDiv).
|
then(selectContentFirstDiv).
|
||||||
then(dispatchSelectionEvent).
|
then(dispatchSelectionEvent).
|
||||||
then(closeWindow).
|
then(closeWindow).
|
||||||
then(loader.unload).
|
catch(assert.fail);
|
||||||
then(done, assert.fail);
|
|
||||||
|
loader.unload();
|
||||||
};
|
};
|
||||||
|
|
||||||
exports["test PBPW Textarea OnSelect Listener"] = function(assert, done) {
|
exports["test PBPW Textarea OnSelect Listener"] = function*(assert) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
|
||||||
@ -899,72 +883,73 @@ exports["test PBPW Textarea OnSelect Listener"] = function(assert, done) {
|
|||||||
|
|
||||||
assert.pass();
|
assert.pass();
|
||||||
|
|
||||||
open(URL, {private: true}).
|
yield open(URL, { private: true }).
|
||||||
then(selectTextarea).
|
then(selectTextarea).
|
||||||
then(dispatchOnSelectEvent).
|
then(dispatchOnSelectEvent).
|
||||||
then(closeWindow).
|
then(closeWindow).
|
||||||
then(loader.unload).
|
catch(assert.fail);
|
||||||
then(done, assert.fail);
|
|
||||||
|
loader.unload();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
exports["test PBPW Single DOM Selection"] = function(assert, done) {
|
exports["test PBPW Single DOM Selection"] = function*(assert) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
let window = yield open(URL, { private: true });
|
||||||
|
|
||||||
open(URL, {private: true}).then(selectFirstDiv).then(function(window) {
|
selectFirstDiv(window);
|
||||||
|
|
||||||
assert.equal(selection.isContiguous, false,
|
assert.equal(selection.isContiguous, false,
|
||||||
"selection.isContiguous with single DOM Selection in PBPW works.");
|
"selection.isContiguous with single DOM Selection in PBPW works.");
|
||||||
|
|
||||||
assert.equal(selection.text, null,
|
assert.equal(selection.text, null,
|
||||||
"selection.text with single DOM Selection in PBPW works.");
|
"selection.text with single DOM Selection in PBPW works.");
|
||||||
|
|
||||||
assert.equal(selection.html, null,
|
assert.equal(selection.html, null,
|
||||||
"selection.html with single DOM Selection in PBPW works.");
|
"selection.html with single DOM Selection in PBPW works.");
|
||||||
|
|
||||||
let selectionCount = 0;
|
let selectionCount = 0;
|
||||||
for each (let sel in selection)
|
for (let sel of selection)
|
||||||
selectionCount++;
|
selectionCount++;
|
||||||
|
|
||||||
assert.equal(selectionCount, 0,
|
assert.equal(selectionCount, 0, "No iterable selection in PBPW");
|
||||||
"No iterable selection in PBPW");
|
|
||||||
|
|
||||||
return window;
|
yield closeWindow(window);
|
||||||
}).then(closeWindow).then(loader.unload).then(done, assert.fail);
|
loader.unload();
|
||||||
};
|
};
|
||||||
|
|
||||||
exports["test PBPW Textarea Selection"] = function(assert, done) {
|
exports["test PBPW Textarea Selection"] = function(assert, done) {
|
||||||
let loader = Loader(module);
|
let loader = Loader(module);
|
||||||
let selection = loader.require("sdk/selection");
|
let selection = loader.require("sdk/selection");
|
||||||
|
let window = yield open(URL, { private: true });
|
||||||
|
|
||||||
open(URL, {private: true}).then(selectTextarea).then(function(window) {
|
selectTextarea(window);
|
||||||
|
|
||||||
assert.equal(selection.isContiguous, false,
|
assert.equal(selection.isContiguous, false,
|
||||||
"selection.isContiguous with Textarea Selection in PBPW works.");
|
"selection.isContiguous with Textarea Selection in PBPW works.");
|
||||||
|
|
||||||
assert.equal(selection.text, null,
|
assert.equal(selection.text, null,
|
||||||
"selection.text with Textarea Selection in PBPW works.");
|
"selection.text with Textarea Selection in PBPW works.");
|
||||||
|
|
||||||
assert.strictEqual(selection.html, null,
|
assert.strictEqual(selection.html, null,
|
||||||
"selection.html with Textarea Selection in PBPW works.");
|
"selection.html with Textarea Selection in PBPW works.");
|
||||||
|
|
||||||
let selectionCount = 0;
|
let selectionCount = 0;
|
||||||
for each (let sel in selection) {
|
for (let sel of selection) {
|
||||||
selectionCount++;
|
selectionCount++;
|
||||||
|
|
||||||
assert.equal(sel.text, null,
|
assert.equal(sel.text, null,
|
||||||
"iterable selection.text with Textarea Selection in PBPW works.");
|
"iterable selection.text with Textarea Selection in PBPW works.");
|
||||||
|
|
||||||
assert.strictEqual(sel.html, null,
|
assert.strictEqual(sel.html, null,
|
||||||
"iterable selection.html with Textarea Selection in PBPW works.");
|
"iterable selection.html with Textarea Selection in PBPW works.");
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.equal(selectionCount, 0,
|
assert.equal(selectionCount, 0, "No iterable selection in PBPW");
|
||||||
"No iterable selection in PBPW");
|
|
||||||
|
|
||||||
return window;
|
yield closeWindow(window);
|
||||||
}).then(closeWindow).then(loader.unload).then(done, assert.fail);
|
loader.unload();
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: test Selection Listener on long-held connection (Bug 661884)
|
// TODO: test Selection Listener on long-held connection (Bug 661884)
|
||||||
@ -985,7 +970,7 @@ exports["test Selection Listener on long-held connection"] = function(assert, do
|
|||||||
|
|
||||||
// If the platform doesn't support the PBPW, we're replacing PBPW tests
|
// If the platform doesn't support the PBPW, we're replacing PBPW tests
|
||||||
if (!require("sdk/private-browsing/utils").isWindowPBSupported) {
|
if (!require("sdk/private-browsing/utils").isWindowPBSupported) {
|
||||||
Object.keys(module.exports).forEach(function(key) {
|
Object.keys(module.exports).forEach((key) => {
|
||||||
if (key.indexOf("test PBPW") === 0) {
|
if (key.indexOf("test PBPW") === 0) {
|
||||||
module.exports[key] = function Unsupported (assert) {
|
module.exports[key] = function Unsupported (assert) {
|
||||||
assert.pass("Private Window Per Browsing is not supported on this platform.");
|
assert.pass("Private Window Per Browsing is not supported on this platform.");
|
||||||
@ -994,4 +979,4 @@ if (!require("sdk/private-browsing/utils").isWindowPBSupported) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
require("test").run(exports)
|
require("sdk/test").run(exports);
|
||||||
|
@ -211,7 +211,7 @@ exports.testActiveWindow = function(assert, done) {
|
|||||||
let rawWindow2, rawWindow3;
|
let rawWindow2, rawWindow3;
|
||||||
|
|
||||||
let testSteps = [
|
let testSteps = [
|
||||||
function() {
|
() => {
|
||||||
assert.equal(windows.length, 3, "Correct number of browser windows");
|
assert.equal(windows.length, 3, "Correct number of browser windows");
|
||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
@ -223,20 +223,17 @@ exports.testActiveWindow = function(assert, done) {
|
|||||||
|
|
||||||
continueAfterFocus(rawWindow2);
|
continueAfterFocus(rawWindow2);
|
||||||
rawWindow2.focus();
|
rawWindow2.focus();
|
||||||
},
|
}, () => {
|
||||||
function() {
|
|
||||||
assert.equal(windows.activeWindow.title, window2.title, "Correct active window - 2");
|
assert.equal(windows.activeWindow.title, window2.title, "Correct active window - 2");
|
||||||
|
|
||||||
continueAfterFocus(rawWindow2);
|
continueAfterFocus(rawWindow2);
|
||||||
window2.activate();
|
window2.activate();
|
||||||
},
|
}, () => {
|
||||||
function() {
|
|
||||||
assert.equal(windows.activeWindow.title, window2.title, "Correct active window - 2");
|
assert.equal(windows.activeWindow.title, window2.title, "Correct active window - 2");
|
||||||
|
|
||||||
continueAfterFocus(rawWindow3);
|
continueAfterFocus(rawWindow3);
|
||||||
window3.activate();
|
window3.activate();
|
||||||
},
|
}, () => {
|
||||||
function() {
|
|
||||||
assert.equal(windows.activeWindow.title, window3.title, "Correct active window - 3");
|
assert.equal(windows.activeWindow.title, window3.title, "Correct active window - 3");
|
||||||
finishTest();
|
finishTest();
|
||||||
}
|
}
|
||||||
@ -251,10 +248,10 @@ exports.testActiveWindow = function(assert, done) {
|
|||||||
|
|
||||||
windows.open({
|
windows.open({
|
||||||
url: "data:text/html;charset=utf-8,<title>window 2</title>",
|
url: "data:text/html;charset=utf-8,<title>window 2</title>",
|
||||||
onOpen: function(window) {
|
onOpen: (window) => {
|
||||||
assert.pass('window 2 open');
|
assert.pass('window 2 open');
|
||||||
|
|
||||||
window.tabs.activeTab.on('ready', function() {
|
window.tabs.activeTab.once('ready', () => {
|
||||||
assert.pass('window 2 tab activated');
|
assert.pass('window 2 tab activated');
|
||||||
|
|
||||||
window2 = window;
|
window2 = window;
|
||||||
@ -267,10 +264,10 @@ exports.testActiveWindow = function(assert, done) {
|
|||||||
|
|
||||||
windows.open({
|
windows.open({
|
||||||
url: "data:text/html;charset=utf-8,<title>window 3</title>",
|
url: "data:text/html;charset=utf-8,<title>window 3</title>",
|
||||||
onOpen: function(window) {
|
onOpen: (window) => {
|
||||||
assert.pass('window 3 open');
|
assert.pass('window 3 open');
|
||||||
|
|
||||||
window.tabs.activeTab.on('ready', function onReady() {
|
window.tabs.activeTab.once('ready', () => {
|
||||||
assert.pass('window 3 tab activated');
|
assert.pass('window 3 tab activated');
|
||||||
|
|
||||||
window3 = window;
|
window3 = window;
|
||||||
@ -282,7 +279,6 @@ exports.testActiveWindow = function(assert, done) {
|
|||||||
assert.equal(rawWindow3.document.title, window3.title, "Saw correct title on window 3");
|
assert.equal(rawWindow3.document.title, window3.title, "Saw correct title on window 3");
|
||||||
|
|
||||||
continueAfterFocus(rawWindow3);
|
continueAfterFocus(rawWindow3);
|
||||||
rawWindow3.focus();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -290,24 +286,16 @@ exports.testActiveWindow = function(assert, done) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function nextStep() {
|
let nextStep = () => testSteps.length ? setTimeout(testSteps.shift()) : null;
|
||||||
if (testSteps.length) {
|
let continueAfterFocus = (w) => onFocus(w).then(nextStep);
|
||||||
setTimeout(testSteps.shift())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let continueAfterFocus = function(w) onFocus(w).then(nextStep);
|
|
||||||
|
|
||||||
function finishTest() {
|
function finishTest() {
|
||||||
// close unactive window first to avoid unnecessary focus changing
|
// close unactive window first to avoid unnecessary focus changing
|
||||||
window2.close(function() {
|
window2.close(() => window3.close(() => {
|
||||||
window3.close(function() {
|
assert.equal(rawWindow2.closed, true, 'window 2 is closed');
|
||||||
assert.equal(rawWindow2.closed, true, 'window 2 is closed');
|
assert.equal(rawWindow3.closed, true, 'window 3 is closed');
|
||||||
assert.equal(rawWindow3.closed, true, 'window 3 is closed');
|
done();
|
||||||
|
}));
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
|
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
|
||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4abb193ddae0f9780ad12ffe5e31772feee3926a"/>
|
<project name="gaia" path="gaia" remote="mozillaorg" revision="8e02f689b0fc39cb6ccdc22d02ed7e219c58faa7"/>
|
||||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4abb193ddae0f9780ad12ffe5e31772feee3926a"/>
|
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8e02f689b0fc39cb6ccdc22d02ed7e219c58faa7"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
</project>
|
</project>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4abb193ddae0f9780ad12ffe5e31772feee3926a"/>
|
<project name="gaia" path="gaia" remote="mozillaorg" revision="8e02f689b0fc39cb6ccdc22d02ed7e219c58faa7"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
||||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="facdb3593e63dcbb740709303a5b2527113c50a0"/>
|
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="facdb3593e63dcbb740709303a5b2527113c50a0"/>
|
||||||
@ -130,8 +130,8 @@
|
|||||||
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
|
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
|
||||||
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="197cd9492b9fadaa915c5daf36ff557f8f4a8d1c"/>
|
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="197cd9492b9fadaa915c5daf36ff557f8f4a8d1c"/>
|
||||||
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
|
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
|
||||||
<project name="libnfcemu" path="external/libnfcemu" remote="b2g" revision="682e556367e0049bb3ae127cec6e6c459abca1b0"/>
|
<project name="libnfcemu" path="external/libnfcemu" remote="b2g" revision="c7ccf6eff27f99e39a9eca94cde48aaece5e47db"/>
|
||||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="57b16fcb790bdf0b53b3c6435a37ccc8ca90ed36"/>
|
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d259117b4976decbe2f76eeed85218bf0109190f"/>
|
||||||
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
|
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
|
||||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9f28c4faea3b2f01db227b2467b08aeba96d9bec"/>
|
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9f28c4faea3b2f01db227b2467b08aeba96d9bec"/>
|
||||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="7e5ae3ef2fe1cbc1c9a4fc718b01b7f28b1c5486"/>
|
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="7e5ae3ef2fe1cbc1c9a4fc718b01b7f28b1c5486"/>
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
|
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
|
||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4abb193ddae0f9780ad12ffe5e31772feee3926a"/>
|
<project name="gaia" path="gaia" remote="mozillaorg" revision="8e02f689b0fc39cb6ccdc22d02ed7e219c58faa7"/>
|
||||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4abb193ddae0f9780ad12ffe5e31772feee3926a"/>
|
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8e02f689b0fc39cb6ccdc22d02ed7e219c58faa7"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
|
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
|
||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4abb193ddae0f9780ad12ffe5e31772feee3926a"/>
|
<project name="gaia" path="gaia" remote="mozillaorg" revision="8e02f689b0fc39cb6ccdc22d02ed7e219c58faa7"/>
|
||||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
</project>
|
</project>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4abb193ddae0f9780ad12ffe5e31772feee3926a"/>
|
<project name="gaia" path="gaia" remote="mozillaorg" revision="8e02f689b0fc39cb6ccdc22d02ed7e219c58faa7"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
||||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="facdb3593e63dcbb740709303a5b2527113c50a0"/>
|
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="facdb3593e63dcbb740709303a5b2527113c50a0"/>
|
||||||
|
@ -4,6 +4,6 @@
|
|||||||
"remote": "",
|
"remote": "",
|
||||||
"branch": ""
|
"branch": ""
|
||||||
},
|
},
|
||||||
"revision": "d43b2e2dba496f624d62c1b72a22dc5e34953fbd",
|
"revision": "7c8bbd0eb4543c729b029339cb79bfaa4ec23069",
|
||||||
"repo_path": "/integration/gaia-central"
|
"repo_path": "/integration/gaia-central"
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4abb193ddae0f9780ad12ffe5e31772feee3926a"/>
|
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8e02f689b0fc39cb6ccdc22d02ed7e219c58faa7"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4abb193ddae0f9780ad12ffe5e31772feee3926a"/>
|
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8e02f689b0fc39cb6ccdc22d02ed7e219c58faa7"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
</project>
|
</project>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4abb193ddae0f9780ad12ffe5e31772feee3926a"/>
|
<project name="gaia" path="gaia" remote="mozillaorg" revision="8e02f689b0fc39cb6ccdc22d02ed7e219c58faa7"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
||||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="facdb3593e63dcbb740709303a5b2527113c50a0"/>
|
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="facdb3593e63dcbb740709303a5b2527113c50a0"/>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4abb193ddae0f9780ad12ffe5e31772feee3926a"/>
|
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8e02f689b0fc39cb6ccdc22d02ed7e219c58faa7"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="6969df171e5295f855f12d12db0382048e6892e7"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||||
|
@ -298,16 +298,28 @@ function ensureSnippetsMapThen(aCallback)
|
|||||||
|
|
||||||
function onSearchSubmit(aEvent)
|
function onSearchSubmit(aEvent)
|
||||||
{
|
{
|
||||||
let searchTerms = document.getElementById("searchText").value;
|
let searchText = document.getElementById("searchText");
|
||||||
|
let searchTerms = searchText.value;
|
||||||
let engineName = document.documentElement.getAttribute("searchEngineName");
|
let engineName = document.documentElement.getAttribute("searchEngineName");
|
||||||
|
|
||||||
if (engineName && searchTerms.length > 0) {
|
if (engineName && searchTerms.length > 0) {
|
||||||
// Send an event that will perform a search and Firefox Health Report will
|
// Send an event that will perform a search and Firefox Health Report will
|
||||||
// record that a search from about:home has occurred.
|
// record that a search from about:home has occurred.
|
||||||
let eventData = JSON.stringify({
|
|
||||||
|
let eventData = {
|
||||||
engineName: engineName,
|
engineName: engineName,
|
||||||
searchTerms: searchTerms
|
searchTerms: searchTerms
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (searchText.hasAttribute("selection-index")) {
|
||||||
|
eventData.selection = {
|
||||||
|
index: searchText.getAttribute("selection-index"),
|
||||||
|
kind: searchText.getAttribute("selection-kind")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
eventData = JSON.stringify(eventData);
|
||||||
|
|
||||||
let event = new CustomEvent("AboutHomeSearchEvent", {detail: eventData});
|
let event = new CustomEvent("AboutHomeSearchEvent", {detail: eventData});
|
||||||
document.dispatchEvent(event);
|
document.dispatchEvent(event);
|
||||||
}
|
}
|
||||||
|
@ -3125,9 +3125,13 @@ const BrowserSearch = {
|
|||||||
* @param source
|
* @param source
|
||||||
* (string) Where the search originated from. See the FHR
|
* (string) Where the search originated from. See the FHR
|
||||||
* SearchesProvider for allowed values.
|
* SearchesProvider for allowed values.
|
||||||
|
* @param selection [optional]
|
||||||
|
* ({index: The selected index, kind: "key" or "mouse"}) If
|
||||||
|
* the search was a suggested search, this indicates where the
|
||||||
|
* item was in the suggestion list and how the user selected it.
|
||||||
*/
|
*/
|
||||||
recordSearchInHealthReport: function (engine, source) {
|
recordSearchInHealthReport: function (engine, source, selection) {
|
||||||
BrowserUITelemetry.countSearchEvent(source);
|
BrowserUITelemetry.countSearchEvent(source, null, selection);
|
||||||
#ifdef MOZ_SERVICES_HEALTHREPORT
|
#ifdef MOZ_SERVICES_HEALTHREPORT
|
||||||
let reporter = Cc["@mozilla.org/datareporting/service;1"]
|
let reporter = Cc["@mozilla.org/datareporting/service;1"]
|
||||||
.getService()
|
.getService()
|
||||||
|
@ -33,13 +33,24 @@ let gSearch = {
|
|||||||
if (event) {
|
if (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
let searchStr = this._nodes.text.value;
|
let searchText = this._nodes.text;
|
||||||
|
let searchStr = searchText.value;
|
||||||
if (this.currentEngineName && searchStr.length) {
|
if (this.currentEngineName && searchStr.length) {
|
||||||
this._send("Search", {
|
|
||||||
|
let eventData = {
|
||||||
engineName: this.currentEngineName,
|
engineName: this.currentEngineName,
|
||||||
searchString: searchStr,
|
searchString: searchStr,
|
||||||
whence: "newtab",
|
whence: "newtab",
|
||||||
});
|
}
|
||||||
|
|
||||||
|
if (searchText.hasAttribute("selection-index")) {
|
||||||
|
eventData.selection = {
|
||||||
|
index: searchText.getAttribute("selection-index"),
|
||||||
|
kind: searchText.getAttribute("selection-kind")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this._send("Search", eventData);
|
||||||
}
|
}
|
||||||
this._suggestionController.addInputValueToFormHistory();
|
this._suggestionController.addInputValueToFormHistory();
|
||||||
},
|
},
|
||||||
|
@ -179,6 +179,13 @@ SearchSuggestionUIController.prototype = {
|
|||||||
case event.DOM_VK_RETURN:
|
case event.DOM_VK_RETURN:
|
||||||
if (this.selectedIndex >= 0) {
|
if (this.selectedIndex >= 0) {
|
||||||
this.input.value = this.suggestionAtIndex(this.selectedIndex);
|
this.input.value = this.suggestionAtIndex(this.selectedIndex);
|
||||||
|
this.input.setAttribute("selection-index", this.selectedIndex);
|
||||||
|
this.input.setAttribute("selection-kind", "key");
|
||||||
|
} else {
|
||||||
|
// If we didn't select anything, make sure to remove the attributes
|
||||||
|
// in case they were populated last time.
|
||||||
|
this.input.removeAttribute("selection-index");
|
||||||
|
this.input.removeAttribute("selection-kind");
|
||||||
}
|
}
|
||||||
this._stickyInputValue = this.input.value;
|
this._stickyInputValue = this.input.value;
|
||||||
this._hideSuggestions();
|
this._hideSuggestions();
|
||||||
@ -228,6 +235,8 @@ SearchSuggestionUIController.prototype = {
|
|||||||
let suggestion = this.suggestionAtIndex(idx);
|
let suggestion = this.suggestionAtIndex(idx);
|
||||||
this._stickyInputValue = suggestion;
|
this._stickyInputValue = suggestion;
|
||||||
this.input.value = suggestion;
|
this.input.value = suggestion;
|
||||||
|
this.input.setAttribute("selection-index", idx);
|
||||||
|
this.input.setAttribute("selection-kind", "mouse");
|
||||||
this._hideSuggestions();
|
this._hideSuggestions();
|
||||||
if (this.onClick) {
|
if (this.onClick) {
|
||||||
this.onClick.call(null);
|
this.onClick.call(null);
|
||||||
|
@ -151,7 +151,7 @@ loop.conversation = (function(OT, mozL10n) {
|
|||||||
*
|
*
|
||||||
* Required options:
|
* Required options:
|
||||||
* - {loop.shared.models.ConversationModel} conversation Conversation model.
|
* - {loop.shared.models.ConversationModel} conversation Conversation model.
|
||||||
* - {loop.shared.components.Notifier} notifier Notifier component.
|
* - {loop.shared.models.NotificationCollection} notifications
|
||||||
*
|
*
|
||||||
* @type {loop.shared.router.BaseConversationRouter}
|
* @type {loop.shared.router.BaseConversationRouter}
|
||||||
*/
|
*/
|
||||||
@ -206,7 +206,7 @@ loop.conversation = (function(OT, mozL10n) {
|
|||||||
console.error("Failed to get the sessionData", err);
|
console.error("Failed to get the sessionData", err);
|
||||||
// XXX Not the ideal response, but bug 1047410 will be replacing
|
// XXX Not the ideal response, but bug 1047410 will be replacing
|
||||||
// this by better "call failed" UI.
|
// this by better "call failed" UI.
|
||||||
this._notifier.errorL10n("cannot_start_call_session_not_ready");
|
this._notifications.errorL10n("cannot_start_call_session_not_ready");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +331,7 @@ loop.conversation = (function(OT, mozL10n) {
|
|||||||
_handleSessionError: function() {
|
_handleSessionError: function() {
|
||||||
// XXX Not the ideal response, but bug 1047410 will be replacing
|
// XXX Not the ideal response, but bug 1047410 will be replacing
|
||||||
// this by better "call failed" UI.
|
// this by better "call failed" UI.
|
||||||
this._notifier.errorL10n("cannot_start_call_session_not_ready");
|
this._notifications.errorL10n("cannot_start_call_session_not_ready");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -376,7 +376,7 @@ loop.conversation = (function(OT, mozL10n) {
|
|||||||
conversation: new loop.shared.models.ConversationModel(
|
conversation: new loop.shared.models.ConversationModel(
|
||||||
{}, // Model attributes
|
{}, // Model attributes
|
||||||
{sdk: OT}), // Model dependencies
|
{sdk: OT}), // Model dependencies
|
||||||
notifier: new sharedViews.NotificationListView({el: "#messages"})
|
notifications: new loop.shared.models.NotificationCollection()
|
||||||
});
|
});
|
||||||
Backbone.history.start();
|
Backbone.history.start();
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ loop.conversation = (function(OT, mozL10n) {
|
|||||||
*
|
*
|
||||||
* Required options:
|
* Required options:
|
||||||
* - {loop.shared.models.ConversationModel} conversation Conversation model.
|
* - {loop.shared.models.ConversationModel} conversation Conversation model.
|
||||||
* - {loop.shared.components.Notifier} notifier Notifier component.
|
* - {loop.shared.models.NotificationCollection} notifications
|
||||||
*
|
*
|
||||||
* @type {loop.shared.router.BaseConversationRouter}
|
* @type {loop.shared.router.BaseConversationRouter}
|
||||||
*/
|
*/
|
||||||
@ -206,7 +206,7 @@ loop.conversation = (function(OT, mozL10n) {
|
|||||||
console.error("Failed to get the sessionData", err);
|
console.error("Failed to get the sessionData", err);
|
||||||
// XXX Not the ideal response, but bug 1047410 will be replacing
|
// XXX Not the ideal response, but bug 1047410 will be replacing
|
||||||
// this by better "call failed" UI.
|
// this by better "call failed" UI.
|
||||||
this._notifier.errorL10n("cannot_start_call_session_not_ready");
|
this._notifications.errorL10n("cannot_start_call_session_not_ready");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +331,7 @@ loop.conversation = (function(OT, mozL10n) {
|
|||||||
_handleSessionError: function() {
|
_handleSessionError: function() {
|
||||||
// XXX Not the ideal response, but bug 1047410 will be replacing
|
// XXX Not the ideal response, but bug 1047410 will be replacing
|
||||||
// this by better "call failed" UI.
|
// this by better "call failed" UI.
|
||||||
this._notifier.errorL10n("cannot_start_call_session_not_ready");
|
this._notifications.errorL10n("cannot_start_call_session_not_ready");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -376,7 +376,7 @@ loop.conversation = (function(OT, mozL10n) {
|
|||||||
conversation: new loop.shared.models.ConversationModel(
|
conversation: new loop.shared.models.ConversationModel(
|
||||||
{}, // Model attributes
|
{}, // Model attributes
|
||||||
{sdk: OT}), // Model dependencies
|
{sdk: OT}), // Model dependencies
|
||||||
notifier: new sharedViews.NotificationListView({el: "#messages"})
|
notifications: new loop.shared.models.NotificationCollection()
|
||||||
});
|
});
|
||||||
Backbone.history.start();
|
Backbone.history.start();
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var sharedViews = loop.shared.views,
|
var sharedViews = loop.shared.views,
|
||||||
|
sharedModels = loop.shared.models,
|
||||||
// aliasing translation function as __ for concision
|
// aliasing translation function as __ for concision
|
||||||
__ = mozL10n.get;
|
__ = mozL10n.get;
|
||||||
|
|
||||||
@ -261,7 +262,7 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
propTypes: {
|
propTypes: {
|
||||||
callUrl: React.PropTypes.string,
|
callUrl: React.PropTypes.string,
|
||||||
callUrlExpiry: React.PropTypes.number,
|
callUrlExpiry: React.PropTypes.number,
|
||||||
notifier: React.PropTypes.object.isRequired,
|
notifications: React.PropTypes.object.isRequired,
|
||||||
client: React.PropTypes.object.isRequired
|
client: React.PropTypes.object.isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -296,10 +297,10 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onCallUrlReceived: function(err, callUrlData) {
|
_onCallUrlReceived: function(err, callUrlData) {
|
||||||
this.props.notifier.clear();
|
this.props.notifications.reset();
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
this.props.notifier.errorL10n("unable_retrieve_url");
|
this.props.notifications.errorL10n("unable_retrieve_url");
|
||||||
this.setState(this.getInitialState());
|
this.setState(this.getInitialState());
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
@ -314,7 +315,7 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
callUrlExpiry: callUrlData.expiresAt});
|
callUrlExpiry: callUrlData.expiresAt});
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
this.props.notifier.errorL10n("unable_retrieve_url");
|
this.props.notifications.errorL10n("unable_retrieve_url");
|
||||||
this.setState(this.getInitialState());
|
this.setState(this.getInitialState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -411,17 +412,20 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
*/
|
*/
|
||||||
var PanelView = React.createClass({displayName: 'PanelView',
|
var PanelView = React.createClass({displayName: 'PanelView',
|
||||||
propTypes: {
|
propTypes: {
|
||||||
notifier: React.PropTypes.object.isRequired,
|
notifications: React.PropTypes.object.isRequired,
|
||||||
client: React.PropTypes.object.isRequired,
|
client: React.PropTypes.object.isRequired,
|
||||||
// Mostly used for UI components showcase and unit tests
|
// Mostly used for UI components showcase and unit tests
|
||||||
callUrl: React.PropTypes.string
|
callUrl: React.PropTypes.string
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
var NotificationListView = sharedViews.NotificationListView;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
React.DOM.div(null,
|
React.DOM.div(null,
|
||||||
|
NotificationListView({notifications: this.props.notifications}),
|
||||||
CallUrlResult({client: this.props.client,
|
CallUrlResult({client: this.props.client,
|
||||||
notifier: this.props.notifier,
|
notifications: this.props.notifications,
|
||||||
callUrl: this.props.callUrl}),
|
callUrl: this.props.callUrl}),
|
||||||
ToSView(null),
|
ToSView(null),
|
||||||
React.DOM.div({className: "footer"},
|
React.DOM.div({className: "footer"},
|
||||||
@ -454,7 +458,6 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
|
|
||||||
this._registerVisibilityChangeEvent();
|
this._registerVisibilityChangeEvent();
|
||||||
|
|
||||||
this.on("panel:open panel:closed", this.clearNotifications, this);
|
|
||||||
this.on("panel:open", this.reset, this);
|
this.on("panel:open", this.reset, this);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -483,20 +486,16 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
this.reset();
|
this.reset();
|
||||||
},
|
},
|
||||||
|
|
||||||
clearNotifications: function() {
|
|
||||||
this._notifier.clear();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets this router to its initial state.
|
* Resets this router to its initial state.
|
||||||
*/
|
*/
|
||||||
reset: function() {
|
reset: function() {
|
||||||
this._notifier.clear();
|
this._notifications.reset();
|
||||||
var client = new loop.Client({
|
var client = new loop.Client({
|
||||||
baseServerUrl: navigator.mozLoop.serverUrl
|
baseServerUrl: navigator.mozLoop.serverUrl
|
||||||
});
|
});
|
||||||
this.loadReactComponent(PanelView({client: client,
|
this.loadReactComponent(
|
||||||
notifier: this._notifier}));
|
PanelView({client: client, notifications: this._notifications}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -510,7 +509,7 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
|
|
||||||
router = new PanelRouter({
|
router = new PanelRouter({
|
||||||
document: document,
|
document: document,
|
||||||
notifier: new sharedViews.NotificationListView({el: "#messages"})
|
notifications: new sharedModels.NotificationCollection()
|
||||||
});
|
});
|
||||||
Backbone.history.start();
|
Backbone.history.start();
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var sharedViews = loop.shared.views,
|
var sharedViews = loop.shared.views,
|
||||||
|
sharedModels = loop.shared.models,
|
||||||
// aliasing translation function as __ for concision
|
// aliasing translation function as __ for concision
|
||||||
__ = mozL10n.get;
|
__ = mozL10n.get;
|
||||||
|
|
||||||
@ -261,7 +262,7 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
propTypes: {
|
propTypes: {
|
||||||
callUrl: React.PropTypes.string,
|
callUrl: React.PropTypes.string,
|
||||||
callUrlExpiry: React.PropTypes.number,
|
callUrlExpiry: React.PropTypes.number,
|
||||||
notifier: React.PropTypes.object.isRequired,
|
notifications: React.PropTypes.object.isRequired,
|
||||||
client: React.PropTypes.object.isRequired
|
client: React.PropTypes.object.isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -296,10 +297,10 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onCallUrlReceived: function(err, callUrlData) {
|
_onCallUrlReceived: function(err, callUrlData) {
|
||||||
this.props.notifier.clear();
|
this.props.notifications.reset();
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
this.props.notifier.errorL10n("unable_retrieve_url");
|
this.props.notifications.errorL10n("unable_retrieve_url");
|
||||||
this.setState(this.getInitialState());
|
this.setState(this.getInitialState());
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
@ -314,7 +315,7 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
callUrlExpiry: callUrlData.expiresAt});
|
callUrlExpiry: callUrlData.expiresAt});
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
this.props.notifier.errorL10n("unable_retrieve_url");
|
this.props.notifications.errorL10n("unable_retrieve_url");
|
||||||
this.setState(this.getInitialState());
|
this.setState(this.getInitialState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -411,17 +412,20 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
*/
|
*/
|
||||||
var PanelView = React.createClass({
|
var PanelView = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
notifier: React.PropTypes.object.isRequired,
|
notifications: React.PropTypes.object.isRequired,
|
||||||
client: React.PropTypes.object.isRequired,
|
client: React.PropTypes.object.isRequired,
|
||||||
// Mostly used for UI components showcase and unit tests
|
// Mostly used for UI components showcase and unit tests
|
||||||
callUrl: React.PropTypes.string
|
callUrl: React.PropTypes.string
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
var NotificationListView = sharedViews.NotificationListView;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<NotificationListView notifications={this.props.notifications} />
|
||||||
<CallUrlResult client={this.props.client}
|
<CallUrlResult client={this.props.client}
|
||||||
notifier={this.props.notifier}
|
notifications={this.props.notifications}
|
||||||
callUrl={this.props.callUrl} />
|
callUrl={this.props.callUrl} />
|
||||||
<ToSView />
|
<ToSView />
|
||||||
<div className="footer">
|
<div className="footer">
|
||||||
@ -454,7 +458,6 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
|
|
||||||
this._registerVisibilityChangeEvent();
|
this._registerVisibilityChangeEvent();
|
||||||
|
|
||||||
this.on("panel:open panel:closed", this.clearNotifications, this);
|
|
||||||
this.on("panel:open", this.reset, this);
|
this.on("panel:open", this.reset, this);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -483,20 +486,16 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
this.reset();
|
this.reset();
|
||||||
},
|
},
|
||||||
|
|
||||||
clearNotifications: function() {
|
|
||||||
this._notifier.clear();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets this router to its initial state.
|
* Resets this router to its initial state.
|
||||||
*/
|
*/
|
||||||
reset: function() {
|
reset: function() {
|
||||||
this._notifier.clear();
|
this._notifications.reset();
|
||||||
var client = new loop.Client({
|
var client = new loop.Client({
|
||||||
baseServerUrl: navigator.mozLoop.serverUrl
|
baseServerUrl: navigator.mozLoop.serverUrl
|
||||||
});
|
});
|
||||||
this.loadReactComponent(<PanelView client={client}
|
this.loadReactComponent(
|
||||||
notifier={this._notifier} />);
|
<PanelView client={client} notifications={this._notifications}/>);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -510,7 +509,7 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
|
|
||||||
router = new PanelRouter({
|
router = new PanelRouter({
|
||||||
document: document,
|
document: document,
|
||||||
notifier: new sharedViews.NotificationListView({el: "#messages"})
|
notifications: new sharedModels.NotificationCollection()
|
||||||
});
|
});
|
||||||
Backbone.history.start();
|
Backbone.history.start();
|
||||||
|
|
||||||
|
@ -224,6 +224,7 @@ p {
|
|||||||
background: #eee;
|
background: #eee;
|
||||||
padding: .2em 1em;
|
padding: .2em 1em;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
border-bottom: 2px solid #E9E9E9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert p.message {
|
.alert p.message {
|
||||||
@ -232,8 +233,13 @@ p {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.alert.alert-error {
|
.alert.alert-error {
|
||||||
background: #f99;
|
display: flex;
|
||||||
border: 1px solid #f77;
|
align-content: center;
|
||||||
|
padding: 5px;
|
||||||
|
font-size: 10px;
|
||||||
|
justify-content: center;
|
||||||
|
color: #FFF;
|
||||||
|
background: repeating-linear-gradient(-45deg, #D74345, #D74345 10px, #D94B4D 10px, #D94B4D 20px) repeat scroll 0% 0% transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert.alert-warning {
|
.alert.alert-warning {
|
||||||
|
@ -393,15 +393,14 @@
|
|||||||
background-image: url("../img/sad.png");
|
background-image: url("../img/sad.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
.feedback button.back {
|
.fx-embedded-btn-back {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding: .2rem .8rem;
|
||||||
|
border: 1px solid #aaa;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
border: 1px solid #CCC;
|
background: transparent;
|
||||||
color: #CCC;
|
color: #777;
|
||||||
font-size: 11px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 3px 10px;
|
|
||||||
display: inline;
|
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.feedback label {
|
.feedback label {
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
var loop = loop || {};
|
var loop = loop || {};
|
||||||
loop.shared = loop.shared || {};
|
loop.shared = loop.shared || {};
|
||||||
loop.shared.models = (function() {
|
loop.shared.models = (function(l10n) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -375,7 +375,43 @@ loop.shared.models = (function() {
|
|||||||
* Notification collection
|
* Notification collection
|
||||||
*/
|
*/
|
||||||
var NotificationCollection = Backbone.Collection.extend({
|
var NotificationCollection = Backbone.Collection.extend({
|
||||||
model: NotificationModel
|
model: NotificationModel,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a warning notification to the stack and renders it.
|
||||||
|
*
|
||||||
|
* @return {String} message
|
||||||
|
*/
|
||||||
|
warn: function(message) {
|
||||||
|
this.add({level: "warning", message: message});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a l10n warning notification to the stack and renders it.
|
||||||
|
*
|
||||||
|
* @param {String} messageId L10n message id
|
||||||
|
*/
|
||||||
|
warnL10n: function(messageId) {
|
||||||
|
this.warn(l10n.get(messageId));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an error notification to the stack and renders it.
|
||||||
|
*
|
||||||
|
* @return {String} message
|
||||||
|
*/
|
||||||
|
error: function(message) {
|
||||||
|
this.add({level: "error", message: message});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a l10n rror notification to the stack and renders it.
|
||||||
|
*
|
||||||
|
* @param {String} messageId L10n message id
|
||||||
|
*/
|
||||||
|
errorL10n: function(messageId) {
|
||||||
|
this.error(l10n.get(messageId));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -383,4 +419,4 @@ loop.shared.models = (function() {
|
|||||||
NotificationCollection: NotificationCollection,
|
NotificationCollection: NotificationCollection,
|
||||||
NotificationModel: NotificationModel
|
NotificationModel: NotificationModel
|
||||||
};
|
};
|
||||||
})();
|
})(navigator.mozL10n || document.mozL10n);
|
||||||
|
@ -23,25 +23,25 @@ loop.shared.router = (function() {
|
|||||||
_activeView: undefined,
|
_activeView: undefined,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifications dispatcher.
|
* Notifications collection.
|
||||||
* @type {loop.shared.views.NotificationListView}
|
* @type {loop.shared.models.NotificationCollection}
|
||||||
*/
|
*/
|
||||||
_notifier: undefined,
|
_notifications: undefined,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* Required options:
|
* Required options:
|
||||||
* - {loop.shared.views.NotificationListView} notifier Notifier view.
|
* - {loop.shared.models.NotificationCollection} notifications
|
||||||
*
|
*
|
||||||
* @param {Object} options Options object.
|
* @param {Object} options Options object.
|
||||||
*/
|
*/
|
||||||
constructor: function(options) {
|
constructor: function(options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
if (!options.notifier) {
|
if (!options.notifications) {
|
||||||
throw new Error("missing required notifier");
|
throw new Error("missing required notifications");
|
||||||
}
|
}
|
||||||
this._notifier = options.notifier;
|
this._notifications = options.notifications;
|
||||||
|
|
||||||
Backbone.Router.apply(this, arguments);
|
Backbone.Router.apply(this, arguments);
|
||||||
},
|
},
|
||||||
@ -144,7 +144,7 @@ loop.shared.router = (function() {
|
|||||||
*/
|
*/
|
||||||
_notifyError: function(error) {
|
_notifyError: function(error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
this._notifier.errorL10n("connection_error_see_console_notification");
|
this._notifications.errorL10n("connection_error_see_console_notification");
|
||||||
this.endCall();
|
this.endCall();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -169,7 +169,7 @@ loop.shared.router = (function() {
|
|||||||
* @param {Object} event
|
* @param {Object} event
|
||||||
*/
|
*/
|
||||||
_onPeerHungup: function() {
|
_onPeerHungup: function() {
|
||||||
this._notifier.warnL10n("peer_ended_conversation2");
|
this._notifications.warnL10n("peer_ended_conversation2");
|
||||||
this.endCall();
|
this.endCall();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -177,7 +177,7 @@ loop.shared.router = (function() {
|
|||||||
* Network disconnected. Notifies the user and ends the call.
|
* Network disconnected. Notifies the user and ends the call.
|
||||||
*/
|
*/
|
||||||
_onNetworkDisconnected: function() {
|
_onNetworkDisconnected: function() {
|
||||||
this._notifier.warnL10n("network_disconnected");
|
this._notifications.warnL10n("network_disconnected");
|
||||||
this.endCall();
|
this.endCall();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -407,7 +407,8 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||||||
var backButton = React.DOM.div(null);
|
var backButton = React.DOM.div(null);
|
||||||
if (this.props.reset) {
|
if (this.props.reset) {
|
||||||
backButton = (
|
backButton = (
|
||||||
React.DOM.button({className: "back", type: "button", onClick: this.props.reset},
|
React.DOM.button({className: "fx-embedded-btn-back", type: "button",
|
||||||
|
onClick: this.props.reset},
|
||||||
"« ", l10n.get("feedback_back_button")
|
"« ", l10n.get("feedback_back_button")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -651,133 +652,55 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||||||
/**
|
/**
|
||||||
* Notification view.
|
* Notification view.
|
||||||
*/
|
*/
|
||||||
var NotificationView = BaseView.extend({
|
var NotificationView = React.createClass({
|
||||||
template: _.template([
|
displayName: 'NotificationView',
|
||||||
'<div class="alert alert-<%- level %>">',
|
mixins: [Backbone.Events],
|
||||||
' <button class="close"></button>',
|
|
||||||
' <p class="message"><%- message %></p>',
|
|
||||||
'</div>'
|
|
||||||
].join("")),
|
|
||||||
|
|
||||||
events: {
|
propTypes: {
|
||||||
"click .close": "dismiss"
|
notification: React.PropTypes.object.isRequired,
|
||||||
},
|
key: React.PropTypes.number.isRequired
|
||||||
|
|
||||||
dismiss: function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
this.$el.addClass("fade-out");
|
|
||||||
setTimeout(function() {
|
|
||||||
this.collection.remove(this.model);
|
|
||||||
this.remove();
|
|
||||||
}.bind(this), 500); // XXX make timeout value configurable
|
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
this.$el.html(this.template(this.model.toJSON()));
|
var notification = this.props.notification;
|
||||||
return this;
|
return (
|
||||||
|
React.DOM.div({key: this.props.key,
|
||||||
|
className: "alert alert-" + notification.get("level")},
|
||||||
|
React.DOM.span({className: "message"}, notification.get("message"))
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification list view.
|
* Notification list view.
|
||||||
*/
|
*/
|
||||||
var NotificationListView = Backbone.View.extend({
|
var NotificationListView = React.createClass({displayName: 'NotificationListView',
|
||||||
/**
|
mixins: [Backbone.Events],
|
||||||
* Constructor.
|
|
||||||
*
|
propTypes: {
|
||||||
* Available options:
|
notifications: React.PropTypes.object.isRequired
|
||||||
* - {loop.shared.models.NotificationCollection} collection Notifications
|
|
||||||
* collection
|
|
||||||
*
|
|
||||||
* @param {Object} options Options object
|
|
||||||
*/
|
|
||||||
initialize: function(options) {
|
|
||||||
options = options || {};
|
|
||||||
if (!options.collection) {
|
|
||||||
this.collection = new sharedModels.NotificationCollection();
|
|
||||||
}
|
|
||||||
this.listenTo(this.collection, "reset add remove", this.render);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
componentDidMount: function() {
|
||||||
* Clears the notification stack.
|
this.listenTo(this.props.notifications, "reset add remove", function() {
|
||||||
*/
|
this.forceUpdate();
|
||||||
clear: function() {
|
}.bind(this));
|
||||||
this.collection.reset();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
componentWillUnmount: function() {
|
||||||
* Adds a new notification to the stack, triggering rendering of it.
|
this.stopListening(this.props.notifications);
|
||||||
*
|
|
||||||
* @param {Object|NotificationModel} notification Notification data.
|
|
||||||
*/
|
|
||||||
notify: function(notification) {
|
|
||||||
this.collection.add(notification);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a new notification to the stack using an l10n message identifier,
|
|
||||||
* triggering rendering of it.
|
|
||||||
*
|
|
||||||
* @param {String} messageId L10n message id
|
|
||||||
* @param {String} level Notification level
|
|
||||||
*/
|
|
||||||
notifyL10n: function(messageId, level) {
|
|
||||||
this.notify({
|
|
||||||
message: l10n.get(messageId),
|
|
||||||
level: level
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a warning notification to the stack and renders it.
|
|
||||||
*
|
|
||||||
* @return {String} message
|
|
||||||
*/
|
|
||||||
warn: function(message) {
|
|
||||||
this.notify({level: "warning", message: message});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a l10n warning notification to the stack and renders it.
|
|
||||||
*
|
|
||||||
* @param {String} messageId L10n message id
|
|
||||||
*/
|
|
||||||
warnL10n: function(messageId) {
|
|
||||||
this.warn(l10n.get(messageId));
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an error notification to the stack and renders it.
|
|
||||||
*
|
|
||||||
* @return {String} message
|
|
||||||
*/
|
|
||||||
error: function(message) {
|
|
||||||
this.notify({level: "error", message: message});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a l10n rror notification to the stack and renders it.
|
|
||||||
*
|
|
||||||
* @param {String} messageId L10n message id
|
|
||||||
*/
|
|
||||||
errorL10n: function(messageId) {
|
|
||||||
this.error(l10n.get(messageId));
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders this view.
|
|
||||||
*
|
|
||||||
* @return {loop.shared.views.NotificationListView}
|
|
||||||
*/
|
|
||||||
render: function() {
|
render: function() {
|
||||||
this.$el.html(this.collection.map(function(notification) {
|
return (
|
||||||
return new NotificationView({
|
React.DOM.div({id: "messages"},
|
||||||
model: notification,
|
this.props.notifications.map(function(notification, key) {
|
||||||
collection: this.collection
|
return NotificationView({key: key, notification: notification});
|
||||||
}).render().$el;
|
})
|
||||||
}.bind(this)));
|
|
||||||
return this;
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -817,7 +740,6 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||||||
FeedbackView: FeedbackView,
|
FeedbackView: FeedbackView,
|
||||||
MediaControlButton: MediaControlButton,
|
MediaControlButton: MediaControlButton,
|
||||||
NotificationListView: NotificationListView,
|
NotificationListView: NotificationListView,
|
||||||
NotificationView: NotificationView,
|
|
||||||
UnsupportedBrowserView: UnsupportedBrowserView,
|
UnsupportedBrowserView: UnsupportedBrowserView,
|
||||||
UnsupportedDeviceView: UnsupportedDeviceView
|
UnsupportedDeviceView: UnsupportedDeviceView
|
||||||
};
|
};
|
||||||
|
@ -407,7 +407,8 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||||||
var backButton = <div />;
|
var backButton = <div />;
|
||||||
if (this.props.reset) {
|
if (this.props.reset) {
|
||||||
backButton = (
|
backButton = (
|
||||||
<button className="back" type="button" onClick={this.props.reset}>
|
<button className="fx-embedded-btn-back" type="button"
|
||||||
|
onClick={this.props.reset}>
|
||||||
« {l10n.get("feedback_back_button")}
|
« {l10n.get("feedback_back_button")}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
@ -651,133 +652,55 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||||||
/**
|
/**
|
||||||
* Notification view.
|
* Notification view.
|
||||||
*/
|
*/
|
||||||
var NotificationView = BaseView.extend({
|
var NotificationView = React.createClass({
|
||||||
template: _.template([
|
displayName: 'NotificationView',
|
||||||
'<div class="alert alert-<%- level %>">',
|
mixins: [Backbone.Events],
|
||||||
' <button class="close"></button>',
|
|
||||||
' <p class="message"><%- message %></p>',
|
|
||||||
'</div>'
|
|
||||||
].join("")),
|
|
||||||
|
|
||||||
events: {
|
propTypes: {
|
||||||
"click .close": "dismiss"
|
notification: React.PropTypes.object.isRequired,
|
||||||
},
|
key: React.PropTypes.number.isRequired
|
||||||
|
|
||||||
dismiss: function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
this.$el.addClass("fade-out");
|
|
||||||
setTimeout(function() {
|
|
||||||
this.collection.remove(this.model);
|
|
||||||
this.remove();
|
|
||||||
}.bind(this), 500); // XXX make timeout value configurable
|
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
this.$el.html(this.template(this.model.toJSON()));
|
var notification = this.props.notification;
|
||||||
return this;
|
return (
|
||||||
|
<div key={this.props.key}
|
||||||
|
className={"alert alert-" + notification.get("level")}>
|
||||||
|
<span className="message">{notification.get("message")}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification list view.
|
* Notification list view.
|
||||||
*/
|
*/
|
||||||
var NotificationListView = Backbone.View.extend({
|
var NotificationListView = React.createClass({
|
||||||
/**
|
mixins: [Backbone.Events],
|
||||||
* Constructor.
|
|
||||||
*
|
propTypes: {
|
||||||
* Available options:
|
notifications: React.PropTypes.object.isRequired
|
||||||
* - {loop.shared.models.NotificationCollection} collection Notifications
|
|
||||||
* collection
|
|
||||||
*
|
|
||||||
* @param {Object} options Options object
|
|
||||||
*/
|
|
||||||
initialize: function(options) {
|
|
||||||
options = options || {};
|
|
||||||
if (!options.collection) {
|
|
||||||
this.collection = new sharedModels.NotificationCollection();
|
|
||||||
}
|
|
||||||
this.listenTo(this.collection, "reset add remove", this.render);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
componentDidMount: function() {
|
||||||
* Clears the notification stack.
|
this.listenTo(this.props.notifications, "reset add remove", function() {
|
||||||
*/
|
this.forceUpdate();
|
||||||
clear: function() {
|
}.bind(this));
|
||||||
this.collection.reset();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
componentWillUnmount: function() {
|
||||||
* Adds a new notification to the stack, triggering rendering of it.
|
this.stopListening(this.props.notifications);
|
||||||
*
|
|
||||||
* @param {Object|NotificationModel} notification Notification data.
|
|
||||||
*/
|
|
||||||
notify: function(notification) {
|
|
||||||
this.collection.add(notification);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a new notification to the stack using an l10n message identifier,
|
|
||||||
* triggering rendering of it.
|
|
||||||
*
|
|
||||||
* @param {String} messageId L10n message id
|
|
||||||
* @param {String} level Notification level
|
|
||||||
*/
|
|
||||||
notifyL10n: function(messageId, level) {
|
|
||||||
this.notify({
|
|
||||||
message: l10n.get(messageId),
|
|
||||||
level: level
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a warning notification to the stack and renders it.
|
|
||||||
*
|
|
||||||
* @return {String} message
|
|
||||||
*/
|
|
||||||
warn: function(message) {
|
|
||||||
this.notify({level: "warning", message: message});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a l10n warning notification to the stack and renders it.
|
|
||||||
*
|
|
||||||
* @param {String} messageId L10n message id
|
|
||||||
*/
|
|
||||||
warnL10n: function(messageId) {
|
|
||||||
this.warn(l10n.get(messageId));
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an error notification to the stack and renders it.
|
|
||||||
*
|
|
||||||
* @return {String} message
|
|
||||||
*/
|
|
||||||
error: function(message) {
|
|
||||||
this.notify({level: "error", message: message});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a l10n rror notification to the stack and renders it.
|
|
||||||
*
|
|
||||||
* @param {String} messageId L10n message id
|
|
||||||
*/
|
|
||||||
errorL10n: function(messageId) {
|
|
||||||
this.error(l10n.get(messageId));
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders this view.
|
|
||||||
*
|
|
||||||
* @return {loop.shared.views.NotificationListView}
|
|
||||||
*/
|
|
||||||
render: function() {
|
render: function() {
|
||||||
this.$el.html(this.collection.map(function(notification) {
|
return (
|
||||||
return new NotificationView({
|
<div id="messages">{
|
||||||
model: notification,
|
this.props.notifications.map(function(notification, key) {
|
||||||
collection: this.collection
|
return <NotificationView key={key} notification={notification}/>;
|
||||||
}).render().$el;
|
})
|
||||||
}.bind(this)));
|
}
|
||||||
return this;
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -817,7 +740,6 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||||||
FeedbackView: FeedbackView,
|
FeedbackView: FeedbackView,
|
||||||
MediaControlButton: MediaControlButton,
|
MediaControlButton: MediaControlButton,
|
||||||
NotificationListView: NotificationListView,
|
NotificationListView: NotificationListView,
|
||||||
NotificationView: NotificationView,
|
|
||||||
UnsupportedBrowserView: UnsupportedBrowserView,
|
UnsupportedBrowserView: UnsupportedBrowserView,
|
||||||
UnsupportedDeviceView: UnsupportedDeviceView
|
UnsupportedDeviceView: UnsupportedDeviceView
|
||||||
};
|
};
|
||||||
|
@ -135,8 +135,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* Required options:
|
* Required options:
|
||||||
* - {loop.shared.model.ConversationModel} model Conversation model.
|
* - {loop.shared.models.ConversationModel} model Conversation model.
|
||||||
* - {loop.shared.views.NotificationListView} notifier Notifier component.
|
* - {loop.shared.models.NotificationCollection} notifications
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
model: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
model: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||||
.isRequired,
|
.isRequired,
|
||||||
// XXX Check more tightly here when we start injecting window.loop.*
|
// XXX Check more tightly here when we start injecting window.loop.*
|
||||||
notifier: React.PropTypes.object.isRequired,
|
notifications: React.PropTypes.object.isRequired,
|
||||||
client: React.PropTypes.object.isRequired
|
client: React.PropTypes.object.isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -167,14 +167,11 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
this._onSessionError);
|
this._onSessionError);
|
||||||
this.props.client.requestCallUrlInfo(this.props.model.get("loopToken"),
|
this.props.client.requestCallUrlInfo(this.props.model.get("loopToken"),
|
||||||
this._setConversationTimestamp);
|
this._setConversationTimestamp);
|
||||||
// XXX DOM element does not exist before React view gets instantiated
|
|
||||||
// We should turn the notifier into a react component
|
|
||||||
this.props.notifier.$el = $("#messages");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onSessionError: function(error) {
|
_onSessionError: function(error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
this.props.notifier.errorL10n("unable_retrieve_call_info");
|
this.props.notifications.errorL10n("unable_retrieve_call_info");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -195,7 +192,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
|
|
||||||
_setConversationTimestamp: function(err, callUrlInfo) {
|
_setConversationTimestamp: function(err, callUrlInfo) {
|
||||||
if (err) {
|
if (err) {
|
||||||
this.props.notifier.errorL10n("unable_retrieve_call_info");
|
this.props.notifications.errorL10n("unable_retrieve_call_info");
|
||||||
} else {
|
} else {
|
||||||
var date = (new Date(callUrlInfo.urlCreationDate * 1000));
|
var date = (new Date(callUrlInfo.urlCreationDate * 1000));
|
||||||
var options = {year: "numeric", month: "long", day: "numeric"};
|
var options = {year: "numeric", month: "long", day: "numeric"};
|
||||||
@ -343,7 +340,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
setupOutgoingCall: function() {
|
setupOutgoingCall: function() {
|
||||||
var loopToken = this._conversation.get("loopToken");
|
var loopToken = this._conversation.get("loopToken");
|
||||||
if (!loopToken) {
|
if (!loopToken) {
|
||||||
this._notifier.errorL10n("missing_conversation_info");
|
this._notifications.errorL10n("missing_conversation_info");
|
||||||
this.navigate("home", {trigger: true});
|
this.navigate("home", {trigger: true});
|
||||||
} else {
|
} else {
|
||||||
var callType = this._conversation.get("selectedCallType");
|
var callType = this._conversation.get("selectedCallType");
|
||||||
@ -361,7 +358,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
this._onSessionExpired();
|
this._onSessionExpired();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this._notifier.errorL10n("missing_conversation_info");
|
this._notifications.errorL10n("missing_conversation_info");
|
||||||
this.navigate("home", {trigger: true});
|
this.navigate("home", {trigger: true});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -378,7 +375,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
startCall: function() {
|
startCall: function() {
|
||||||
var loopToken = this._conversation.get("loopToken");
|
var loopToken = this._conversation.get("loopToken");
|
||||||
if (!loopToken) {
|
if (!loopToken) {
|
||||||
this._notifier.errorL10n("missing_conversation_info");
|
this._notifications.errorL10n("missing_conversation_info");
|
||||||
this.navigate("home", {trigger: true});
|
this.navigate("home", {trigger: true});
|
||||||
} else {
|
} else {
|
||||||
this._setupWebSocketAndCallView(loopToken);
|
this._setupWebSocketAndCallView(loopToken);
|
||||||
@ -404,7 +401,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
}.bind(this), function() {
|
}.bind(this), function() {
|
||||||
// XXX Not the ideal response, but bug 1047410 will be replacing
|
// XXX Not the ideal response, but bug 1047410 will be replacing
|
||||||
// this by better "call failed" UI.
|
// this by better "call failed" UI.
|
||||||
this._notifier.errorL10n("cannot_start_call_session_not_ready");
|
this._notifications.errorL10n("cannot_start_call_session_not_ready");
|
||||||
return;
|
return;
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
@ -451,7 +448,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
*/
|
*/
|
||||||
_handleCallRejected: function() {
|
_handleCallRejected: function() {
|
||||||
this.endCall();
|
this.endCall();
|
||||||
this._notifier.errorL10n("call_timeout_notification_text");
|
this._notifications.errorL10n("call_timeout_notification_text");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -466,7 +463,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onTimeout: function() {
|
_onTimeout: function() {
|
||||||
this._notifier.errorL10n("call_timeout_notification_text");
|
this._notifications.errorL10n("call_timeout_notification_text");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -504,7 +501,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
|
|
||||||
var startView = StartConversationView({
|
var startView = StartConversationView({
|
||||||
model: this._conversation,
|
model: this._conversation,
|
||||||
notifier: this._notifier,
|
notifications: this._notifications,
|
||||||
client: this._client
|
client: this._client
|
||||||
});
|
});
|
||||||
this._conversation.once("call:outgoing:setup", this.setupOutgoingCall, this);
|
this._conversation.once("call:outgoing:setup", this.setupOutgoingCall, this);
|
||||||
@ -557,7 +554,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
});
|
});
|
||||||
var router = new WebappRouter({
|
var router = new WebappRouter({
|
||||||
helper: helper,
|
helper: helper,
|
||||||
notifier: new sharedViews.NotificationListView({el: "#messages"}),
|
notifications: new sharedModels.NotificationCollection(),
|
||||||
client: client,
|
client: client,
|
||||||
conversation: new sharedModels.ConversationModel({}, {
|
conversation: new sharedModels.ConversationModel({}, {
|
||||||
sdk: OT,
|
sdk: OT,
|
||||||
|
@ -135,8 +135,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* Required options:
|
* Required options:
|
||||||
* - {loop.shared.model.ConversationModel} model Conversation model.
|
* - {loop.shared.models.ConversationModel} model Conversation model.
|
||||||
* - {loop.shared.views.NotificationListView} notifier Notifier component.
|
* - {loop.shared.models.NotificationCollection} notifications
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
model: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
model: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||||
.isRequired,
|
.isRequired,
|
||||||
// XXX Check more tightly here when we start injecting window.loop.*
|
// XXX Check more tightly here when we start injecting window.loop.*
|
||||||
notifier: React.PropTypes.object.isRequired,
|
notifications: React.PropTypes.object.isRequired,
|
||||||
client: React.PropTypes.object.isRequired
|
client: React.PropTypes.object.isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -167,14 +167,11 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
this._onSessionError);
|
this._onSessionError);
|
||||||
this.props.client.requestCallUrlInfo(this.props.model.get("loopToken"),
|
this.props.client.requestCallUrlInfo(this.props.model.get("loopToken"),
|
||||||
this._setConversationTimestamp);
|
this._setConversationTimestamp);
|
||||||
// XXX DOM element does not exist before React view gets instantiated
|
|
||||||
// We should turn the notifier into a react component
|
|
||||||
this.props.notifier.$el = $("#messages");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onSessionError: function(error) {
|
_onSessionError: function(error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
this.props.notifier.errorL10n("unable_retrieve_call_info");
|
this.props.notifications.errorL10n("unable_retrieve_call_info");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -195,7 +192,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
|
|
||||||
_setConversationTimestamp: function(err, callUrlInfo) {
|
_setConversationTimestamp: function(err, callUrlInfo) {
|
||||||
if (err) {
|
if (err) {
|
||||||
this.props.notifier.errorL10n("unable_retrieve_call_info");
|
this.props.notifications.errorL10n("unable_retrieve_call_info");
|
||||||
} else {
|
} else {
|
||||||
var date = (new Date(callUrlInfo.urlCreationDate * 1000));
|
var date = (new Date(callUrlInfo.urlCreationDate * 1000));
|
||||||
var options = {year: "numeric", month: "long", day: "numeric"};
|
var options = {year: "numeric", month: "long", day: "numeric"};
|
||||||
@ -343,7 +340,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
setupOutgoingCall: function() {
|
setupOutgoingCall: function() {
|
||||||
var loopToken = this._conversation.get("loopToken");
|
var loopToken = this._conversation.get("loopToken");
|
||||||
if (!loopToken) {
|
if (!loopToken) {
|
||||||
this._notifier.errorL10n("missing_conversation_info");
|
this._notifications.errorL10n("missing_conversation_info");
|
||||||
this.navigate("home", {trigger: true});
|
this.navigate("home", {trigger: true});
|
||||||
} else {
|
} else {
|
||||||
var callType = this._conversation.get("selectedCallType");
|
var callType = this._conversation.get("selectedCallType");
|
||||||
@ -361,7 +358,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
this._onSessionExpired();
|
this._onSessionExpired();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this._notifier.errorL10n("missing_conversation_info");
|
this._notifications.errorL10n("missing_conversation_info");
|
||||||
this.navigate("home", {trigger: true});
|
this.navigate("home", {trigger: true});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -378,7 +375,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
startCall: function() {
|
startCall: function() {
|
||||||
var loopToken = this._conversation.get("loopToken");
|
var loopToken = this._conversation.get("loopToken");
|
||||||
if (!loopToken) {
|
if (!loopToken) {
|
||||||
this._notifier.errorL10n("missing_conversation_info");
|
this._notifications.errorL10n("missing_conversation_info");
|
||||||
this.navigate("home", {trigger: true});
|
this.navigate("home", {trigger: true});
|
||||||
} else {
|
} else {
|
||||||
this._setupWebSocketAndCallView(loopToken);
|
this._setupWebSocketAndCallView(loopToken);
|
||||||
@ -404,7 +401,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
}.bind(this), function() {
|
}.bind(this), function() {
|
||||||
// XXX Not the ideal response, but bug 1047410 will be replacing
|
// XXX Not the ideal response, but bug 1047410 will be replacing
|
||||||
// this by better "call failed" UI.
|
// this by better "call failed" UI.
|
||||||
this._notifier.errorL10n("cannot_start_call_session_not_ready");
|
this._notifications.errorL10n("cannot_start_call_session_not_ready");
|
||||||
return;
|
return;
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
@ -451,7 +448,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
*/
|
*/
|
||||||
_handleCallRejected: function() {
|
_handleCallRejected: function() {
|
||||||
this.endCall();
|
this.endCall();
|
||||||
this._notifier.errorL10n("call_timeout_notification_text");
|
this._notifications.errorL10n("call_timeout_notification_text");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -466,7 +463,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onTimeout: function() {
|
_onTimeout: function() {
|
||||||
this._notifier.errorL10n("call_timeout_notification_text");
|
this._notifications.errorL10n("call_timeout_notification_text");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -504,7 +501,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
|
|
||||||
var startView = StartConversationView({
|
var startView = StartConversationView({
|
||||||
model: this._conversation,
|
model: this._conversation,
|
||||||
notifier: this._notifier,
|
notifications: this._notifications,
|
||||||
client: this._client
|
client: this._client
|
||||||
});
|
});
|
||||||
this._conversation.once("call:outgoing:setup", this.setupOutgoingCall, this);
|
this._conversation.once("call:outgoing:setup", this.setupOutgoingCall, this);
|
||||||
@ -557,7 +554,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
});
|
});
|
||||||
var router = new WebappRouter({
|
var router = new WebappRouter({
|
||||||
helper: helper,
|
helper: helper,
|
||||||
notifier: new sharedViews.NotificationListView({el: "#messages"}),
|
notifications: new sharedModels.NotificationCollection(),
|
||||||
client: client,
|
client: client,
|
||||||
conversation: new sharedModels.ConversationModel({}, {
|
conversation: new sharedModels.ConversationModel({}, {
|
||||||
sdk: OT,
|
sdk: OT,
|
||||||
|
@ -11,18 +11,12 @@ describe("loop.conversation", function() {
|
|||||||
|
|
||||||
var ConversationRouter = loop.conversation.ConversationRouter,
|
var ConversationRouter = loop.conversation.ConversationRouter,
|
||||||
sandbox,
|
sandbox,
|
||||||
notifier;
|
notifications;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
sandbox = sinon.sandbox.create();
|
sandbox = sinon.sandbox.create();
|
||||||
sandbox.useFakeTimers();
|
sandbox.useFakeTimers();
|
||||||
notifier = {
|
notifications = new loop.shared.models.NotificationCollection();
|
||||||
notify: sandbox.spy(),
|
|
||||||
warn: sandbox.spy(),
|
|
||||||
warnL10n: sandbox.spy(),
|
|
||||||
error: sandbox.spy(),
|
|
||||||
errorL10n: sandbox.spy()
|
|
||||||
};
|
|
||||||
|
|
||||||
navigator.mozLoop = {
|
navigator.mozLoop = {
|
||||||
doNotDisturb: true,
|
doNotDisturb: true,
|
||||||
@ -73,8 +67,6 @@ describe("loop.conversation", function() {
|
|||||||
"initialize");
|
"initialize");
|
||||||
sandbox.stub(loop.shared.models.ConversationModel.prototype,
|
sandbox.stub(loop.shared.models.ConversationModel.prototype,
|
||||||
"initialize");
|
"initialize");
|
||||||
sandbox.stub(loop.shared.views.NotificationListView.prototype,
|
|
||||||
"initialize");
|
|
||||||
|
|
||||||
sandbox.stub(Backbone.history, "start");
|
sandbox.stub(Backbone.history, "start");
|
||||||
});
|
});
|
||||||
@ -132,7 +124,7 @@ describe("loop.conversation", function() {
|
|||||||
router = new ConversationRouter({
|
router = new ConversationRouter({
|
||||||
client: client,
|
client: client,
|
||||||
conversation: conversation,
|
conversation: conversation,
|
||||||
notifier: notifier
|
notifications: notifications
|
||||||
});
|
});
|
||||||
sandbox.stub(router, "loadView");
|
sandbox.stub(router, "loadView");
|
||||||
sandbox.stub(conversation, "incoming");
|
sandbox.stub(conversation, "incoming");
|
||||||
@ -181,11 +173,12 @@ describe("loop.conversation", function() {
|
|||||||
|
|
||||||
it("should display an error if requestCallsInfo returns an error",
|
it("should display an error if requestCallsInfo returns an error",
|
||||||
function(){
|
function(){
|
||||||
|
sandbox.stub(notifications, "errorL10n");
|
||||||
client.requestCallsInfo.callsArgWith(1, "failed");
|
client.requestCallsInfo.callsArgWith(1, "failed");
|
||||||
|
|
||||||
router.incoming(42);
|
router.incoming(42);
|
||||||
|
|
||||||
sinon.assert.calledOnce(notifier.errorL10n);
|
sinon.assert.calledOnce(notifications.errorL10n);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("requestCallsInfo successful", function() {
|
describe("requestCallsInfo successful", function() {
|
||||||
@ -297,12 +290,13 @@ describe("loop.conversation", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should display an error", function(done) {
|
it("should display an error", function(done) {
|
||||||
|
sandbox.stub(notifications, "errorL10n");
|
||||||
router._setupWebSocketAndCallView();
|
router._setupWebSocketAndCallView();
|
||||||
|
|
||||||
promise.then(function() {
|
promise.then(function() {
|
||||||
}, function () {
|
}, function () {
|
||||||
sinon.assert.calledOnce(router._notifier.errorL10n);
|
sinon.assert.calledOnce(router._notifications.errorL10n);
|
||||||
sinon.assert.calledWithExactly(router._notifier.errorL10n,
|
sinon.assert.calledWithExactly(router._notifications.errorL10n,
|
||||||
"cannot_start_call_session_not_ready");
|
"cannot_start_call_session_not_ready");
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -374,10 +368,11 @@ describe("loop.conversation", function() {
|
|||||||
|
|
||||||
it("should notify the user when session is not set",
|
it("should notify the user when session is not set",
|
||||||
function() {
|
function() {
|
||||||
|
sandbox.stub(notifications, "errorL10n");
|
||||||
router.conversation();
|
router.conversation();
|
||||||
|
|
||||||
sinon.assert.calledOnce(router._notifier.errorL10n);
|
sinon.assert.calledOnce(router._notifications.errorL10n);
|
||||||
sinon.assert.calledWithExactly(router._notifier.errorL10n,
|
sinon.assert.calledWithExactly(router._notifications.errorL10n,
|
||||||
"cannot_start_call_session_not_ready");
|
"cannot_start_call_session_not_ready");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -517,7 +512,7 @@ describe("loop.conversation", function() {
|
|||||||
router = new loop.conversation.ConversationRouter({
|
router = new loop.conversation.ConversationRouter({
|
||||||
client: client,
|
client: client,
|
||||||
conversation: conversation,
|
conversation: conversation,
|
||||||
notifier: notifier
|
notifications: notifications
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -10,11 +10,11 @@ var TestUtils = React.addons.TestUtils;
|
|||||||
describe("loop.panel", function() {
|
describe("loop.panel", function() {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var sandbox, notifier, fakeXHR, requests = [];
|
var sandbox, notifications, fakeXHR, requests = [];
|
||||||
|
|
||||||
function createTestRouter(fakeDocument) {
|
function createTestRouter(fakeDocument) {
|
||||||
return new loop.panel.PanelRouter({
|
return new loop.panel.PanelRouter({
|
||||||
notifier: notifier,
|
notifications: notifications,
|
||||||
document: fakeDocument
|
document: fakeDocument
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -27,14 +27,7 @@ describe("loop.panel", function() {
|
|||||||
fakeXHR.xhr.onCreate = function (xhr) {
|
fakeXHR.xhr.onCreate = function (xhr) {
|
||||||
requests.push(xhr);
|
requests.push(xhr);
|
||||||
};
|
};
|
||||||
notifier = {
|
notifications = new loop.shared.models.NotificationCollection();
|
||||||
clear: sandbox.spy(),
|
|
||||||
notify: sandbox.spy(),
|
|
||||||
warn: sandbox.spy(),
|
|
||||||
warnL10n: sandbox.spy(),
|
|
||||||
error: sandbox.spy(),
|
|
||||||
errorL10n: sandbox.spy()
|
|
||||||
};
|
|
||||||
|
|
||||||
navigator.mozLoop = {
|
navigator.mozLoop = {
|
||||||
doNotDisturb: true,
|
doNotDisturb: true,
|
||||||
@ -63,15 +56,15 @@ describe("loop.panel", function() {
|
|||||||
|
|
||||||
describe("loop.panel.PanelRouter", function() {
|
describe("loop.panel.PanelRouter", function() {
|
||||||
describe("#constructor", function() {
|
describe("#constructor", function() {
|
||||||
it("should require a notifier", function() {
|
it("should require a notifications collection", function() {
|
||||||
expect(function() {
|
expect(function() {
|
||||||
new loop.panel.PanelRouter();
|
new loop.panel.PanelRouter();
|
||||||
}).to.Throw(Error, /missing required notifier/);
|
}).to.Throw(Error, /missing required notifications/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should require a document", function() {
|
it("should require a document", function() {
|
||||||
expect(function() {
|
expect(function() {
|
||||||
new loop.panel.PanelRouter({notifier: notifier});
|
new loop.panel.PanelRouter({notifications: notifications});
|
||||||
}).to.Throw(Error, /missing required document/);
|
}).to.Throw(Error, /missing required document/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -101,9 +94,10 @@ describe("loop.panel", function() {
|
|||||||
|
|
||||||
describe("#reset", function() {
|
describe("#reset", function() {
|
||||||
it("should clear all pending notifications", function() {
|
it("should clear all pending notifications", function() {
|
||||||
|
sandbox.stub(notifications, "reset");
|
||||||
router.reset();
|
router.reset();
|
||||||
|
|
||||||
sinon.assert.calledOnce(notifier.clear);
|
sinon.assert.calledOnce(notifications.reset);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should load the home view", function() {
|
it("should load the home view", function() {
|
||||||
@ -213,7 +207,7 @@ describe("loop.panel", function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
view = TestUtils.renderIntoDocument(loop.panel.PanelView({
|
view = TestUtils.renderIntoDocument(loop.panel.PanelView({
|
||||||
notifier: notifier,
|
notifications: notifications,
|
||||||
client: fakeClient
|
client: fakeClient
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
@ -324,8 +318,9 @@ describe("loop.panel", function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sandbox.stub(notifications, "reset");
|
||||||
view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
||||||
notifier: notifier,
|
notifications: notifications,
|
||||||
client: fakeClient
|
client: fakeClient
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
@ -350,7 +345,7 @@ describe("loop.panel", function() {
|
|||||||
it("should make a request to requestCallUrl", function() {
|
it("should make a request to requestCallUrl", function() {
|
||||||
sandbox.stub(fakeClient, "requestCallUrl");
|
sandbox.stub(fakeClient, "requestCallUrl");
|
||||||
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
||||||
notifier: notifier,
|
notifications: notifications,
|
||||||
client: fakeClient
|
client: fakeClient
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -363,7 +358,7 @@ describe("loop.panel", function() {
|
|||||||
// Cancel requestCallUrl effect to keep the state pending
|
// Cancel requestCallUrl effect to keep the state pending
|
||||||
fakeClient.requestCallUrl = sandbox.stub();
|
fakeClient.requestCallUrl = sandbox.stub();
|
||||||
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
||||||
notifier: notifier,
|
notifications: notifications,
|
||||||
client: fakeClient
|
client: fakeClient
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -387,14 +382,14 @@ describe("loop.panel", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should reset all pending notifications", function() {
|
it("should reset all pending notifications", function() {
|
||||||
sinon.assert.calledOnce(view.props.notifier.clear);
|
sinon.assert.calledOnce(view.props.notifications.reset);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should display a share button for email", function() {
|
it("should display a share button for email", function() {
|
||||||
fakeClient.requestCallUrl = sandbox.stub();
|
fakeClient.requestCallUrl = sandbox.stub();
|
||||||
var mailto = 'mailto:?subject=email-subject&body=http://example.com';
|
var mailto = 'mailto:?subject=email-subject&body=http://example.com';
|
||||||
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
||||||
notifier: notifier,
|
notifications: notifications,
|
||||||
client: fakeClient
|
client: fakeClient
|
||||||
}));
|
}));
|
||||||
view.setState({pending: false, callUrl: "http://example.com"});
|
view.setState({pending: false, callUrl: "http://example.com"});
|
||||||
@ -407,7 +402,7 @@ describe("loop.panel", function() {
|
|||||||
it("should feature a copy button capable of copying the call url when clicked", function() {
|
it("should feature a copy button capable of copying the call url when clicked", function() {
|
||||||
fakeClient.requestCallUrl = sandbox.stub();
|
fakeClient.requestCallUrl = sandbox.stub();
|
||||||
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
||||||
notifier: notifier,
|
notifications: notifications,
|
||||||
client: fakeClient
|
client: fakeClient
|
||||||
}));
|
}));
|
||||||
view.setState({
|
view.setState({
|
||||||
@ -427,7 +422,7 @@ describe("loop.panel", function() {
|
|||||||
it("should note the call url expiry when the url is copied via button",
|
it("should note the call url expiry when the url is copied via button",
|
||||||
function() {
|
function() {
|
||||||
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
||||||
notifier: notifier,
|
notifications: notifications,
|
||||||
client: fakeClient
|
client: fakeClient
|
||||||
}));
|
}));
|
||||||
view.setState({
|
view.setState({
|
||||||
@ -447,7 +442,7 @@ describe("loop.panel", function() {
|
|||||||
it("should note the call url expiry when the url is emailed",
|
it("should note the call url expiry when the url is emailed",
|
||||||
function() {
|
function() {
|
||||||
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
||||||
notifier: notifier,
|
notifications: notifications,
|
||||||
client: fakeClient
|
client: fakeClient
|
||||||
}));
|
}));
|
||||||
view.setState({
|
view.setState({
|
||||||
@ -468,7 +463,7 @@ describe("loop.panel", function() {
|
|||||||
it("should note the call url expiry when the url is copied manually",
|
it("should note the call url expiry when the url is copied manually",
|
||||||
function() {
|
function() {
|
||||||
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
||||||
notifier: notifier,
|
notifications: notifications,
|
||||||
client: fakeClient
|
client: fakeClient
|
||||||
}));
|
}));
|
||||||
view.setState({
|
view.setState({
|
||||||
@ -490,13 +485,14 @@ describe("loop.panel", function() {
|
|||||||
fakeClient.requestCallUrl = function(_, cb) {
|
fakeClient.requestCallUrl = function(_, cb) {
|
||||||
cb("fake error");
|
cb("fake error");
|
||||||
};
|
};
|
||||||
|
sandbox.stub(notifications, "errorL10n");
|
||||||
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
||||||
notifier: notifier,
|
notifications: notifications,
|
||||||
client: fakeClient
|
client: fakeClient
|
||||||
}));
|
}));
|
||||||
|
|
||||||
sinon.assert.calledOnce(notifier.errorL10n);
|
sinon.assert.calledOnce(notifications.errorL10n);
|
||||||
sinon.assert.calledWithExactly(notifier.errorL10n,
|
sinon.assert.calledWithExactly(notifications.errorL10n,
|
||||||
"unable_retrieve_url");
|
"unable_retrieve_url");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -401,4 +401,58 @@ describe("loop.shared.models", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("NotificationCollection", function() {
|
||||||
|
var collection, notifData, testNotif;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
collection = new sharedModels.NotificationCollection();
|
||||||
|
sandbox.stub(l10n, "get", function(x) {
|
||||||
|
return "translated:" + x;
|
||||||
|
});
|
||||||
|
notifData = {level: "error", message: "plop"};
|
||||||
|
testNotif = new sharedModels.NotificationModel(notifData);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#warn", function() {
|
||||||
|
it("should add a warning notification to the stack", function() {
|
||||||
|
collection.warn("watch out");
|
||||||
|
|
||||||
|
expect(collection).to.have.length.of(1);
|
||||||
|
expect(collection.at(0).get("level")).eql("warning");
|
||||||
|
expect(collection.at(0).get("message")).eql("watch out");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#warnL10n", function() {
|
||||||
|
it("should warn using a l10n string id", function() {
|
||||||
|
collection.warnL10n("fakeId");
|
||||||
|
|
||||||
|
expect(collection).to.have.length.of(1);
|
||||||
|
expect(collection.at(0).get("level")).eql("warning");
|
||||||
|
expect(collection.at(0).get("message")).eql("translated:fakeId");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#error", function() {
|
||||||
|
it("should add an error notification to the stack", function() {
|
||||||
|
collection.error("wrong");
|
||||||
|
|
||||||
|
expect(collection).to.have.length.of(1);
|
||||||
|
expect(collection.at(0).get("level")).eql("error");
|
||||||
|
expect(collection.at(0).get("message")).eql("wrong");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#errorL10n", function() {
|
||||||
|
it("should notify an error using a l10n string id", function() {
|
||||||
|
collection.errorL10n("fakeId");
|
||||||
|
|
||||||
|
expect(collection).to.have.length.of(1);
|
||||||
|
expect(collection.at(0).get("level")).eql("error");
|
||||||
|
expect(collection.at(0).get("message")).eql("translated:fakeId");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -9,17 +9,13 @@ var expect = chai.expect;
|
|||||||
describe("loop.shared.router", function() {
|
describe("loop.shared.router", function() {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var sandbox, notifier;
|
var sandbox, notifications;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
sandbox = sinon.sandbox.create();
|
sandbox = sinon.sandbox.create();
|
||||||
notifier = {
|
notifications = new loop.shared.models.NotificationCollection();
|
||||||
notify: sandbox.spy(),
|
sandbox.stub(notifications, "errorL10n");
|
||||||
warn: sandbox.spy(),
|
sandbox.stub(notifications, "warnL10n");
|
||||||
warnL10n: sandbox.spy(),
|
|
||||||
error: sandbox.spy(),
|
|
||||||
errorL10n: sandbox.spy()
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
@ -36,19 +32,19 @@ describe("loop.shared.router", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("#constructor", function() {
|
describe("#constructor", function() {
|
||||||
it("should require a notifier", function() {
|
it("should require a notifications collection", function() {
|
||||||
expect(function() {
|
expect(function() {
|
||||||
new loop.shared.router.BaseRouter();
|
new loop.shared.router.BaseRouter();
|
||||||
}).to.Throw(Error, /missing required notifier/);
|
}).to.Throw(Error, /missing required notifications/);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("inherited", function() {
|
describe("inherited", function() {
|
||||||
var ExtendedRouter = loop.shared.router.BaseRouter.extend({});
|
var ExtendedRouter = loop.shared.router.BaseRouter.extend({});
|
||||||
|
|
||||||
it("should require a notifier", function() {
|
it("should require a notifications collection", function() {
|
||||||
expect(function() {
|
expect(function() {
|
||||||
new ExtendedRouter();
|
new ExtendedRouter();
|
||||||
}).to.Throw(Error, /missing required notifier/);
|
}).to.Throw(Error, /missing required notifications/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -62,7 +58,7 @@ describe("loop.shared.router", function() {
|
|||||||
template: _.template("<p>plop</p>")
|
template: _.template("<p>plop</p>")
|
||||||
});
|
});
|
||||||
view = new TestView();
|
view = new TestView();
|
||||||
router = new TestRouter({notifier: notifier});
|
router = new TestRouter({notifications: notifications});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#loadView", function() {
|
describe("#loadView", function() {
|
||||||
@ -131,7 +127,7 @@ describe("loop.shared.router", function() {
|
|||||||
};
|
};
|
||||||
router = new TestRouter({
|
router = new TestRouter({
|
||||||
conversation: conversation,
|
conversation: conversation,
|
||||||
notifier: notifier,
|
notifications: notifications,
|
||||||
client: {}
|
client: {}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -141,8 +137,8 @@ describe("loop.shared.router", function() {
|
|||||||
it("should warn the user when .connect() call fails", function() {
|
it("should warn the user when .connect() call fails", function() {
|
||||||
conversation.trigger("session:connection-error");
|
conversation.trigger("session:connection-error");
|
||||||
|
|
||||||
sinon.assert.calledOnce(notifier.errorL10n);
|
sinon.assert.calledOnce(notifications.errorL10n);
|
||||||
sinon.assert.calledWithExactly(notifier.errorL10n, sinon.match.string);
|
sinon.assert.calledWithExactly(notifications.errorL10n, sinon.match.string);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should invoke endCall()", function() {
|
it("should invoke endCall()", function() {
|
||||||
@ -163,8 +159,8 @@ describe("loop.shared.router", function() {
|
|||||||
it("should warn the user when peer hangs up", function() {
|
it("should warn the user when peer hangs up", function() {
|
||||||
conversation.trigger("session:peer-hungup");
|
conversation.trigger("session:peer-hungup");
|
||||||
|
|
||||||
sinon.assert.calledOnce(notifier.warnL10n);
|
sinon.assert.calledOnce(notifications.warnL10n);
|
||||||
sinon.assert.calledWithExactly(notifier.warnL10n,
|
sinon.assert.calledWithExactly(notifications.warnL10n,
|
||||||
"peer_ended_conversation2");
|
"peer_ended_conversation2");
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -178,8 +174,8 @@ describe("loop.shared.router", function() {
|
|||||||
it("should warn the user when network disconnects", function() {
|
it("should warn the user when network disconnects", function() {
|
||||||
conversation.trigger("session:network-disconnected");
|
conversation.trigger("session:network-disconnected");
|
||||||
|
|
||||||
sinon.assert.calledOnce(notifier.warnL10n);
|
sinon.assert.calledOnce(notifications.warnL10n);
|
||||||
sinon.assert.calledWithExactly(notifier.warnL10n,
|
sinon.assert.calledWithExactly(notifications.warnL10n,
|
||||||
"network_disconnected");
|
"network_disconnected");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -597,195 +597,28 @@ describe("loop.shared.views", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("NotificationView", function() {
|
|
||||||
var collection, model, view;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
$("#fixtures").append('<div id="test-notif"></div>');
|
|
||||||
model = new sharedModels.NotificationModel({
|
|
||||||
level: "error",
|
|
||||||
message: "plop"
|
|
||||||
});
|
|
||||||
collection = new sharedModels.NotificationCollection([model]);
|
|
||||||
view = new sharedViews.NotificationView({
|
|
||||||
el: $("#test-notif"),
|
|
||||||
collection: collection,
|
|
||||||
model: model
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#dismiss", function() {
|
|
||||||
it("should automatically dismiss notification after 500ms", function() {
|
|
||||||
view.render().dismiss({preventDefault: sandbox.spy()});
|
|
||||||
|
|
||||||
expect(view.$(".message").text()).eql("plop");
|
|
||||||
|
|
||||||
sandbox.clock.tick(500);
|
|
||||||
|
|
||||||
expect(collection).to.have.length.of(0);
|
|
||||||
expect($("#test-notif").html()).eql(undefined);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#render", function() {
|
|
||||||
it("should render template with model attribute values", function() {
|
|
||||||
view.render();
|
|
||||||
|
|
||||||
expect(view.$(".message").text()).eql("plop");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("NotificationListView", function() {
|
describe("NotificationListView", function() {
|
||||||
var coll, notifData, testNotif;
|
var coll, view, testNotif;
|
||||||
|
|
||||||
|
function mountTestComponent(props) {
|
||||||
|
return TestUtils.renderIntoDocument(sharedViews.NotificationListView(props));
|
||||||
|
}
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
sandbox.stub(l10n, "get", function(x) {
|
sandbox.stub(l10n, "get", function(x) {
|
||||||
return "translated:" + x;
|
return "translated:" + x;
|
||||||
});
|
});
|
||||||
notifData = {level: "error", message: "plop"};
|
|
||||||
testNotif = new sharedModels.NotificationModel(notifData);
|
|
||||||
coll = new sharedModels.NotificationCollection();
|
coll = new sharedModels.NotificationCollection();
|
||||||
|
view = mountTestComponent({notifications: coll});
|
||||||
|
testNotif = {level: "warning", message: "foo"};
|
||||||
|
sinon.spy(view, "render");
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#initialize", function() {
|
afterEach(function() {
|
||||||
it("should accept a collection option", function() {
|
view.render.restore();
|
||||||
var view = new sharedViews.NotificationListView({collection: coll});
|
|
||||||
|
|
||||||
expect(view.collection).to.be.an.instanceOf(
|
|
||||||
sharedModels.NotificationCollection);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should set a default collection when none is passed", function() {
|
|
||||||
var view = new sharedViews.NotificationListView();
|
|
||||||
|
|
||||||
expect(view.collection).to.be.an.instanceOf(
|
|
||||||
sharedModels.NotificationCollection);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#clear", function() {
|
|
||||||
it("should clear all notifications from the collection", function() {
|
|
||||||
var view = new sharedViews.NotificationListView();
|
|
||||||
view.notify(testNotif);
|
|
||||||
|
|
||||||
view.clear();
|
|
||||||
|
|
||||||
expect(coll).to.have.length.of(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#notify", function() {
|
|
||||||
var view;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
view = new sharedViews.NotificationListView({collection: coll});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("adds a new notification to the stack", function() {
|
|
||||||
it("using a plain object", function() {
|
|
||||||
view.notify(notifData);
|
|
||||||
|
|
||||||
expect(coll).to.have.length.of(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("using a NotificationModel instance", function() {
|
|
||||||
view.notify(testNotif);
|
|
||||||
|
|
||||||
expect(coll).to.have.length.of(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#notifyL10n", function() {
|
|
||||||
var view;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
view = new sharedViews.NotificationListView({collection: coll});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should translate a message string identifier", function() {
|
|
||||||
view.notifyL10n("fakeId", "warning");
|
|
||||||
|
|
||||||
sinon.assert.calledOnce(l10n.get);
|
|
||||||
sinon.assert.calledWithExactly(l10n.get, "fakeId");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should notify end user with the provided message", function() {
|
|
||||||
sandbox.stub(view, "notify");
|
|
||||||
|
|
||||||
view.notifyL10n("fakeId", "warning");
|
|
||||||
|
|
||||||
sinon.assert.calledOnce(view.notify);
|
|
||||||
sinon.assert.calledWithExactly(view.notify, {
|
|
||||||
message: "translated:fakeId",
|
|
||||||
level: "warning"
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#warn", function() {
|
|
||||||
it("should add a warning notification to the stack", function() {
|
|
||||||
var view = new sharedViews.NotificationListView({collection: coll});
|
|
||||||
|
|
||||||
view.warn("watch out");
|
|
||||||
|
|
||||||
expect(coll).to.have.length.of(1);
|
|
||||||
expect(coll.at(0).get("level")).eql("warning");
|
|
||||||
expect(coll.at(0).get("message")).eql("watch out");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#warnL10n", function() {
|
|
||||||
it("should warn using a l10n string id", function() {
|
|
||||||
var view = new sharedViews.NotificationListView({collection: coll});
|
|
||||||
sandbox.stub(view, "notify");
|
|
||||||
|
|
||||||
view.warnL10n("fakeId");
|
|
||||||
|
|
||||||
sinon.assert.called(view.notify);
|
|
||||||
sinon.assert.calledWithExactly(view.notify, {
|
|
||||||
message: "translated:fakeId",
|
|
||||||
level: "warning"
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#error", function() {
|
|
||||||
it("should add an error notification to the stack", function() {
|
|
||||||
var view = new sharedViews.NotificationListView({collection: coll});
|
|
||||||
|
|
||||||
view.error("wrong");
|
|
||||||
|
|
||||||
expect(coll).to.have.length.of(1);
|
|
||||||
expect(coll.at(0).get("level")).eql("error");
|
|
||||||
expect(coll.at(0).get("message")).eql("wrong");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#errorL10n", function() {
|
|
||||||
it("should notify an error using a l10n string id", function() {
|
|
||||||
var view = new sharedViews.NotificationListView({collection: coll});
|
|
||||||
sandbox.stub(view, "notify");
|
|
||||||
|
|
||||||
view.errorL10n("fakeId");
|
|
||||||
|
|
||||||
sinon.assert.called(view.notify);
|
|
||||||
sinon.assert.calledWithExactly(view.notify, {
|
|
||||||
message: "translated:fakeId",
|
|
||||||
level: "error"
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Collection events", function() {
|
describe("Collection events", function() {
|
||||||
var view;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
sandbox.stub(sharedViews.NotificationListView.prototype, "render");
|
|
||||||
view = new sharedViews.NotificationListView({collection: coll});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render when a notification is added to the collection",
|
it("should render when a notification is added to the collection",
|
||||||
function() {
|
function() {
|
||||||
coll.add(testNotif);
|
coll.add(testNotif);
|
||||||
@ -798,7 +631,7 @@ describe("loop.shared.views", function() {
|
|||||||
coll.add(testNotif);
|
coll.add(testNotif);
|
||||||
coll.remove(testNotif);
|
coll.remove(testNotif);
|
||||||
|
|
||||||
sinon.assert.calledTwice(view.render);
|
sinon.assert.calledOnce(view.render);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render when the collection is reset", function() {
|
it("should render when the collection is reset", function() {
|
||||||
|
@ -13,20 +13,14 @@ describe("loop.webapp", function() {
|
|||||||
var sharedModels = loop.shared.models,
|
var sharedModels = loop.shared.models,
|
||||||
sharedViews = loop.shared.views,
|
sharedViews = loop.shared.views,
|
||||||
sandbox,
|
sandbox,
|
||||||
notifier;
|
notifications;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
sandbox = sinon.sandbox.create();
|
sandbox = sinon.sandbox.create();
|
||||||
// conversation#outgoing sets timers, so we need to use fake ones
|
// conversation#outgoing sets timers, so we need to use fake ones
|
||||||
// to prevent random failures.
|
// to prevent random failures.
|
||||||
sandbox.useFakeTimers();
|
sandbox.useFakeTimers();
|
||||||
notifier = {
|
notifications = new sharedModels.NotificationCollection();
|
||||||
notify: sandbox.spy(),
|
|
||||||
warn: sandbox.spy(),
|
|
||||||
warnL10n: sandbox.spy(),
|
|
||||||
error: sandbox.spy(),
|
|
||||||
errorL10n: sandbox.spy(),
|
|
||||||
};
|
|
||||||
loop.config.pendingCallTimeout = 1000;
|
loop.config.pendingCallTimeout = 1000;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -88,7 +82,7 @@ describe("loop.webapp", function() {
|
|||||||
helper: {},
|
helper: {},
|
||||||
client: client,
|
client: client,
|
||||||
conversation: conversation,
|
conversation: conversation,
|
||||||
notifier: notifier
|
notifications: notifications
|
||||||
});
|
});
|
||||||
sandbox.stub(router, "loadView");
|
sandbox.stub(router, "loadView");
|
||||||
sandbox.stub(router, "navigate");
|
sandbox.stub(router, "navigate");
|
||||||
@ -107,10 +101,11 @@ describe("loop.webapp", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should notify the user if session token is missing", function() {
|
it("should notify the user if session token is missing", function() {
|
||||||
|
sandbox.stub(notifications, "errorL10n");
|
||||||
router.startCall();
|
router.startCall();
|
||||||
|
|
||||||
sinon.assert.calledOnce(notifier.errorL10n);
|
sinon.assert.calledOnce(notifications.errorL10n);
|
||||||
sinon.assert.calledWithExactly(notifier.errorL10n,
|
sinon.assert.calledWithExactly(notifications.errorL10n,
|
||||||
"missing_conversation_info");
|
"missing_conversation_info");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -194,13 +189,14 @@ describe("loop.webapp", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should display an error", function() {
|
it("should display an error", function(done) {
|
||||||
|
sandbox.stub(notifications, "errorL10n");
|
||||||
router._setupWebSocketAndCallView();
|
router._setupWebSocketAndCallView();
|
||||||
|
|
||||||
promise.then(function() {
|
promise.then(function() {
|
||||||
}, function () {
|
}, function () {
|
||||||
sinon.assert.calledOnce(router._notifier.errorL10n);
|
sinon.assert.calledOnce(router._notifications.errorL10n);
|
||||||
sinon.assert.calledWithExactly(router._notifier.errorL10n,
|
sinon.assert.calledWithExactly(router._notifications.errorL10n,
|
||||||
"cannot_start_call_session_not_ready");
|
"cannot_start_call_session_not_ready");
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -242,13 +238,15 @@ describe("loop.webapp", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should display an error message", function() {
|
it("should display an error message", function() {
|
||||||
|
sandbox.stub(notifications, "errorL10n");
|
||||||
|
|
||||||
router._websocket.trigger("progress", {
|
router._websocket.trigger("progress", {
|
||||||
state: "terminated",
|
state: "terminated",
|
||||||
reason: "reject"
|
reason: "reject"
|
||||||
});
|
});
|
||||||
|
|
||||||
sinon.assert.calledOnce(router._notifier.errorL10n);
|
sinon.assert.calledOnce(router._notifications.errorL10n);
|
||||||
sinon.assert.calledWithExactly(router._notifier.errorL10n,
|
sinon.assert.calledWithExactly(router._notifications.errorL10n,
|
||||||
"call_timeout_notification_text");
|
"call_timeout_notification_text");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -472,9 +470,10 @@ describe("loop.webapp", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should display an error", function() {
|
it("should display an error", function() {
|
||||||
|
sandbox.stub(notifications, "errorL10n");
|
||||||
conversation.setupOutgoingCall();
|
conversation.setupOutgoingCall();
|
||||||
|
|
||||||
sinon.assert.calledOnce(notifier.errorL10n);
|
sinon.assert.calledOnce(notifications.errorL10n);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -513,10 +512,11 @@ describe("loop.webapp", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should notify the user on any other error", function() {
|
it("should notify the user on any other error", function() {
|
||||||
|
sandbox.stub(notifications, "errorL10n");
|
||||||
client.requestCallInfo.callsArgWith(2, {errno: 104});
|
client.requestCallInfo.callsArgWith(2, {errno: 104});
|
||||||
conversation.setupOutgoingCall();
|
conversation.setupOutgoingCall();
|
||||||
|
|
||||||
sinon.assert.calledOnce(notifier.errorL10n);
|
sinon.assert.calledOnce(notifications.errorL10n);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should call outgoing on the conversation model when details " +
|
it("should call outgoing on the conversation model when details " +
|
||||||
@ -565,7 +565,7 @@ describe("loop.webapp", function() {
|
|||||||
view = React.addons.TestUtils.renderIntoDocument(
|
view = React.addons.TestUtils.renderIntoDocument(
|
||||||
loop.webapp.StartConversationView({
|
loop.webapp.StartConversationView({
|
||||||
model: conversation,
|
model: conversation,
|
||||||
notifier: notifier,
|
notifications: notifications,
|
||||||
client: standaloneClientStub
|
client: standaloneClientStub
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -657,7 +657,7 @@ describe("loop.webapp", function() {
|
|||||||
view = React.addons.TestUtils.renderIntoDocument(
|
view = React.addons.TestUtils.renderIntoDocument(
|
||||||
loop.webapp.StartConversationView({
|
loop.webapp.StartConversationView({
|
||||||
model: conversation,
|
model: conversation,
|
||||||
notifier: notifier,
|
notifications: notifications,
|
||||||
client: {requestCallUrlInfo: requestCallUrlInfo}
|
client: {requestCallUrlInfo: requestCallUrlInfo}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -678,10 +678,11 @@ describe("loop.webapp", function() {
|
|||||||
|
|
||||||
it("should trigger a notication when a session:error model event is " +
|
it("should trigger a notication when a session:error model event is " +
|
||||||
" received", function() {
|
" received", function() {
|
||||||
|
sandbox.stub(notifications, "errorL10n");
|
||||||
conversation.trigger("session:error", "tech error");
|
conversation.trigger("session:error", "tech error");
|
||||||
|
|
||||||
sinon.assert.calledOnce(notifier.errorL10n);
|
sinon.assert.calledOnce(notifications.errorL10n);
|
||||||
sinon.assert.calledWithExactly(notifier.errorL10n,
|
sinon.assert.calledWithExactly(notifications.errorL10n,
|
||||||
"unable_retrieve_call_info");
|
"unable_retrieve_call_info");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -714,7 +715,7 @@ describe("loop.webapp", function() {
|
|||||||
view = React.addons.TestUtils.renderIntoDocument(
|
view = React.addons.TestUtils.renderIntoDocument(
|
||||||
loop.webapp.StartConversationView({
|
loop.webapp.StartConversationView({
|
||||||
model: conversation,
|
model: conversation,
|
||||||
notifier: notifier,
|
notifications: notifications,
|
||||||
client: {requestCallUrlInfo: requestCallUrlInfo}
|
client: {requestCallUrlInfo: requestCallUrlInfo}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -730,7 +731,7 @@ describe("loop.webapp", function() {
|
|||||||
view = React.addons.TestUtils.renderIntoDocument(
|
view = React.addons.TestUtils.renderIntoDocument(
|
||||||
loop.webapp.StartConversationView({
|
loop.webapp.StartConversationView({
|
||||||
model: conversation,
|
model: conversation,
|
||||||
notifier: notifier,
|
notifications: notifications,
|
||||||
client: {requestCallUrlInfo: requestCallUrlInfo}
|
client: {requestCallUrlInfo: requestCallUrlInfo}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -58,7 +58,9 @@
|
|||||||
});
|
});
|
||||||
mockConversationModel.startSession = noop;
|
mockConversationModel.startSession = noop;
|
||||||
|
|
||||||
var mockNotifier = {};
|
var notifications = new loop.shared.models.NotificationCollection();
|
||||||
|
var errNotifications = new loop.shared.models.NotificationCollection();
|
||||||
|
errNotifications.error("Error!");
|
||||||
|
|
||||||
var Example = React.createClass({displayName: 'Example',
|
var Example = React.createClass({displayName: 'Example',
|
||||||
render: function() {
|
render: function() {
|
||||||
@ -117,11 +119,14 @@
|
|||||||
React.DOM.strong(null, "Note:"), " 332px wide."
|
React.DOM.strong(null, "Note:"), " 332px wide."
|
||||||
),
|
),
|
||||||
Example({summary: "Call URL retrieved", dashed: "true", style: {width: "332px"}},
|
Example({summary: "Call URL retrieved", dashed: "true", style: {width: "332px"}},
|
||||||
PanelView({client: mockClient, notifier: mockNotifier,
|
PanelView({client: mockClient, notifications: notifications,
|
||||||
callUrl: "http://invalid.example.url/"})
|
callUrl: "http://invalid.example.url/"})
|
||||||
),
|
),
|
||||||
Example({summary: "Pending call url retrieval", dashed: "true", style: {width: "332px"}},
|
Example({summary: "Pending call url retrieval", dashed: "true", style: {width: "332px"}},
|
||||||
PanelView({client: mockClient, notifier: mockNotifier})
|
PanelView({client: mockClient, notifications: notifications})
|
||||||
|
),
|
||||||
|
Example({summary: "Error Notification", dashed: "true", style: {width: "332px"}},
|
||||||
|
PanelView({client: mockClient, notifications: errNotifications})
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -192,7 +197,7 @@
|
|||||||
React.DOM.div({className: "standalone"},
|
React.DOM.div({className: "standalone"},
|
||||||
StartConversationView({model: mockConversationModel,
|
StartConversationView({model: mockConversationModel,
|
||||||
client: mockClient,
|
client: mockClient,
|
||||||
notifier: mockNotifier,
|
notifications: notifications,
|
||||||
showCallOptionsMenu: true})
|
showCallOptionsMenu: true})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -58,7 +58,9 @@
|
|||||||
});
|
});
|
||||||
mockConversationModel.startSession = noop;
|
mockConversationModel.startSession = noop;
|
||||||
|
|
||||||
var mockNotifier = {};
|
var notifications = new loop.shared.models.NotificationCollection();
|
||||||
|
var errNotifications = new loop.shared.models.NotificationCollection();
|
||||||
|
errNotifications.error("Error!");
|
||||||
|
|
||||||
var Example = React.createClass({
|
var Example = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
@ -117,11 +119,14 @@
|
|||||||
<strong>Note:</strong> 332px wide.
|
<strong>Note:</strong> 332px wide.
|
||||||
</p>
|
</p>
|
||||||
<Example summary="Call URL retrieved" dashed="true" style={{width: "332px"}}>
|
<Example summary="Call URL retrieved" dashed="true" style={{width: "332px"}}>
|
||||||
<PanelView client={mockClient} notifier={mockNotifier}
|
<PanelView client={mockClient} notifications={notifications}
|
||||||
callUrl="http://invalid.example.url/" />
|
callUrl="http://invalid.example.url/" />
|
||||||
</Example>
|
</Example>
|
||||||
<Example summary="Pending call url retrieval" dashed="true" style={{width: "332px"}}>
|
<Example summary="Pending call url retrieval" dashed="true" style={{width: "332px"}}>
|
||||||
<PanelView client={mockClient} notifier={mockNotifier} />
|
<PanelView client={mockClient} notifications={notifications} />
|
||||||
|
</Example>
|
||||||
|
<Example summary="Error Notification" dashed="true" style={{width: "332px"}}>
|
||||||
|
<PanelView client={mockClient} notifications={errNotifications}/>
|
||||||
</Example>
|
</Example>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
@ -192,7 +197,7 @@
|
|||||||
<div className="standalone">
|
<div className="standalone">
|
||||||
<StartConversationView model={mockConversationModel}
|
<StartConversationView model={mockConversationModel}
|
||||||
client={mockClient}
|
client={mockClient}
|
||||||
notifier={mockNotifier}
|
notifications={notifications}
|
||||||
showCallOptionsMenu={true} />
|
showCallOptionsMenu={true} />
|
||||||
</div>
|
</div>
|
||||||
</Example>
|
</Example>
|
||||||
|
@ -77,20 +77,20 @@
|
|||||||
<h1 class="titleText showPrivate">&aboutPrivateBrowsing.title;</h1>
|
<h1 class="titleText showPrivate">&aboutPrivateBrowsing.title;</h1>
|
||||||
<h1 class="titleText showNormal">&aboutPrivateBrowsing.title.normal;</h1>
|
<h1 class="titleText showNormal">&aboutPrivateBrowsing.title.normal;</h1>
|
||||||
|
|
||||||
<p class="showPrivate">&aboutPrivateBrowsing.subtitle;</p>
|
<p class="subtitleText showPrivate">&aboutPrivateBrowsing.subtitle;</p>
|
||||||
<p class="showNormal">&aboutPrivateBrowsing.subtitle.normal;</p>
|
<p class="subtitleText showNormal">&aboutPrivateBrowsing.subtitle.normal;</p>
|
||||||
|
|
||||||
<p>&aboutPrivateBrowsing.description;</p>
|
<p class="descriptionText">&aboutPrivateBrowsing.description;</p>
|
||||||
|
|
||||||
<p class="showNormal">&aboutPrivateBrowsing.notPrivate;</p>
|
<p class="notPrivateText showNormal">&aboutPrivateBrowsing.notPrivate;</p>
|
||||||
|
|
||||||
<button xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
<button xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||||
class="showNormal"
|
class="openPrivate showNormal"
|
||||||
label="&privatebrowsingpage.openPrivateWindow.label;"
|
label="&privatebrowsingpage.openPrivateWindow.label;"
|
||||||
accesskey="&privatebrowsingpage.openPrivateWindow.accesskey;"
|
accesskey="&privatebrowsingpage.openPrivateWindow.accesskey;"
|
||||||
oncommand="openPrivateWindow();"/>
|
oncommand="openPrivateWindow();"/>
|
||||||
<div class="showPrivate">
|
<div class="showPrivate">
|
||||||
<p>&aboutPrivateBrowsing.moreInfo;</p>
|
<p class="moreInfoText">&aboutPrivateBrowsing.moreInfo;</p>
|
||||||
<p><a id="learnMore" target="_blank">&aboutPrivateBrowsing.learnMore;</a></p>
|
<p><a id="learnMore" target="_blank">&aboutPrivateBrowsing.learnMore;</a></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,19 +4,21 @@
|
|||||||
// Tests devtools API
|
// Tests devtools API
|
||||||
|
|
||||||
const Cu = Components.utils;
|
const Cu = Components.utils;
|
||||||
const toolId = "test-tool";
|
const toolId1 = "test-tool-1";
|
||||||
|
const toolId2 = "test-tool-2";
|
||||||
|
|
||||||
let tempScope = {};
|
let tempScope = {};
|
||||||
Cu.import("resource://gre/modules/devtools/event-emitter.js", tempScope);
|
Cu.import("resource://gre/modules/devtools/event-emitter.js", tempScope);
|
||||||
let EventEmitter = tempScope.EventEmitter;
|
let EventEmitter = tempScope.EventEmitter;
|
||||||
|
|
||||||
function test() {
|
function test() {
|
||||||
addTab("about:blank").then(runTests);
|
addTab("about:blank").then(runTests1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function runTests(aTab) {
|
// Test scenario 1: the tool definition build method returns a promise.
|
||||||
|
function runTests1(aTab) {
|
||||||
let toolDefinition = {
|
let toolDefinition = {
|
||||||
id: toolId,
|
id: toolId1,
|
||||||
isTargetSupported: function() true,
|
isTargetSupported: function() true,
|
||||||
visibilityswitch: "devtools.test-tool.enabled",
|
visibilityswitch: "devtools.test-tool.enabled",
|
||||||
url: "about:blank",
|
url: "about:blank",
|
||||||
@ -28,40 +30,131 @@ function runTests(aTab) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ok(gDevTools, "gDevTools exists");
|
ok(gDevTools, "gDevTools exists");
|
||||||
is(gDevTools.getToolDefinitionMap().has(toolId), false,
|
is(gDevTools.getToolDefinitionMap().has(toolId1), false,
|
||||||
"The tool is not registered");
|
"The tool is not registered");
|
||||||
|
|
||||||
gDevTools.registerTool(toolDefinition);
|
gDevTools.registerTool(toolDefinition);
|
||||||
is(gDevTools.getToolDefinitionMap().has(toolId), true,
|
is(gDevTools.getToolDefinitionMap().has(toolId1), true,
|
||||||
"The tool is registered");
|
"The tool is registered");
|
||||||
|
|
||||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||||
|
|
||||||
gDevTools.showToolbox(target, toolId).then(function (toolbox) {
|
let events = {};
|
||||||
|
|
||||||
|
// Check events on the gDevTools and toolbox objects.
|
||||||
|
gDevTools.once(toolId1 + "-init", (event, toolbox, iframe) => {
|
||||||
|
ok(iframe, "iframe argument available");
|
||||||
|
|
||||||
|
toolbox.once(toolId1 + "-init", (event, iframe) => {
|
||||||
|
ok(iframe, "iframe argument available");
|
||||||
|
events["init"] = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
gDevTools.once(toolId1 + "-ready", (event, toolbox, panel) => {
|
||||||
|
ok(panel, "panel argument available");
|
||||||
|
|
||||||
|
toolbox.once(toolId1 + "-ready", (event, panel) => {
|
||||||
|
ok(panel, "panel argument available");
|
||||||
|
events["ready"] = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
gDevTools.showToolbox(target, toolId1).then(function (toolbox) {
|
||||||
is(toolbox.target, target, "toolbox target is correct");
|
is(toolbox.target, target, "toolbox target is correct");
|
||||||
is(toolbox._host.hostTab, gBrowser.selectedTab, "toolbox host is correct");
|
is(toolbox._host.hostTab, gBrowser.selectedTab, "toolbox host is correct");
|
||||||
|
|
||||||
|
ok(events["init"], "init event fired");
|
||||||
|
ok(events["ready"], "ready event fired");
|
||||||
|
|
||||||
|
gDevTools.unregisterTool(toolId1);
|
||||||
|
|
||||||
|
runTests2();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test scenario 2: the tool definition build method returns panel instance.
|
||||||
|
function runTests2() {
|
||||||
|
let toolDefinition = {
|
||||||
|
id: toolId2,
|
||||||
|
isTargetSupported: function() true,
|
||||||
|
visibilityswitch: "devtools.test-tool.enabled",
|
||||||
|
url: "about:blank",
|
||||||
|
label: "someLabel",
|
||||||
|
build: function(iframeWindow, toolbox) {
|
||||||
|
return new DevToolPanel(iframeWindow, toolbox);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
is(gDevTools.getToolDefinitionMap().has(toolId2), false,
|
||||||
|
"The tool is not registered");
|
||||||
|
|
||||||
|
gDevTools.registerTool(toolDefinition);
|
||||||
|
is(gDevTools.getToolDefinitionMap().has(toolId2), true,
|
||||||
|
"The tool is registered");
|
||||||
|
|
||||||
|
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||||
|
|
||||||
|
let events = {};
|
||||||
|
|
||||||
|
// Check events on the gDevTools and toolbox objects.
|
||||||
|
gDevTools.once(toolId2 + "-init", (event, toolbox, iframe) => {
|
||||||
|
ok(iframe, "iframe argument available");
|
||||||
|
|
||||||
|
toolbox.once(toolId2 + "-init", (event, iframe) => {
|
||||||
|
ok(iframe, "iframe argument available");
|
||||||
|
events["init"] = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
gDevTools.once(toolId2 + "-build", (event, toolbox, panel, iframe) => {
|
||||||
|
ok(panel, "panel argument available");
|
||||||
|
|
||||||
|
toolbox.once(toolId2 + "-build", (event, panel, iframe) => {
|
||||||
|
ok(panel, "panel argument available");
|
||||||
|
events["build"] = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
gDevTools.once(toolId2 + "-ready", (event, toolbox, panel) => {
|
||||||
|
ok(panel, "panel argument available");
|
||||||
|
|
||||||
|
toolbox.once(toolId2 + "-ready", (event, panel) => {
|
||||||
|
ok(panel, "panel argument available");
|
||||||
|
events["ready"] = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
gDevTools.showToolbox(target, toolId2).then(function (toolbox) {
|
||||||
|
is(toolbox.target, target, "toolbox target is correct");
|
||||||
|
is(toolbox._host.hostTab, gBrowser.selectedTab, "toolbox host is correct");
|
||||||
|
|
||||||
|
ok(events["init"], "init event fired");
|
||||||
|
ok(events["build"], "build event fired");
|
||||||
|
ok(events["ready"], "ready event fired");
|
||||||
|
|
||||||
continueTests(toolbox);
|
continueTests(toolbox);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function continueTests(toolbox, panel) {
|
function continueTests(toolbox, panel) {
|
||||||
ok(toolbox.getCurrentPanel(), "panel value is correct");
|
ok(toolbox.getCurrentPanel(), "panel value is correct");
|
||||||
is(toolbox.currentToolId, toolId, "toolbox _currentToolId is correct");
|
is(toolbox.currentToolId, toolId2, "toolbox _currentToolId is correct");
|
||||||
|
|
||||||
ok(!toolbox.doc.getElementById("toolbox-tab-" + toolId).hasAttribute("icon-invertable"),
|
ok(!toolbox.doc.getElementById("toolbox-tab-" + toolId2).hasAttribute("icon-invertable"),
|
||||||
"The tool tab does not have the invertable attribute");
|
"The tool tab does not have the invertable attribute");
|
||||||
|
|
||||||
ok(toolbox.doc.getElementById("toolbox-tab-inspector").hasAttribute("icon-invertable"),
|
ok(toolbox.doc.getElementById("toolbox-tab-inspector").hasAttribute("icon-invertable"),
|
||||||
"The builtin tool tabs do have the invertable attribute");
|
"The builtin tool tabs do have the invertable attribute");
|
||||||
|
|
||||||
let toolDefinitions = gDevTools.getToolDefinitionMap();
|
let toolDefinitions = gDevTools.getToolDefinitionMap();
|
||||||
is(toolDefinitions.has(toolId), true, "The tool is in gDevTools");
|
is(toolDefinitions.has(toolId2), true, "The tool is in gDevTools");
|
||||||
|
|
||||||
let toolDefinition = toolDefinitions.get(toolId);
|
let toolDefinition = toolDefinitions.get(toolId2);
|
||||||
is(toolDefinition.id, toolId, "toolDefinition id is correct");
|
is(toolDefinition.id, toolId2, "toolDefinition id is correct");
|
||||||
|
|
||||||
gDevTools.unregisterTool(toolId);
|
gDevTools.unregisterTool(toolId2);
|
||||||
is(gDevTools.getToolDefinitionMap().has(toolId), false,
|
is(gDevTools.getToolDefinitionMap().has(toolId2), false,
|
||||||
"The tool is no longer registered");
|
"The tool is no longer registered");
|
||||||
|
|
||||||
// Wait for unregisterTool to select the next tool before
|
// Wait for unregisterTool to select the next tool before
|
||||||
|
@ -1,46 +1,60 @@
|
|||||||
/* Any copyright is dedicated to the Public Domain.
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
// Tests that changing preferences in the options panel updates the prefs
|
||||||
|
// and toggles appropriate things in the toolbox.
|
||||||
|
|
||||||
let doc = null, toolbox = null, panelWin = null, modifiedPrefs = [];
|
let doc = null, toolbox = null, panelWin = null, modifiedPrefs = [];
|
||||||
|
|
||||||
function test() {
|
let test = asyncTest(function*() {
|
||||||
const URL = "data:text/html;charset=utf8,test for dynamically registering and unregistering tools";
|
const URL = "data:text/html;charset=utf8,test for dynamically registering and unregistering tools";
|
||||||
Task.spawn(function* () {
|
registerNewTool();
|
||||||
let tab = yield addTab(URL);
|
let tab = yield addTab(URL);
|
||||||
let target = TargetFactory.forTab(tab);
|
let target = TargetFactory.forTab(tab);
|
||||||
let toolbox = yield gDevTools.showToolbox(target);
|
toolbox = yield gDevTools.showToolbox(target);
|
||||||
yield testSelectTool(toolbox);
|
|
||||||
yield testOptionsShortcut();
|
|
||||||
yield testOptions();
|
|
||||||
yield testToggleTools();
|
|
||||||
}).then(cleanup, errorHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testSelectTool(aToolbox) {
|
|
||||||
let deferred = promise.defer();
|
|
||||||
|
|
||||||
toolbox = aToolbox;
|
|
||||||
doc = toolbox.doc;
|
doc = toolbox.doc;
|
||||||
toolbox.once("options-selected", () => {
|
yield testSelectTool();
|
||||||
ok(true, "Toolbox selected via selectTool method");
|
yield testOptionsShortcut();
|
||||||
deferred.resolve();
|
yield testOptions();
|
||||||
});
|
yield testToggleTools();
|
||||||
toolbox.selectTool("options");
|
yield cleanup();
|
||||||
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
function registerNewTool() {
|
||||||
|
let toolDefinition = {
|
||||||
|
id: "test-tool",
|
||||||
|
isTargetSupported: function() true,
|
||||||
|
visibilityswitch: "devtools.test-tool.enabled",
|
||||||
|
url: "about:blank",
|
||||||
|
label: "someLabel"
|
||||||
|
};
|
||||||
|
|
||||||
|
ok(gDevTools, "gDevTools exists");
|
||||||
|
ok(!gDevTools.getToolDefinitionMap().has("test-tool"),
|
||||||
|
"The tool is not registered");
|
||||||
|
|
||||||
|
gDevTools.registerTool(toolDefinition);
|
||||||
|
ok(gDevTools.getToolDefinitionMap().has("test-tool"),
|
||||||
|
"The tool is registered");
|
||||||
}
|
}
|
||||||
|
|
||||||
function testOptionsShortcut() {
|
function* testSelectTool() {
|
||||||
let deferred = promise.defer();
|
info ("Checking to make sure that the options panel can be selected.");
|
||||||
|
|
||||||
toolbox.selectTool("webconsole")
|
let onceSelected = toolbox.once("options-selected");
|
||||||
|
toolbox.selectTool("options");
|
||||||
|
yield onceSelected;
|
||||||
|
ok(true, "Toolbox selected via selectTool method");
|
||||||
|
}
|
||||||
|
|
||||||
|
function* testOptionsShortcut() {
|
||||||
|
info ("Selecting another tool, then reselecting options panel with keyboard.");
|
||||||
|
|
||||||
|
yield toolbox.selectTool("webconsole")
|
||||||
.then(() => synthesizeKeyFromKeyTag("toolbox-options-key", doc))
|
.then(() => synthesizeKeyFromKeyTag("toolbox-options-key", doc))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
ok(true, "Toolbox selected via shortcut key");
|
ok(true, "Toolbox selected via shortcut key");
|
||||||
deferred.resolve();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function* testOptions() {
|
function* testOptions() {
|
||||||
@ -103,7 +117,7 @@ function* testMenuList(menulist) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function testMouseClick(node, prefValue) {
|
function* testMouseClick(node, prefValue) {
|
||||||
let deferred = promise.defer();
|
let deferred = promise.defer();
|
||||||
|
|
||||||
let pref = node.getAttribute("data-pref");
|
let pref = node.getAttribute("data-pref");
|
||||||
@ -127,14 +141,17 @@ function testMouseClick(node, prefValue) {
|
|||||||
EventUtils.synthesizeMouseAtCenter(node, {}, panelWin);
|
EventUtils.synthesizeMouseAtCenter(node, {}, panelWin);
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
yield deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
function testToggleTools() {
|
function* testToggleTools() {
|
||||||
let toolNodes = panelWin.document.querySelectorAll("#default-tools-box > checkbox:not([unsupported])");
|
let toolNodes = panelWin.document.querySelectorAll("#default-tools-box > checkbox:not([unsupported]), #additional-tools-box > checkbox:not([unsupported])");
|
||||||
let enabledTools = Array.prototype.filter.call(toolNodes, node => node.checked);
|
let enabledTools = [...toolNodes].filter(node => node.checked);
|
||||||
|
|
||||||
|
let toggleableTools = gDevTools.getDefaultTools().filter(tool => {
|
||||||
|
return tool.visibilityswitch;
|
||||||
|
}).concat(gDevTools.getAdditionalTools());
|
||||||
|
|
||||||
let toggleableTools = gDevTools.getDefaultTools().filter(tool => tool.visibilityswitch);
|
|
||||||
for (let node of toolNodes) {
|
for (let node of toolNodes) {
|
||||||
let id = node.getAttribute("id");
|
let id = node.getAttribute("id");
|
||||||
ok (toggleableTools.some(tool => tool.id === id),
|
ok (toggleableTools.some(tool => tool.id === id),
|
||||||
@ -148,23 +165,22 @@ function testToggleTools() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Toggle each tool
|
// Toggle each tool
|
||||||
let p = promise.resolve();
|
|
||||||
for (let node of toolNodes) {
|
for (let node of toolNodes) {
|
||||||
p = p.then(toggleTool.bind(null, node));
|
yield toggleTool(node);
|
||||||
}
|
}
|
||||||
// Toggle again to reset tool enablement state
|
// Toggle again to reset tool enablement state
|
||||||
for (let node of toolNodes) {
|
for (let node of toolNodes) {
|
||||||
p = p.then(toggleTool.bind(null, node));
|
yield toggleTool(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that a tool can still be added when no tabs are present:
|
// Test that a tool can still be added when no tabs are present:
|
||||||
// Disable all tools
|
// Disable all tools
|
||||||
for (let node of enabledTools) {
|
for (let node of enabledTools) {
|
||||||
p = p.then(toggleTool.bind(null, node));
|
yield toggleTool(node);
|
||||||
}
|
}
|
||||||
// Re-enable the tools which are enabled by default
|
// Re-enable the tools which are enabled by default
|
||||||
for (let node of enabledTools) {
|
for (let node of enabledTools) {
|
||||||
p = p.then(toggleTool.bind(null, node));
|
yield toggleTool(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle first, middle, and last tools to ensure that toolbox tabs are
|
// Toggle first, middle, and last tools to ensure that toolbox tabs are
|
||||||
@ -173,20 +189,19 @@ function testToggleTools() {
|
|||||||
middleTool = toolNodes[(toolNodes.length / 2) | 0],
|
middleTool = toolNodes[(toolNodes.length / 2) | 0],
|
||||||
lastTool = toolNodes[toolNodes.length - 1];
|
lastTool = toolNodes[toolNodes.length - 1];
|
||||||
|
|
||||||
p = p.then(toggleTool.bind(null, firstTool))
|
yield toggleTool(firstTool);
|
||||||
.then(toggleTool.bind(null, firstTool))
|
yield toggleTool(firstTool);
|
||||||
.then(toggleTool.bind(null, middleTool))
|
yield toggleTool(middleTool);
|
||||||
.then(toggleTool.bind(null, middleTool))
|
yield toggleTool(middleTool);
|
||||||
.then(toggleTool.bind(null, lastTool))
|
yield toggleTool(lastTool);
|
||||||
.then(toggleTool.bind(null, lastTool));
|
yield toggleTool(lastTool);
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleTool(node) {
|
function* toggleTool(node) {
|
||||||
let deferred = promise.defer();
|
let deferred = promise.defer();
|
||||||
|
|
||||||
let toolId = node.getAttribute("id");
|
let toolId = node.getAttribute("id");
|
||||||
|
let onRegistrationChange;
|
||||||
if (node.checked) {
|
if (node.checked) {
|
||||||
gDevTools.once("tool-unregistered", checkUnregistered.bind(null, toolId, deferred));
|
gDevTools.once("tool-unregistered", checkUnregistered.bind(null, toolId, deferred));
|
||||||
} else {
|
} else {
|
||||||
@ -195,7 +210,7 @@ function toggleTool(node) {
|
|||||||
node.scrollIntoView();
|
node.scrollIntoView();
|
||||||
EventUtils.synthesizeMouseAtCenter(node, {}, panelWin);
|
EventUtils.synthesizeMouseAtCenter(node, {}, panelWin);
|
||||||
|
|
||||||
return deferred.promise;
|
yield deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkUnregistered(toolId, deferred, event, data) {
|
function checkUnregistered(toolId, deferred, event, data) {
|
||||||
@ -249,32 +264,12 @@ function GetPref(name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function SetPref(name, value) {
|
function* cleanup() {
|
||||||
let type = Services.prefs.getPrefType(name);
|
gDevTools.unregisterTool("test-tool");
|
||||||
switch (type) {
|
yield toolbox.destroy();
|
||||||
case Services.prefs.PREF_STRING:
|
gBrowser.removeCurrentTab();
|
||||||
return Services.prefs.setCharPref(name, value);
|
for (let pref of modifiedPrefs) {
|
||||||
case Services.prefs.PREF_INT:
|
Services.prefs.clearUserPref(pref);
|
||||||
return Services.prefs.setIntPref(name, value);
|
|
||||||
case Services.prefs.PREF_BOOL:
|
|
||||||
return Services.prefs.setBoolPref(name, value);
|
|
||||||
default:
|
|
||||||
throw new Error("Unknown type");
|
|
||||||
}
|
}
|
||||||
}
|
toolbox = doc = panelWin = modifiedPrefs = null;
|
||||||
|
|
||||||
function cleanup() {
|
|
||||||
toolbox.destroy().then(function() {
|
|
||||||
gBrowser.removeCurrentTab();
|
|
||||||
for (let pref of modifiedPrefs) {
|
|
||||||
Services.prefs.clearUserPref(pref);
|
|
||||||
}
|
|
||||||
toolbox = doc = panelWin = modifiedPrefs = null;
|
|
||||||
finish();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function errorHandler(error) {
|
|
||||||
ok(false, "Unexpected error: " + error);
|
|
||||||
cleanup();
|
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ OptionsPanel.prototype = {
|
|||||||
|
|
||||||
let onCheckboxClick = (checkbox) => {
|
let onCheckboxClick = (checkbox) => {
|
||||||
let toolDefinition = toggleableButtons.filter(tool => tool.id === checkbox.id)[0];
|
let toolDefinition = toggleableButtons.filter(tool => tool.id === checkbox.id)[0];
|
||||||
SetPref(toolDefinition.visibilityswitch, checkbox.checked);
|
Services.prefs.setBoolPref(toolDefinition.visibilityswitch, checkbox.checked);
|
||||||
setToolboxButtonsVisibility();
|
setToolboxButtonsVisibility();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -198,7 +198,7 @@ OptionsPanel.prototype = {
|
|||||||
let onCheckboxClick = function(id) {
|
let onCheckboxClick = function(id) {
|
||||||
let toolDefinition = gDevTools._tools.get(id);
|
let toolDefinition = gDevTools._tools.get(id);
|
||||||
// Set the kill switch pref boolean to true
|
// Set the kill switch pref boolean to true
|
||||||
SetPref(toolDefinition.visibilityswitch, this.checked);
|
Services.prefs.setBoolPref(toolDefinition.visibilityswitch, this.checked);
|
||||||
if (this.checked) {
|
if (this.checked) {
|
||||||
gDevTools.emit("tool-registered", id);
|
gDevTools.emit("tool-registered", id);
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,8 @@ const ToolboxButtons = [
|
|||||||
!target.isAddon && target.activeTab && target.activeTab.traits.frames
|
!target.isAddon && target.activeTab && target.activeTab.traits.frames
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{ id: "command-button-splitconsole" },
|
{ id: "command-button-splitconsole",
|
||||||
|
isTargetSupported: target => !target.isAddon },
|
||||||
{ id: "command-button-responsive" },
|
{ id: "command-button-responsive" },
|
||||||
{ id: "command-button-paintflashing" },
|
{ id: "command-button-paintflashing" },
|
||||||
{ id: "command-button-tilt" },
|
{ id: "command-button-tilt" },
|
||||||
@ -619,11 +620,6 @@ Toolbox.prototype = {
|
|||||||
this._buildPickerButton();
|
this._buildPickerButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.target.isLocalTab) {
|
|
||||||
this.setToolboxButtonsVisibility();
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
let spec = CommandUtils.getCommandbarSpec("devtools.toolbox.toolbarSpec");
|
let spec = CommandUtils.getCommandbarSpec("devtools.toolbox.toolbarSpec");
|
||||||
let environment = CommandUtils.createEnvironment(this, '_target');
|
let environment = CommandUtils.createEnvironment(this, '_target');
|
||||||
return CommandUtils.createRequisition(environment).then(requisition => {
|
return CommandUtils.createRequisition(environment).then(requisition => {
|
||||||
@ -631,7 +627,11 @@ Toolbox.prototype = {
|
|||||||
return CommandUtils.createButtons(spec, this.target, this.doc,
|
return CommandUtils.createButtons(spec, this.target, this.doc,
|
||||||
requisition).then(buttons => {
|
requisition).then(buttons => {
|
||||||
let container = this.doc.getElementById("toolbox-buttons");
|
let container = this.doc.getElementById("toolbox-buttons");
|
||||||
buttons.forEach(container.appendChild.bind(container));
|
buttons.forEach(button=> {
|
||||||
|
if (button) {
|
||||||
|
container.appendChild(button);
|
||||||
|
}
|
||||||
|
});
|
||||||
this.setToolboxButtonsVisibility();
|
this.setToolboxButtonsVisibility();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -875,18 +875,49 @@ Toolbox.prototype = {
|
|||||||
iframe.tooltip = "aHTMLTooltip";
|
iframe.tooltip = "aHTMLTooltip";
|
||||||
iframe.style.visibility = "hidden";
|
iframe.style.visibility = "hidden";
|
||||||
|
|
||||||
let vbox = this.doc.getElementById("toolbox-panel-" + id);
|
gDevTools.emit(id + "-init", this, iframe);
|
||||||
vbox.appendChild(iframe);
|
this.emit(id + "-init", iframe);
|
||||||
|
|
||||||
|
// If no parent yet, append the frame into default location.
|
||||||
|
if (!iframe.parentNode) {
|
||||||
|
let vbox = this.doc.getElementById("toolbox-panel-" + id);
|
||||||
|
vbox.appendChild(iframe);
|
||||||
|
}
|
||||||
|
|
||||||
let onLoad = () => {
|
let onLoad = () => {
|
||||||
// Prevent flicker while loading by waiting to make visible until now.
|
// Prevent flicker while loading by waiting to make visible until now.
|
||||||
iframe.style.visibility = "visible";
|
iframe.style.visibility = "visible";
|
||||||
|
|
||||||
|
// The build method should return a panel instance, so events can
|
||||||
|
// be fired with the panel as an argument. However, in order to keep
|
||||||
|
// backward compatibility with existing extensions do a check
|
||||||
|
// for a promise return value.
|
||||||
let built = definition.build(iframe.contentWindow, this);
|
let built = definition.build(iframe.contentWindow, this);
|
||||||
|
if (!(built instanceof Promise)) {
|
||||||
|
let panel = built;
|
||||||
|
iframe.panel = panel;
|
||||||
|
|
||||||
|
gDevTools.emit(id + "-build", this, panel);
|
||||||
|
this.emit(id + "-build", panel);
|
||||||
|
|
||||||
|
// The panel can implement an 'open' method for asynchronous
|
||||||
|
// initialization sequence.
|
||||||
|
if (typeof panel.open == "function") {
|
||||||
|
built = panel.open();
|
||||||
|
} else {
|
||||||
|
let deferred = promise.defer();
|
||||||
|
deferred.resolve(panel);
|
||||||
|
built = deferred.promise;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait till the panel is fully ready and fire 'ready' events.
|
||||||
promise.resolve(built).then((panel) => {
|
promise.resolve(built).then((panel) => {
|
||||||
this._toolPanels.set(id, panel);
|
this._toolPanels.set(id, panel);
|
||||||
this.emit(id + "-ready", panel);
|
|
||||||
gDevTools.emit(id + "-ready", this, panel);
|
gDevTools.emit(id + "-ready", this, panel);
|
||||||
|
this.emit(id + "-ready", panel);
|
||||||
|
|
||||||
deferred.resolve(panel);
|
deferred.resolve(panel);
|
||||||
}, console.error);
|
}, console.error);
|
||||||
};
|
};
|
||||||
@ -1514,7 +1545,7 @@ Toolbox.prototype = {
|
|||||||
// Remove the host UI
|
// Remove the host UI
|
||||||
outstanding.push(this.destroyHost());
|
outstanding.push(this.destroyHost());
|
||||||
|
|
||||||
if (this.target.isLocalTab) {
|
if (this._requisition) {
|
||||||
this._requisition.destroy();
|
this._requisition.destroy();
|
||||||
}
|
}
|
||||||
this._telemetry.toolClosed("toolbox");
|
this._telemetry.toolClosed("toolbox");
|
||||||
|
@ -82,8 +82,7 @@ Tools.options = {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
build: function(iframeWindow, toolbox) {
|
build: function(iframeWindow, toolbox) {
|
||||||
let panel = new OptionsPanel(iframeWindow, toolbox);
|
return new OptionsPanel(iframeWindow, toolbox);
|
||||||
return panel.open();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,8 +114,7 @@ Tools.webConsole = {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
build: function(iframeWindow, toolbox) {
|
build: function(iframeWindow, toolbox) {
|
||||||
let panel = new WebConsolePanel(iframeWindow, toolbox);
|
return new WebConsolePanel(iframeWindow, toolbox);
|
||||||
return panel.open();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -149,8 +147,7 @@ Tools.inspector = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
build: function(iframeWindow, toolbox) {
|
build: function(iframeWindow, toolbox) {
|
||||||
let panel = new InspectorPanel(iframeWindow, toolbox);
|
return new InspectorPanel(iframeWindow, toolbox);
|
||||||
return panel.open();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -175,8 +172,7 @@ Tools.jsdebugger = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
build: function(iframeWindow, toolbox) {
|
build: function(iframeWindow, toolbox) {
|
||||||
let panel = new DebuggerPanel(iframeWindow, toolbox);
|
return new DebuggerPanel(iframeWindow, toolbox);
|
||||||
return panel.open();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -200,8 +196,7 @@ Tools.styleEditor = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
build: function(iframeWindow, toolbox) {
|
build: function(iframeWindow, toolbox) {
|
||||||
let panel = new StyleEditorPanel(iframeWindow, toolbox);
|
return new StyleEditorPanel(iframeWindow, toolbox);
|
||||||
return panel.open();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -221,8 +216,7 @@ Tools.shaderEditor = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
build: function(iframeWindow, toolbox) {
|
build: function(iframeWindow, toolbox) {
|
||||||
let panel = new ShaderEditorPanel(iframeWindow, toolbox);
|
return new ShaderEditorPanel(iframeWindow, toolbox);
|
||||||
return panel.open();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -242,8 +236,7 @@ Tools.canvasDebugger = {
|
|||||||
return !target.isAddon && !target.chrome;
|
return !target.isAddon && !target.chrome;
|
||||||
},
|
},
|
||||||
build: function (iframeWindow, toolbox) {
|
build: function (iframeWindow, toolbox) {
|
||||||
let panel = new CanvasDebuggerPanel(iframeWindow, toolbox);
|
return new CanvasDebuggerPanel(iframeWindow, toolbox);
|
||||||
return panel.open();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -261,8 +254,7 @@ Tools.webAudioEditor = {
|
|||||||
return !target.isAddon;
|
return !target.isAddon;
|
||||||
},
|
},
|
||||||
build: function(iframeWindow, toolbox) {
|
build: function(iframeWindow, toolbox) {
|
||||||
let panel = new WebAudioEditorPanel(iframeWindow, toolbox);
|
return new WebAudioEditorPanel(iframeWindow, toolbox);
|
||||||
return panel.open();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -288,8 +280,7 @@ Tools.jsprofiler = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
build: function (frame, target) {
|
build: function (frame, target) {
|
||||||
let panel = new ProfilerPanel(frame, target);
|
return new ProfilerPanel(frame, target);
|
||||||
return panel.open();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -314,8 +305,7 @@ Tools.netMonitor = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
build: function(iframeWindow, toolbox) {
|
build: function(iframeWindow, toolbox) {
|
||||||
let panel = new NetMonitorPanel(iframeWindow, toolbox);
|
return new NetMonitorPanel(iframeWindow, toolbox);
|
||||||
return panel.open();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -341,8 +331,7 @@ Tools.storage = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
build: function(iframeWindow, toolbox) {
|
build: function(iframeWindow, toolbox) {
|
||||||
let panel = new StoragePanel(iframeWindow, toolbox);
|
return new StoragePanel(iframeWindow, toolbox);
|
||||||
return panel.open();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -364,8 +353,7 @@ Tools.scratchpad = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
build: function(iframeWindow, toolbox) {
|
build: function(iframeWindow, toolbox) {
|
||||||
let panel = new ScratchpadPanel(iframeWindow, toolbox);
|
return new ScratchpadPanel(iframeWindow, toolbox);
|
||||||
return panel.open();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2187,6 +2187,7 @@ function truncateString(str, maxLength) {
|
|||||||
"…" +
|
"…" +
|
||||||
str.substring(str.length - Math.floor(maxLength / 2));
|
str.substring(str.length - Math.floor(maxLength / 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse attribute names and values from a string.
|
* Parse attribute names and values from a string.
|
||||||
*
|
*
|
||||||
@ -2200,49 +2201,16 @@ function truncateString(str, maxLength) {
|
|||||||
function parseAttributeValues(attr, doc) {
|
function parseAttributeValues(attr, doc) {
|
||||||
attr = attr.trim();
|
attr = attr.trim();
|
||||||
|
|
||||||
// Prepare other versions of the string to be parsed by appending a " or '
|
// Handle bad user inputs by appending a " or ' if it fails to parse without
|
||||||
// and using those if the first one fails to parse without these characters
|
// them. Also note that a SVG tag is used to make sure the HTML parser
|
||||||
let stringsToParse = [
|
// preserves mixed-case attributes
|
||||||
"<div " + attr + "></div>",
|
let el = DOMParser.parseFromString("<svg " + attr + "></svg>", "text/html").body.childNodes[0] ||
|
||||||
"<div " + attr + "\"></div>",
|
DOMParser.parseFromString("<svg " + attr + "\"></svg>", "text/html").body.childNodes[0] ||
|
||||||
"<div " + attr + "'></div>"
|
DOMParser.parseFromString("<svg " + attr + "'></svg>", "text/html").body.childNodes[0];
|
||||||
];
|
|
||||||
|
|
||||||
// Try to parse as XML, this way, if the string is wellformed, this will
|
|
||||||
// preserve the case.
|
|
||||||
let parsedAttributes = [];
|
|
||||||
for (let str of stringsToParse) {
|
|
||||||
let parsed = DOMParser.parseFromString(str, "text/xml");
|
|
||||||
// The XML parser generates a valid XML document even when parsing errors
|
|
||||||
// occur, in which case the document contains a <parsererror> node, so check
|
|
||||||
// that the document contains our expected DIV
|
|
||||||
if (parsed.childNodes[0].localName === "div") {
|
|
||||||
for (let {name, value} of parsed.childNodes[0].attributes) {
|
|
||||||
parsedAttributes.push({ name, value });
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the XML parsing failed, parse as HTML to get malformed attributes
|
|
||||||
if (parsedAttributes.length === 0) {
|
|
||||||
for (let str of stringsToParse) {
|
|
||||||
let parsed = DOMParser.parseFromString(str, "text/html");
|
|
||||||
// Check that the parsed document does contain the expected DIV as a child
|
|
||||||
// of <body>
|
|
||||||
if (parsed.body.childNodes[0]) {
|
|
||||||
for (let {name, value} of parsed.body.childNodes[0].attributes) {
|
|
||||||
parsedAttributes.push({ name, value });
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let div = doc.createElement("div");
|
let div = doc.createElement("div");
|
||||||
|
|
||||||
let attributes = [];
|
let attributes = [];
|
||||||
for (let {name, value} of parsedAttributes) {
|
for (let {name, value} of el.attributes) {
|
||||||
// Try to set on an element in the document, throws exception on bad input.
|
// Try to set on an element in the document, throws exception on bad input.
|
||||||
// Prevents InvalidCharacterError - "String contains an invalid character".
|
// Prevents InvalidCharacterError - "String contains an invalid character".
|
||||||
try {
|
try {
|
||||||
|
@ -45,11 +45,8 @@ function* testWellformedMixedCase(inspector) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function* testMalformedMixedCase(inspector) {
|
function* testMalformedMixedCase(inspector) {
|
||||||
info("Modifying a mixed-case attribute, making sure to generate a parsing" +
|
info("Modifying a malformed, mixed-case attribute, " +
|
||||||
"error, and expecting the attribute's case to NOT be preserved");
|
"expecting the attribute's case to be preserved");
|
||||||
// See /browser/devtools/markupview/markup-view.js:parseAttributeValues
|
|
||||||
// When attributes are malformed, they cannot be parsed with the XML parser
|
|
||||||
// and so we fall back to the HTML parser which lowercases attributes.
|
|
||||||
|
|
||||||
info("Listening to markup mutations");
|
info("Listening to markup mutations");
|
||||||
let onMutated = inspector.once("markupmutation");
|
let onMutated = inspector.once("markupmutation");
|
||||||
@ -67,7 +64,7 @@ function* testMalformedMixedCase(inspector) {
|
|||||||
yield onMutated;
|
yield onMutated;
|
||||||
|
|
||||||
assertAttributes("svg", {
|
assertAttributes("svg", {
|
||||||
"viewbox": "<>",
|
"viewBox": "<>",
|
||||||
"width": "200",
|
"width": "200",
|
||||||
"height": "200"
|
"height": "200"
|
||||||
});
|
});
|
||||||
|
@ -1212,9 +1212,6 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||||||
this.updateMenuView(template, 'method', aMethod);
|
this.updateMenuView(template, 'method', aMethod);
|
||||||
this.updateMenuView(template, 'url', aUrl);
|
this.updateMenuView(template, 'url', aUrl);
|
||||||
|
|
||||||
let waterfall = $(".requests-menu-waterfall", template);
|
|
||||||
waterfall.style.backgroundImage = this._cachedWaterfallBackground;
|
|
||||||
|
|
||||||
// Flatten the DOM by removing one redundant box (the template container).
|
// Flatten the DOM by removing one redundant box (the template container).
|
||||||
for (let node of template.childNodes) {
|
for (let node of template.childNodes) {
|
||||||
fragment.appendChild(node.cloneNode(true));
|
fragment.appendChild(node.cloneNode(true));
|
||||||
@ -1378,7 +1375,6 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||||||
// Redraw and set the canvas background for each waterfall view.
|
// Redraw and set the canvas background for each waterfall view.
|
||||||
this._showWaterfallDivisionLabels(scale);
|
this._showWaterfallDivisionLabels(scale);
|
||||||
this._drawWaterfallBackground(scale);
|
this._drawWaterfallBackground(scale);
|
||||||
this._flushWaterfallBackgrounds();
|
|
||||||
|
|
||||||
// Apply CSS transforms to each waterfall in this container totalTime
|
// Apply CSS transforms to each waterfall in this container totalTime
|
||||||
// accurately translate and resize as needed.
|
// accurately translate and resize as needed.
|
||||||
@ -1497,8 +1493,8 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||||||
let pixelArray = imageData.data;
|
let pixelArray = imageData.data;
|
||||||
|
|
||||||
let buf = new ArrayBuffer(pixelArray.length);
|
let buf = new ArrayBuffer(pixelArray.length);
|
||||||
let buf8 = new Uint8ClampedArray(buf);
|
let view8bit = new Uint8ClampedArray(buf);
|
||||||
let data32 = new Uint32Array(buf);
|
let view32bit = new Uint32Array(buf);
|
||||||
|
|
||||||
// Build new millisecond tick lines...
|
// Build new millisecond tick lines...
|
||||||
let timingStep = REQUESTS_WATERFALL_BACKGROUND_TICKS_MULTIPLE;
|
let timingStep = REQUESTS_WATERFALL_BACKGROUND_TICKS_MULTIPLE;
|
||||||
@ -1520,26 +1516,16 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||||||
let increment = scaledStep * Math.pow(2, i);
|
let increment = scaledStep * Math.pow(2, i);
|
||||||
for (let x = 0; x < canvasWidth; x += increment) {
|
for (let x = 0; x < canvasWidth; x += increment) {
|
||||||
let position = (window.isRTL ? canvasWidth - x : x) | 0;
|
let position = (window.isRTL ? canvasWidth - x : x) | 0;
|
||||||
data32[position] = (alphaComponent << 24) | (b << 16) | (g << 8) | r;
|
view32bit[position] = (alphaComponent << 24) | (b << 16) | (g << 8) | r;
|
||||||
}
|
}
|
||||||
alphaComponent += REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_ADD;
|
alphaComponent += REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_ADD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush the image data and cache the waterfall background.
|
// Flush the image data and cache the waterfall background.
|
||||||
pixelArray.set(buf8);
|
pixelArray.set(view8bit);
|
||||||
ctx.putImageData(imageData, 0, 0);
|
ctx.putImageData(imageData, 0, 0);
|
||||||
this._cachedWaterfallBackground = "url(" + canvas.toDataURL() + ")";
|
document.mozSetImageElement("waterfall-background", canvas);
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reapplies the current waterfall background on all request items.
|
|
||||||
*/
|
|
||||||
_flushWaterfallBackgrounds: function() {
|
|
||||||
for (let { target } of this) {
|
|
||||||
let waterfallNode = $(".requests-menu-waterfall", target);
|
|
||||||
waterfallNode.style.backgroundImage = this._cachedWaterfallBackground;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1762,7 +1748,6 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||||||
_canvas: null,
|
_canvas: null,
|
||||||
_ctx: null,
|
_ctx: null,
|
||||||
_cachedWaterfallWidth: 0,
|
_cachedWaterfallWidth: 0,
|
||||||
_cachedWaterfallBackground: "",
|
|
||||||
_firstRequestStartedMillis: -1,
|
_firstRequestStartedMillis: -1,
|
||||||
_lastRequestEndedMillis: -1,
|
_lastRequestEndedMillis: -1,
|
||||||
_updateQueue: [],
|
_updateQueue: [],
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
|
skip-if = e10s # Bug 1058898
|
||||||
subsuite = devtools
|
subsuite = devtools
|
||||||
support-files =
|
support-files =
|
||||||
doc_simple-test.html
|
doc_simple-test.html
|
||||||
|
@ -104,6 +104,13 @@ let CommandUtils = {
|
|||||||
if (command == null) {
|
if (command == null) {
|
||||||
throw new Error("No command '" + typed + "'");
|
throw new Error("No command '" + typed + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not build a button for a non-remote safe command in a non-local target.
|
||||||
|
if (!target.isLocalTab && !command.isRemoteSafe) {
|
||||||
|
requisition.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (command.buttonId != null) {
|
if (command.buttonId != null) {
|
||||||
button.id = command.buttonId;
|
button.id = command.buttonId;
|
||||||
if (command.buttonClass != null) {
|
if (command.buttonClass != null) {
|
||||||
|
@ -14,6 +14,7 @@ exports.items = [
|
|||||||
buttonId: "command-button-splitconsole",
|
buttonId: "command-button-splitconsole",
|
||||||
buttonClass: "command-button command-button-invertable",
|
buttonClass: "command-button command-button-invertable",
|
||||||
tooltipText: gcli.lookup("splitconsoleTooltip"),
|
tooltipText: gcli.lookup("splitconsoleTooltip"),
|
||||||
|
isRemoteSafe: true,
|
||||||
state: {
|
state: {
|
||||||
isChecked: function(target) {
|
isChecked: function(target) {
|
||||||
let toolbox = gDevTools.getToolbox(target);
|
let toolbox = gDevTools.getToolbox(target);
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
subsuite = devtools
|
subsuite = devtools
|
||||||
support-files =
|
support-files =
|
||||||
addons/simulators.json
|
addons/simulators.json
|
||||||
|
doc_tabs.html
|
||||||
head.js
|
head.js
|
||||||
templates.json
|
templates.json
|
||||||
|
|
||||||
[browser_tabs.js]
|
[browser_tabs.js]
|
||||||
skip-if = true # Fails on TBPL, to be fixed in bug 1062611
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
const TEST_URI = "http://example.com/browser/browser/devtools/webide/test/doc_tabs.html";
|
||||||
|
|
||||||
function test() {
|
function test() {
|
||||||
waitForExplicitFinish();
|
waitForExplicitFinish();
|
||||||
SimpleTest.requestCompleteLog();
|
SimpleTest.requestCompleteLog();
|
||||||
@ -9,10 +11,14 @@ function test() {
|
|||||||
Task.spawn(function() {
|
Task.spawn(function() {
|
||||||
const { DebuggerServer } =
|
const { DebuggerServer } =
|
||||||
Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
|
Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
|
||||||
|
|
||||||
|
// Since we test the connections set below, destroy the server in case it
|
||||||
|
// was left open.
|
||||||
|
DebuggerServer.destroy();
|
||||||
DebuggerServer.init(function () { return true; });
|
DebuggerServer.init(function () { return true; });
|
||||||
DebuggerServer.addBrowserActors();
|
DebuggerServer.addBrowserActors();
|
||||||
|
|
||||||
let tab = yield addTab("about:newtab");
|
let tab = yield addTab(TEST_URI);
|
||||||
|
|
||||||
let win = yield openWebIDE();
|
let win = yield openWebIDE();
|
||||||
|
|
||||||
@ -23,15 +29,13 @@ function test() {
|
|||||||
yield selectTabProject(win);
|
yield selectTabProject(win);
|
||||||
|
|
||||||
let project = win.AppManager.selectedProject;
|
let project = win.AppManager.selectedProject;
|
||||||
is(project.location, "about:newtab", "Location is correct");
|
is(project.location, TEST_URI, "Location is correct");
|
||||||
is(project.name, "New Tab", "Name is correct");
|
is(project.name, "example.com: Test Tab", "Name is correct");
|
||||||
|
|
||||||
yield closeWebIDE(win);
|
yield closeWebIDE(win);
|
||||||
DebuggerServer.destroy();
|
DebuggerServer.destroy();
|
||||||
yield removeTab(tab);
|
yield removeTab(tab);
|
||||||
|
}).then(finish, handleError);
|
||||||
finish();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectToLocal(win) {
|
function connectToLocal(win) {
|
||||||
|
15
browser/devtools/webide/test/doc_tabs.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!-- Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||||
|
<!doctype html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<title>Test Tab</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
Test Tab
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -153,3 +153,8 @@ function removeTab(aTab, aWindow) {
|
|||||||
targetBrowser.removeTab(aTab);
|
targetBrowser.removeTab(aTab);
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleError(aError) {
|
||||||
|
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
@ -67,8 +67,7 @@ window.busy-determined #action-busy-undetermined {
|
|||||||
|
|
||||||
#project-panel-button {
|
#project-panel-button {
|
||||||
-moz-box-pack: start;
|
-moz-box-pack: start;
|
||||||
width: 150px;
|
max-width: calc(50vw - 100px);
|
||||||
max-width: 150px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#project-panel-button > .panel-button-image {
|
#project-panel-button > .panel-button-image {
|
||||||
@ -88,8 +87,7 @@ window.busy-determined #action-busy-undetermined {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#project-panel-button > .panel-button-label {
|
#project-panel-button > .panel-button-label {
|
||||||
width: 150px;
|
-moz-box-flex: 1;
|
||||||
max-width: 150px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Panel buttons - runtime */
|
/* Panel buttons - runtime */
|
||||||
|
@ -71,6 +71,17 @@ object with the following properties:
|
|||||||
- ``move`` counts the number of times an item is moved somewhere else (but not to the palette);
|
- ``move`` counts the number of times an item is moved somewhere else (but not to the palette);
|
||||||
- ``remove`` counts the number of times an item is removed to the palette;
|
- ``remove`` counts the number of times an item is removed to the palette;
|
||||||
- ``reset`` counts the number of times the 'restore defaults' button is used;
|
- ``reset`` counts the number of times the 'restore defaults' button is used;
|
||||||
|
- ``search`` is an object tracking searches of various types, keyed off the search
|
||||||
|
location, storing a number indicating how often the respective type of search
|
||||||
|
has happened.
|
||||||
|
- There are also two special keys that mean slightly different things.
|
||||||
|
- ``urlbar-keyword`` records searches that would have been an invalid-protocol
|
||||||
|
error, but are now keyword searches. They are also counted in the ``urlbar``
|
||||||
|
keyword (along with all the other urlbar searches).
|
||||||
|
- ``selection`` searches records selections of search suggestions. They include
|
||||||
|
the source, the index of the selection, and the kind of selection (mouse or
|
||||||
|
enter key). Selection searches are also counted in their sources.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
``UITour``
|
``UITour``
|
||||||
|
@ -66,6 +66,33 @@
|
|||||||
<!ENTITY cmd.clearList.accesskey "a">
|
<!ENTITY cmd.clearList.accesskey "a">
|
||||||
<!ENTITY cmd.clearDownloads.label "Clear Downloads">
|
<!ENTITY cmd.clearDownloads.label "Clear Downloads">
|
||||||
<!ENTITY cmd.clearDownloads.accesskey "D">
|
<!ENTITY cmd.clearDownloads.accesskey "D">
|
||||||
|
<!-- LOCALIZATION NOTE (cmd.unblock.label):
|
||||||
|
This command may be shown in the context menu, as a menu button item, or as
|
||||||
|
a text link when malware or potentially unwanted downloads are blocked.
|
||||||
|
Note: This string doesn't exist in the UI yet. See bug 1053890.
|
||||||
|
-->
|
||||||
|
<!ENTITY cmd.unblock.label "Unblock">
|
||||||
|
<!ENTITY cmd.unblock.accesskey "U">
|
||||||
|
<!-- LOCALIZATION NOTE (cmd.removeFile.label):
|
||||||
|
This command may be shown in the context menu or as a menu button label
|
||||||
|
when malware or potentially unwanted downloads are blocked.
|
||||||
|
Note: This string doesn't exist in the UI yet. See bug 1053890.
|
||||||
|
-->
|
||||||
|
<!ENTITY cmd.removeFile.label "Remove File">
|
||||||
|
<!ENTITY cmd.removeFile.accesskey "m">
|
||||||
|
|
||||||
|
<!-- LOCALIZATION NOTE (blocked.label):
|
||||||
|
Shown as a tag before the file name for some types of blocked downloads.
|
||||||
|
Note: This string doesn't exist in the UI yet. See bug 1053890.
|
||||||
|
-->
|
||||||
|
<!ENTITY blocked.label "BLOCKED">
|
||||||
|
|
||||||
|
<!-- LOCALIZATION NOTE (learnMore.label):
|
||||||
|
Shown as a text link for some types of blocked downloads, for example
|
||||||
|
malware, when there is an associated explanation page on the Mozilla site.
|
||||||
|
Note: This string doesn't exist in the UI yet. See bug 1053890.
|
||||||
|
-->
|
||||||
|
<!ENTITY learnMore.label "Learn More">
|
||||||
|
|
||||||
<!-- LOCALIZATION NOTE (downloadsHistory.label, downloadsHistory.accesskey):
|
<!-- LOCALIZATION NOTE (downloadsHistory.label, downloadsHistory.accesskey):
|
||||||
This string is shown at the bottom of the Downloads Panel when all the
|
This string is shown at the bottom of the Downloads Panel when all the
|
||||||
|
@ -38,13 +38,40 @@ stateBlockedPolicy=Blocked by your security zone policy
|
|||||||
# Indicates that the download was blocked after scanning.
|
# Indicates that the download was blocked after scanning.
|
||||||
stateDirty=Blocked: May contain a virus or spyware
|
stateDirty=Blocked: May contain a virus or spyware
|
||||||
|
|
||||||
|
# LOCALIZATION NOTE (blockedMalware, blockedPotentiallyUnwanted,
|
||||||
|
# blockedUncommon):
|
||||||
|
# These strings are shown in the panel for some types of blocked downloads, and
|
||||||
|
# are immediately followed by the "Learn More" link, thus they must end with a
|
||||||
|
# period. You may need to adjust "downloadDetails.width" in "downloads.dtd" if
|
||||||
|
# this turns out to be longer than the other existing status strings.
|
||||||
|
# Note: These strings don't exist in the UI yet. See bug 1053890.
|
||||||
|
blockedMalware=This file contains a virus or malware.
|
||||||
|
blockedPotentiallyUnwanted=This file may harm your computer.
|
||||||
|
blockedUncommon=This file may not be safe to open.
|
||||||
|
|
||||||
|
# LOCALIZATION NOTE (unblockHeader, unblockTypeMalware,
|
||||||
|
# unblockTypePotentiallyUnwanted, unblockTypeUncommon,
|
||||||
|
# unblockTip, unblockButtonContinue, unblockButtonCancel):
|
||||||
|
# These strings are displayed in the dialog shown when the user asks a blocked
|
||||||
|
# download to be unblocked. The severity of the threat is expressed in
|
||||||
|
# descending order by the unblockType strings, it is higher for files detected
|
||||||
|
# as malware and lower for uncommon downloads.
|
||||||
|
# Note: These strings don't exist in the UI yet. See bug 1053890.
|
||||||
|
unblockHeader=Are you sure you want to unblock this file?
|
||||||
|
unblockTypeMalware=This file contains a virus or other malware that will harm your computer.
|
||||||
|
unblockTypePotentiallyUnwanted=This file, disguised as a helpful download, will make unexpected changes to your programs and settings.
|
||||||
|
unblockTypeUncommon=This file has been downloaded from an unfamiliar and potentially dangerous website and may not be safe to open.
|
||||||
|
unblockTip=You can search for an alternate download source or try to download the file again later.
|
||||||
|
unblockButtonContinue=Unblock anyway
|
||||||
|
unblockButtonCancel=Keep me safe
|
||||||
|
|
||||||
# LOCALIZATION NOTE (sizeWithUnits):
|
# LOCALIZATION NOTE (sizeWithUnits):
|
||||||
# %1$S is replaced with the size number, and %2$S with the measurement unit.
|
# %1$S is replaced with the size number, and %2$S with the measurement unit.
|
||||||
sizeWithUnits=%1$S %2$S
|
sizeWithUnits=%1$S %2$S
|
||||||
sizeUnknown=Unknown size
|
sizeUnknown=Unknown size
|
||||||
|
|
||||||
# LOCALIZATION NOTE (shortTimeLeftSeconds, shortTimeLeftMinutes,
|
# LOCALIZATION NOTE (shortTimeLeftSeconds, shortTimeLeftMinutes,
|
||||||
# shortTimeLeftHours, shortTimeLeftDays):
|
# shortTimeLeftHours, shortTimeLeftDays):
|
||||||
# These values are displayed in the downloads indicator in the main browser
|
# These values are displayed in the downloads indicator in the main browser
|
||||||
# window, where space is available for three characters maximum. %1$S is
|
# window, where space is available for three characters maximum. %1$S is
|
||||||
# replaced with the time left for the given measurement unit. Even for days,
|
# replaced with the time left for the given measurement unit. Even for days,
|
||||||
|
@ -191,7 +191,7 @@ let AboutHome = {
|
|||||||
|
|
||||||
let engine = Services.search.currentEngine;
|
let engine = Services.search.currentEngine;
|
||||||
#ifdef MOZ_SERVICES_HEALTHREPORT
|
#ifdef MOZ_SERVICES_HEALTHREPORT
|
||||||
window.BrowserSearch.recordSearchInHealthReport(engine, "abouthome");
|
window.BrowserSearch.recordSearchInHealthReport(engine, "abouthome", data.selection);
|
||||||
#endif
|
#endif
|
||||||
// Trigger a search through nsISearchEngine.getSubmission()
|
// Trigger a search through nsISearchEngine.getSubmission()
|
||||||
let submission = engine.getSubmission(data.searchTerms, null, "homepage");
|
let submission = engine.getSubmission(data.searchTerms, null, "homepage");
|
||||||
|
@ -179,6 +179,7 @@ this.BrowserUITelemetry = {
|
|||||||
|
|
||||||
Services.obs.addObserver(this, "sessionstore-windows-restored", false);
|
Services.obs.addObserver(this, "sessionstore-windows-restored", false);
|
||||||
Services.obs.addObserver(this, "browser-delayed-startup-finished", false);
|
Services.obs.addObserver(this, "browser-delayed-startup-finished", false);
|
||||||
|
Services.obs.addObserver(this, "autocomplete-did-enter-text", false);
|
||||||
CustomizableUI.addListener(this);
|
CustomizableUI.addListener(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -190,6 +191,13 @@ this.BrowserUITelemetry = {
|
|||||||
case "browser-delayed-startup-finished":
|
case "browser-delayed-startup-finished":
|
||||||
this._registerWindow(aSubject);
|
this._registerWindow(aSubject);
|
||||||
break;
|
break;
|
||||||
|
case "autocomplete-did-enter-text":
|
||||||
|
let input = aSubject.QueryInterface(Ci.nsIAutoCompleteInput);
|
||||||
|
if (input && input.id == "urlbar" && !input.inPrivateContext &&
|
||||||
|
input.popup.selectedIndex != -1) {
|
||||||
|
this._logAwesomeBarSearchResult(input.textValue);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -554,11 +562,25 @@ this.BrowserUITelemetry = {
|
|||||||
this._countEvent(["customize", aEventType]);
|
this._countEvent(["customize", aEventType]);
|
||||||
},
|
},
|
||||||
|
|
||||||
countSearchEvent: function(source, query) {
|
countSearchEvent: function(source, query, selection) {
|
||||||
this._countEvent(["search", source]);
|
this._countEvent(["search", source]);
|
||||||
if ((/^[a-zA-Z]+:[^\/\\]/).test(query)) {
|
if ((/^[a-zA-Z]+:[^\/\\]/).test(query)) {
|
||||||
this._countEvent(["search", "urlbar-keyword"]);
|
this._countEvent(["search", "urlbar-keyword"]);
|
||||||
}
|
}
|
||||||
|
if (selection) {
|
||||||
|
this._countEvent(["search", "selection", source, selection.index, selection.kind]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_logAwesomeBarSearchResult: function (url) {
|
||||||
|
let spec = Services.search.parseSubmissionURL(url);
|
||||||
|
if (spec.engine) {
|
||||||
|
let matchedEngine = "default";
|
||||||
|
if (spec.engine.name !== Services.search.currentEngine.name) {
|
||||||
|
matchedEngine = "other";
|
||||||
|
}
|
||||||
|
this.countSearchEvent("autocomplete-" + matchedEngine);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_durations: {
|
_durations: {
|
||||||
|
@ -206,7 +206,7 @@ this.ContentSearch = {
|
|||||||
]);
|
]);
|
||||||
let browserWin = msg.target.ownerDocument.defaultView;
|
let browserWin = msg.target.ownerDocument.defaultView;
|
||||||
let engine = Services.search.getEngineByName(data.engineName);
|
let engine = Services.search.getEngineByName(data.engineName);
|
||||||
browserWin.BrowserSearch.recordSearchInHealthReport(engine, data.whence);
|
browserWin.BrowserSearch.recordSearchInHealthReport(engine, data.whence, data.selection);
|
||||||
let submission = engine.getSubmission(data.searchString, "", data.whence);
|
let submission = engine.getSubmission(data.searchString, "", data.whence);
|
||||||
browserWin.loadURI(submission.uri.spec, null, submission.postData);
|
browserWin.loadURI(submission.uri.spec, null, submission.postData);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
@ -320,7 +320,10 @@ label.requests-menu-status-code {
|
|||||||
.requests-menu-subitem.requests-menu-waterfall {
|
.requests-menu-subitem.requests-menu-waterfall {
|
||||||
-moz-padding-start: 0px;
|
-moz-padding-start: 0px;
|
||||||
-moz-padding-end: 4px;
|
-moz-padding-end: 4px;
|
||||||
background-repeat: repeat-y; /* Background created on a <canvas> in js. */
|
/* Background created on a <canvas> in js. */
|
||||||
|
/* @see browser/devtools/netmonitor/netmonitor-view.js */
|
||||||
|
background-image: -moz-element(#waterfall-background);
|
||||||
|
background-repeat: repeat-y;
|
||||||
background-position: -1px center;
|
background-position: -1px center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 615 B After Width: | Height: | Size: 577 B |
Before Width: | Height: | Size: 641 B After Width: | Height: | Size: 590 B |
Before Width: | Height: | Size: 222 B After Width: | Height: | Size: 136 B |
Before Width: | Height: | Size: 609 B After Width: | Height: | Size: 557 B |
Before Width: | Height: | Size: 455 B After Width: | Height: | Size: 409 B |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 971 B |
Before Width: | Height: | Size: 798 B After Width: | Height: | Size: 789 B |
Before Width: | Height: | Size: 800 B After Width: | Height: | Size: 799 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 791 B After Width: | Height: | Size: 140 B |
Before Width: | Height: | Size: 776 B After Width: | Height: | Size: 142 B |
Before Width: | Height: | Size: 767 B After Width: | Height: | Size: 152 B |
Before Width: | Height: | Size: 352 B After Width: | Height: | Size: 307 B |
Before Width: | Height: | Size: 425 B After Width: | Height: | Size: 421 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 520 B After Width: | Height: | Size: 462 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 550 B After Width: | Height: | Size: 540 B |
Before Width: | Height: | Size: 118 B After Width: | Height: | Size: 115 B |