summaryrefslogtreecommitdiffstats
path: root/toolkit/components/passwordmgr/test/browser/browser_autofill_hidden_document.js
blob: e7af2f8b84fbf6cf7d23e9e2ac2b14bc5874fe3f (plain)
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
Services.scriptloader.loadSubScript(
  "chrome://mochitests/content/browser/browser/components/aboutlogins/tests/browser/head.js",
  this
);

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 FORM_MULTIPAGE_URL = `https://example.org${TEST_URL_PATH}form_multipage.html`;
const testUrls = [FORM_URL, FORMLESS_URL, FORM_MULTIPAGE_URL];
const testUrlsWithForm = [FORM_URL, FORM_MULTIPAGE_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;
}

add_setup(async function () {
  Services.prefs.setBoolPref("signon.usernameOnlyForm.enabled", true);
  registerCleanupFunction(() => {
    Services.prefs.clearUserPref("signon.usernameOnlyForm.enabled");
  });

  Services.logins.removeAllUserFacingLogins();
  let login = LoginTestUtils.testData.formLogin({
    origin: "https://example.org",
    formActionOrigin: "https://example.org",
    username: "user1",
    password: "pass1",
  });
  await Services.logins.addLoginAsync(login);
});

testUrlsWithForm.forEach(testUrl => {
  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);
    Assert.equal(
      tab1Visibility,
      "visible",
      "The first tab should be foreground"
    );

    let formProcessedPromise = listenForTestNotification("FormProcessed");
    BrowserTestUtils.loadURIString(tab1.linkedBrowser, testUrl);
    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);
    Assert.equal(
      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.loadURIString(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");
    });

    Assert.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);
    Assert.equal(
      tab1Visibility,
      "visible",
      "The first tab should be foreground"
    );
    Assert.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,
        };
      }
    );
    Assert.equal(fieldValues.username, "user1", "Checking filled username");

    // skip password test for a username-only form
    if (![FORM_MULTIPAGE_URL].includes(testUrl)) {
      Assert.equal(fieldValues.password, "pass1", "Checking filled password");
    }

    gBrowser.removeTab(tab1);
    gBrowser.removeTab(tab2);
  });
});

testUrlsWithForm.forEach(testUrl => {
  add_task(async function test_immediate_autofill_with_primarypassword() {
    LoginTestUtils.primaryPassword.enable();
    await LoginTestUtils.reloadData();
    info(
      `Have enabled primaryPassword, now isLoggedIn? ${Services.logins.isLoggedIn}`
    );

    registerCleanupFunction(async function () {
      LoginTestUtils.primaryPassword.disable();
      await LoginTestUtils.reloadData();
    });

    // 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 primary password dialog is shown"
    );
    Assert.equal(
      await getDocumentVisibilityState(tab2.linkedBrowser),
      "visible",
      "The second tab should be visible"
    );

    const tab1Visibility = await getDocumentVisibilityState(tab1.linkedBrowser);
    Assert.equal(
      tab1Visibility,
      "hidden",
      "The first tab should be backgrounded"
    );

    const dialogObserved = waitForMPDialog("authenticate", tab1.ownerGlobal);

    // 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.loadURIString(tab1.linkedBrowser, testUrl);
    await Promise.all([formProcessedPromise, dialogObserved]);

    Assert.ok(
      formProcessedPromise,
      "Observer should be notified when form is loaded into a hidden document"
    );
    Assert.ok(
      dialogObserved,
      "MP Dialog should be shown when form is loaded into a hidden document"
    );

    gBrowser.removeTab(tab1);
    gBrowser.removeTab(tab2);
  });
});