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:
Daniel Varga 2019-02-02 11:44:15 +02:00
commit 69d3c6c61d
90 changed files with 2947 additions and 2160 deletions

View File

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

View File

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

View File

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

View File

@ -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]

View File

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

View File

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

View File

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

View File

@ -148,6 +148,7 @@ class MOZ_RAII BufferReader {
const uint8_t* Read(size_t aCount) {
if (aCount > mRemaining) {
mPtr += mRemaining;
mRemaining = 0;
return nullptr;
}

View File

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

View File

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

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

View 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]() {
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();

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

View File

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

View File

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

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

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

View File

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

View File

@ -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,

View File

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

View File

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

View File

@ -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,

View File

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

View File

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

View File

@ -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,

View File

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

View File

@ -445,6 +445,8 @@ enum class BailoutReturnStub {
GetProp,
GetPropSuper,
SetProp,
GetElem,
GetElemSuper,
Call,
New,
Count

View File

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

View File

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

View File

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

View File

@ -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:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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.
*/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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];
};
},
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -40,7 +40,7 @@ function doxhr(URL, user, pass, next) {
};
xhr.onerror = function() {
ok(false, "request passed");
finishTest();
SimpleTest.finish();
};
xhr.send();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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"],

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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