1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
|
const TEST_URL_PATH = "/browser/toolkit/components/passwordmgr/test/browser/";
const INITIAL_URL = `about:blank`;
const FORM_URL = `https://example.org${TEST_URL_PATH}form_basic.html`;
const FORMLESS_URL = `https://example.org${TEST_URL_PATH}formless_basic.html`;
const testUrls = [FORM_URL, FORMLESS_URL];
const BRAND_BUNDLE = Services.strings.createBundle(
"chrome://branding/locale/brand.properties"
);
const BRAND_FULL_NAME = BRAND_BUNDLE.GetStringFromName("brandFullName");
async function getDocumentVisibilityState(browser) {
let visibility = await SpecialPowers.spawn(browser, [], async function() {
return content.document.visibilityState;
});
return visibility;
}
// Waits for the master password prompt and cancels it.
function observeMasterPasswordDialog(window, result) {
let closedPromise;
function topicObserver(subject) {
let expected = "Password Required - " + BRAND_FULL_NAME;
if (subject.Dialog.args.title == expected) {
result.wasShown = true;
subject.Dialog.ui.button1.click();
closedPromise = BrowserTestUtils.waitForEvent(
window,
"DOMModalDialogClosed"
);
}
}
Services.obs.addObserver(topicObserver, "common-dialog-loaded");
let waited = TestUtils.waitForCondition(() => {
return result.wasShown;
}, "Wait for master password dialog");
return Promise.all([waited, closedPromise])
.catch(ex => {
info(
`observeMasterPasswordDialog, caught exception from topicObserved: ${ex}`
);
})
.finally(() => {
Services.obs.removeObserver(topicObserver, "common-dialog-loaded");
});
}
add_task(async function setup() {
Services.logins.removeAllUserFacingLogins();
let login = LoginTestUtils.testData.formLogin({
origin: "http://example.org",
formActionOrigin: "http://example.org",
username: "user1",
password: "pass1",
});
Services.logins.addLogin(login);
});
add_task(async function test_processed_form_fired() {
// Sanity check. If this doesnt work any results for the subsequent tasks are suspect
const tab1 = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
INITIAL_URL
);
let tab1Visibility = await getDocumentVisibilityState(tab1.linkedBrowser);
is(tab1Visibility, "visible", "The first tab should be foreground");
let formProcessedPromise = listenForTestNotification("FormProcessed");
BrowserTestUtils.loadURI(tab1.linkedBrowser, FORM_URL);
await formProcessedPromise;
gBrowser.removeTab(tab1);
});
testUrls.forEach(testUrl => {
add_task(async function test_defer_autofill_until_visible() {
let result, tab1Visibility;
// open 2 tabs
const tab1 = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
INITIAL_URL
);
const tab2 = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
INITIAL_URL
);
// confirm document is hidden
tab1Visibility = await getDocumentVisibilityState(tab1.linkedBrowser);
is(tab1Visibility, "hidden", "The first tab should be backgrounded");
// we shouldn't even try to autofill while hidden, so wait for the document to be in the
// non-visible pending queue instead.
let formFilled = false;
listenForTestNotification("FormProcessed").then(() => {
formFilled = true;
});
BrowserTestUtils.loadURI(tab1.linkedBrowser, testUrl);
await TestUtils.waitForCondition(() => {
let windowGlobal = tab1.linkedBrowser.browsingContext.currentWindowGlobal;
if (!windowGlobal || windowGlobal.documentURI.spec == "about:blank") {
return false;
}
let actor = windowGlobal.getActor("LoginManager");
return actor.sendQuery("PasswordManager:formIsPending");
});
ok(
!formFilled,
"Observer should not be notified when form is loaded into a hidden document"
);
// Add the observer before switching tab
let formProcessedPromise = listenForTestNotification("FormProcessed");
await BrowserTestUtils.switchTab(gBrowser, tab1);
result = await formProcessedPromise;
tab1Visibility = await getDocumentVisibilityState(tab1.linkedBrowser);
is(tab1Visibility, "visible", "The first tab should be foreground");
ok(
result,
"Observer should be notified when input's document becomes visible"
);
// the form should have been autofilled with the login
let fieldValues = await SpecialPowers.spawn(
tab1.linkedBrowser,
[],
function() {
let doc = content.document;
return {
username: doc.getElementById("form-basic-username").value,
password: doc.getElementById("form-basic-password").value,
};
}
);
is(fieldValues.username, "user1", "Checking filled username");
is(fieldValues.password, "pass1", "Checking filled password");
gBrowser.removeTab(tab1);
gBrowser.removeTab(tab2);
});
});
add_task(async function test_immediate_autofill_with_masterpassword() {
// Set master password prompt timeout to 3s.
// If this test goes intermittent, you likely have to increase this value.
await SpecialPowers.pushPrefEnv({
set: [["signon.masterPasswordReprompt.timeout_ms", 3000]],
});
LoginTestUtils.masterPassword.enable();
await LoginTestUtils.reloadData();
info(
`Have enabled masterPassword, now isLoggedIn? ${Services.logins.isLoggedIn}`
);
registerCleanupFunction(async function() {
LoginTestUtils.masterPassword.disable();
await LoginTestUtils.reloadData();
});
let dialogResult, tab1Visibility, dialogObserved;
// open 2 tabs
const tab1 = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
INITIAL_URL
);
const tab2 = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
INITIAL_URL
);
info(
"load a background login form tab with a matching saved login " +
"and wait to see if the master password dialog is shown"
);
is(
await getDocumentVisibilityState(tab2.linkedBrowser),
"visible",
"The second tab should be visible"
);
tab1Visibility = await getDocumentVisibilityState(tab1.linkedBrowser);
is(tab1Visibility, "hidden", "The first tab should be backgrounded");
dialogResult = { wasShown: false };
dialogObserved = observeMasterPasswordDialog(tab1.ownerGlobal, dialogResult);
// In this case we will try to autofill while hidden, so look for the passwordmgr-processed-form
// to be observed
let formProcessedPromise = listenForTestNotification("FormProcessed");
BrowserTestUtils.loadURI(tab1.linkedBrowser, FORM_URL);
await Promise.all([formProcessedPromise, dialogObserved]);
let wasProcessed = await formProcessedPromise;
ok(
wasProcessed,
"Observer should be notified when form is loaded into a hidden document"
);
ok(
dialogResult && dialogResult.wasShown,
"MP Dialog should be shown when form is loaded into a hidden document"
);
gBrowser.removeTab(tab1);
gBrowser.removeTab(tab2);
});
|