mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Merge mozilla-inbound to mozilla-central. a=merge
--HG-- rename : toolkit/components/passwordmgr/test/test_xhr.html => toolkit/components/passwordmgr/test/mochitest/test_xhr.html
This commit is contained in:
commit
69d3c6c61d
@ -729,7 +729,7 @@ html|input.urlbar-input {
|
||||
}
|
||||
|
||||
#PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] {
|
||||
-moz-binding: url("chrome://global/content/bindings/autocomplete.xml#autocomplete-richlistitem-insecure-field");
|
||||
-moz-binding: none;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
|
@ -37,9 +37,9 @@ if (AppConstants.DEBUG ||
|
||||
EXPECTED_REFLOWS_FIRST_OPEN.push(
|
||||
{
|
||||
stack: [
|
||||
"_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
|
||||
"handleOverUnderflow@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_reuseAcItem@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_reuseAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_invalidate@chrome://global/content/bindings/autocomplete.xml",
|
||||
"invalidate@chrome://global/content/bindings/autocomplete.xml",
|
||||
@ -49,8 +49,8 @@ EXPECTED_REFLOWS_FIRST_OPEN.push(
|
||||
|
||||
{
|
||||
stack: [
|
||||
"_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
|
||||
"handleOverUnderflow@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openPopup@chrome://global/content/bindings/autocomplete.xml",
|
||||
@ -91,8 +91,8 @@ if (AppConstants.RELEASE_OR_BETA) {
|
||||
},
|
||||
{
|
||||
stack: [
|
||||
"_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_adjustAcItem@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_adjustAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_invalidate@chrome://global/content/bindings/autocomplete.xml",
|
||||
"invalidate@chrome://global/content/bindings/autocomplete.xml",
|
||||
|
@ -38,9 +38,9 @@ if (AppConstants.DEBUG ||
|
||||
EXPECTED_REFLOWS_FIRST_OPEN.push(
|
||||
{
|
||||
stack: [
|
||||
"_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
|
||||
"handleOverUnderflow@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_reuseAcItem@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_reuseAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_invalidate@chrome://global/content/bindings/autocomplete.xml",
|
||||
"invalidate@chrome://global/content/bindings/autocomplete.xml",
|
||||
@ -50,8 +50,8 @@ EXPECTED_REFLOWS_FIRST_OPEN.push(
|
||||
|
||||
{
|
||||
stack: [
|
||||
"_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
|
||||
"handleOverUnderflow@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openPopup@chrome://global/content/bindings/autocomplete.xml",
|
||||
@ -75,9 +75,9 @@ EXPECTED_REFLOWS_FIRST_OPEN.push(
|
||||
const EXPECTED_REFLOWS_SECOND_OPEN = [
|
||||
{
|
||||
stack: [
|
||||
"_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
|
||||
"handleOverUnderflow@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_reuseAcItem@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_reuseAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_invalidate@chrome://global/content/bindings/autocomplete.xml",
|
||||
"invalidate@chrome://global/content/bindings/autocomplete.xml",
|
||||
|
@ -6,6 +6,7 @@ support-files =
|
||||
formautofill_parent_utils.js
|
||||
|
||||
[test_address_level_1_submission.html]
|
||||
[test_autofill_and_ordinal_forms.html]
|
||||
[test_autofocus_form.html]
|
||||
skip-if = verify
|
||||
[test_basic_autocomplete_form.html]
|
||||
|
@ -0,0 +1,115 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test autofill submit</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="formautofill_common.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
/* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
let MOCK_STORAGE = [{
|
||||
"given-name": "John",
|
||||
"additional-name": "R",
|
||||
"family-name": "Smith",
|
||||
"organization": "Sesame Street",
|
||||
"street-address": "123 Sesame Street.",
|
||||
"tel": "+13453453456",
|
||||
"country": "US",
|
||||
"address-level1": "NY",
|
||||
}];
|
||||
|
||||
initPopupListener();
|
||||
|
||||
add_task(async function setupStorage() {
|
||||
await addAddress(MOCK_STORAGE[0]);
|
||||
|
||||
await updateFormHistory([
|
||||
{op: "add", fieldname: "username", value: "petya"},
|
||||
{op: "add", fieldname: "current-password", value: "abrh#25_,K"},
|
||||
]);
|
||||
});
|
||||
|
||||
add_task(async function check_switch_autofill_form_popup() {
|
||||
await setInput("#tel", "");
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
await expectPopup();
|
||||
checkMenuEntries(
|
||||
[
|
||||
`{"primary":"+13453453456","secondary":"123 Sesame Street."}`,
|
||||
`{"primary":"","secondary":"","categories":["name","organization","address","tel"],"focusedCategory":"tel"}`,
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
await testMenuEntry(0, "!(el instanceof MozElements.MozAutocompleteRichlistitem)");
|
||||
});
|
||||
|
||||
add_task(async function check_switch_oridnal_form_popup() {
|
||||
// We need an intentional wait here before switching form.
|
||||
await sleep();
|
||||
await setInput("#username", "");
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
await expectPopup();
|
||||
checkMenuEntries(["petya"], false);
|
||||
|
||||
await testMenuEntry(0, "el instanceof MozElements.MozAutocompleteRichlistitem");
|
||||
});
|
||||
|
||||
add_task(async function check_switch_autofill_form_popup_back() {
|
||||
// We need an intentional wait here before switching form.
|
||||
await sleep();
|
||||
await setInput("#tel", "");
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
await expectPopup();
|
||||
checkMenuEntries(
|
||||
[
|
||||
`{"primary":"+13453453456","secondary":"123 Sesame Street."}`,
|
||||
`{"primary":"","secondary":"","categories":["name","organization","address","tel"],"focusedCategory":"tel"}`,
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
await testMenuEntry(0, "!(el instanceof MozElements.MozAutocompleteRichlistitem)");
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<div>
|
||||
|
||||
<h2>Address form</h2>
|
||||
<form class="alignedLabels">
|
||||
<label>given-name: <input autocomplete="given-name" autofocus></label>
|
||||
<label>additional-name: <input id="additional-name" autocomplete="additional-name"></label>
|
||||
<label>family-name: <input autocomplete="family-name"></label>
|
||||
<label>organization: <input autocomplete="organization"></label>
|
||||
<label>street-address: <input autocomplete="street-address"></label>
|
||||
<label>address-level1: <input autocomplete="address-level1"></label>
|
||||
<label>postal-code: <input autocomplete="postal-code"></label>
|
||||
<label>country: <input autocomplete="country"></label>
|
||||
<label>country-name: <input autocomplete="country-name"></label>
|
||||
<label>tel: <input id="tel" autocomplete="tel"></label>
|
||||
<p>
|
||||
<input type="submit" value="Submit">
|
||||
<button type="reset">Reset</button>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<h2>Ordinal form</h2>
|
||||
<form class="alignedLabels">
|
||||
<label>username: <input id="username" autocomplete="username"></label>
|
||||
<p><input type="submit" value="Username"></p>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1254,6 +1254,11 @@ Editor.prototype = {
|
||||
* editor), but we do want to always disable it if it is preffed off.
|
||||
*/
|
||||
setupAutoCompletion: function() {
|
||||
if (!this.config.autocomplete && !this.initializeAutoCompletion) {
|
||||
// Do nothing since there is no autocomplete config and no autocompletion have
|
||||
// been initialized.
|
||||
return;
|
||||
}
|
||||
// The autocomplete module will overwrite this.initializeAutoCompletion
|
||||
// with a mode specific autocompletion handler.
|
||||
if (!this.initializeAutoCompletion) {
|
||||
|
@ -11,9 +11,9 @@ const TESTCASE_URI = TEST_BASE_HTTP + "autocomplete.html";
|
||||
const AUTOCOMPLETION_PREF = "devtools.styleeditor.autocompletion-enabled";
|
||||
|
||||
add_task(async function() {
|
||||
Services.prefs.setBoolPref(AUTOCOMPLETION_PREF, false);
|
||||
const { ui } = await openStyleEditorForURL(TESTCASE_URI);
|
||||
const editor = await ui.editors[0].getSourceEditor();
|
||||
editor.sourceEditor.setOption("autocomplete", false);
|
||||
|
||||
is(editor.sourceEditor.getOption("autocomplete"), false,
|
||||
"Autocompletion option does not exist");
|
||||
@ -21,6 +21,15 @@ add_task(async function() {
|
||||
"Autocompletion popup does not exist");
|
||||
});
|
||||
|
||||
add_task(async function() {
|
||||
Services.prefs.setBoolPref(AUTOCOMPLETION_PREF, false);
|
||||
const { ui } = await openStyleEditorForURL(TESTCASE_URI);
|
||||
const editor = await ui.editors[0].getSourceEditor();
|
||||
|
||||
is(editor.sourceEditor.getOption("autocomplete"), false,
|
||||
"Autocompletion option does not exist");
|
||||
});
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref(AUTOCOMPLETION_PREF);
|
||||
});
|
||||
|
@ -148,6 +148,7 @@ class MOZ_RAII BufferReader {
|
||||
|
||||
const uint8_t* Read(size_t aCount) {
|
||||
if (aCount > mRemaining) {
|
||||
mPtr += mRemaining;
|
||||
mRemaining = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ support-files =
|
||||
pluginstream.js
|
||||
post.sjs
|
||||
plugin-utils.js
|
||||
!/toolkit/components/passwordmgr/test/authenticate.sjs
|
||||
|
||||
[test_bug1028200-1.html]
|
||||
skip-if = !crashreporter
|
||||
|
@ -2111,9 +2111,11 @@ void gfxPlatform::GetPlatformCMSOutputProfile(void*& mem, size_t& size) {
|
||||
void gfxPlatform::GetCMSOutputProfileData(void*& mem, size_t& size) {
|
||||
nsAutoCString fname;
|
||||
Preferences::GetCString("gfx.color_management.display_profile", fname);
|
||||
mem = nullptr;
|
||||
if (!fname.IsEmpty()) {
|
||||
qcms_data_from_path(fname.get(), &mem, &size);
|
||||
} else {
|
||||
}
|
||||
if (mem == nullptr) {
|
||||
gfxPlatform::GetPlatform()->GetPlatformCMSOutputProfile(mem, size);
|
||||
}
|
||||
}
|
||||
|
48
js/src/jit-test/tests/ion/inlining/getelem-getter-bailout.js
Normal file
48
js/src/jit-test/tests/ion/inlining/getelem-getter-bailout.js
Normal file
@ -0,0 +1,48 @@
|
||||
// Test bailouts in inlined jsop_getelem accesses.
|
||||
|
||||
// Defined outside of the test functions to ensure they're recognised as
|
||||
// constants in Ion.
|
||||
var atom = "prop";
|
||||
var symbol = Symbol();
|
||||
|
||||
function testAtom() {
|
||||
var holder = {
|
||||
get [atom]() {
|
||||
bailout();
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
function f() {
|
||||
for (var i = 0; i < 2000; ++i) {
|
||||
var x = holder[atom];
|
||||
assertEq(x, 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
testAtom();
|
||||
|
||||
function testSymbol() {
|
||||
var holder = {
|
||||
get [symbol]() {
|
||||
bailout();
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
function f() {
|
||||
for (var i = 0; i < 2000; ++i) {
|
||||
var x = holder[symbol];
|
||||
assertEq(x, 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
testSymbol();
|
@ -0,0 +1,48 @@
|
||||
// Test bailouts in inlined jsop_getelem accesses.
|
||||
|
||||
// Defined outside of the test functions to ensure they're recognised as
|
||||
// constants in Ion.
|
||||
var atom = "prop";
|
||||
var symbol = Symbol();
|
||||
|
||||
function testAtom() {
|
||||
var holder = {
|
||||
get [atom]() {
|
||||
new Error().stack;
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
function f() {
|
||||
for (var i = 0; i < 2000; ++i) {
|
||||
var x = holder[atom];
|
||||
assertEq(x, 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
testAtom();
|
||||
|
||||
function testSymbol() {
|
||||
var holder = {
|
||||
get [symbol]() {
|
||||
new Error().stack;
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
function f() {
|
||||
for (var i = 0; i < 2000; ++i) {
|
||||
var x = holder[symbol];
|
||||
assertEq(x, 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
testSymbol();
|
113
js/src/jit-test/tests/ion/inlining/getelem-getter-id-mismatch.js
Normal file
113
js/src/jit-test/tests/ion/inlining/getelem-getter-id-mismatch.js
Normal file
@ -0,0 +1,113 @@
|
||||
// Ensure BaselineInspector properly handles mixed atom/symbols when determining
|
||||
// whether or not a jsop_getelem access to a getter can be inlined.
|
||||
|
||||
// Defined outside of the test functions to ensure they're recognised as
|
||||
// constants in Ion.
|
||||
var atom1 = "prop1";
|
||||
var atom2 = "prop2";
|
||||
var sym1 = Symbol();
|
||||
var sym2 = Symbol();
|
||||
|
||||
function testAtomAtom() {
|
||||
var holder = {
|
||||
get [atom1]() { return 1; },
|
||||
get [atom2]() { return 2; },
|
||||
};
|
||||
|
||||
function get(name) {
|
||||
// Single access point called with different atoms.
|
||||
return holder[name];
|
||||
}
|
||||
|
||||
function f() {
|
||||
for (var i = 0; i < 1000; ++i) {
|
||||
var x = get(atom1);
|
||||
var y = get(atom2);
|
||||
assertEq(x, 1);
|
||||
assertEq(y, 2);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
testAtomAtom();
|
||||
|
||||
function testAtomSymbol() {
|
||||
var holder = {
|
||||
get [atom1]() { return 1; },
|
||||
get [sym2]() { return 2; },
|
||||
};
|
||||
|
||||
function get(name) {
|
||||
// Single access point called with atom and symbol.
|
||||
return holder[name];
|
||||
}
|
||||
|
||||
function f() {
|
||||
for (var i = 0; i < 1000; ++i) {
|
||||
var x = get(atom1);
|
||||
var y = get(sym2);
|
||||
assertEq(x, 1);
|
||||
assertEq(y, 2);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
testAtomSymbol();
|
||||
|
||||
function testSymbolAtom() {
|
||||
var holder = {
|
||||
get [sym1]() { return 1; },
|
||||
get [atom2]() { return 2; },
|
||||
};
|
||||
|
||||
function get(name) {
|
||||
// Single access point called with symbol and atom.
|
||||
return holder[name];
|
||||
}
|
||||
|
||||
function f() {
|
||||
for (var i = 0; i < 1000; ++i) {
|
||||
var x = get(sym1);
|
||||
var y = get(atom2);
|
||||
assertEq(x, 1);
|
||||
assertEq(y, 2);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
testSymbolAtom();
|
||||
|
||||
function testSymbolSymbol() {
|
||||
var holder = {
|
||||
get [sym1]() { return 1; },
|
||||
get [sym2]() { return 2; },
|
||||
};
|
||||
|
||||
function get(name) {
|
||||
// Single access point called with different symbols.
|
||||
return holder[name];
|
||||
}
|
||||
|
||||
function f() {
|
||||
for (var i = 0; i < 1000; ++i) {
|
||||
var x = get(sym1);
|
||||
var y = get(sym2);
|
||||
assertEq(x, 1);
|
||||
assertEq(y, 2);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
testSymbolSymbol();
|
@ -0,0 +1,79 @@
|
||||
// Test for inlined getters for jsop_getelem accesses, where the getter is a
|
||||
// property on the prototype and a megamorphic IC is attached.
|
||||
|
||||
function makeObjects(name) {
|
||||
class Base {
|
||||
constructor(v) {
|
||||
this._prop = v;
|
||||
}
|
||||
get [name]() {
|
||||
return this._prop;
|
||||
}
|
||||
}
|
||||
|
||||
// When we hit |TYPE_FLAG_OBJECT_COUNT_LIMIT|, the objects are marked as
|
||||
// |TYPE_FLAG_ANYOBJECT|. That means less than |TYPE_FLAG_OBJECT_COUNT_LIMIT|
|
||||
// objects need to be created to have no unknown objects in the type set.
|
||||
const TYPE_FLAG_OBJECT_COUNT_LIMIT = 7;
|
||||
|
||||
// |ICState::ICState::MaxOptimizedStubs| defines the maximum number of
|
||||
// optimized stubs, so as soon as we hit the maximum number, the megamorphic
|
||||
// state is entered.
|
||||
const ICState_MaxOptimizedStubs = 6;
|
||||
|
||||
// Create enough classes to enter megamorphic state, but not too much to
|
||||
// have |TYPE_FLAG_ANYOBJECT| in the TypeSet.
|
||||
const OBJECT_COUNT = Math.min(ICState_MaxOptimizedStubs, TYPE_FLAG_OBJECT_COUNT_LIMIT);
|
||||
|
||||
var objects = [];
|
||||
for (var i = 0; i < OBJECT_COUNT; ++i) {
|
||||
objects.push(new class extends Base {}(1));
|
||||
}
|
||||
|
||||
return objects;
|
||||
}
|
||||
|
||||
// Defined outside of the test functions to ensure they're recognised as
|
||||
// constants in Ion.
|
||||
var atom = "prop";
|
||||
var symbol = Symbol();
|
||||
|
||||
function testAtom() {
|
||||
var objects = makeObjects(atom);
|
||||
|
||||
function f() {
|
||||
var actual = 0;
|
||||
var expected = 0;
|
||||
for (var i = 0; i < 1000; i++) {
|
||||
var obj = objects[i % objects.length];
|
||||
actual += obj[atom];
|
||||
expected += obj._prop;
|
||||
}
|
||||
assertEq(actual, expected);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; ++i) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
testAtom();
|
||||
|
||||
function testSymbol() {
|
||||
var objects = makeObjects(symbol);
|
||||
|
||||
function f() {
|
||||
var actual = 0;
|
||||
var expected = 0;
|
||||
for (var i = 0; i < 1000; i++) {
|
||||
var obj = objects[i % objects.length];
|
||||
actual += obj[symbol];
|
||||
expected += obj._prop;
|
||||
}
|
||||
assertEq(actual, expected);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; ++i) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
testSymbol();
|
@ -0,0 +1,52 @@
|
||||
// With-Statements are not supported in Ion, therefore functions containing
|
||||
// them can't be inlined in Ion. However it's still possible to inline the
|
||||
// property access to the getter into a simple guard-shape instruction.
|
||||
|
||||
// Defined outside of the test functions to ensure they're recognised as
|
||||
// constants in Ion.
|
||||
var atom = "prop";
|
||||
var symbol = Symbol();
|
||||
|
||||
function testAtom() {
|
||||
var holder = {
|
||||
get [atom]() {
|
||||
with ({}) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function f() {
|
||||
for (var i = 0; i < 1000; ++i) {
|
||||
var x = holder[atom];
|
||||
assertEq(x, 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
testAtom();
|
||||
|
||||
function testSymbol() {
|
||||
var holder = {
|
||||
get [symbol]() {
|
||||
with ({}) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function f() {
|
||||
for (var i = 0; i < 1000; ++i) {
|
||||
var x = holder[symbol];
|
||||
assertEq(x, 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
testSymbol();
|
51
js/src/jit-test/tests/ion/inlining/getelem-getter-own.js
Normal file
51
js/src/jit-test/tests/ion/inlining/getelem-getter-own.js
Normal file
@ -0,0 +1,51 @@
|
||||
// Test for inlined getters for jsop_getelem accesses, where the getter is an
|
||||
// own property.
|
||||
|
||||
// Defined outside of the test functions to ensure they're recognised as
|
||||
// constants in Ion.
|
||||
var atom1 = "prop1";
|
||||
var atom2 = "prop2";
|
||||
var sym1 = Symbol();
|
||||
var sym2 = Symbol();
|
||||
|
||||
function testAtom() {
|
||||
var holder = {
|
||||
get [atom1]() { return 1; },
|
||||
get [atom2]() { return 2; },
|
||||
};
|
||||
|
||||
function f() {
|
||||
for (var i = 0; i < 1000; ++i) {
|
||||
var x = holder[atom1];
|
||||
var y = holder[atom2];
|
||||
assertEq(x, 1);
|
||||
assertEq(y, 2);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
testAtom();
|
||||
|
||||
function testSymbol() {
|
||||
var holder = {
|
||||
get [sym1]() { return 1; },
|
||||
get [sym2]() { return 2; },
|
||||
};
|
||||
|
||||
function f() {
|
||||
for (var i = 0; i < 1000; ++i) {
|
||||
var x = holder[sym1];
|
||||
var y = holder[sym2];
|
||||
assertEq(x, 1);
|
||||
assertEq(y, 2);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
testSymbol();
|
55
js/src/jit-test/tests/ion/inlining/getelem-getter-proto.js
Normal file
55
js/src/jit-test/tests/ion/inlining/getelem-getter-proto.js
Normal file
@ -0,0 +1,55 @@
|
||||
// Test for inlined getters for jsop_getelem accesses, where the getter is a
|
||||
// property on the prototype.
|
||||
|
||||
// Defined outside of the test functions to ensure they're recognised as
|
||||
// constants in Ion.
|
||||
var atom1 = "prop1";
|
||||
var atom2 = "prop2";
|
||||
var sym1 = Symbol();
|
||||
var sym2 = Symbol();
|
||||
|
||||
function testAtom() {
|
||||
var proto = {
|
||||
get [atom1]() { return 1; },
|
||||
get [atom2]() { return 2; },
|
||||
};
|
||||
|
||||
var holder = Object.create(proto);
|
||||
|
||||
function f() {
|
||||
for (var i = 0; i < 1000; ++i) {
|
||||
var x = holder[atom1];
|
||||
var y = holder[atom2];
|
||||
assertEq(x, 1);
|
||||
assertEq(y, 2);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
testAtom();
|
||||
|
||||
function testSymbol() {
|
||||
var proto = {
|
||||
get [sym1]() { return 1; },
|
||||
get [sym2]() { return 2; },
|
||||
};
|
||||
|
||||
var holder = Object.create(proto);
|
||||
|
||||
function f() {
|
||||
for (var i = 0; i < 1000; ++i) {
|
||||
var x = holder[sym1];
|
||||
var y = holder[sym2];
|
||||
assertEq(x, 1);
|
||||
assertEq(y, 2);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
testSymbol();
|
@ -13,6 +13,7 @@
|
||||
#include "jit/BaselineIC.h"
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/CompileInfo.h"
|
||||
#include "jit/Ion.h"
|
||||
#include "jit/JitSpewer.h"
|
||||
#include "jit/mips32/Simulator-mips32.h"
|
||||
#include "jit/mips64/Simulator-mips64.h"
|
||||
@ -436,7 +437,7 @@ struct BaselineStackBuilder {
|
||||
#ifdef DEBUG
|
||||
static inline bool IsInlinableFallback(ICFallbackStub* icEntry) {
|
||||
return icEntry->isCall_Fallback() || icEntry->isGetProp_Fallback() ||
|
||||
icEntry->isSetProp_Fallback();
|
||||
icEntry->isSetProp_Fallback() || icEntry->isGetElem_Fallback();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -449,6 +450,9 @@ static inline void* GetStubReturnAddress(JSContext* cx, jsbytecode* pc) {
|
||||
if (IsSetPropPC(pc)) {
|
||||
return jitRealm->bailoutReturnAddr(BailoutReturnStub::SetProp);
|
||||
}
|
||||
if (IsGetElemPC(pc)) {
|
||||
return jitRealm->bailoutReturnAddr(BailoutReturnStub::GetElem);
|
||||
}
|
||||
|
||||
// This should be a call op of some kind, now.
|
||||
MOZ_ASSERT(IsCallPC(pc) && !IsSpreadCallPC(pc));
|
||||
@ -901,7 +905,7 @@ static bool InitFromBailout(JSContext* cx, size_t frameNo, HandleFunction fun,
|
||||
uint32_t pushedSlots = 0;
|
||||
AutoValueVector savedCallerArgs(cx);
|
||||
bool needToSaveArgs =
|
||||
op == JSOP_FUNAPPLY || IsGetPropPC(pc) || IsSetPropPC(pc);
|
||||
op == JSOP_FUNAPPLY || IsIonInlinableGetterOrSetterPC(pc);
|
||||
if (iter.moreFrames() && (op == JSOP_FUNCALL || needToSaveArgs)) {
|
||||
uint32_t inlined_args = 0;
|
||||
if (op == JSOP_FUNCALL) {
|
||||
@ -909,7 +913,7 @@ static bool InitFromBailout(JSContext* cx, size_t frameNo, HandleFunction fun,
|
||||
} else if (op == JSOP_FUNAPPLY) {
|
||||
inlined_args = 2 + blFrame->numActualArgs();
|
||||
} else {
|
||||
MOZ_ASSERT(IsGetPropPC(pc) || IsSetPropPC(pc));
|
||||
MOZ_ASSERT(IsIonInlinableGetterOrSetterPC(pc));
|
||||
inlined_args = 2 + IsSetPropPC(pc);
|
||||
}
|
||||
|
||||
@ -1085,16 +1089,18 @@ static bool InitFromBailout(JSContext* cx, size_t frameNo, HandleFunction fun,
|
||||
// include the this. When inlining that is not included.
|
||||
// So the exprStackSlots will be one less.
|
||||
MOZ_ASSERT(expectedDepth - exprStackSlots <= 1);
|
||||
} else if (iter.moreFrames() && (IsGetPropPC(pc) || IsSetPropPC(pc))) {
|
||||
} else if (iter.moreFrames() && IsIonInlinableGetterOrSetterPC(pc)) {
|
||||
// Accessors coming out of ion are inlined via a complete
|
||||
// lie perpetrated by the compiler internally. Ion just rearranges
|
||||
// the stack, and pretends that it looked like a call all along.
|
||||
// This means that the depth is actually one *more* than expected
|
||||
// by the interpreter, as there is now a JSFunction, |this| and [arg],
|
||||
// rather than the expected |this| and [arg]
|
||||
// rather than the expected |this| and [arg].
|
||||
// If the inlined accessor is a getelem operation, the numbers do match,
|
||||
// but that's just because getelem expects one more item on the stack.
|
||||
// Note that none of that was pushed, but it's still reflected
|
||||
// in exprStackSlots.
|
||||
MOZ_ASSERT(exprStackSlots - expectedDepth == 1);
|
||||
MOZ_ASSERT(exprStackSlots - expectedDepth == (IsGetElemPC(pc) ? 0 : 1));
|
||||
} else {
|
||||
// For fun.apply({}, arguments) the reconstructStackDepth will
|
||||
// have stackdepth 4, but it could be that we inlined the
|
||||
|
@ -499,11 +499,12 @@ void ICStubIterator::unlink(JSContext* cx) {
|
||||
case Call_ScriptedFunCall:
|
||||
case Call_ConstStringSplit:
|
||||
case WarmUpCounter_Fallback:
|
||||
// These two fallback stubs don't actually make non-tail calls,
|
||||
// These three fallback stubs don't actually make non-tail calls,
|
||||
// but the fallback code for the bailout path needs to pop the stub frame
|
||||
// pushed during the bailout.
|
||||
case GetProp_Fallback:
|
||||
case SetProp_Fallback:
|
||||
case GetElem_Fallback:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@ -2195,22 +2196,55 @@ bool ICGetElem_Fallback::Compiler::generateStubCode(MacroAssembler& masm) {
|
||||
masm.pushValue(R1); // Index
|
||||
masm.pushValue(Address(masm.getStackPointer(), sizeof(Value) * 5)); // Obj
|
||||
masm.push(ICStubReg);
|
||||
pushStubPayload(masm, R0.scratchReg());
|
||||
masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
|
||||
return tailCallVM(DoGetElemSuperFallbackInfo, masm);
|
||||
if (!tailCallVM(DoGetElemSuperFallbackInfo, masm)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Ensure stack is fully synced for the expression decompiler.
|
||||
masm.pushValue(R0);
|
||||
masm.pushValue(R1);
|
||||
|
||||
// Push arguments.
|
||||
masm.pushValue(R1);
|
||||
masm.pushValue(R0);
|
||||
masm.push(ICStubReg);
|
||||
masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
|
||||
if (!tailCallVM(DoGetElemFallbackInfo, masm)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure stack is fully synced for the expression decompiler.
|
||||
masm.pushValue(R0);
|
||||
masm.pushValue(R1);
|
||||
// This is the resume point used when bailout rewrites call stack to undo
|
||||
// Ion inlined frames. The return address pushed onto reconstructed stack
|
||||
// will point here.
|
||||
assumeStubFrame();
|
||||
bailoutReturnOffset_.bind(masm.currentOffset());
|
||||
|
||||
// Push arguments.
|
||||
masm.pushValue(R1);
|
||||
masm.pushValue(R0);
|
||||
masm.push(ICStubReg);
|
||||
pushStubPayload(masm, R0.scratchReg());
|
||||
leaveStubFrame(masm, true);
|
||||
|
||||
return tailCallVM(DoGetElemFallbackInfo, masm);
|
||||
// When we get here, ICStubReg contains the ICGetElem_Fallback stub,
|
||||
// which we can't use to enter the TypeMonitor IC, because it's a
|
||||
// MonitoredFallbackStub instead of a MonitoredStub. So, we cheat. Note that
|
||||
// we must have a non-null fallbackMonitorStub here because InitFromBailout
|
||||
// delazifies.
|
||||
masm.loadPtr(Address(ICStubReg,
|
||||
ICMonitoredFallbackStub::offsetOfFallbackMonitorStub()),
|
||||
ICStubReg);
|
||||
EmitEnterTypeMonitorIC(masm,
|
||||
ICTypeMonitor_Fallback::offsetOfFirstMonitorStub());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ICGetElem_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm,
|
||||
Handle<JitCode*> code) {
|
||||
BailoutReturnStub kind = hasReceiver_ ? BailoutReturnStub::GetElemSuper
|
||||
: BailoutReturnStub::GetElem;
|
||||
void* address = code->raw() + bailoutReturnOffset_.offset();
|
||||
cx->realm()->jitRealm()->initBailoutReturnAddr(address, getKey(), kind);
|
||||
}
|
||||
|
||||
static void SetUpdateStubData(ICCacheIR_Updated* stub,
|
||||
|
@ -1694,8 +1694,11 @@ class ICGetElem_Fallback : public ICMonitoredFallbackStub {
|
||||
// Compiler for this stub kind.
|
||||
class Compiler : public ICStubCompiler {
|
||||
protected:
|
||||
CodeOffset bailoutReturnOffset_;
|
||||
bool hasReceiver_;
|
||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
|
||||
void postGenerateStubCode(MacroAssembler& masm,
|
||||
Handle<JitCode*> code) override;
|
||||
|
||||
virtual int32_t getKey() const override {
|
||||
return static_cast<int32_t>(kind) |
|
||||
|
@ -1066,8 +1066,42 @@ static bool AddCacheIRGlobalGetter(
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool GuardSpecificAtomOrSymbol(CacheIRReader& reader, ICStub* stub,
|
||||
const CacheIRStubInfo* stubInfo,
|
||||
ValOperandId keyId, jsid id) {
|
||||
// Try to match an id guard emitted by IRGenerator::emitIdGuard.
|
||||
if (JSID_IS_ATOM(id)) {
|
||||
if (!reader.matchOp(CacheOp::GuardIsString, keyId)) {
|
||||
return false;
|
||||
}
|
||||
if (!reader.matchOp(CacheOp::GuardSpecificAtom, keyId)) {
|
||||
return false;
|
||||
}
|
||||
JSString* str =
|
||||
stubInfo->getStubField<JSString*>(stub, reader.stubOffset()).get();
|
||||
if (AtomToId(&str->asAtom()) != id) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(JSID_IS_SYMBOL(id));
|
||||
if (!reader.matchOp(CacheOp::GuardIsSymbol, keyId)) {
|
||||
return false;
|
||||
}
|
||||
if (!reader.matchOp(CacheOp::GuardSpecificSymbol, keyId)) {
|
||||
return false;
|
||||
}
|
||||
Symbol* sym =
|
||||
stubInfo->getStubField<Symbol*>(stub, reader.stubOffset()).get();
|
||||
if (SYMBOL_TO_JSID(sym) != id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool AddCacheIRGetPropFunction(
|
||||
ICCacheIR_Monitored* stub, bool innerized, JSObject** holder,
|
||||
ICCacheIR_Monitored* stub, jsid id, bool innerized, JSObject** holder,
|
||||
Shape** holderShape, JSFunction** commonGetter, Shape** globalShape,
|
||||
bool* isOwnProperty, BaselineInspector::ReceiverVector& receivers,
|
||||
BaselineInspector::ObjectGroupVector& convertUnboxedGroups,
|
||||
@ -1075,6 +1109,7 @@ static bool AddCacheIRGetPropFunction(
|
||||
// We match either an own getter:
|
||||
//
|
||||
// GuardIsObject objId
|
||||
// [..Id Guard..]
|
||||
// [..WindowProxy innerization..]
|
||||
// <GuardReceiver objId>
|
||||
// Call(Scripted|Native)GetterResult objId
|
||||
@ -1082,6 +1117,7 @@ static bool AddCacheIRGetPropFunction(
|
||||
// Or a getter on the prototype:
|
||||
//
|
||||
// GuardIsObject objId
|
||||
// [..Id Guard..]
|
||||
// [..WindowProxy innerization..]
|
||||
// <GuardReceiver objId>
|
||||
// LoadObject holderId
|
||||
@ -1096,6 +1132,10 @@ static bool AddCacheIRGetPropFunction(
|
||||
// GuardClass objId WindowProxy
|
||||
// objId = LoadWrapperTarget objId
|
||||
// GuardSpecificObject objId, <global>
|
||||
//
|
||||
// If we test for a specific jsid, [..Id Guard..] is implemented through:
|
||||
// GuardIs(String|Symbol) keyId
|
||||
// GuardSpecific(Atom|Symbol) keyId, <atom|symbol>
|
||||
|
||||
CacheIRReader reader(stub->stubInfo());
|
||||
|
||||
@ -1106,6 +1146,13 @@ static bool AddCacheIRGetPropFunction(
|
||||
receivers, convertUnboxedGroups, script);
|
||||
}
|
||||
|
||||
if (!JSID_IS_EMPTY(id)) {
|
||||
ValOperandId keyId = ValOperandId(1);
|
||||
if (!GuardSpecificAtomOrSymbol(reader, stub, stub->stubInfo(), keyId, id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (innerized) {
|
||||
if (!reader.matchOp(CacheOp::GuardClass, objId) ||
|
||||
reader.guardClassKind() != GuardClassKind::WindowProxy) {
|
||||
@ -1211,23 +1258,30 @@ static bool AddCacheIRGetPropFunction(
|
||||
}
|
||||
|
||||
bool BaselineInspector::commonGetPropFunction(
|
||||
jsbytecode* pc, bool innerized, JSObject** holder, Shape** holderShape,
|
||||
JSFunction** commonGetter, Shape** globalShape, bool* isOwnProperty,
|
||||
ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups) {
|
||||
jsbytecode* pc, jsid id, bool innerized, JSObject** holder,
|
||||
Shape** holderShape, JSFunction** commonGetter, Shape** globalShape,
|
||||
bool* isOwnProperty, ReceiverVector& receivers,
|
||||
ObjectGroupVector& convertUnboxedGroups) {
|
||||
if (!hasICScript()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(IsGetPropPC(pc) || IsGetElemPC(pc) || JSOp(*pc) == JSOP_GETGNAME);
|
||||
MOZ_ASSERT(receivers.empty());
|
||||
MOZ_ASSERT(convertUnboxedGroups.empty());
|
||||
|
||||
// Only GetElem operations need to guard against a specific property id.
|
||||
if (!IsGetElemPC(pc)) {
|
||||
id = JSID_EMPTY;
|
||||
}
|
||||
|
||||
*globalShape = nullptr;
|
||||
*commonGetter = nullptr;
|
||||
const ICEntry& entry = icEntryFromPC(pc);
|
||||
|
||||
for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
|
||||
if (stub->isCacheIR_Monitored()) {
|
||||
if (!AddCacheIRGetPropFunction(stub->toCacheIR_Monitored(), innerized,
|
||||
if (!AddCacheIRGetPropFunction(stub->toCacheIR_Monitored(), id, innerized,
|
||||
holder, holderShape, commonGetter,
|
||||
globalShape, isOwnProperty, receivers,
|
||||
convertUnboxedGroups, script)) {
|
||||
@ -1254,13 +1308,18 @@ bool BaselineInspector::commonGetPropFunction(
|
||||
}
|
||||
|
||||
static JSFunction* GetMegamorphicGetterSetterFunction(
|
||||
ICStub* stub, const CacheIRStubInfo* stubInfo, bool isGetter) {
|
||||
ICStub* stub, const CacheIRStubInfo* stubInfo, jsid id, bool isGetter) {
|
||||
// We match:
|
||||
//
|
||||
// GuardIsObject objId
|
||||
// [..Id Guard..]
|
||||
// GuardHasGetterSetter objId propShape
|
||||
//
|
||||
// propShape has the getter/setter we're interested in.
|
||||
//
|
||||
// If we test for a specific jsid, [..Id Guard..] is implemented through:
|
||||
// GuardIs(String|Symbol) keyId
|
||||
// GuardSpecific(Atom|Symbol) keyId, <atom|symbol>
|
||||
|
||||
CacheIRReader reader(stubInfo);
|
||||
|
||||
@ -1269,6 +1328,13 @@ static JSFunction* GetMegamorphicGetterSetterFunction(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!JSID_IS_EMPTY(id)) {
|
||||
ValOperandId keyId = ValOperandId(1);
|
||||
if (!GuardSpecificAtomOrSymbol(reader, stub, stubInfo, keyId, id)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!reader.matchOp(CacheOp::GuardHasGetterSetter, objId)) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -1280,11 +1346,21 @@ static JSFunction* GetMegamorphicGetterSetterFunction(
|
||||
}
|
||||
|
||||
bool BaselineInspector::megamorphicGetterSetterFunction(
|
||||
jsbytecode* pc, bool isGetter, JSFunction** getterOrSetter) {
|
||||
jsbytecode* pc, jsid id, bool isGetter, JSFunction** getterOrSetter) {
|
||||
if (!hasICScript()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(IsGetPropPC(pc) || IsGetElemPC(pc) || IsSetPropPC(pc) ||
|
||||
JSOp(*pc) == JSOP_GETGNAME || JSOp(*pc) == JSOP_INITGLEXICAL ||
|
||||
JSOp(*pc) == JSOP_INITPROP || JSOp(*pc) == JSOP_INITLOCKEDPROP ||
|
||||
JSOp(*pc) == JSOP_INITHIDDENPROP);
|
||||
|
||||
// Only GetElem operations need to guard against a specific property id.
|
||||
if (!IsGetElemPC(pc)) {
|
||||
id = JSID_EMPTY;
|
||||
}
|
||||
|
||||
*getterOrSetter = nullptr;
|
||||
const ICEntry& entry = icEntryFromPC(pc);
|
||||
|
||||
@ -1292,7 +1368,7 @@ bool BaselineInspector::megamorphicGetterSetterFunction(
|
||||
if (stub->isCacheIR_Monitored()) {
|
||||
MOZ_ASSERT(isGetter);
|
||||
JSFunction* getter = GetMegamorphicGetterSetterFunction(
|
||||
stub, stub->toCacheIR_Monitored()->stubInfo(), isGetter);
|
||||
stub, stub->toCacheIR_Monitored()->stubInfo(), id, isGetter);
|
||||
if (!getter || (*getterOrSetter && *getterOrSetter != getter)) {
|
||||
return false;
|
||||
}
|
||||
@ -1302,7 +1378,7 @@ bool BaselineInspector::megamorphicGetterSetterFunction(
|
||||
if (stub->isCacheIR_Updated()) {
|
||||
MOZ_ASSERT(!isGetter);
|
||||
JSFunction* setter = GetMegamorphicGetterSetterFunction(
|
||||
stub, stub->toCacheIR_Updated()->stubInfo(), isGetter);
|
||||
stub, stub->toCacheIR_Updated()->stubInfo(), id, isGetter);
|
||||
if (!setter || (*getterOrSetter && *getterOrSetter != setter)) {
|
||||
return false;
|
||||
}
|
||||
@ -1439,6 +1515,9 @@ bool BaselineInspector::commonSetPropFunction(
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(IsSetPropPC(pc) || JSOp(*pc) == JSOP_INITGLEXICAL ||
|
||||
JSOp(*pc) == JSOP_INITPROP || JSOp(*pc) == JSOP_INITLOCKEDPROP ||
|
||||
JSOp(*pc) == JSOP_INITHIDDENPROP);
|
||||
MOZ_ASSERT(receivers.empty());
|
||||
MOZ_ASSERT(convertUnboxedGroups.empty());
|
||||
|
||||
|
@ -121,12 +121,13 @@ class BaselineInspector {
|
||||
// global object) instead. In this case we should only look for Baseline
|
||||
// stubs that performed the same optimization.
|
||||
MOZ_MUST_USE bool commonGetPropFunction(
|
||||
jsbytecode* pc, bool innerized, JSObject** holder, Shape** holderShape,
|
||||
JSFunction** commonGetter, Shape** globalShape, bool* isOwnProperty,
|
||||
ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups);
|
||||
jsbytecode* pc, jsid id, bool innerized, JSObject** holder,
|
||||
Shape** holderShape, JSFunction** commonGetter, Shape** globalShape,
|
||||
bool* isOwnProperty, ReceiverVector& receivers,
|
||||
ObjectGroupVector& convertUnboxedGroups);
|
||||
|
||||
MOZ_MUST_USE bool megamorphicGetterSetterFunction(
|
||||
jsbytecode* pc, bool isGetter, JSFunction** getterOrSetter);
|
||||
jsbytecode* pc, jsid id, bool isGetter, JSFunction** getterOrSetter);
|
||||
|
||||
MOZ_MUST_USE bool commonSetPropFunction(
|
||||
jsbytecode* pc, JSObject** holder, Shape** holderShape,
|
||||
|
@ -169,12 +169,17 @@ static inline bool IsIonEnabled(JSContext* cx) {
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool IsIonInlinableGetterOrSetterPC(jsbytecode* pc) {
|
||||
// GETPROP, CALLPROP, LENGTH, GETELEM, and JSOP_CALLELEM. (Inlined Getters)
|
||||
// SETPROP, SETNAME, SETGNAME (Inlined Setters)
|
||||
return IsGetPropPC(pc) || IsGetElemPC(pc) || IsSetPropPC(pc);
|
||||
}
|
||||
|
||||
inline bool IsIonInlinablePC(jsbytecode* pc) {
|
||||
// CALL, FUNCALL, FUNAPPLY, EVAL, NEW (Normal Callsites)
|
||||
// GETPROP, CALLPROP, and LENGTH. (Inlined Getters)
|
||||
// SETPROP, SETNAME, SETGNAME (Inlined Setters)
|
||||
return (IsCallPC(pc) && !IsSpreadCallPC(pc)) || IsGetPropPC(pc) ||
|
||||
IsSetPropPC(pc);
|
||||
// or an inlinable getter or setter.
|
||||
return (IsCallPC(pc) && !IsSpreadCallPC(pc)) ||
|
||||
IsIonInlinableGetterOrSetterPC(pc);
|
||||
}
|
||||
|
||||
inline bool TooManyActualArguments(unsigned nargs) {
|
||||
|
@ -7996,7 +7996,8 @@ AbortReasonOr<Ok> IonBuilder::jsop_getgname(PropertyName* name) {
|
||||
if (!forceInlineCaches() && obj->is<GlobalObject>()) {
|
||||
TemporaryTypeSet* types = bytecodeTypes(pc);
|
||||
MDefinition* globalObj = constant(ObjectValue(*obj));
|
||||
MOZ_TRY(getPropTryCommonGetter(&emitted, globalObj, name, types));
|
||||
MOZ_TRY(
|
||||
getPropTryCommonGetter(&emitted, globalObj, NameToId(name), types));
|
||||
if (emitted) {
|
||||
return Ok();
|
||||
}
|
||||
@ -8586,6 +8587,13 @@ AbortReasonOr<Ok> IonBuilder::getElemTryGetProp(bool* emitted, MDefinition* obj,
|
||||
return Ok();
|
||||
}
|
||||
|
||||
trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter);
|
||||
MOZ_TRY(getPropTryCommonGetter(emitted, obj, id, types));
|
||||
if (*emitted) {
|
||||
index->setImplicitlyUsedUnchecked();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
@ -10134,10 +10142,10 @@ AbortReasonOr<Ok> IonBuilder::jsop_getelem_super() {
|
||||
}
|
||||
|
||||
NativeObject* IonBuilder::commonPrototypeWithGetterSetter(
|
||||
TemporaryTypeSet* types, PropertyName* name, bool isGetter,
|
||||
JSFunction* getterOrSetter, bool* guardGlobal) {
|
||||
TemporaryTypeSet* types, jsid id, bool isGetter, JSFunction* getterOrSetter,
|
||||
bool* guardGlobal) {
|
||||
// If there's a single object on the proto chain of all objects in |types|
|
||||
// that contains a property |name| with |getterOrSetter| getter or setter
|
||||
// that contains a property |id| with |getterOrSetter| getter or setter
|
||||
// function, return that object.
|
||||
|
||||
// No sense looking if we don't know what's going on.
|
||||
@ -10163,7 +10171,7 @@ NativeObject* IonBuilder::commonPrototypeWithGetterSetter(
|
||||
return nullptr;
|
||||
}
|
||||
JSObject* singleton = key->isSingleton() ? key->singleton() : nullptr;
|
||||
if (ObjectHasExtraOwnProperty(realm, key, NameToId(name))) {
|
||||
if (ObjectHasExtraOwnProperty(realm, key, id)) {
|
||||
if (!singleton || !singleton->is<GlobalObject>()) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -10195,7 +10203,7 @@ NativeObject* IonBuilder::commonPrototypeWithGetterSetter(
|
||||
}
|
||||
|
||||
NativeObject* singletonNative = &singleton->as<NativeObject>();
|
||||
if (Shape* propShape = singletonNative->lookupPure(name)) {
|
||||
if (Shape* propShape = singletonNative->lookupPure(id)) {
|
||||
// We found a property. Check if it's the getter or setter
|
||||
// we're looking for.
|
||||
Value getterSetterVal = ObjectValue(*getterOrSetter);
|
||||
@ -10221,7 +10229,7 @@ NativeObject* IonBuilder::commonPrototypeWithGetterSetter(
|
||||
// Test for isOwnProperty() without freezing. If we end up
|
||||
// optimizing, freezePropertiesForCommonPropFunc will freeze the
|
||||
// property type sets later on.
|
||||
HeapTypeSetKey property = key->property(NameToId(name));
|
||||
HeapTypeSetKey property = key->property(id);
|
||||
if (TypeSet* types = property.maybeTypes()) {
|
||||
if (!types->empty() || types->nonDataProperty()) {
|
||||
return nullptr;
|
||||
@ -10254,8 +10262,8 @@ NativeObject* IonBuilder::commonPrototypeWithGetterSetter(
|
||||
}
|
||||
|
||||
AbortReasonOr<Ok> IonBuilder::freezePropertiesForCommonPrototype(
|
||||
TemporaryTypeSet* types, PropertyName* name, JSObject* foundProto,
|
||||
bool allowEmptyTypesforGlobal /* = false*/) {
|
||||
TemporaryTypeSet* types, jsid id, JSObject* foundProto,
|
||||
bool allowEmptyTypesforGlobal) {
|
||||
for (unsigned i = 0; i < types->getObjectCount(); i++) {
|
||||
// If we found a Singleton object's own-property, there's nothing to
|
||||
// freeze.
|
||||
@ -10273,7 +10281,7 @@ AbortReasonOr<Ok> IonBuilder::freezePropertiesForCommonPrototype(
|
||||
return abort(AbortReason::Alloc);
|
||||
}
|
||||
|
||||
HeapTypeSetKey property = key->property(NameToId(name));
|
||||
HeapTypeSetKey property = key->property(id);
|
||||
MOZ_ALWAYS_TRUE(
|
||||
!property.isOwnProperty(constraints(), allowEmptyTypesforGlobal));
|
||||
|
||||
@ -10290,9 +10298,8 @@ AbortReasonOr<Ok> IonBuilder::freezePropertiesForCommonPrototype(
|
||||
}
|
||||
|
||||
AbortReasonOr<bool> IonBuilder::testCommonGetterSetter(
|
||||
TemporaryTypeSet* types, PropertyName* name, bool isGetter,
|
||||
JSFunction* getterOrSetter, MDefinition** guard,
|
||||
Shape* globalShape /* = nullptr*/,
|
||||
TemporaryTypeSet* types, jsid id, bool isGetter, JSFunction* getterOrSetter,
|
||||
MDefinition** guard, Shape* globalShape /* = nullptr*/,
|
||||
MDefinition** globalGuard /* = nullptr */) {
|
||||
MOZ_ASSERT(getterOrSetter);
|
||||
MOZ_ASSERT_IF(globalShape, globalGuard);
|
||||
@ -10301,7 +10308,7 @@ AbortReasonOr<bool> IonBuilder::testCommonGetterSetter(
|
||||
// Check if all objects being accessed will lookup the name through
|
||||
// foundProto.
|
||||
NativeObject* foundProto = commonPrototypeWithGetterSetter(
|
||||
types, name, isGetter, getterOrSetter, &guardGlobal);
|
||||
types, id, isGetter, getterOrSetter, &guardGlobal);
|
||||
if (!foundProto || (guardGlobal && !globalShape)) {
|
||||
trackOptimizationOutcome(TrackedOutcome::MultiProtoPaths);
|
||||
return false;
|
||||
@ -10311,7 +10318,7 @@ AbortReasonOr<bool> IonBuilder::testCommonGetterSetter(
|
||||
// ensure there isn't a lower shadowing getter or setter installed in the
|
||||
// future.
|
||||
MOZ_TRY(
|
||||
freezePropertiesForCommonPrototype(types, name, foundProto, guardGlobal));
|
||||
freezePropertiesForCommonPrototype(types, id, foundProto, guardGlobal));
|
||||
|
||||
// Add a shape guard on the prototype we found the property on. The rest of
|
||||
// the prototype chain is guarded by TI freezes, except when name is a global
|
||||
@ -10328,7 +10335,7 @@ AbortReasonOr<bool> IonBuilder::testCommonGetterSetter(
|
||||
|
||||
// If the getter/setter is not configurable we don't have to guard on the
|
||||
// proto's shape.
|
||||
Shape* propShape = foundProto->lookupPure(name);
|
||||
Shape* propShape = foundProto->lookupPure(id);
|
||||
MOZ_ASSERT_IF(isGetter, propShape->getterObject() == getterOrSetter);
|
||||
MOZ_ASSERT_IF(!isGetter, propShape->setterObject() == getterOrSetter);
|
||||
if (propShape && !propShape->configurable()) {
|
||||
@ -10718,7 +10725,7 @@ AbortReasonOr<Ok> IonBuilder::jsop_getprop(PropertyName* name) {
|
||||
|
||||
// Try to inline a common property getter, or make a call.
|
||||
trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter);
|
||||
MOZ_TRY(getPropTryCommonGetter(&emitted, obj, name, types));
|
||||
MOZ_TRY(getPropTryCommonGetter(&emitted, obj, NameToId(name), types));
|
||||
if (emitted) {
|
||||
return Ok();
|
||||
}
|
||||
@ -11328,8 +11335,7 @@ MDefinition* IonBuilder::addShapeGuardsForGetterSetter(
|
||||
}
|
||||
|
||||
AbortReasonOr<Ok> IonBuilder::getPropTryCommonGetter(bool* emitted,
|
||||
MDefinition* obj,
|
||||
PropertyName* name,
|
||||
MDefinition* obj, jsid id,
|
||||
TemporaryTypeSet* types,
|
||||
bool innerized) {
|
||||
MOZ_ASSERT(*emitted == false);
|
||||
@ -11348,14 +11354,14 @@ AbortReasonOr<Ok> IonBuilder::getPropTryCommonGetter(bool* emitted,
|
||||
BaselineInspector::ReceiverVector receivers(alloc());
|
||||
BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
|
||||
if (inspector->commonGetPropFunction(
|
||||
pc, innerized, &foundProto, &lastProperty, &commonGetter,
|
||||
pc, id, innerized, &foundProto, &lastProperty, &commonGetter,
|
||||
&globalShape, &isOwnProperty, receivers, convertUnboxedGroups)) {
|
||||
bool canUseTIForGetter = false;
|
||||
if (!isOwnProperty) {
|
||||
// If it's not an own property, try to use TI to avoid shape guards.
|
||||
// For own properties we use the path below.
|
||||
MOZ_TRY_VAR(canUseTIForGetter,
|
||||
testCommonGetterSetter(objTypes, name,
|
||||
testCommonGetterSetter(objTypes, id,
|
||||
/* isGetter = */ true, commonGetter,
|
||||
&guard, globalShape, &globalGuard));
|
||||
}
|
||||
@ -11370,11 +11376,11 @@ AbortReasonOr<Ok> IonBuilder::getPropTryCommonGetter(bool* emitted,
|
||||
}
|
||||
}
|
||||
} else if (inspector->megamorphicGetterSetterFunction(
|
||||
pc, /* isGetter = */ true, &commonGetter)) {
|
||||
pc, id, /* isGetter = */ true, &commonGetter)) {
|
||||
// Try to use TI to guard on this getter.
|
||||
bool canUseTIForGetter = false;
|
||||
MOZ_TRY_VAR(canUseTIForGetter,
|
||||
testCommonGetterSetter(objTypes, name, /* isGetter = */ true,
|
||||
testCommonGetterSetter(objTypes, id, /* isGetter = */ true,
|
||||
commonGetter, &guard));
|
||||
if (!canUseTIForGetter) {
|
||||
return Ok();
|
||||
@ -11910,7 +11916,7 @@ AbortReasonOr<Ok> IonBuilder::getPropTryInnerize(bool* emitted,
|
||||
}
|
||||
|
||||
trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter);
|
||||
MOZ_TRY(getPropTryCommonGetter(emitted, inner, name, types,
|
||||
MOZ_TRY(getPropTryCommonGetter(emitted, inner, NameToId(name), types,
|
||||
/* innerized = */ true));
|
||||
if (*emitted) {
|
||||
return Ok();
|
||||
@ -12025,10 +12031,10 @@ AbortReasonOr<Ok> IonBuilder::setPropTryCommonSetter(bool* emitted,
|
||||
if (!isOwnProperty) {
|
||||
// If it's not an own property, try to use TI to avoid shape guards.
|
||||
// For own properties we use the path below.
|
||||
MOZ_TRY_VAR(
|
||||
canUseTIForSetter,
|
||||
testCommonGetterSetter(objTypes, name, /* isGetter = */ false,
|
||||
commonSetter, &guard));
|
||||
MOZ_TRY_VAR(canUseTIForSetter,
|
||||
testCommonGetterSetter(objTypes, NameToId(name),
|
||||
/* isGetter = */ false, commonSetter,
|
||||
&guard));
|
||||
}
|
||||
if (!canUseTIForSetter) {
|
||||
// If it's an own property or type information is bad, we can still
|
||||
@ -12041,12 +12047,13 @@ AbortReasonOr<Ok> IonBuilder::setPropTryCommonSetter(bool* emitted,
|
||||
}
|
||||
}
|
||||
} else if (inspector->megamorphicGetterSetterFunction(
|
||||
pc, /* isGetter = */ false, &commonSetter)) {
|
||||
pc, NameToId(name), /* isGetter = */ false, &commonSetter)) {
|
||||
// Try to use TI to guard on this setter.
|
||||
bool canUseTIForSetter = false;
|
||||
MOZ_TRY_VAR(canUseTIForSetter,
|
||||
testCommonGetterSetter(objTypes, name, /* isGetter = */ false,
|
||||
commonSetter, &guard));
|
||||
MOZ_TRY_VAR(
|
||||
canUseTIForSetter,
|
||||
testCommonGetterSetter(objTypes, NameToId(name),
|
||||
/* isGetter = */ false, commonSetter, &guard));
|
||||
if (!canUseTIForSetter) {
|
||||
return Ok();
|
||||
}
|
||||
|
@ -276,8 +276,7 @@ class IonBuilder : public MIRGenerator,
|
||||
PropertyName* name, BarrierKind barrier,
|
||||
TemporaryTypeSet* types);
|
||||
AbortReasonOr<Ok> getPropTryCommonGetter(bool* emitted, MDefinition* obj,
|
||||
PropertyName* name,
|
||||
TemporaryTypeSet* types,
|
||||
jsid id, TemporaryTypeSet* types,
|
||||
bool innerized = false);
|
||||
AbortReasonOr<Ok> getPropTryInlineAccess(bool* emitted, MDefinition* obj,
|
||||
PropertyName* name,
|
||||
@ -864,19 +863,18 @@ class IonBuilder : public MIRGenerator,
|
||||
MDefinition* specializeInlinedReturn(MDefinition* rdef, MBasicBlock* exit);
|
||||
|
||||
NativeObject* commonPrototypeWithGetterSetter(TemporaryTypeSet* types,
|
||||
PropertyName* name,
|
||||
bool isGetter,
|
||||
jsid id, bool isGetter,
|
||||
JSFunction* getterOrSetter,
|
||||
bool* guardGlobal);
|
||||
AbortReasonOr<Ok> freezePropertiesForCommonPrototype(
|
||||
TemporaryTypeSet* types, PropertyName* name, JSObject* foundProto,
|
||||
bool allowEmptyTypesForGlobal = false);
|
||||
TemporaryTypeSet* types, jsid id, JSObject* foundProto,
|
||||
bool allowEmptyTypesForGlobal);
|
||||
/*
|
||||
* Callers must pass a non-null globalGuard if they pass a non-null
|
||||
* globalShape.
|
||||
*/
|
||||
AbortReasonOr<bool> testCommonGetterSetter(
|
||||
TemporaryTypeSet* types, PropertyName* name, bool isGetter,
|
||||
TemporaryTypeSet* types, jsid id, bool isGetter,
|
||||
JSFunction* getterOrSetter, MDefinition** guard,
|
||||
Shape* globalShape = nullptr, MDefinition** globalGuard = nullptr);
|
||||
AbortReasonOr<bool> testShouldDOMCall(TypeSet* inTypes, JSFunction* func,
|
||||
|
@ -2033,7 +2033,7 @@ void InlineFrameIterator::findNextFrame() {
|
||||
if (JSOp(*pc_) == JSOP_FUNCALL) {
|
||||
MOZ_ASSERT(GET_ARGC(pc_) > 0);
|
||||
numActualArgs_ = GET_ARGC(pc_) - 1;
|
||||
} else if (IsGetPropPC(pc_)) {
|
||||
} else if (IsGetPropPC(pc_) || IsGetElemPC(pc_)) {
|
||||
numActualArgs_ = 0;
|
||||
} else if (IsSetPropPC(pc_)) {
|
||||
numActualArgs_ = 1;
|
||||
@ -2207,7 +2207,7 @@ bool InlineFrameIterator::isConstructing() const {
|
||||
++parent;
|
||||
|
||||
// Inlined Getters and Setters are never constructing.
|
||||
if (IsGetPropPC(parent.pc()) || IsSetPropPC(parent.pc())) {
|
||||
if (IsIonInlinableGetterOrSetterPC(parent.pc())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -445,6 +445,8 @@ enum class BailoutReturnStub {
|
||||
GetProp,
|
||||
GetPropSuper,
|
||||
SetProp,
|
||||
GetElem,
|
||||
GetElemSuper,
|
||||
Call,
|
||||
New,
|
||||
Count
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "builtin/String.h"
|
||||
#include "builtin/TypedObject.h"
|
||||
#include "gc/Heap.h"
|
||||
#include "jit/Ion.h"
|
||||
#include "jit/JitSpewer.h"
|
||||
#include "jit/JSJitFrameIter.h"
|
||||
#include "jit/MIR.h"
|
||||
@ -87,8 +88,8 @@ bool MResumePoint::writeRecoverData(CompactBufferWriter& writer) const {
|
||||
// include the this. When inlining that is not included. So the
|
||||
// exprStackSlots will be one less.
|
||||
MOZ_ASSERT(stackDepth - exprStack <= 1);
|
||||
} else if (JSOp(*bailPC) != JSOP_FUNAPPLY && !IsGetPropPC(bailPC) &&
|
||||
!IsSetPropPC(bailPC)) {
|
||||
} else if (JSOp(*bailPC) != JSOP_FUNAPPLY &&
|
||||
!IsIonInlinableGetterOrSetterPC(bailPC)) {
|
||||
// For fun.apply({}, arguments) the reconstructStackDepth will
|
||||
// have stackdepth 4, but it could be that we inlined the
|
||||
// funapply. In that case exprStackSlots, will have the real
|
||||
|
@ -152,8 +152,6 @@ int32_t VideoCaptureImpl::IncomingFrame(uint8_t* videoFrame,
|
||||
return -1;
|
||||
}
|
||||
|
||||
int stride_y = width;
|
||||
int stride_uv = (width + 1) / 2;
|
||||
int target_width = width;
|
||||
int target_height = height;
|
||||
|
||||
@ -167,6 +165,9 @@ int32_t VideoCaptureImpl::IncomingFrame(uint8_t* videoFrame,
|
||||
target_height = width;
|
||||
}
|
||||
|
||||
int stride_y = target_width;
|
||||
int stride_uv = (target_width + 1) / 2;
|
||||
|
||||
// Setting absolute height (in case it was negative).
|
||||
// In Windows, the image starts bottom left, instead of top left.
|
||||
// Setting a negative source height, inverts the image (within LibYuv).
|
||||
|
@ -28,13 +28,18 @@ started and run. It may contain any of the following fields:
|
||||
<tr id=capability-binary>
|
||||
<td><code>binary</code>
|
||||
<td align="center">string
|
||||
<td>Absolute path of the Firefox binary,
|
||||
e.g. <code>/usr/bin/firefox</code>
|
||||
or <code>/Applications/Firefox.app/Contents/MacOS/firefox</code>,
|
||||
<td><p>
|
||||
Absolute path of the Firefox binary
|
||||
to select which custom browser binary to use.
|
||||
If left undefined geckodriver will attempt
|
||||
to deduce the default location of Firefox
|
||||
on the current system.
|
||||
|
||||
<p>
|
||||
On macOS the path must be absolute to the browser binary,
|
||||
e.g. <code>/Applications/Firefox.app/Contents/MacOS/firefox</code>.
|
||||
Specifying an application bundle such as <code>/Applications/Firefox.app</code>
|
||||
will <em>not</em> work.
|
||||
</tr>
|
||||
|
||||
<tr id=capability-args>
|
||||
|
@ -725,8 +725,20 @@ class ADBDevice(ADBCommand):
|
||||
|
||||
self._selinux = None
|
||||
self.enforcing = 'Permissive'
|
||||
self.version = int(self.shell_output("getprop ro.build.version.sdk",
|
||||
timeout=timeout))
|
||||
|
||||
self.version = 0
|
||||
while self.version < 1 and (time.time() - start_time) <= float(timeout):
|
||||
try:
|
||||
version = self.shell_output("getprop ro.build.version.sdk",
|
||||
timeout=timeout)
|
||||
self.version = int(version)
|
||||
except ValueError:
|
||||
self._logger.info("unexpected ro.build.version.sdk: '%s'" % version)
|
||||
time.sleep(2)
|
||||
if self.version < 1:
|
||||
# note slightly different meaning to the ADBTimeoutError here (and above):
|
||||
# failed to get valid (numeric) version string in all attempts in allowed time
|
||||
raise ADBTimeoutError("ADBDevice: unable to determine ro.build.version.sdk.")
|
||||
|
||||
# Do we have pidof?
|
||||
if self.version >= version_codes.N:
|
||||
|
@ -324,8 +324,7 @@ class AndroidMixin(object):
|
||||
self.device.install_app(apk)
|
||||
except (mozdevice.ADBError, mozdevice.ADBTimeoutError):
|
||||
self.info('Failed to install %s on %s' %
|
||||
(self.installer_path, self.device_name),
|
||||
exc_info=1)
|
||||
(self.installer_path, self.device_name))
|
||||
self.fatal('INFRA-ERROR: Failed to install %s' %
|
||||
self.installer_path,
|
||||
EXIT_STATUS_DICT[TBPL_RETRY])
|
||||
|
@ -417,7 +417,7 @@ class MarionetteCoverageProtocolPart(CoverageProtocolPart):
|
||||
return
|
||||
|
||||
script = """
|
||||
const {PerTestCoverageUtils} = ChromeUtils.import("resource://reftest/PerTestCoverageUtils.jsm");
|
||||
const {PerTestCoverageUtils} = ChromeUtils.import("chrome://marionette/content/PerTestCoverageUtils.jsm");
|
||||
return PerTestCoverageUtils.enabled;
|
||||
"""
|
||||
with self.marionette.using_context(self.marionette.CONTEXT_CHROME):
|
||||
@ -427,7 +427,7 @@ class MarionetteCoverageProtocolPart(CoverageProtocolPart):
|
||||
script = """
|
||||
var callback = arguments[arguments.length - 1];
|
||||
|
||||
const {PerTestCoverageUtils} = ChromeUtils.import("resource://reftest/PerTestCoverageUtils.jsm");
|
||||
const {PerTestCoverageUtils} = ChromeUtils.import("chrome://marionette/content/PerTestCoverageUtils.jsm");
|
||||
PerTestCoverageUtils.beforeTest().then(callback, callback);
|
||||
"""
|
||||
with self.marionette.using_context(self.marionette.CONTEXT_CHROME):
|
||||
@ -447,7 +447,7 @@ class MarionetteCoverageProtocolPart(CoverageProtocolPart):
|
||||
script = """
|
||||
var callback = arguments[arguments.length - 1];
|
||||
|
||||
const {PerTestCoverageUtils} = ChromeUtils.import("resource://reftest/PerTestCoverageUtils.jsm");
|
||||
const {PerTestCoverageUtils} = ChromeUtils.import("chrome://marionette/content/PerTestCoverageUtils.jsm");
|
||||
PerTestCoverageUtils.afterTest().then(callback, callback);
|
||||
"""
|
||||
with self.marionette.using_context(self.marionette.CONTEXT_CHROME):
|
||||
|
@ -150,12 +150,12 @@ add_task(async function test_popup_url() {
|
||||
|
||||
results[1].removeAttribute("selected");
|
||||
|
||||
let urlText = document.getAnonymousElementByAttribute(results[1], "anonid", "url-text");
|
||||
let urlText = results[1]._urlText;
|
||||
Assert.equal(window.getComputedStyle(urlText).color,
|
||||
`rgb(${hexToRGB(POPUP_URL_COLOR_DARK).join(", ")})`,
|
||||
`Urlbar popup url color should be set to ${POPUP_URL_COLOR_DARK}`);
|
||||
|
||||
let actionText = document.getAnonymousElementByAttribute(results[1], "anonid", "action-text");
|
||||
let actionText = results[1]._actionText;
|
||||
Assert.equal(window.getComputedStyle(actionText).color,
|
||||
`rgb(${hexToRGB(POPUP_ACTION_COLOR_DARK).join(", ")})`,
|
||||
`Urlbar popup action color should be set to ${POPUP_ACTION_COLOR_DARK}`);
|
||||
@ -203,19 +203,19 @@ add_task(async function test_popup_url() {
|
||||
`rgb(${hexToRGB(POPUP_TEXT_COLOR_BRIGHT).join(", ")})`,
|
||||
`Popup color should be set to ${POPUP_TEXT_COLOR_BRIGHT}`);
|
||||
|
||||
urlText = document.getAnonymousElementByAttribute(results[1], "anonid", "url-text");
|
||||
urlText = results[1]._urlText;
|
||||
Assert.equal(window.getComputedStyle(urlText).color,
|
||||
`rgb(${hexToRGB(POPUP_URL_COLOR_BRIGHT).join(", ")})`,
|
||||
`Urlbar popup url color should be set to ${POPUP_URL_COLOR_BRIGHT}`);
|
||||
|
||||
actionText = document.getAnonymousElementByAttribute(results[1], "anonid", "action-text");
|
||||
actionText = results[1]._actionText;
|
||||
Assert.equal(window.getComputedStyle(actionText).color,
|
||||
`rgb(${hexToRGB(POPUP_ACTION_COLOR_BRIGHT).join(", ")})`,
|
||||
`Urlbar popup action color should be set to ${POPUP_ACTION_COLOR_BRIGHT}`);
|
||||
|
||||
// Since brighttext is enabled, the seperator color should be
|
||||
// POPUP_TEXT_COLOR_BRIGHT with added alpha.
|
||||
let separator = document.getAnonymousElementByAttribute(results[1], "anonid", "separator");
|
||||
let separator = results[1]._separator;
|
||||
Assert.equal(window.getComputedStyle(separator).color,
|
||||
`rgba(${hexToRGB(POPUP_TEXT_COLOR_BRIGHT).join(", ")}, 0.5)`,
|
||||
`Urlbar popup separator color should be set to ${POPUP_TEXT_COLOR_BRIGHT} with alpha`);
|
||||
@ -245,7 +245,7 @@ add_task(async function test_popup_url() {
|
||||
let GRAY_TEXT = window.getComputedStyle(span).color;
|
||||
span.remove();
|
||||
|
||||
separator = document.getAnonymousElementByAttribute(results[1], "anonid", "separator");
|
||||
separator = results[1]._separator;
|
||||
Assert.equal(window.getComputedStyle(separator).color,
|
||||
GRAY_TEXT,
|
||||
`Urlbar popup separator color should be set to ${GRAY_TEXT}`);
|
||||
|
@ -15,7 +15,7 @@
|
||||
// This file defines content scripts.
|
||||
/* eslint-env mozilla/frame-script */
|
||||
|
||||
let baseUrl = "http://mochi.test:8888/tests/toolkit/components/passwordmgr/test/authenticate.sjs";
|
||||
let baseUrl = "http://mochi.test:8888/tests/toolkit/components/passwordmgr/test/mochitest/authenticate.sjs";
|
||||
function testXHR(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let xhr = new XMLHttpRequest();
|
||||
|
@ -7,7 +7,7 @@
|
||||
if CONFIG['MOZ_BUILD_APP'] == 'browser':
|
||||
DEFINES['MOZ_BUILD_APP_IS_BROWSER'] = True
|
||||
|
||||
MOCHITEST_MANIFESTS += ['test/mochitest.ini', 'test/mochitest/mochitest.ini']
|
||||
MOCHITEST_MANIFESTS += ['test/mochitest/mochitest.ini']
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
|
||||
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
|
||||
|
||||
|
@ -3,11 +3,18 @@
|
||||
module.exports = {
|
||||
"extends": [
|
||||
"plugin:mozilla/mochitest-test",
|
||||
"plugin:mozilla/chrome-test"
|
||||
],
|
||||
"globals": {
|
||||
"promptDone": true,
|
||||
"startTest": true,
|
||||
// Make no-undef happy with our runInParent mixed environments since you
|
||||
// can't indicate a single function is a new env.
|
||||
"assert": true,
|
||||
"addMessageListener": true,
|
||||
"sendAsyncMessage": true,
|
||||
|
||||
},
|
||||
"rules": {
|
||||
"brace-style": ["error", "1tbs", {"allowSingleLine": false}],
|
||||
"no-undef": "off",
|
||||
"no-unused-vars": "off",
|
||||
},
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*
|
||||
/**
|
||||
* Shared functions generally available for testing login components.
|
||||
*/
|
||||
|
||||
|
@ -31,7 +31,6 @@ add_task(async function test_empty_password() {
|
||||
|
||||
let notificationElement = PopupNotifications.panel.childNodes[0];
|
||||
let passwordTextbox = notificationElement.querySelector("#password-notification-password");
|
||||
let toggleCheckbox = notificationElement.querySelector("#password-notification-visibilityToggle");
|
||||
|
||||
// Synthesize input to empty the field
|
||||
passwordTextbox.focus();
|
||||
|
@ -133,6 +133,7 @@ add_task(async function test_normal_new_password_4() {
|
||||
"#pass": "notifyp1",
|
||||
"#newpass": "notifyp2",
|
||||
});
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-change", PopupNotifications, browser);
|
||||
ok(notif, "got notification popup");
|
||||
if (notif) {
|
||||
@ -172,6 +173,7 @@ add_task(async function test_normal_with_login_6() {
|
||||
"#pass": "notifyp1",
|
||||
"#newpass": "notifyp2",
|
||||
});
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-change", PopupNotifications, browser);
|
||||
ok(notif, "got notification popup");
|
||||
if (notif) {
|
||||
|
@ -9,7 +9,7 @@
|
||||
<script>
|
||||
|
||||
// Ignore the '?' and split on |
|
||||
[username, password, features, autoClose] = window.location.search.substring(1).split("|");
|
||||
let [username, password, features, autoClose] = window.location.search.substring(1).split("|");
|
||||
|
||||
var url = "subtst_notifications_11_popup.html?" + username + "|" + password;
|
||||
var popupWin = window.open(url, "subtst_11", features);
|
||||
|
@ -15,7 +15,7 @@
|
||||
<script>
|
||||
function submitForm() {
|
||||
// Get the password from the query string (exclude '?').
|
||||
[username, password] = window.location.search.substring(1).split("|");
|
||||
let [username, password] = window.location.search.substring(1).split("|");
|
||||
userField.value = username;
|
||||
passField.value = password;
|
||||
form.submit();
|
||||
|
@ -1,16 +0,0 @@
|
||||
[DEFAULT]
|
||||
prefs =
|
||||
signon.rememberSignons=true
|
||||
signon.autofillForms.http=true
|
||||
security.insecure_field_warning.contextual.enabled=false
|
||||
network.auth.non-web-content-triggered-resources-http-auth-allow=true
|
||||
skip-if = e10s
|
||||
support-files =
|
||||
authenticate.sjs
|
||||
blank.html
|
||||
formsubmit.sjs
|
||||
prompt_common.js
|
||||
pwmgr_common.js
|
||||
|
||||
[test_xhr.html]
|
||||
skip-if = toolkit == 'android' # Tests desktop prompts
|
@ -7,16 +7,17 @@ prefs =
|
||||
|
||||
support-files =
|
||||
../../../prompts/test/chromeScript.js
|
||||
../../../prompts/test/prompt_common.js
|
||||
!/toolkit/components/prompts/test/prompt_common.js
|
||||
../../../satchel/test/parent_utils.js
|
||||
../../../satchel/test/satchel_common.js
|
||||
!/toolkit/components/satchel/test/satchel_common.js
|
||||
../blank.html
|
||||
../browser/form_autofocus_js.html
|
||||
../browser/form_basic.html
|
||||
../browser/formless_basic.html
|
||||
../browser/form_cross_origin_secure_action.html
|
||||
../pwmgr_common.js
|
||||
auth2/authenticate.sjs
|
||||
pwmgr_common.js
|
||||
pwmgr_common_parent.js
|
||||
../authenticate.sjs
|
||||
skip-if = toolkit == 'android' && !isFennec # Don't run on GeckoView
|
||||
|
||||
@ -93,6 +94,8 @@ skip-if = e10s || os == "linux" || toolkit == 'android' # Tests desktop prompts
|
||||
[test_recipe_login_fields.html]
|
||||
[test_username_focus.html]
|
||||
skip-if = toolkit == 'android' # android:autocomplete.
|
||||
[test_xhr.html]
|
||||
skip-if = toolkit == 'android' # Tests desktop prompts
|
||||
[test_xhr_2.html]
|
||||
[test_xml_load.html]
|
||||
skip-if = toolkit == 'android' # Tests desktop prompts
|
||||
|
334
toolkit/components/passwordmgr/test/mochitest/pwmgr_common.js
Normal file
334
toolkit/components/passwordmgr/test/mochitest/pwmgr_common.js
Normal file
@ -0,0 +1,334 @@
|
||||
/**
|
||||
* Helpers for password manager mochitest-plain tests.
|
||||
*/
|
||||
|
||||
// Copied from LoginTestUtils.masterPassword.masterPassword to use from the content process.
|
||||
const MASTER_PASSWORD = "omgsecret!";
|
||||
const TESTS_DIR = "/tests/toolkit/components/passwordmgr/test/";
|
||||
|
||||
/**
|
||||
* Returns the element with the specified |name| attribute.
|
||||
*/
|
||||
function $_(formNum, name) {
|
||||
var form = document.getElementById("form" + formNum);
|
||||
if (!form) {
|
||||
ok(false, "$_ couldn't find requested form " + formNum);
|
||||
return null;
|
||||
}
|
||||
|
||||
var element = form.children.namedItem(name);
|
||||
if (!element) {
|
||||
ok(false, "$_ couldn't find requested element " + name);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Note that namedItem is a bit stupid, and will prefer an
|
||||
// |id| attribute over a |name| attribute when looking for
|
||||
// the element. Login Mananger happens to use .namedItem
|
||||
// anyway, but let's rigorously check it here anyway so
|
||||
// that we don't end up with tests that mistakenly pass.
|
||||
|
||||
if (element.getAttribute("name") != name) {
|
||||
ok(false, "$_ got confused.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a form for expected values. If an argument is null, a field's
|
||||
* expected value will be the default value.
|
||||
*
|
||||
* <form id="form#">
|
||||
* checkForm(#, "foo");
|
||||
*/
|
||||
function checkForm(formNum, val1, val2, val3) {
|
||||
var e, form = document.getElementById("form" + formNum);
|
||||
ok(form, "Locating form " + formNum);
|
||||
|
||||
var numToCheck = arguments.length - 1;
|
||||
|
||||
if (!numToCheck--) {
|
||||
return;
|
||||
}
|
||||
e = form.elements[0];
|
||||
if (val1 == null) {
|
||||
is(e.value, e.defaultValue, "Test default value of field " + e.name +
|
||||
" in form " + formNum);
|
||||
} else {
|
||||
is(e.value, val1, "Test value of field " + e.name +
|
||||
" in form " + formNum);
|
||||
}
|
||||
|
||||
|
||||
if (!numToCheck--) {
|
||||
return;
|
||||
}
|
||||
e = form.elements[1];
|
||||
if (val2 == null) {
|
||||
is(e.value, e.defaultValue, "Test default value of field " + e.name +
|
||||
" in form " + formNum);
|
||||
} else {
|
||||
is(e.value, val2, "Test value of field " + e.name +
|
||||
" in form " + formNum);
|
||||
}
|
||||
|
||||
|
||||
if (!numToCheck--) {
|
||||
return;
|
||||
}
|
||||
e = form.elements[2];
|
||||
if (val3 == null) {
|
||||
is(e.value, e.defaultValue, "Test default value of field " + e.name +
|
||||
" in form " + formNum);
|
||||
} else {
|
||||
is(e.value, val3, "Test value of field " + e.name +
|
||||
" in form " + formNum);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a form for unmodified values from when page was loaded.
|
||||
*
|
||||
* <form id="form#">
|
||||
* checkUnmodifiedForm(#);
|
||||
*/
|
||||
function checkUnmodifiedForm(formNum) {
|
||||
var form = document.getElementById("form" + formNum);
|
||||
ok(form, "Locating form " + formNum);
|
||||
|
||||
for (var i = 0; i < form.elements.length; i++) {
|
||||
var ele = form.elements[i];
|
||||
|
||||
// No point in checking form submit/reset buttons.
|
||||
if (ele.type == "submit" || ele.type == "reset") {
|
||||
continue;
|
||||
}
|
||||
|
||||
is(ele.value, ele.defaultValue, "Test to default value of field " +
|
||||
ele.name + " in form " + formNum);
|
||||
}
|
||||
}
|
||||
|
||||
function registerRunTests() {
|
||||
return new Promise(resolve => {
|
||||
// We provide a general mechanism for our tests to know when they can
|
||||
// safely run: we add a final form that we know will be filled in, wait
|
||||
// for the login manager to tell us that it's filled in and then continue
|
||||
// with the rest of the tests.
|
||||
window.addEventListener("DOMContentLoaded", (event) => {
|
||||
var form = document.createElement("form");
|
||||
form.id = "observerforcer";
|
||||
var username = document.createElement("input");
|
||||
username.name = "testuser";
|
||||
form.appendChild(username);
|
||||
var password = document.createElement("input");
|
||||
password.name = "testpass";
|
||||
password.type = "password";
|
||||
form.appendChild(password);
|
||||
|
||||
var observer = SpecialPowers.wrapCallback(function(subject, topic, data) {
|
||||
var formLikeRoot = subject;
|
||||
if (formLikeRoot.id !== "observerforcer") {
|
||||
return;
|
||||
}
|
||||
SpecialPowers.removeObserver(observer, "passwordmgr-processed-form");
|
||||
formLikeRoot.remove();
|
||||
SimpleTest.executeSoon(() => {
|
||||
var runTestEvent = new Event("runTests");
|
||||
window.dispatchEvent(runTestEvent);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
SpecialPowers.addObserver(observer, "passwordmgr-processed-form");
|
||||
|
||||
document.body.appendChild(form);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function enableMasterPassword() {
|
||||
setMasterPassword(true);
|
||||
}
|
||||
|
||||
function disableMasterPassword() {
|
||||
setMasterPassword(false);
|
||||
}
|
||||
|
||||
function setMasterPassword(enable) {
|
||||
PWMGR_COMMON_PARENT.sendSyncMessage("setMasterPassword", { enable });
|
||||
}
|
||||
|
||||
function isLoggedIn() {
|
||||
return PWMGR_COMMON_PARENT.sendSyncMessage("isLoggedIn")[0][0];
|
||||
}
|
||||
|
||||
function logoutMasterPassword() {
|
||||
runInParent(function parent_logoutMasterPassword() {
|
||||
var sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing);
|
||||
sdr.logoutAndTeardown();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves when a specified number of forms have been processed.
|
||||
*/
|
||||
function promiseFormsProcessed(expectedCount = 1) {
|
||||
var processedCount = 0;
|
||||
return new Promise((resolve, reject) => {
|
||||
function onProcessedForm(subject, topic, data) {
|
||||
processedCount++;
|
||||
if (processedCount == expectedCount) {
|
||||
SpecialPowers.removeObserver(onProcessedForm, "passwordmgr-processed-form");
|
||||
resolve(SpecialPowers.Cu.waiveXrays(subject), data);
|
||||
}
|
||||
}
|
||||
SpecialPowers.addObserver(onProcessedForm, "passwordmgr-processed-form");
|
||||
});
|
||||
}
|
||||
|
||||
function loadRecipes(recipes) {
|
||||
info("Loading recipes");
|
||||
return new Promise(resolve => {
|
||||
PWMGR_COMMON_PARENT.addMessageListener("loadedRecipes", function loaded() {
|
||||
PWMGR_COMMON_PARENT.removeMessageListener("loadedRecipes", loaded);
|
||||
resolve(recipes);
|
||||
});
|
||||
PWMGR_COMMON_PARENT.sendAsyncMessage("loadRecipes", recipes);
|
||||
});
|
||||
}
|
||||
|
||||
function resetRecipes() {
|
||||
info("Resetting recipes");
|
||||
return new Promise(resolve => {
|
||||
PWMGR_COMMON_PARENT.addMessageListener("recipesReset", function reset() {
|
||||
PWMGR_COMMON_PARENT.removeMessageListener("recipesReset", reset);
|
||||
resolve();
|
||||
});
|
||||
PWMGR_COMMON_PARENT.sendAsyncMessage("resetRecipes");
|
||||
});
|
||||
}
|
||||
|
||||
function promiseStorageChanged(expectedChangeTypes) {
|
||||
return new Promise((resolve, reject) => {
|
||||
function onStorageChanged({ topic, data }) {
|
||||
let changeType = expectedChangeTypes.shift();
|
||||
is(data, changeType, "Check expected passwordmgr-storage-changed type");
|
||||
if (expectedChangeTypes.length === 0) {
|
||||
PWMGR_COMMON_PARENT.removeMessageListener("storageChanged", onStorageChanged);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
PWMGR_COMMON_PARENT.addMessageListener("storageChanged", onStorageChanged);
|
||||
});
|
||||
}
|
||||
|
||||
function promisePromptShown(expectedTopic) {
|
||||
return new Promise((resolve, reject) => {
|
||||
function onPromptShown({ topic, data }) {
|
||||
is(topic, expectedTopic, "Check expected prompt topic");
|
||||
PWMGR_COMMON_PARENT.removeMessageListener("promptShown", onPromptShown);
|
||||
resolve();
|
||||
}
|
||||
PWMGR_COMMON_PARENT.addMessageListener("promptShown", onPromptShown);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a function synchronously in the parent process and destroy it in the test cleanup function.
|
||||
* @param {Function|String} aFunctionOrURL - either a function that will be stringified and run
|
||||
* or the URL to a JS file.
|
||||
* @return {Object} - the return value of loadChromeScript providing message-related methods.
|
||||
* @see loadChromeScript in specialpowersAPI.js
|
||||
*/
|
||||
function runInParent(aFunctionOrURL) {
|
||||
let chromeScript = SpecialPowers.loadChromeScript(aFunctionOrURL);
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
chromeScript.destroy();
|
||||
});
|
||||
return chromeScript;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run commonInit synchronously in the parent then run the test function after the runTests event.
|
||||
*
|
||||
* @param {Function} aFunction The test function to run
|
||||
*/
|
||||
function runChecksAfterCommonInit(aFunction = null) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
if (aFunction) {
|
||||
window.addEventListener("runTests", aFunction);
|
||||
PWMGR_COMMON_PARENT.addMessageListener("registerRunTests", () => registerRunTests());
|
||||
}
|
||||
PWMGR_COMMON_PARENT.sendSyncMessage("setupParent");
|
||||
return PWMGR_COMMON_PARENT;
|
||||
}
|
||||
|
||||
// Begin code that runs immediately for all tests that include this file.
|
||||
|
||||
const PWMGR_COMMON_PARENT = runInParent(SimpleTest.getTestFileURL("pwmgr_common_parent.js"));
|
||||
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
SpecialPowers.popPrefEnv();
|
||||
runInParent(function cleanupParent() {
|
||||
// eslint-disable-next-line no-shadow
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
// eslint-disable-next-line no-shadow
|
||||
const {LoginManagerParent} = ChromeUtils.import("resource://gre/modules/LoginManagerParent.jsm");
|
||||
|
||||
// Remove all logins and disabled hosts
|
||||
Services.logins.removeAllLogins();
|
||||
|
||||
let disabledHosts = Services.logins.getAllDisabledHosts();
|
||||
disabledHosts.forEach(host => Services.logins.setLoginSavingEnabled(host, true));
|
||||
|
||||
let authMgr = Cc["@mozilla.org/network/http-auth-manager;1"].
|
||||
getService(Ci.nsIHttpAuthManager);
|
||||
authMgr.clearAll();
|
||||
|
||||
if (LoginManagerParent._recipeManager) {
|
||||
LoginManagerParent._recipeManager.reset();
|
||||
}
|
||||
|
||||
// Cleanup PopupNotifications (if on a relevant platform)
|
||||
let chromeWin = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (chromeWin && chromeWin.PopupNotifications) {
|
||||
let notes = chromeWin.PopupNotifications._currentNotifications;
|
||||
if (notes.length > 0) {
|
||||
dump("Removing " + notes.length + " popup notifications.\n");
|
||||
}
|
||||
for (let note of notes) {
|
||||
note.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let { LoginHelper } = SpecialPowers.Cu.import("resource://gre/modules/LoginHelper.jsm", {});
|
||||
/**
|
||||
* Proxy for Services.logins (nsILoginManager).
|
||||
* Only supports arguments which support structured clone plus {nsILoginInfo}
|
||||
* Assumes properties are methods.
|
||||
*/
|
||||
this.LoginManager = new Proxy({}, {
|
||||
get(target, prop, receiver) {
|
||||
return (...args) => {
|
||||
let loginInfoIndices = [];
|
||||
let cloneableArgs = args.map((val, index) => {
|
||||
if (SpecialPowers.call_Instanceof(val, SpecialPowers.Ci.nsILoginInfo)) {
|
||||
loginInfoIndices.push(index);
|
||||
return LoginHelper.loginToVanillaObject(val);
|
||||
}
|
||||
|
||||
return val;
|
||||
});
|
||||
|
||||
return PWMGR_COMMON_PARENT.sendSyncMessage("proxyLoginManager", {
|
||||
args: cloneableArgs,
|
||||
loginInfoIndices,
|
||||
methodName: prop,
|
||||
})[0][0];
|
||||
};
|
||||
},
|
||||
});
|
@ -0,0 +1,158 @@
|
||||
/**
|
||||
* Loaded as a frame script to do privileged things in mochitest-plain tests.
|
||||
* See pwmgr_common.js for the content process companion.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
// assert is available to chrome scripts loaded via SpecialPowers.loadChromeScript.
|
||||
/* global assert */
|
||||
/* eslint-env mozilla/frame-script */
|
||||
|
||||
var {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
var {LoginHelper} = ChromeUtils.import("resource://gre/modules/LoginHelper.jsm");
|
||||
var {LoginManagerParent} = ChromeUtils.import("resource://gre/modules/LoginManagerParent.jsm");
|
||||
const {LoginTestUtils} = ChromeUtils.import("resource://testing-common/LoginTestUtils.jsm");
|
||||
var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
/**
|
||||
* Init with a common login
|
||||
* If selfFilling is true or non-undefined, fires an event at the page so that
|
||||
* the test can start checking filled-in values. Tests that check observer
|
||||
* notifications might be confused by this.
|
||||
*/
|
||||
function commonInit(selfFilling) {
|
||||
var pwmgr = Services.logins;
|
||||
assert.ok(pwmgr != null, "Access LoginManager");
|
||||
|
||||
// Check that initial state has no logins
|
||||
var logins = pwmgr.getAllLogins();
|
||||
assert.equal(logins.length, 0, "Not expecting logins to be present");
|
||||
var disabledHosts = pwmgr.getAllDisabledHosts();
|
||||
if (disabledHosts.length) {
|
||||
assert.ok(false, "Warning: wasn't expecting disabled hosts to be present.");
|
||||
for (var host of disabledHosts) {
|
||||
pwmgr.setLoginSavingEnabled(host, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a login that's used in multiple tests
|
||||
var login = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
login.init("http://mochi.test:8888", "http://mochi.test:8888", null,
|
||||
"testuser", "testpass", "uname", "pword");
|
||||
pwmgr.addLogin(login);
|
||||
|
||||
// Last sanity check
|
||||
logins = pwmgr.getAllLogins();
|
||||
assert.equal(logins.length, 1, "Checking for successful init login");
|
||||
disabledHosts = pwmgr.getAllDisabledHosts();
|
||||
assert.equal(disabledHosts.length, 0, "Checking for no disabled hosts");
|
||||
|
||||
if (selfFilling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify the content side that initialization is done and tests can start.
|
||||
sendAsyncMessage("registerRunTests");
|
||||
}
|
||||
|
||||
function dumpLogins() {
|
||||
let logins = Services.logins.getAllLogins();
|
||||
assert.ok(true, "----- dumpLogins: have " + logins.length + " logins. -----");
|
||||
for (var i = 0; i < logins.length; i++) {
|
||||
dumpLogin("login #" + i + " --- ", logins[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function dumpLogin(label, login) {
|
||||
var loginText = "";
|
||||
loginText += "host: ";
|
||||
loginText += login.hostname;
|
||||
loginText += " / formURL: ";
|
||||
loginText += login.formSubmitURL;
|
||||
loginText += " / realm: ";
|
||||
loginText += login.httpRealm;
|
||||
loginText += " / user: ";
|
||||
loginText += login.username;
|
||||
loginText += " / pass: ";
|
||||
loginText += login.password;
|
||||
loginText += " / ufield: ";
|
||||
loginText += login.usernameField;
|
||||
loginText += " / pfield: ";
|
||||
loginText += login.passwordField;
|
||||
assert.ok(true, label + loginText);
|
||||
}
|
||||
|
||||
function onStorageChanged(subject, topic, data) {
|
||||
sendAsyncMessage("storageChanged", {
|
||||
topic,
|
||||
data,
|
||||
});
|
||||
}
|
||||
Services.obs.addObserver(onStorageChanged, "passwordmgr-storage-changed");
|
||||
|
||||
function onPrompt(subject, topic, data) {
|
||||
sendAsyncMessage("promptShown", {
|
||||
topic,
|
||||
data,
|
||||
});
|
||||
}
|
||||
Services.obs.addObserver(onPrompt, "passwordmgr-prompt-change");
|
||||
Services.obs.addObserver(onPrompt, "passwordmgr-prompt-save");
|
||||
|
||||
|
||||
// Begin message listeners
|
||||
|
||||
addMessageListener("setupParent", ({selfFilling = false} = {selfFilling: false}) => {
|
||||
commonInit(selfFilling);
|
||||
sendAsyncMessage("doneSetup");
|
||||
});
|
||||
|
||||
addMessageListener("loadRecipes", async function(recipes) {
|
||||
var recipeParent = await LoginManagerParent.recipeParentPromise;
|
||||
await recipeParent.load(recipes);
|
||||
sendAsyncMessage("loadedRecipes", recipes);
|
||||
});
|
||||
|
||||
addMessageListener("resetRecipes", async function() {
|
||||
let recipeParent = await LoginManagerParent.recipeParentPromise;
|
||||
await recipeParent.reset();
|
||||
sendAsyncMessage("recipesReset");
|
||||
});
|
||||
|
||||
addMessageListener("proxyLoginManager", msg => {
|
||||
// Recreate nsILoginInfo objects from vanilla JS objects.
|
||||
let recreatedArgs = msg.args.map((arg, index) => {
|
||||
if (msg.loginInfoIndices.includes(index)) {
|
||||
return LoginHelper.vanillaObjectToLogin(arg);
|
||||
}
|
||||
|
||||
return arg;
|
||||
});
|
||||
|
||||
let rv = Services.logins[msg.methodName](...recreatedArgs);
|
||||
if (rv instanceof Ci.nsILoginInfo) {
|
||||
rv = LoginHelper.loginToVanillaObject(rv);
|
||||
} else if (Array.isArray(rv) && rv.length > 0 && rv[0] instanceof Ci.nsILoginInfo) {
|
||||
rv = rv.map(login => LoginHelper.loginToVanillaObject(login));
|
||||
}
|
||||
return rv;
|
||||
});
|
||||
|
||||
addMessageListener("isLoggedIn", () => {
|
||||
// This can't use the LoginManager proxy in pwmgr_common.js since it's not a method.
|
||||
return Services.logins.isLoggedIn;
|
||||
});
|
||||
|
||||
addMessageListener("setMasterPassword", ({ enable }) => {
|
||||
if (enable) {
|
||||
LoginTestUtils.masterPassword.enable();
|
||||
} else {
|
||||
LoginTestUtils.masterPassword.disable();
|
||||
}
|
||||
});
|
||||
|
||||
Services.mm.addMessageListener("RemoteLogins:onFormSubmit", function onFormSubmit(message) {
|
||||
sendAsyncMessage("formSubmissionProcessed", message.data, message.objects);
|
||||
});
|
@ -6,7 +6,7 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<script type="text/javascript" src="../../../satchel/test/satchel_common.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
|
@ -6,14 +6,13 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<script type="text/javascript" src="../../../satchel/test/satchel_common.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal",
|
||||
true]]});
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<script type="text/javascript" src="../../../satchel/test/satchel_common.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
|
@ -10,8 +10,7 @@
|
||||
<body>
|
||||
Login Manager test: Bug 444968
|
||||
<script>
|
||||
let pwmgrCommonScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
|
||||
pwmgrCommonScript.sendSyncMessage("setupParent", { selfFilling: true });
|
||||
PWMGR_COMMON_PARENT.sendSyncMessage("setupParent", { selfFilling: true });
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<script type="text/javascript" src="../../../satchel/test/satchel_common.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
|
@ -40,6 +40,7 @@ function startTest() {
|
||||
$_(1, "pword").value = "newpass1";
|
||||
$_(1, "qword").value = "newpass1";
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
var button = getFormSubmitButton(1);
|
||||
|
||||
todo(false, "form submission disabled, can't auto-accept dialog yet");
|
||||
|
@ -6,7 +6,7 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<script type="text/javascript" src="../../../satchel/test/satchel_common.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
@ -56,6 +56,8 @@ var setupScript = runInParent(function setup() {
|
||||
"form9userAB", "form9pass", "uname", "pword");
|
||||
var login8B = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete5", null,
|
||||
"form9userAAB", "form9pass", "uname", "pword");
|
||||
// Reference by `eval` in the message listeners below.
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
var login8C = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete5", null,
|
||||
"form9userAABzz", "form9pass", "uname", "pword");
|
||||
|
||||
@ -453,8 +455,7 @@ add_task(async function test_form1_delete() {
|
||||
|
||||
// Delete the first entry (of 4), "tempuser1"
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
var numLogins;
|
||||
numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
let numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
is(numLogins, 5, "Correct number of logins before deleting one");
|
||||
|
||||
let countChangedPromise = notifyMenuChanged(3);
|
||||
@ -497,7 +498,7 @@ add_task(async function test_form1_delete_second() {
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
synthesizeKey("KEY_Delete", {shiftKey: true});
|
||||
checkACForm("", "");
|
||||
numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
let numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
is(numLogins, 3, "Correct number of logins after deleting one");
|
||||
synthesizeKey("KEY_Enter");
|
||||
await promiseFormsProcessed();
|
||||
@ -529,7 +530,7 @@ add_task(async function test_form1_delete_last() {
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
synthesizeKey("KEY_Delete", {shiftKey: true});
|
||||
checkACForm("", "");
|
||||
numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
let numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
is(numLogins, 2, "Correct number of logins after deleting one");
|
||||
synthesizeKey("KEY_Enter");
|
||||
await promiseFormsProcessed();
|
||||
@ -560,7 +561,7 @@ add_task(async function test_form1_check_only_entry_remaining() {
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
synthesizeKey("KEY_Delete", {shiftKey: true});
|
||||
checkACForm("", "");
|
||||
numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
let numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
is(numLogins, 1, "Correct number of logins after deleting one");
|
||||
|
||||
// remove the login that's not shown in the list.
|
||||
|
@ -14,14 +14,14 @@ runChecksAfterCommonInit(() => startTest());
|
||||
|
||||
runInParent(function setup() {
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
login1 = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
login2 = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
login3 = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
login4 = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
let login1 = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
let login2 = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
let login3 = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
let login4 = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
|
||||
login1.init("http://mochi.test:8888", "http://bug600551-1", null,
|
||||
"testuser@example.com", "testpass1", "", "");
|
||||
|
@ -21,8 +21,8 @@ runInParent(() => {
|
||||
//
|
||||
// Note: pwlogin2 is deleted at the end of the test.
|
||||
|
||||
pwlogin1 = new nsLoginInfo();
|
||||
pwlogin2 = new nsLoginInfo();
|
||||
let pwlogin1 = new nsLoginInfo();
|
||||
let pwlogin2 = new nsLoginInfo();
|
||||
|
||||
pwlogin1.init("http://mochi.test:8888", "http://mochi.test:1111", null,
|
||||
"", "1234", "uname", "pword");
|
||||
|
@ -80,8 +80,8 @@
|
||||
let mm = runInParent(() => {
|
||||
const { classes: parentCc, interfaces: parentCi, utils: parentCu } = Components;
|
||||
|
||||
parentCu.import("resource://gre/modules/Services.jsm");
|
||||
parentCu.import("resource://gre/modules/NetUtil.jsm");
|
||||
let {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
let {NetUtil} = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
|
||||
parentCu.import("resource://gre/modules/Timer.jsm");
|
||||
parentCu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
|
@ -40,7 +40,7 @@ function doxhr(URL, user, pass, next) {
|
||||
};
|
||||
xhr.onerror = function() {
|
||||
ok(false, "request passed");
|
||||
finishTest();
|
||||
SimpleTest.finish();
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<script type="text/javascript" src="../../../satchel/test/satchel_common.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
|
@ -10,18 +10,16 @@
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/javascript">
|
||||
let chromeScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.getElementById("loginFrame").addEventListener("load", (evt) => {
|
||||
// Tell the parent to setup test logins.
|
||||
chromeScript.sendAsyncMessage("setupParent", { selfFilling: true });
|
||||
PWMGR_COMMON_PARENT.sendAsyncMessage("setupParent", { selfFilling: true });
|
||||
});
|
||||
});
|
||||
|
||||
let doneSetupPromise = new Promise(resolve => {
|
||||
// When the setup is done, load a recipe for this test.
|
||||
chromeScript.addMessageListener("doneSetup", function doneSetup() {
|
||||
PWMGR_COMMON_PARENT.addMessageListener("doneSetup", function doneSetup() {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
@ -13,8 +13,6 @@
|
||||
const LMCBackstagePass = SpecialPowers.Cu.import("resource://gre/modules/LoginManagerContent.jsm");
|
||||
const { LoginManagerContent, LoginFormFactory } = LMCBackstagePass;
|
||||
|
||||
let chromeScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
|
||||
|
||||
let loadPromise = new Promise(resolve => {
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.getElementById("loginFrame").addEventListener("load", (evt) => {
|
||||
@ -126,9 +124,9 @@ const TESTCASES = [
|
||||
function getSubmitMessage() {
|
||||
info("getSubmitMessage");
|
||||
return new Promise((resolve, reject) => {
|
||||
chromeScript.addMessageListener("formSubmissionProcessed", function processed(...args) {
|
||||
PWMGR_COMMON_PARENT.addMessageListener("formSubmissionProcessed", function processed(...args) {
|
||||
info("got formSubmissionProcessed");
|
||||
chromeScript.removeMessageListener("formSubmissionProcessed", processed);
|
||||
PWMGR_COMMON_PARENT.removeMessageListener("formSubmissionProcessed", processed);
|
||||
resolve(...args);
|
||||
});
|
||||
});
|
||||
|
@ -13,8 +13,6 @@
|
||||
const LMCBackstagePass = SpecialPowers.Cu.import("resource://gre/modules/LoginManagerContent.jsm");
|
||||
const { LoginManagerContent, LoginFormFactory } = LMCBackstagePass;
|
||||
|
||||
let chromeScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
|
||||
|
||||
let loadPromise = new Promise(resolve => {
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.getElementById("loginFrame").addEventListener("load", (evt) => {
|
||||
@ -125,9 +123,9 @@ const TESTCASES = [
|
||||
function getSubmitMessage() {
|
||||
info("getSubmitMessage");
|
||||
return new Promise((resolve, reject) => {
|
||||
chromeScript.addMessageListener("formSubmissionProcessed", function processed(...args) {
|
||||
PWMGR_COMMON_PARENT.addMessageListener("formSubmissionProcessed", function processed(...args) {
|
||||
info("got formSubmissionProcessed");
|
||||
chromeScript.removeMessageListener("formSubmissionProcessed", processed);
|
||||
PWMGR_COMMON_PARENT.removeMessageListener("formSubmissionProcessed", processed);
|
||||
resolve(...args);
|
||||
});
|
||||
});
|
||||
|
@ -15,8 +15,6 @@ const { LoginManagerContent, LoginFormFactory } = LMCBackstagePass;
|
||||
|
||||
SimpleTest.requestFlakyTimeout("Testing that a message doesn't arrive");
|
||||
|
||||
let chromeScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
|
||||
|
||||
let loadPromise = new Promise(resolve => {
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.getElementById("loginFrame").addEventListener("load", (evt) => {
|
||||
@ -40,10 +38,10 @@ add_task(async function setup() {
|
||||
info("Waiting for page and frame loads");
|
||||
await loadPromise;
|
||||
|
||||
chromeScript.addMessageListener("formSubmissionProcessed", submissionProcessed);
|
||||
PWMGR_COMMON_PARENT.addMessageListener("formSubmissionProcessed", submissionProcessed);
|
||||
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
chromeScript.removeMessageListener("formSubmissionProcessed", submissionProcessed);
|
||||
PWMGR_COMMON_PARENT.removeMessageListener("formSubmissionProcessed", submissionProcessed);
|
||||
});
|
||||
});
|
||||
|
||||
@ -76,7 +74,8 @@ const TESTCASES = [
|
||||
add_task(async function test() {
|
||||
let loginFrame = document.getElementById("loginFrame");
|
||||
|
||||
var android = navigator.appVersion.includes("Android");
|
||||
let waitTime;
|
||||
let android = navigator.appVersion.includes("Android");
|
||||
if (android) {
|
||||
// intermittent failures on Android Debug at 5 seconds
|
||||
waitTime = 10000;
|
||||
|
@ -6,7 +6,7 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<script type="text/javascript" src="../../../satchel/test/satchel_common.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
@ -513,7 +513,7 @@ add_task(async function test_form1_delete_second() {
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
synthesizeKey("KEY_Delete", {shiftKey: true});
|
||||
checkACForm("", "");
|
||||
numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
let numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
is(numLogins, 3, "Correct number of logins after deleting one");
|
||||
synthesizeKey("KEY_Enter");
|
||||
await promiseFormsProcessed();
|
||||
@ -547,7 +547,7 @@ add_task(async function test_form1_delete_last() {
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
synthesizeKey("KEY_Delete", {shiftKey: true});
|
||||
checkACForm("", "");
|
||||
numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
let numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
is(numLogins, 2, "Correct number of logins after deleting one");
|
||||
synthesizeKey("KEY_Enter");
|
||||
await promiseFormsProcessed();
|
||||
@ -580,7 +580,7 @@ add_task(async function test_form1_check_only_entry_remaining() {
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
synthesizeKey("KEY_Delete", {shiftKey: true});
|
||||
checkACForm("", "");
|
||||
numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
let numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
is(numLogins, 1, "Correct number of logins after deleting one");
|
||||
|
||||
// remove the login that's not shown in the list.
|
||||
|
@ -6,7 +6,7 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<script type="text/javascript" src="../../../satchel/test/satchel_common.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<script type="text/javascript" src="prompt_common.js"></script>
|
||||
<script type="text/javascript" src="../../../prompts/test/prompt_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
@ -32,7 +32,6 @@ var exampleOrg = "https://example.org/tests/toolkit/components/passwordmgr/test/
|
||||
var chromeScript = runChecksAfterCommonInit();
|
||||
|
||||
runInParent(() => {
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo);
|
||||
@ -75,7 +74,7 @@ add_task(async function test_1() {
|
||||
};
|
||||
var action = {
|
||||
buttonClick: "ok",
|
||||
passField: masterPassword,
|
||||
passField: MASTER_PASSWORD,
|
||||
};
|
||||
var promptDone = handlePrompt(state, action);
|
||||
|
||||
@ -142,7 +141,7 @@ add_task(async function test_3() {
|
||||
};
|
||||
var action = {
|
||||
buttonClick: "ok",
|
||||
passField: masterPassword,
|
||||
passField: MASTER_PASSWORD,
|
||||
};
|
||||
var promptDone = handlePrompt(state, action);
|
||||
|
||||
@ -247,7 +246,7 @@ add_task(async function test_4() {
|
||||
// fill existing MP dialog with MP.
|
||||
action = {
|
||||
buttonClick: "ok",
|
||||
passField: masterPassword,
|
||||
passField: MASTER_PASSWORD,
|
||||
};
|
||||
await handlePrompt(state, action);
|
||||
await fillPromise;
|
||||
|
@ -6,7 +6,7 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<script type="text/javascript" src="../../../satchel/test/satchel_common.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<script type="text/javascript" src="prompt_common.js"></script>
|
||||
<script type="text/javascript" src="../../../prompts/test/prompt_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<script type="text/javascript" src="prompt_common.js"></script>
|
||||
<script type="text/javascript" src="../../../prompts/test/prompt_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
|
||||
<script class="testbody" type="text/javascript">
|
||||
@ -21,6 +21,9 @@
|
||||
// Used by prompt_common.js.
|
||||
isTabModal = false;
|
||||
|
||||
// These are magically defined on the window due to the iframe IDs
|
||||
/* global iframe1, iframe2a, iframe2b */
|
||||
|
||||
let chromeScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
|
||||
|
||||
/**
|
||||
@ -194,8 +197,8 @@
|
||||
let iframe2aDoc = await iframe2aDocPromise;
|
||||
let iframe2bDoc = await iframe2bDocPromise;
|
||||
|
||||
authok1 = iframe1Doc.getElementById("ok").textContent;
|
||||
proxyok1 = iframe1Doc.getElementById("proxy").textContent;
|
||||
let authok1 = iframe1Doc.getElementById("ok").textContent;
|
||||
let proxyok1 = iframe1Doc.getElementById("proxy").textContent;
|
||||
|
||||
let authok2a = iframe2aDoc.getElementById("ok").textContent;
|
||||
let proxyok2a = iframe2aDoc.getElementById("proxy").textContent;
|
||||
@ -217,8 +220,6 @@
|
||||
// and web authentication.
|
||||
|
||||
let iframe1DocPromise = promiseLoadedContentDoc(iframe1);
|
||||
let iframe2aDocPromise = promiseLoadedContentDoc(iframe2a);
|
||||
let iframe2bDocPromise = promiseLoadedContentDoc(iframe2b);
|
||||
|
||||
iframe1.src = EXAMPLE_COM + "subtst_prompt_async.html";
|
||||
iframe2a.src = "about:blank";
|
||||
@ -350,8 +351,8 @@
|
||||
|
||||
let iframe1Doc = await iframe1DocPromise;
|
||||
|
||||
authok1 = iframe1Doc.getElementById("ok").textContent;
|
||||
proxyok1 = iframe1Doc.getElementById("proxy").textContent;
|
||||
let authok1 = iframe1Doc.getElementById("ok").textContent;
|
||||
let proxyok1 = iframe1Doc.getElementById("proxy").textContent;
|
||||
|
||||
is(authok1, "FAIL", "WWW Authorization FAILED, frame1");
|
||||
is(proxyok1, "PASS", "Proxy Authorization OK, frame1");
|
||||
@ -442,9 +443,9 @@
|
||||
|
||||
let iframe1Doc = await iframe1DocPromise;
|
||||
|
||||
authok1 = iframe1Doc.getElementById("ok").textContent;
|
||||
proxyok1 = iframe1Doc.getElementById("proxy").textContent;
|
||||
footnote = iframe1Doc.getElementById("footnote").textContent;
|
||||
let authok1 = iframe1Doc.getElementById("ok").textContent;
|
||||
let proxyok1 = iframe1Doc.getElementById("proxy").textContent;
|
||||
let footnote = iframe1Doc.getElementById("footnote").textContent;
|
||||
|
||||
is(authok1, "FAIL", "WWW Authorization FAILED, frame1");
|
||||
is(proxyok1, "PASS", "Proxy Authorization OK, frame1");
|
||||
@ -481,9 +482,9 @@
|
||||
await handlePrompt(state, action);
|
||||
|
||||
let iframe1Doc = await iframe1DocPromise;
|
||||
authok1 = iframe1Doc.getElementById("ok").textContent;
|
||||
proxyok1 = iframe1Doc.getElementById("proxy").textContent;
|
||||
footnote = iframe1Doc.getElementById("footnote").textContent;
|
||||
let authok1 = iframe1Doc.getElementById("ok").textContent;
|
||||
let proxyok1 = iframe1Doc.getElementById("proxy").textContent;
|
||||
let footnote = iframe1Doc.getElementById("footnote").textContent;
|
||||
|
||||
is(authok1, "PASS", "WWW Authorization OK, frame1");
|
||||
is(proxyok1, "PASS", "Proxy Authorization OK, frame1");
|
||||
@ -495,7 +496,6 @@
|
||||
// Check we process all challenges sent by server when
|
||||
// user cancels prompts
|
||||
let iframe1DocPromise = promiseLoadedContentDoc(iframe1);
|
||||
expectedDialogs = 5;
|
||||
iframe1.src = EXAMPLE_COM + "authenticate.sjs?" +
|
||||
"user=user6name&" +
|
||||
"pass=user6pass&" +
|
||||
@ -553,9 +553,9 @@
|
||||
await handlePrompt(state, action);
|
||||
|
||||
let iframe1Doc = await iframe1DocPromise;
|
||||
authok1 = iframe1Doc.getElementById("ok").textContent;
|
||||
proxyok1 = iframe1Doc.getElementById("proxy").textContent;
|
||||
footnote = iframe1Doc.getElementById("footnote").textContent;
|
||||
let authok1 = iframe1Doc.getElementById("ok").textContent;
|
||||
let proxyok1 = iframe1Doc.getElementById("proxy").textContent;
|
||||
let footnote = iframe1Doc.getElementById("footnote").textContent;
|
||||
|
||||
is(authok1, "PASS", "WWW Authorization OK, frame1");
|
||||
is(proxyok1, "PASS", "Proxy Authorization OK, frame1");
|
||||
|
@ -6,7 +6,7 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<script type="text/javascript" src="prompt_common.js"></script>
|
||||
<script type="text/javascript" src="../../../prompts/test/prompt_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
@ -25,10 +25,7 @@ isTabModal = false;
|
||||
|
||||
const AUTHENTICATE_PATH = new URL("authenticate.sjs", window.location.href).pathname;
|
||||
|
||||
let chromeScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
|
||||
|
||||
runInParent(() => {
|
||||
// eslint-disable-next-line no-shadow
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
let login3A, login3B, login4;
|
||||
|
@ -6,7 +6,7 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<script type="text/javascript" src="prompt_common.js"></script>
|
||||
<script type="text/javascript" src="../../../prompts/test/prompt_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
@ -21,8 +21,6 @@
|
||||
// Force parent to not look for tab-modal prompts, as they're not used for auth prompts.
|
||||
isTabModal = false;
|
||||
|
||||
let chromeScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
|
||||
|
||||
runInParent(() => {
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<script type="text/javascript" src="prompt_common.js"></script>
|
||||
<script type="text/javascript" src="../../../prompts/test/prompt_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
@ -34,8 +34,6 @@ var authinfo = {
|
||||
// Force parent to not look for tab-modal prompts, as they're not used for auth prompts.
|
||||
isTabModal = false;
|
||||
|
||||
let chromeScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
|
||||
|
||||
let prompterParent = runInParent(() => {
|
||||
const promptFac = Cc["@mozilla.org/passwordmanager/authpromptfactory;1"].
|
||||
getService(Ci.nsIPromptFactory);
|
||||
|
@ -6,7 +6,7 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<script type="text/javascript" src="prompt_common.js"></script>
|
||||
<script type="text/javascript" src="../../../prompts/test/prompt_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
@ -185,9 +185,9 @@ add_task(async function test_autologin() {
|
||||
proxyAuthinfo.realm = "Proxy Realm";
|
||||
proxyAuthinfo.flags = Ci.nsIAuthInformation.AUTH_PROXY;
|
||||
|
||||
time1 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
|
||||
let time1 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
|
||||
isOk = prompter2.promptAuth(proxyChannel, level, proxyAuthinfo);
|
||||
time2 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
|
||||
let time2 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
|
||||
|
||||
ok(isOk, "Checking dialog return value (accept)");
|
||||
isnot(time1, time2, "Checking that timeLastUsed was updated");
|
||||
@ -221,11 +221,11 @@ add_task(async function test_autologin_incorrect() {
|
||||
proxyAuthinfo.realm = "Proxy Realm";
|
||||
proxyAuthinfo.flags = (Ci.nsIAuthInformation.AUTH_PROXY | Ci.nsIAuthInformation.PREVIOUS_FAILED);
|
||||
|
||||
time1 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
|
||||
let time1 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
|
||||
promptDone = handlePrompt(state, action);
|
||||
isOk = prompter2.promptAuth(proxyChannel, level, proxyAuthinfo);
|
||||
await promptDone;
|
||||
time2 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
|
||||
let time2 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
|
||||
|
||||
ok(isOk, "Checking dialog return value (accept)");
|
||||
isnot(time1, time2, "Checking that timeLastUsed was updated");
|
||||
|
@ -7,27 +7,21 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<script type="text/javascript" src="../../../satchel/test/satchel_common.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
let pwmgrCommonScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
|
||||
|
||||
let readyPromise = registerRunTests();
|
||||
let chromeScript = runInParent(function chromeSetup() {
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
let login1A = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
let login1B = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
let login2A = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
let login2B = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
let login2C = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
|
||||
login1A.init("http://mochi.test:8888", "http://username-focus-1", null,
|
||||
"testuser1A", "testpass1A", "", "");
|
||||
@ -128,7 +122,7 @@ add_task(async function test_autofilled() {
|
||||
removeFocus();
|
||||
usernameField.value = "testuser";
|
||||
info("Focus when we don't have an exact match");
|
||||
shownPromise = promiseACShown();
|
||||
let shownPromise = promiseACShown();
|
||||
usernameField.focus();
|
||||
await shownPromise;
|
||||
});
|
||||
@ -143,7 +137,7 @@ add_task(async function test_autofilled_prefilled_un() {
|
||||
removeFocus();
|
||||
usernameField.value = "testuser";
|
||||
info("Focus when we don't have an exact match");
|
||||
shownPromise = promiseACShown();
|
||||
let shownPromise = promiseACShown();
|
||||
usernameField.focus();
|
||||
await shownPromise;
|
||||
});
|
||||
@ -166,7 +160,7 @@ add_task(async function test_autofilled_focused_dynamic() {
|
||||
removeFocus();
|
||||
passwordField.value = "test";
|
||||
info("Focus when we don't have an exact match");
|
||||
shownPromise = promiseACShown();
|
||||
let shownPromise = promiseACShown();
|
||||
usernameField.focus();
|
||||
await shownPromise;
|
||||
});
|
||||
@ -176,7 +170,7 @@ add_task(async function test_autofilled_focused_dynamic() {
|
||||
add_task(async function test_multiple() {
|
||||
let usernameField = $_("-multiple", "uname");
|
||||
info("Fields not filled due to multiple so autocomplete upon focus");
|
||||
shownPromise = promiseACShown();
|
||||
let shownPromise = promiseACShown();
|
||||
usernameField.focus();
|
||||
await shownPromise;
|
||||
});
|
||||
@ -205,7 +199,7 @@ add_task(async function test_multiple_prefilled_un1() {
|
||||
removeFocus();
|
||||
usernameField.value = "testuser";
|
||||
info("Focus when we don't have an exact match");
|
||||
shownPromise = promiseACShown();
|
||||
let shownPromise = promiseACShown();
|
||||
usernameField.focus();
|
||||
await shownPromise;
|
||||
});
|
||||
@ -220,7 +214,7 @@ add_task(async function test_multiple_prefilled_un2() {
|
||||
removeFocus();
|
||||
usernameField.value = "testuser";
|
||||
info("Focus when we don't have an exact match");
|
||||
shownPromise = promiseACShown();
|
||||
let shownPromise = promiseACShown();
|
||||
usernameField.focus();
|
||||
await shownPromise;
|
||||
});
|
||||
@ -243,7 +237,7 @@ add_task(async function test_multiple_prefilled_focused_dynamic() {
|
||||
removeFocus();
|
||||
passwordField.value = "test";
|
||||
info("Focus when we don't have an exact match");
|
||||
shownPromise = promiseACShown();
|
||||
let shownPromise = promiseACShown();
|
||||
usernameField.focus();
|
||||
await shownPromise;
|
||||
});
|
||||
|
172
toolkit/components/passwordmgr/test/mochitest/test_xhr.html
Normal file
172
toolkit/components/passwordmgr/test/mochitest/test_xhr.html
Normal file
@ -0,0 +1,172 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for XHR prompts</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<script type="text/javascript" src="../../../prompts/test/prompt_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: XHR prompt
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content" style="display: none">
|
||||
<iframe id="iframe"></iframe>
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Login Manager: XHR prompts. **/
|
||||
function makeRequest(uri) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new XMLHttpRequest();
|
||||
request.open("GET", uri, true);
|
||||
request.addEventListener("loadend", function onLoadEnd() {
|
||||
let result = xhrLoad(request.responseXML);
|
||||
resolve(result);
|
||||
});
|
||||
request.send(null);
|
||||
});
|
||||
}
|
||||
|
||||
function xhrLoad(xmlDoc) {
|
||||
// The server echos back the user/pass it received.
|
||||
var username = xmlDoc.getElementById("user").textContent;
|
||||
var password = xmlDoc.getElementById("pass").textContent;
|
||||
var authok = xmlDoc.getElementById("ok").textContent;
|
||||
return {username, password, authok};
|
||||
}
|
||||
|
||||
// Force parent to not look for tab-modal prompts, as they're not used for auth prompts.
|
||||
isTabModal = false;
|
||||
|
||||
let prompterParent = runInParent(() => {
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const promptFac = Cc["@mozilla.org/passwordmanager/authpromptfactory;1"].
|
||||
getService(Ci.nsIPromptFactory);
|
||||
|
||||
let chromeWin = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let prompt = promptFac.getPrompt(chromeWin, Ci.nsIAuthPrompt);
|
||||
|
||||
addMessageListener("proxyPrompter", function onMessage(msg) {
|
||||
let rv = prompt[msg.methodName](...msg.args);
|
||||
return {
|
||||
rv,
|
||||
// Send the args back to content so out/inout args can be checked.
|
||||
args: msg.args,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
let prompter1 = new PrompterProxy(prompterParent);
|
||||
|
||||
add_task(function setup() {
|
||||
runInParent(function initLogins() {
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
let nsLoginInfo = Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo, "init");
|
||||
let login1 = new nsLoginInfo("http://mochi.test:8888", null, "xhr",
|
||||
"xhruser1", "xhrpass1", "", "");
|
||||
let login2 = new nsLoginInfo("http://mochi.test:8888", null, "xhr2",
|
||||
"xhruser2", "xhrpass2", "", "");
|
||||
|
||||
try {
|
||||
Services.logins.addLogin(login1);
|
||||
Services.logins.addLogin(login2);
|
||||
} catch (e) {
|
||||
assert.ok(false, "addLogin threw: " + e);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test1() {
|
||||
let state = {
|
||||
msg: "http://mochi.test:8888 is requesting your username and password. The site says: “xhr”",
|
||||
title: "Authentication Required",
|
||||
textValue: "xhruser1",
|
||||
passValue: "xhrpass1",
|
||||
iconClass: "authentication-icon question-icon",
|
||||
titleHidden: true,
|
||||
textHidden: false,
|
||||
passHidden: false,
|
||||
checkHidden: true,
|
||||
checkMsg: "",
|
||||
checked: false,
|
||||
focused: "textField",
|
||||
defButton: "button0",
|
||||
};
|
||||
let action = {
|
||||
buttonClick: "ok",
|
||||
};
|
||||
let promptDone = handlePrompt(state, action);
|
||||
let requestPromise = makeRequest("authenticate.sjs?user=xhruser1&pass=xhrpass1&realm=xhr");
|
||||
await promptDone;
|
||||
let result = await requestPromise;
|
||||
|
||||
is(result.authok, "PASS", "Checking for successful authentication");
|
||||
is(result.username, "xhruser1", "Checking for username");
|
||||
is(result.password, "xhrpass1", "Checking for password");
|
||||
});
|
||||
|
||||
add_task(async function test2() {
|
||||
// Test correct parenting, by opening another tab in the foreground,
|
||||
// and making sure the prompt re-focuses the original tab when shown:
|
||||
let newWin = window.open();
|
||||
newWin.focus();
|
||||
|
||||
let state = {
|
||||
msg: "http://mochi.test:8888 is requesting your username and password. The site says: “xhr2”",
|
||||
title: "Authentication Required",
|
||||
textValue: "xhruser2",
|
||||
passValue: "xhrpass2",
|
||||
iconClass: "authentication-icon question-icon",
|
||||
titleHidden: true,
|
||||
textHidden: false,
|
||||
passHidden: false,
|
||||
checkHidden: true,
|
||||
checkMsg: "",
|
||||
checked: false,
|
||||
focused: "textField",
|
||||
defButton: "button0",
|
||||
// Check that the dialog is modal, chrome and dependent;
|
||||
// We can't just check window.opener because that'll be
|
||||
// a content window, which therefore isn't exposed (it'll lie and
|
||||
// be null).
|
||||
chrome: true,
|
||||
dialog: true,
|
||||
chromeDependent: true,
|
||||
isWindowModal: true,
|
||||
};
|
||||
let action = {
|
||||
buttonClick: "ok",
|
||||
};
|
||||
let promptDone = handlePrompt(state, action);
|
||||
let requestPromise = makeRequest("authenticate.sjs?user=xhruser2&pass=xhrpass2&realm=xhr2");
|
||||
await promptDone;
|
||||
let result = await requestPromise;
|
||||
|
||||
runInParent(() => {
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Check that the right tab is focused:
|
||||
let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let spec = browserWin.gBrowser.selectedBrowser.currentURI.spec;
|
||||
assert.ok(spec.startsWith("http://mochi.test:8888"),
|
||||
`Tab with remote URI (rather than about:blank)
|
||||
should be focused (${spec})`);
|
||||
});
|
||||
|
||||
is(result.authok, "PASS", "Checking for successful authentication");
|
||||
is(result.username, "xhruser2", "Checking for username");
|
||||
is(result.password, "xhrpass2", "Checking for password");
|
||||
|
||||
newWin.close();
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -6,7 +6,7 @@
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<script type="text/javascript" src="prompt_common.js"></script>
|
||||
<script type="text/javascript" src="../../../prompts/test/prompt_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
@ -114,6 +114,10 @@ add_task(async function test_backgroundTab() {
|
||||
checked: false,
|
||||
focused: "textField",
|
||||
defButton: "button0",
|
||||
chrome: true,
|
||||
dialog: true,
|
||||
chromeDependent: true,
|
||||
isWindowModal: true,
|
||||
};
|
||||
|
||||
let action = {
|
||||
@ -121,7 +125,6 @@ add_task(async function test_backgroundTab() {
|
||||
};
|
||||
|
||||
await handlePrompt(state, action);
|
||||
await checkPromptModal();
|
||||
|
||||
await new Promise(resolve => {
|
||||
let focusScript = runInParent(checkWindowFocus);
|
||||
|
@ -1,75 +0,0 @@
|
||||
/**
|
||||
* NOTE:
|
||||
* This file is currently only being used for tests which haven't been
|
||||
* fixed to work with e10s. Favor using the `prompt_common.js` file that
|
||||
* is in `toolkit/components/prompts/test/` instead.
|
||||
*/
|
||||
/* eslint-disable mozilla/use-chromeutils-generateqi */
|
||||
|
||||
var Ci = SpecialPowers.Ci;
|
||||
ok(Ci != null, "Access Ci");
|
||||
var Cc = SpecialPowers.Cc;
|
||||
ok(Cc != null, "Access Cc");
|
||||
|
||||
var didDialog;
|
||||
|
||||
var timer; // keep in outer scope so it's not GC'd before firing
|
||||
function startCallbackTimer() {
|
||||
didDialog = false;
|
||||
|
||||
// Delay before the callback twiddles the prompt.
|
||||
const dialogDelay = 10;
|
||||
|
||||
// Use a timer to invoke a callback to twiddle the authentication dialog
|
||||
timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.init(observer, dialogDelay, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
|
||||
var observer = SpecialPowers.wrapCallbackObject({
|
||||
QueryInterface(iid) {
|
||||
const interfaces = [Ci.nsIObserver,
|
||||
Ci.nsISupports, Ci.nsISupportsWeakReference];
|
||||
|
||||
if (!interfaces.some(function(v) {
|
||||
return iid.equals(v);
|
||||
})) {
|
||||
throw SpecialPowers.Components.results.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
observe(subject, topic, data) {
|
||||
var doc = getDialogDoc();
|
||||
if (doc) {
|
||||
handleDialog(doc, testNum);
|
||||
} else {
|
||||
startCallbackTimer();
|
||||
} // try again in a bit
|
||||
},
|
||||
});
|
||||
|
||||
function getDialogDoc() {
|
||||
// Find the <browser> which contains notifyWindow, by looking
|
||||
// through all the open windows and all the <browsers> in each.
|
||||
// var enumerator = SpecialPowers.Services.wm.getEnumerator("navigator:browser");
|
||||
for (let {docShell} of SpecialPowers.Services.wm.getXULWindowEnumerator(null)) {
|
||||
var containedDocShells = docShell.getDocShellEnumerator(
|
||||
docShell.typeChrome,
|
||||
docShell.ENUMERATE_FORWARDS);
|
||||
for (let childDocShell of containedDocShells) {
|
||||
// We don't want it if it's not done loading.
|
||||
if (childDocShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE) {
|
||||
continue;
|
||||
}
|
||||
var childDoc = childDocShell.contentViewer.DOMDocument;
|
||||
|
||||
// ok(true, "Got window: " + childDoc.location.href);
|
||||
if (childDoc.location.href == "chrome://global/content/commonDialog.xul") {
|
||||
return childDoc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
@ -1,515 +0,0 @@
|
||||
const TESTS_DIR = "/tests/toolkit/components/passwordmgr/test/";
|
||||
|
||||
/**
|
||||
* Returns the element with the specified |name| attribute.
|
||||
*/
|
||||
function $_(formNum, name) {
|
||||
var form = document.getElementById("form" + formNum);
|
||||
if (!form) {
|
||||
logWarning("$_ couldn't find requested form " + formNum);
|
||||
return null;
|
||||
}
|
||||
|
||||
var element = form.children.namedItem(name);
|
||||
if (!element) {
|
||||
logWarning("$_ couldn't find requested element " + name);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Note that namedItem is a bit stupid, and will prefer an
|
||||
// |id| attribute over a |name| attribute when looking for
|
||||
// the element. Login Mananger happens to use .namedItem
|
||||
// anyway, but let's rigorously check it here anyway so
|
||||
// that we don't end up with tests that mistakenly pass.
|
||||
|
||||
if (element.getAttribute("name") != name) {
|
||||
logWarning("$_ got confused.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a form for expected values. If an argument is null, a field's
|
||||
* expected value will be the default value.
|
||||
*
|
||||
* <form id="form#">
|
||||
* checkForm(#, "foo");
|
||||
*/
|
||||
function checkForm(formNum, val1, val2, val3) {
|
||||
var e, form = document.getElementById("form" + formNum);
|
||||
ok(form, "Locating form " + formNum);
|
||||
|
||||
var numToCheck = arguments.length - 1;
|
||||
|
||||
if (!numToCheck--) {
|
||||
return;
|
||||
}
|
||||
e = form.elements[0];
|
||||
if (val1 == null) {
|
||||
is(e.value, e.defaultValue, "Test default value of field " + e.name +
|
||||
" in form " + formNum);
|
||||
} else {
|
||||
is(e.value, val1, "Test value of field " + e.name +
|
||||
" in form " + formNum);
|
||||
}
|
||||
|
||||
|
||||
if (!numToCheck--) {
|
||||
return;
|
||||
}
|
||||
e = form.elements[1];
|
||||
if (val2 == null) {
|
||||
is(e.value, e.defaultValue, "Test default value of field " + e.name +
|
||||
" in form " + formNum);
|
||||
} else {
|
||||
is(e.value, val2, "Test value of field " + e.name +
|
||||
" in form " + formNum);
|
||||
}
|
||||
|
||||
|
||||
if (!numToCheck--) {
|
||||
return;
|
||||
}
|
||||
e = form.elements[2];
|
||||
if (val3 == null) {
|
||||
is(e.value, e.defaultValue, "Test default value of field " + e.name +
|
||||
" in form " + formNum);
|
||||
} else {
|
||||
is(e.value, val3, "Test value of field " + e.name +
|
||||
" in form " + formNum);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a form for unmodified values from when page was loaded.
|
||||
*
|
||||
* <form id="form#">
|
||||
* checkUnmodifiedForm(#);
|
||||
*/
|
||||
function checkUnmodifiedForm(formNum) {
|
||||
var form = document.getElementById("form" + formNum);
|
||||
ok(form, "Locating form " + formNum);
|
||||
|
||||
for (var i = 0; i < form.elements.length; i++) {
|
||||
var ele = form.elements[i];
|
||||
|
||||
// No point in checking form submit/reset buttons.
|
||||
if (ele.type == "submit" || ele.type == "reset") {
|
||||
continue;
|
||||
}
|
||||
|
||||
is(ele.value, ele.defaultValue, "Test to default value of field " +
|
||||
ele.name + " in form " + formNum);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init with a common login
|
||||
* If selfFilling is true or non-undefined, fires an event at the page so that
|
||||
* the test can start checking filled-in values. Tests that check observer
|
||||
* notifications might be confused by this.
|
||||
*/
|
||||
function commonInit(selfFilling) {
|
||||
// eslint-disable-next-line mozilla/use-services
|
||||
var pwmgr = SpecialPowers.Cc["@mozilla.org/login-manager;1"].
|
||||
getService(SpecialPowers.Ci.nsILoginManager);
|
||||
ok(pwmgr != null, "Access LoginManager");
|
||||
|
||||
// Check that initial state has no logins
|
||||
var logins = pwmgr.getAllLogins();
|
||||
is(logins.length, 0, "Not expecting logins to be present");
|
||||
var disabledHosts = pwmgr.getAllDisabledHosts();
|
||||
if (disabledHosts.length) {
|
||||
ok(false, "Warning: wasn't expecting disabled hosts to be present.");
|
||||
for (var host of disabledHosts) {
|
||||
pwmgr.setLoginSavingEnabled(host, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a login that's used in multiple tests
|
||||
var login = SpecialPowers.Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(SpecialPowers.Ci.nsILoginInfo);
|
||||
login.init("http://mochi.test:8888", "http://mochi.test:8888", null,
|
||||
"testuser", "testpass", "uname", "pword");
|
||||
pwmgr.addLogin(login);
|
||||
|
||||
// Last sanity check
|
||||
logins = pwmgr.getAllLogins();
|
||||
is(logins.length, 1, "Checking for successful init login");
|
||||
disabledHosts = pwmgr.getAllDisabledHosts();
|
||||
is(disabledHosts.length, 0, "Checking for no disabled hosts");
|
||||
|
||||
if (selfFilling) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.sendAsyncMessage) {
|
||||
sendAsyncMessage("registerRunTests");
|
||||
} else {
|
||||
registerRunTests();
|
||||
}
|
||||
}
|
||||
|
||||
function registerRunTests() {
|
||||
return new Promise(resolve => {
|
||||
// We provide a general mechanism for our tests to know when they can
|
||||
// safely run: we add a final form that we know will be filled in, wait
|
||||
// for the login manager to tell us that it's filled in and then continue
|
||||
// with the rest of the tests.
|
||||
window.addEventListener("DOMContentLoaded", (event) => {
|
||||
var form = document.createElement("form");
|
||||
form.id = "observerforcer";
|
||||
var username = document.createElement("input");
|
||||
username.name = "testuser";
|
||||
form.appendChild(username);
|
||||
var password = document.createElement("input");
|
||||
password.name = "testpass";
|
||||
password.type = "password";
|
||||
form.appendChild(password);
|
||||
|
||||
var observer = SpecialPowers.wrapCallback(function(subject, topic, data) {
|
||||
var formLikeRoot = subject;
|
||||
if (formLikeRoot.id !== "observerforcer") {
|
||||
return;
|
||||
}
|
||||
SpecialPowers.removeObserver(observer, "passwordmgr-processed-form");
|
||||
formLikeRoot.remove();
|
||||
SimpleTest.executeSoon(() => {
|
||||
var runTestEvent = new Event("runTests");
|
||||
window.dispatchEvent(runTestEvent);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
SpecialPowers.addObserver(observer, "passwordmgr-processed-form");
|
||||
|
||||
document.body.appendChild(form);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const masterPassword = "omgsecret!";
|
||||
|
||||
function enableMasterPassword() {
|
||||
setMasterPassword(true);
|
||||
}
|
||||
|
||||
function disableMasterPassword() {
|
||||
setMasterPassword(false);
|
||||
}
|
||||
|
||||
function setMasterPassword(enable) {
|
||||
chromeScript.sendSyncMessage("setMasterPassword", { enable });
|
||||
}
|
||||
|
||||
function isLoggedIn() {
|
||||
return chromeScript.sendSyncMessage("isLoggedIn")[0][0];
|
||||
}
|
||||
|
||||
function logoutMasterPassword() {
|
||||
runInParent(function parent_logoutMasterPassword() {
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
var sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing);
|
||||
sdr.logoutAndTeardown();
|
||||
});
|
||||
}
|
||||
|
||||
function dumpLogins(pwmgr) {
|
||||
var logins = pwmgr.getAllLogins();
|
||||
ok(true, "----- dumpLogins: have " + logins.length + " logins. -----");
|
||||
for (var i = 0; i < logins.length; i++) {
|
||||
dumpLogin("login #" + i + " --- ", logins[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function dumpLogin(label, login) {
|
||||
var loginText = "";
|
||||
loginText += "host: ";
|
||||
loginText += login.hostname;
|
||||
loginText += " / formURL: ";
|
||||
loginText += login.formSubmitURL;
|
||||
loginText += " / realm: ";
|
||||
loginText += login.httpRealm;
|
||||
loginText += " / user: ";
|
||||
loginText += login.username;
|
||||
loginText += " / pass: ";
|
||||
loginText += login.password;
|
||||
loginText += " / ufield: ";
|
||||
loginText += login.usernameField;
|
||||
loginText += " / pfield: ";
|
||||
loginText += login.passwordField;
|
||||
ok(true, label + loginText);
|
||||
}
|
||||
|
||||
function getRecipeParent() {
|
||||
// eslint-disable-next-line no-shadow
|
||||
var { LoginManagerParent } = SpecialPowers.Cu.import("resource://gre/modules/LoginManagerParent.jsm", {});
|
||||
if (!LoginManagerParent.recipeParentPromise) {
|
||||
return null;
|
||||
}
|
||||
return LoginManagerParent.recipeParentPromise.then((recipeParent) => {
|
||||
return SpecialPowers.wrap(recipeParent);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves when a specified number of forms have been processed.
|
||||
*/
|
||||
function promiseFormsProcessed(expectedCount = 1) {
|
||||
var processedCount = 0;
|
||||
return new Promise((resolve, reject) => {
|
||||
function onProcessedForm(subject, topic, data) {
|
||||
processedCount++;
|
||||
if (processedCount == expectedCount) {
|
||||
SpecialPowers.removeObserver(onProcessedForm, "passwordmgr-processed-form");
|
||||
resolve(SpecialPowers.Cu.waiveXrays(subject), data);
|
||||
}
|
||||
}
|
||||
SpecialPowers.addObserver(onProcessedForm, "passwordmgr-processed-form");
|
||||
});
|
||||
}
|
||||
|
||||
function loadRecipes(recipes) {
|
||||
info("Loading recipes");
|
||||
return new Promise(resolve => {
|
||||
chromeScript.addMessageListener("loadedRecipes", function loaded() {
|
||||
chromeScript.removeMessageListener("loadedRecipes", loaded);
|
||||
resolve(recipes);
|
||||
});
|
||||
chromeScript.sendAsyncMessage("loadRecipes", recipes);
|
||||
});
|
||||
}
|
||||
|
||||
function resetRecipes() {
|
||||
info("Resetting recipes");
|
||||
return new Promise(resolve => {
|
||||
chromeScript.addMessageListener("recipesReset", function reset() {
|
||||
chromeScript.removeMessageListener("recipesReset", reset);
|
||||
resolve();
|
||||
});
|
||||
chromeScript.sendAsyncMessage("resetRecipes");
|
||||
});
|
||||
}
|
||||
|
||||
function promiseStorageChanged(expectedChangeTypes) {
|
||||
return new Promise((resolve, reject) => {
|
||||
function onStorageChanged({ topic, data }) {
|
||||
let changeType = expectedChangeTypes.shift();
|
||||
is(data, changeType, "Check expected passwordmgr-storage-changed type");
|
||||
if (expectedChangeTypes.length === 0) {
|
||||
chromeScript.removeMessageListener("storageChanged", onStorageChanged);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
chromeScript.addMessageListener("storageChanged", onStorageChanged);
|
||||
});
|
||||
}
|
||||
|
||||
function promisePromptShown(expectedTopic) {
|
||||
return new Promise((resolve, reject) => {
|
||||
function onPromptShown({ topic, data }) {
|
||||
is(topic, expectedTopic, "Check expected prompt topic");
|
||||
chromeScript.removeMessageListener("promptShown", onPromptShown);
|
||||
resolve();
|
||||
}
|
||||
chromeScript.addMessageListener("promptShown", onPromptShown);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a function synchronously in the parent process and destroy it in the test cleanup function.
|
||||
* @param {Function|String} aFunctionOrURL - either a function that will be stringified and run
|
||||
* or the URL to a JS file.
|
||||
* @return {Object} - the return value of loadChromeScript providing message-related methods.
|
||||
* @see loadChromeScript in specialpowersAPI.js
|
||||
*/
|
||||
function runInParent(aFunctionOrURL) {
|
||||
let chromeScript = SpecialPowers.loadChromeScript(aFunctionOrURL);
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
chromeScript.destroy();
|
||||
});
|
||||
return chromeScript;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run commonInit synchronously in the parent then run the test function after the runTests event.
|
||||
*
|
||||
* @param {Function} aFunction The test function to run
|
||||
*/
|
||||
function runChecksAfterCommonInit(aFunction = null) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
let pwmgrCommonScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
|
||||
if (aFunction) {
|
||||
window.addEventListener("runTests", aFunction);
|
||||
pwmgrCommonScript.addMessageListener("registerRunTests", () => registerRunTests());
|
||||
}
|
||||
pwmgrCommonScript.sendSyncMessage("setupParent");
|
||||
return pwmgrCommonScript;
|
||||
}
|
||||
|
||||
// Code to run when loaded as a chrome script in tests via loadChromeScript
|
||||
if (this.addMessageListener) {
|
||||
var SpecialPowers = { Cc, Ci, Cr, Cu };
|
||||
var ok, is;
|
||||
// Ignore ok/is in commonInit since they aren't defined in a chrome script.
|
||||
ok = is = () => {};
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
var {LoginHelper} = ChromeUtils.import("resource://gre/modules/LoginHelper.jsm");
|
||||
var {LoginManagerParent} = ChromeUtils.import("resource://gre/modules/LoginManagerParent.jsm");
|
||||
var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function onStorageChanged(subject, topic, data) {
|
||||
sendAsyncMessage("storageChanged", {
|
||||
topic,
|
||||
data,
|
||||
});
|
||||
}
|
||||
Services.obs.addObserver(onStorageChanged, "passwordmgr-storage-changed");
|
||||
|
||||
function onPrompt(subject, topic, data) {
|
||||
sendAsyncMessage("promptShown", {
|
||||
topic,
|
||||
data,
|
||||
});
|
||||
}
|
||||
Services.obs.addObserver(onPrompt, "passwordmgr-prompt-change");
|
||||
Services.obs.addObserver(onPrompt, "passwordmgr-prompt-save");
|
||||
|
||||
addMessageListener("setupParent", ({selfFilling = false} = {selfFilling: false}) => {
|
||||
commonInit(selfFilling);
|
||||
sendAsyncMessage("doneSetup");
|
||||
});
|
||||
|
||||
addMessageListener("loadRecipes", function(recipes) {
|
||||
(async function() {
|
||||
var recipeParent = await LoginManagerParent.recipeParentPromise;
|
||||
await recipeParent.load(recipes);
|
||||
sendAsyncMessage("loadedRecipes", recipes);
|
||||
})();
|
||||
});
|
||||
|
||||
addMessageListener("resetRecipes", function() {
|
||||
(async function() {
|
||||
let recipeParent = await LoginManagerParent.recipeParentPromise;
|
||||
await recipeParent.reset();
|
||||
sendAsyncMessage("recipesReset");
|
||||
})();
|
||||
});
|
||||
|
||||
addMessageListener("proxyLoginManager", msg => {
|
||||
// Recreate nsILoginInfo objects from vanilla JS objects.
|
||||
let recreatedArgs = msg.args.map((arg, index) => {
|
||||
if (msg.loginInfoIndices.includes(index)) {
|
||||
return LoginHelper.vanillaObjectToLogin(arg);
|
||||
}
|
||||
|
||||
return arg;
|
||||
});
|
||||
|
||||
let rv = Services.logins[msg.methodName](...recreatedArgs);
|
||||
if (rv instanceof Ci.nsILoginInfo) {
|
||||
rv = LoginHelper.loginToVanillaObject(rv);
|
||||
} else if (Array.isArray(rv) && rv.length > 0 && rv[0] instanceof Ci.nsILoginInfo) {
|
||||
rv = rv.map(login => LoginHelper.loginToVanillaObject(login));
|
||||
}
|
||||
return rv;
|
||||
});
|
||||
|
||||
addMessageListener("isLoggedIn", () => {
|
||||
// This can't use the LoginManager proxy below since it's not a method.
|
||||
return Services.logins.isLoggedIn;
|
||||
});
|
||||
|
||||
addMessageListener("setMasterPassword", ({ enable }) => {
|
||||
let oldPW, newPW;
|
||||
if (enable) {
|
||||
oldPW = "";
|
||||
newPW = masterPassword;
|
||||
} else {
|
||||
oldPW = masterPassword;
|
||||
newPW = "";
|
||||
}
|
||||
// Set master password. Note that this logs in the user if no password was
|
||||
// set before. But after logging out the next invocation of pwmgr can
|
||||
// trigger a MP prompt.
|
||||
|
||||
var pk11db = Cc["@mozilla.org/security/pk11tokendb;1"].getService(Ci.nsIPK11TokenDB);
|
||||
var token = pk11db.getInternalKeyToken();
|
||||
dump("MP change from " + oldPW + " to " + newPW + "\n");
|
||||
token.changePassword(oldPW, newPW);
|
||||
token.logoutSimple();
|
||||
});
|
||||
|
||||
Services.mm.addMessageListener("RemoteLogins:onFormSubmit", function onFormSubmit(message) {
|
||||
sendAsyncMessage("formSubmissionProcessed", message.data, message.objects);
|
||||
});
|
||||
} else {
|
||||
// Code to only run in the mochitest pages (not in the chrome script).
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
SpecialPowers.popPrefEnv();
|
||||
runInParent(function cleanupParent() {
|
||||
// eslint-disable-next-line no-shadow
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
// eslint-disable-next-line no-shadow
|
||||
const {LoginManagerParent} = ChromeUtils.import("resource://gre/modules/LoginManagerParent.jsm");
|
||||
|
||||
// Remove all logins and disabled hosts
|
||||
Services.logins.removeAllLogins();
|
||||
|
||||
let disabledHosts = Services.logins.getAllDisabledHosts();
|
||||
disabledHosts.forEach(host => Services.logins.setLoginSavingEnabled(host, true));
|
||||
|
||||
let authMgr = Cc["@mozilla.org/network/http-auth-manager;1"].
|
||||
getService(Ci.nsIHttpAuthManager);
|
||||
authMgr.clearAll();
|
||||
|
||||
if (LoginManagerParent._recipeManager) {
|
||||
LoginManagerParent._recipeManager.reset();
|
||||
}
|
||||
|
||||
// Cleanup PopupNotifications (if on a relevant platform)
|
||||
let chromeWin = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (chromeWin && chromeWin.PopupNotifications) {
|
||||
let notes = chromeWin.PopupNotifications._currentNotifications;
|
||||
if (notes.length > 0) {
|
||||
dump("Removing " + notes.length + " popup notifications.\n");
|
||||
}
|
||||
for (let note of notes) {
|
||||
note.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// eslint-disable-next-line no-shadow
|
||||
let { LoginHelper } = SpecialPowers.Cu.import("resource://gre/modules/LoginHelper.jsm", {});
|
||||
/**
|
||||
* Proxy for Services.logins (nsILoginManager).
|
||||
* Only supports arguments which support structured clone plus {nsILoginInfo}
|
||||
* Assumes properties are methods.
|
||||
*/
|
||||
this.LoginManager = new Proxy({}, {
|
||||
get(target, prop, receiver) {
|
||||
return (...args) => {
|
||||
let loginInfoIndices = [];
|
||||
let cloneableArgs = args.map((val, index) => {
|
||||
if (SpecialPowers.call_Instanceof(val, SpecialPowers.Ci.nsILoginInfo)) {
|
||||
loginInfoIndices.push(index);
|
||||
return LoginHelper.loginToVanillaObject(val);
|
||||
}
|
||||
|
||||
return val;
|
||||
});
|
||||
|
||||
return chromeScript.sendSyncMessage("proxyLoginManager", {
|
||||
args: cloneableArgs,
|
||||
loginInfoIndices,
|
||||
methodName: prop,
|
||||
})[0][0];
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
@ -1,199 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for XHR prompts</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<script type="text/javascript" src="prompt_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: XHR prompt
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content" style="display: none">
|
||||
<iframe id="iframe"></iframe>
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Login Manager: XHR prompts. **/
|
||||
var login1, login2;
|
||||
|
||||
function initLogins() {
|
||||
login1 = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
login2 = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
|
||||
login1.init("http://mochi.test:8888", null, "xhr",
|
||||
"xhruser1", "xhrpass1", "", "");
|
||||
login2.init("http://mochi.test:8888", null, "xhr2",
|
||||
"xhruser2", "xhrpass2", "", "");
|
||||
|
||||
SpecialPowers.Services.logins.addLogin(login1);
|
||||
SpecialPowers.Services.logins.addLogin(login2);
|
||||
}
|
||||
|
||||
function finishTest() {
|
||||
ok(true, "finishTest removing testing logins...");
|
||||
SpecialPowers.Services.logins.removeLogin(login1);
|
||||
SpecialPowers.Services.logins.removeLogin(login2);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function handleDialog(doc, testNum) {
|
||||
ok(true, "handleDialog running for test " + testNum);
|
||||
|
||||
var clickOK = true;
|
||||
var userfield = doc.getElementById("loginTextbox");
|
||||
var passfield = doc.getElementById("password1Textbox");
|
||||
var username = userfield.getAttribute("value");
|
||||
var password = passfield.getAttribute("value");
|
||||
var dialog = doc.getElementById("commonDialog");
|
||||
|
||||
switch (testNum) {
|
||||
case 1:
|
||||
is(username, "xhruser1", "Checking provided username");
|
||||
is(password, "xhrpass1", "Checking provided password");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
is(username, "xhruser2", "Checking provided username");
|
||||
is(password, "xhrpass2", "Checking provided password");
|
||||
|
||||
// Check that the dialog is modal, chrome and dependent;
|
||||
// We can't just check window.opener because that'll be
|
||||
// a content window, which therefore isn't exposed (it'll lie and
|
||||
// be null).
|
||||
var win = doc.defaultView;
|
||||
var Ci = SpecialPowers.Ci;
|
||||
var treeOwner = win.docShell.treeOwner;
|
||||
treeOwner.QueryInterface(Ci.nsIInterfaceRequestor);
|
||||
var flags = treeOwner.getInterface(Ci.nsIXULWindow).chromeFlags;
|
||||
var wbc = treeOwner.getInterface(Ci.nsIWebBrowserChrome);
|
||||
info("Flags: " + flags);
|
||||
ok((flags & Ci.nsIWebBrowserChrome.CHROME_OPENAS_CHROME) != 0,
|
||||
"Dialog should be opened as chrome");
|
||||
ok((flags & Ci.nsIWebBrowserChrome.CHROME_OPENAS_DIALOG) != 0,
|
||||
"Dialog should be opened as a dialog");
|
||||
ok((flags & Ci.nsIWebBrowserChrome.CHROME_DEPENDENT) != 0,
|
||||
"Dialog should be opened as dependent.");
|
||||
ok(wbc.isWindowModal(), "Dialog should be modal");
|
||||
|
||||
// Check that the right tab is focused:
|
||||
var browserWin = SpecialPowers.Services.wm.getMostRecentWindow("navigator:browser");
|
||||
var spec = browserWin.gBrowser.selectedBrowser.currentURI.spec;
|
||||
ok(spec.startsWith("http://mochi.test:8888"),
|
||||
"Tab with remote URI (rather than about:blank) should be focused (" + spec + ")");
|
||||
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
ok(false, "Uhh, unhandled switch for testNum #" + testNum);
|
||||
break;
|
||||
}
|
||||
|
||||
// Explicitly cancel the dialog and report a fail in this failure
|
||||
// case, rather than letting the dialog get stuck due to an auth
|
||||
// failure and having the test timeout.
|
||||
if (!username && !password) {
|
||||
ok(false, "No values prefilled");
|
||||
clickOK = false;
|
||||
}
|
||||
|
||||
if (clickOK) {
|
||||
dialog.acceptDialog();
|
||||
} else {
|
||||
dialog.cancelDialog();
|
||||
}
|
||||
|
||||
ok(true, "handleDialog done");
|
||||
didDialog = true;
|
||||
}
|
||||
|
||||
var newWin;
|
||||
function xhrLoad(xmlDoc) {
|
||||
ok(true, "xhrLoad running for test " + testNum);
|
||||
|
||||
// The server echos back the user/pass it received.
|
||||
var username = xmlDoc.getElementById("user").textContent;
|
||||
var password = xmlDoc.getElementById("pass").textContent;
|
||||
var authok = xmlDoc.getElementById("ok").textContent;
|
||||
|
||||
|
||||
switch (testNum) {
|
||||
case 1:
|
||||
is(username, "xhruser1", "Checking provided username");
|
||||
is(password, "xhrpass1", "Checking provided password");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
is(username, "xhruser2", "Checking provided username");
|
||||
is(password, "xhrpass2", "Checking provided password");
|
||||
|
||||
newWin.close();
|
||||
break;
|
||||
|
||||
default:
|
||||
ok(false, "Uhh, unhandled switch for testNum #" + testNum);
|
||||
break;
|
||||
}
|
||||
|
||||
doTest();
|
||||
}
|
||||
|
||||
function doTest() {
|
||||
switch (++testNum) {
|
||||
case 1:
|
||||
startCallbackTimer();
|
||||
makeRequest("authenticate.sjs?user=xhruser1&pass=xhrpass1&realm=xhr");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Test correct parenting, by opening another tab in the foreground,
|
||||
// and making sure the prompt re-focuses the original tab when shown:
|
||||
newWin = window.open();
|
||||
newWin.focus();
|
||||
startCallbackTimer();
|
||||
makeRequest("authenticate.sjs?user=xhruser2&pass=xhrpass2&realm=xhr2");
|
||||
break;
|
||||
|
||||
default:
|
||||
finishTest();
|
||||
}
|
||||
}
|
||||
|
||||
function makeRequest(uri) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.open("GET", uri, true);
|
||||
request.onreadystatechange = function() {
|
||||
if (request.readyState == 4) {
|
||||
xhrLoad(request.responseXML);
|
||||
}
|
||||
};
|
||||
request.send(null);
|
||||
}
|
||||
|
||||
|
||||
initLogins();
|
||||
|
||||
// clear plain HTTP auth sessions before the test, to allow
|
||||
// running them more than once.
|
||||
var authMgr = SpecialPowers.Cc["@mozilla.org/network/http-auth-manager;1"]
|
||||
.getService(SpecialPowers.Ci.nsIHttpAuthManager);
|
||||
authMgr.clearAll();
|
||||
|
||||
// start the tests
|
||||
testNum = 0;
|
||||
doTest();
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -125,7 +125,8 @@ add_task(function test_initialize() {
|
||||
*/
|
||||
add_task(function test_logins_statistics() {
|
||||
// Repeat the operation twice to test that histograms are not accumulated.
|
||||
for (let repeating of [false, true]) {
|
||||
for (let pass of [1, 2]) {
|
||||
info(`pass ${pass}`);
|
||||
triggerStatisticsCollection();
|
||||
|
||||
// Should record 1 in the bucket corresponding to the number of passwords.
|
||||
|
@ -25,30 +25,6 @@ function handlePromptWhenItAppears(action, isTabModal, isSelect) {
|
||||
}, 100);
|
||||
}
|
||||
|
||||
addMessageListener("checkPromptModal", () => {
|
||||
checkPromptIsModal();
|
||||
});
|
||||
|
||||
function checkPromptIsModal() {
|
||||
// Check that the dialog is modal, chrome and dependent;
|
||||
// We can't just check window.opener because that'll be
|
||||
// a content window, which therefore isn't exposed (it'll lie and
|
||||
// be null).
|
||||
let result = {};
|
||||
let doc = getDialogDoc();
|
||||
let win = doc.defaultView;
|
||||
let treeOwner = win.docShell.treeOwner;
|
||||
treeOwner.QueryInterface(Ci.nsIInterfaceRequestor);
|
||||
let flags = treeOwner.getInterface(Ci.nsIXULWindow).chromeFlags;
|
||||
let wbc = treeOwner.getInterface(Ci.nsIWebBrowserChrome);
|
||||
result.chrome = (flags & Ci.nsIWebBrowserChrome.CHROME_OPENAS_CHROME) != 0;
|
||||
result.dialog = (flags & Ci.nsIWebBrowserChrome.CHROME_OPENAS_DIALOG) != 0;
|
||||
result.chromeDependent = (flags & Ci.nsIWebBrowserChrome.CHROME_DEPENDENT) != 0;
|
||||
result.isWindowModal = wbc.isWindowModal();
|
||||
|
||||
sendAsyncMessage("checkPromptModalResult", result);
|
||||
}
|
||||
|
||||
function handlePrompt(action, isTabModal, isSelect) {
|
||||
let ui;
|
||||
|
||||
@ -152,6 +128,22 @@ function getPromptState(ui) {
|
||||
state.focused = "ERROR: unexpected element focused: " + (e ? e.localName : "<null>");
|
||||
}
|
||||
|
||||
let treeOwner = ui.prompt &&
|
||||
ui.prompt.docShell &&
|
||||
ui.prompt.docShell.treeOwner;
|
||||
if (treeOwner && treeOwner.QueryInterface(Ci.nsIInterfaceRequestor)) {
|
||||
// Check that the dialog is modal, chrome and dependent;
|
||||
// We can't just check window.opener because that'll be
|
||||
// a content window, which therefore isn't exposed (it'll lie and
|
||||
// be null).
|
||||
let flags = treeOwner.getInterface(Ci.nsIXULWindow).chromeFlags;
|
||||
state.chrome = (flags & Ci.nsIWebBrowserChrome.CHROME_OPENAS_CHROME) != 0;
|
||||
state.dialog = (flags & Ci.nsIWebBrowserChrome.CHROME_OPENAS_DIALOG) != 0;
|
||||
state.chromeDependent = (flags & Ci.nsIWebBrowserChrome.CHROME_DEPENDENT) != 0;
|
||||
let wbc = treeOwner.getInterface(Ci.nsIWebBrowserChrome);
|
||||
state.isWindowModal = wbc.isWindowModal();
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
@ -35,20 +35,6 @@ function handlePrompt(state, action) {
|
||||
});
|
||||
}
|
||||
|
||||
function checkPromptModal() {
|
||||
return new Promise(resolve => {
|
||||
gChromeScript.addMessageListener("checkPromptModalResult", function handled(result) {
|
||||
gChromeScript.removeMessageListener("checkPromptModalResult", handled);
|
||||
ok(result.chrome, "Dialog should be opened as chrome");
|
||||
ok(result.dialog, "Dialog should be a dialog");
|
||||
ok(result.chromeDependent, "Dialog should be chrome dependent");
|
||||
ok(result.isWindowModal, "Dialog should be a window modal");
|
||||
resolve(true);
|
||||
});
|
||||
gChromeScript.sendAsyncMessage("checkPromptModal");
|
||||
});
|
||||
}
|
||||
|
||||
function checkPromptState(promptState, expectedState) {
|
||||
info(`checkPromptState: ${expectedState.msg}`);
|
||||
// XXX check title? OS X has title in content
|
||||
@ -92,6 +78,19 @@ function checkPromptState(promptState, expectedState) {
|
||||
} else {
|
||||
is(promptState.focused, expectedState.focused, "Checking focused element");
|
||||
}
|
||||
|
||||
if (expectedState.hasOwnProperty("chrome")) {
|
||||
is(promptState.chrome, expectedState.chrome, "Dialog should be opened as chrome");
|
||||
}
|
||||
if (expectedState.hasOwnProperty("dialog")) {
|
||||
is(promptState.dialog, expectedState.dialog, "Dialog should be opened as a dialog");
|
||||
}
|
||||
if (expectedState.hasOwnProperty("chromeDependent")) {
|
||||
is(promptState.chromeDependent, expectedState.chromeDependent, "Dialog should be opened as dependent");
|
||||
}
|
||||
if (expectedState.hasOwnProperty("isWindowModal")) {
|
||||
is(promptState.isWindowModal, expectedState.isWindowModal, "Dialog should be modal");
|
||||
}
|
||||
}
|
||||
|
||||
function checkEchoedAuthInfo(expectedState, doc) {
|
||||
|
@ -101,6 +101,16 @@ var ParentUtils = {
|
||||
});
|
||||
},
|
||||
|
||||
testMenuEntry(index, statement) {
|
||||
ContentTaskUtils.waitForCondition(() => {
|
||||
let el = gAutocompletePopup.richlistbox.getItemAtIndex(index);
|
||||
let testFunc = new Services.ww.activeWindow.Function("el", `return ${statement}`);
|
||||
return gAutocompletePopup.popupOpen && el && testFunc(el);
|
||||
}, "Testing menu entry").then(() => {
|
||||
sendAsyncMessage("menuEntryTested");
|
||||
});
|
||||
},
|
||||
|
||||
getPopupState() {
|
||||
sendAsyncMessage("gotPopupState", {
|
||||
open: gAutocompletePopup.popupOpen,
|
||||
@ -142,6 +152,9 @@ addMessageListener("waitForMenuChange", ({ expectedCount, expectedFirstValue })
|
||||
addMessageListener("waitForSelectedIndex", ({ expectedIndex }) => {
|
||||
ParentUtils.checkSelectedIndex(expectedIndex);
|
||||
});
|
||||
addMessageListener("waitForMenuEntryTest", ({ index, statement }) => {
|
||||
ParentUtils.testMenuEntry(index, statement);
|
||||
});
|
||||
|
||||
addMessageListener("getPopupState", () => {
|
||||
ParentUtils.getPopupState();
|
||||
|
@ -206,6 +206,16 @@ function notifySelectedIndex(expectedIndex, then = null) {
|
||||
});
|
||||
}
|
||||
|
||||
function testMenuEntry(index, statement) {
|
||||
return new Promise(resolve => {
|
||||
gChromeScript.sendAsyncMessage("waitForMenuEntryTest", { index, statement });
|
||||
gChromeScript.addMessageListener("menuEntryTested", function changed() {
|
||||
gChromeScript.removeMessageListener("menuEntryTested", changed);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getPopupState(then = null) {
|
||||
return new Promise(resolve => {
|
||||
gChromeScript.sendAsyncMessage("getPopupState");
|
||||
|
@ -313,6 +313,60 @@ const BaseControlMixin = Base => {
|
||||
};
|
||||
MozElements.BaseControl = BaseControlMixin(MozXULElement);
|
||||
|
||||
const BaseTextMixin = Base => class extends Base {
|
||||
set label(val) {
|
||||
this.setAttribute("label", val);
|
||||
return val;
|
||||
}
|
||||
|
||||
get label() {
|
||||
return this.getAttribute("label");
|
||||
}
|
||||
|
||||
set crop(val) {
|
||||
this.setAttribute("crop", val);
|
||||
return val;
|
||||
}
|
||||
|
||||
get crop() {
|
||||
return this.getAttribute("crop");
|
||||
}
|
||||
|
||||
set image(val) {
|
||||
this.setAttribute("image", val);
|
||||
return val;
|
||||
}
|
||||
|
||||
get image() {
|
||||
return this.getAttribute("image");
|
||||
}
|
||||
|
||||
set command(val) {
|
||||
this.setAttribute("command", val);
|
||||
return val;
|
||||
}
|
||||
|
||||
get command() {
|
||||
return this.getAttribute("command");
|
||||
}
|
||||
|
||||
set accessKey(val) {
|
||||
// Always store on the control
|
||||
this.setAttribute("accesskey", val);
|
||||
// If there is a label, change the accesskey on the labelElement
|
||||
// if it's also set there
|
||||
if (this.labelElement) {
|
||||
this.labelElement.accessKey = val;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
get accessKey() {
|
||||
return this.labelElement ? this.labelElement.accessKey : this.getAttribute("accesskey");
|
||||
}
|
||||
};
|
||||
MozElements.BaseText = BaseTextMixin(MozXULElement);
|
||||
|
||||
// Attach the base class to the window so other scripts can use it:
|
||||
window.BaseControlMixin = BaseControlMixin;
|
||||
window.MozElementMixin = MozElementMixin;
|
||||
@ -331,6 +385,8 @@ if (!isDummyDocument) {
|
||||
"chrome://global/content/elements/general.js",
|
||||
"chrome://global/content/elements/notificationbox.js",
|
||||
"chrome://global/content/elements/radio.js",
|
||||
"chrome://global/content/elements/richlistbox.js",
|
||||
"chrome://global/content/elements/autocomplete-richlistitem.js",
|
||||
"chrome://global/content/elements/textbox.js",
|
||||
"chrome://global/content/elements/tabbox.js",
|
||||
"chrome://global/content/elements/tree.js",
|
||||
@ -341,7 +397,6 @@ if (!isDummyDocument) {
|
||||
for (let [tag, script] of [
|
||||
["findbar", "chrome://global/content/elements/findbar.js"],
|
||||
["menulist", "chrome://global/content/elements/menulist.js"],
|
||||
["richlistbox", "chrome://global/content/elements/richlistbox.js"],
|
||||
["stringbundle", "chrome://global/content/elements/stringbundle.js"],
|
||||
["printpreview-toolbar", "chrome://global/content/printPreviewToolbar.js"],
|
||||
["editor", "chrome://global/content/elements/editor.js"],
|
||||
|
@ -85,6 +85,7 @@ toolkit.jar:
|
||||
content/global/bindings/toolbarbutton.xml (widgets/toolbarbutton.xml)
|
||||
content/global/bindings/tree.xml (widgets/tree.xml)
|
||||
* content/global/bindings/wizard.xml (widgets/wizard.xml)
|
||||
content/global/elements/autocomplete-richlistitem.js (widgets/autocomplete-richlistitem.js)
|
||||
content/global/elements/browser-custom-element.js (widgets/browser-custom-element.js)
|
||||
content/global/elements/datetimebox.js (widgets/datetimebox.js)
|
||||
content/global/elements/findbar.js (widgets/findbar.js)
|
||||
|
1027
toolkit/content/widgets/autocomplete-richlistitem.js
Normal file
1027
toolkit/content/widgets/autocomplete-richlistitem.js
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -6,6 +6,9 @@
|
||||
// This is loaded into chrome windows with the subscript loader. If you need to
|
||||
// define globals, wrap in a block to prevent leaking onto `window`.
|
||||
|
||||
/**
|
||||
* XUL:richlistbox element.
|
||||
*/
|
||||
MozElements.RichListBox = class RichListBox extends MozElements.BaseControl {
|
||||
constructor() {
|
||||
super();
|
||||
@ -838,3 +841,161 @@ MozXULElement.implementCustomInterface(MozElements.RichListBox, [
|
||||
]);
|
||||
|
||||
customElements.define("richlistbox", MozElements.RichListBox);
|
||||
|
||||
/**
|
||||
* XUL:richlistitem element.
|
||||
*/
|
||||
MozElements.MozRichlistitem = class MozRichlistitem extends MozElements.BaseText {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.selectedByMouseOver = false;
|
||||
|
||||
/**
|
||||
* If there is no modifier key, we select on mousedown, not
|
||||
* click, so that drags work correctly.
|
||||
*/
|
||||
this.addEventListener("mousedown", (event) => {
|
||||
var control = this.control;
|
||||
if (!control || control.disabled)
|
||||
return;
|
||||
if ((!event.ctrlKey || (/Mac/.test(navigator.platform) && event.button == 2)) &&
|
||||
!event.shiftKey && !event.metaKey) {
|
||||
if (!this.selected) {
|
||||
control.selectItem(this);
|
||||
}
|
||||
control.currentItem = this;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* On a click (up+down on the same item), deselect everything
|
||||
* except this item.
|
||||
*/
|
||||
this.addEventListener("click", (event) => {
|
||||
if (event.button != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var control = this.control;
|
||||
if (!control || control.disabled)
|
||||
return;
|
||||
control._userSelecting = true;
|
||||
if (control.selType != "multiple") {
|
||||
control.selectItem(this);
|
||||
} else if (event.ctrlKey || event.metaKey) {
|
||||
control.toggleItemSelection(this);
|
||||
control.currentItem = this;
|
||||
} else if (event.shiftKey) {
|
||||
control.selectItemRange(null, this);
|
||||
control.currentItem = this;
|
||||
} else {
|
||||
/* We want to deselect all the selected items except what was
|
||||
clicked, UNLESS it was a right-click. We have to do this
|
||||
in click rather than mousedown so that you can drag a
|
||||
selected group of items */
|
||||
|
||||
// use selectItemRange instead of selectItem, because this
|
||||
// doesn't de- and reselect this item if it is selected
|
||||
control.selectItemRange(this, this);
|
||||
}
|
||||
control._userSelecting = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* nsIDOMXULSelectControlItemElement
|
||||
*/
|
||||
get label() {
|
||||
const XULNS =
|
||||
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
return Array.map(this.getElementsByTagNameNS(XULNS, "label"),
|
||||
label => label.value)
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
set searchLabel(val) {
|
||||
if (val !== null)
|
||||
this.setAttribute("searchlabel", val);
|
||||
else
|
||||
// fall back to the label property (default value)
|
||||
this.removeAttribute("searchlabel");
|
||||
return val;
|
||||
}
|
||||
|
||||
get searchLabel() {
|
||||
return this.hasAttribute("searchlabel") ?
|
||||
this.getAttribute("searchlabel") : this.label;
|
||||
}
|
||||
/**
|
||||
* nsIDOMXULSelectControlItemElement
|
||||
*/
|
||||
set value(val) {
|
||||
this.setAttribute("value", val);
|
||||
return val;
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this.getAttribute("value");
|
||||
}
|
||||
|
||||
/**
|
||||
* nsIDOMXULSelectControlItemElement
|
||||
*/
|
||||
set selected(val) {
|
||||
if (val)
|
||||
this.setAttribute("selected", "true");
|
||||
else
|
||||
this.removeAttribute("selected");
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
get selected() {
|
||||
return this.getAttribute("selected") == "true";
|
||||
}
|
||||
/**
|
||||
* nsIDOMXULSelectControlItemElement
|
||||
*/
|
||||
get control() {
|
||||
var parent = this.parentNode;
|
||||
while (parent) {
|
||||
if (parent.localName == "richlistbox")
|
||||
return parent;
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
set current(val) {
|
||||
if (val)
|
||||
this.setAttribute("current", "true");
|
||||
else
|
||||
this.removeAttribute("current");
|
||||
return val;
|
||||
}
|
||||
|
||||
get current() {
|
||||
return this.getAttribute("current") == "true";
|
||||
}
|
||||
disconnectedCallback() {
|
||||
var control = this.control;
|
||||
if (!control)
|
||||
return;
|
||||
// When we are destructed and we are current or selected, unselect ourselves
|
||||
// so that richlistbox's selection doesn't point to something not in the DOM.
|
||||
// We don't want to reset last-selected, so we set _suppressOnSelect.
|
||||
if (this.selected) {
|
||||
var suppressSelect = control._suppressOnSelect;
|
||||
control._suppressOnSelect = true;
|
||||
control.removeItemFromSelection(this);
|
||||
control._suppressOnSelect = suppressSelect;
|
||||
}
|
||||
if (this.current)
|
||||
control.currentItem = null;
|
||||
}
|
||||
};
|
||||
|
||||
MozXULElement.implementCustomInterface(
|
||||
MozElements.MozRichlistitem, [Ci.nsIDOMXULSelectControlItemElement]
|
||||
);
|
||||
|
@ -582,8 +582,9 @@ panel[type="autocomplete-richlistbox"] {
|
||||
}
|
||||
|
||||
.autocomplete-richlistitem {
|
||||
-moz-binding: url("chrome://global/content/bindings/autocomplete.xml#autocomplete-richlistitem");
|
||||
-moz-binding: none;
|
||||
-moz-box-orient: vertical;
|
||||
-moz-box-align: center;
|
||||
overflow: -moz-hidden-unscrollable;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user