summaryrefslogtreecommitdiffstats
path: root/toolkit/components/passwordmgr/test/mochitest/test_formless_submit_form_removal_negative.html
blob: dfd7670a12af1b72b451186760e40cb24fe1b664 (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
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Test no capturing of fields outside of a form due to navigation</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <script src="pwmgr_common.js"></script>
  <link rel="stylesheet" href="/tests/SimpleTest/test.css" />
</head>
<body>
<script type="application/javascript">
SimpleTest.requestFlakyTimeout("Testing that a message doesn't arrive");

let loadPromise = new Promise(resolve => {
  document.addEventListener("DOMContentLoaded", () => {
    document.getElementById("loginFrame").addEventListener("load", (evt) => {
      resolve();
    });
  });
});

function submissionProcessed(...args) {
  ok(false, "No formSubmissionProcessed should occur in this test");
  info("got: " + JSON.stringify(args));
}

add_setup(async () => {
  await SpecialPowers.pushPrefEnv({
    set: [
      ["signon.formRemovalCapture.enabled", true],
    ],
  });

  info("Waiting for page and frame loads");
  await loadPromise;

  PWMGR_COMMON_PARENT.addMessageListener("formSubmissionProcessed", submissionProcessed);

  SimpleTest.registerCleanupFunction(() => {
    PWMGR_COMMON_PARENT.removeMessageListener("formSubmissionProcessed", submissionProcessed);
  });
});

const DEFAULT_ORIGIN = "http://test1.mochi.test:8888";
const SCRIPTS = {
  // Test form submission is not triggered when there is no user interaction and
  // no ajax request fired previously.
  REMOVE_TOP: `let e = document.querySelector("html"); e.parentNode.removeChild(e);`,

  // Test the following scripts don't trigger form submissions because of the
  // form removal heuristics
  PUSHSTATE: `history.pushState({}, "Pushed state", "?pushed");`,
  WINDOW_LOCATION: `window.location = "data:text/html;charset=utf-8,window.location";`,
  WINDOW_LOCATION_RELOAD: `window.location.reload();`,
  HISTORY_BACK: `history.back();`,
  HISTORY_GO_MINUS1: `history.go(-1);`,
};

const HEURISTICS = [
  {
    userInput: true,
    ajaxSuccess: true,
  },
  {
    userInput: false,
    ajaxSuccess: true,
  },
  {
    userInput: true,
    ajaxSuccess: false,
  },
  {
    userInput: false,
    ajaxSuccess: false,
  },
];

const TESTCASES = [
  // Begin test cases that shouldn't trigger capture.
  {
    // Empty password field in a form
    document: `<form><input type=password value="xxx"></form>`,
    selectorValues: {
      "[type=password]": "",
    },
  },
  {
    // Empty password field
    document: `<input type=password value="">`,
    selectorValues: {
      "[type=password]": "",
    },
  },
  {
    // Test with an input that would normally be captured but with SCRIPTS that
    // shouldn't trigger capture.
    document: `<input type=password value="">`,
    selectorValues: {
      "[type=password]": "pass2",
    },
    wouldCapture: true,
  },
  {
    // Test with an input that would normally be captured but with SCRIPTS that
    // shouldn't trigger capture.
    document: `<form><input type=password value=""></form>`,
    selectorValues: {
      "[type=password]": "pass2",
    },
    wouldCapture: true,
  },
];

async function testFormlesSubmitNavigationNegative(tc, scriptName, heuristic) {
  let loginFrame = document.getElementById("loginFrame");
  let waitTime;
  let android = navigator.appVersion.includes("Android");
  if (android) {
    // intermittent failures on Android Debug at 5 seconds
    waitTime = 10000;
  } else {
    waitTime = 5000;
  }

  let loadedPromise = new Promise((resolve) => {
    loginFrame.addEventListener("load", function() {
      resolve();
    }, {once: true});
  });

  loginFrame.src = DEFAULT_ORIGIN + "/tests/toolkit/components/passwordmgr/test/mochitest/blank.html";
  await loadedPromise;

  let frameDoc = SpecialPowers.wrap(loginFrame.contentWindow).document;
  let formsProcessed = promiseFormsProcessed();
  frameDoc.documentElement.innerHTML = tc.document;
  await formsProcessed;

  // We eliminate no user input as a reason for not capturing by modifying the value
  setUserInputValues(frameDoc.documentElement, tc.selectorValues, heuristic.userInput);

  if (heuristic.ajaxSuccess) {
    await SpecialPowers.spawn(frameDoc.defaultView, [], async () => {
      await content.fetch("http://test1.mochi.test:8888/tests/toolkit/components/passwordmgr/test/mochitest/blank.html");
    });
  }

  info("Running " + scriptName + " script to check for a submission");
  frameDoc.defaultView.eval(SCRIPTS[scriptName]);

  info("Running " + scriptName + " script to check for a submission 1");
  // Wait to see if the promise above resolves.
  await new Promise(resolve => setTimeout(resolve, waitTime));
  info("Running " + scriptName + " script to check for a submission 2");
  ok(true, "Done waiting for captures");

}

let count = 0;
for (let tc of TESTCASES) {
  for (let scriptName of Object.keys(SCRIPTS)) {
    for (let heuristic of HEURISTICS) {
      let shouldCaptureAFormRemoval = heuristic.ajaxSuccess && heuristic.userInput;
      // Only run the following scripts when we are going to observeform removal change
      // to save some time running this whole test.
      if (["PUSHSTATE", "WINDOW_LOCATION", "WINDOW_LOCATION_RELOAD", "HISTORY_BACK", "HISTORY_GO_MINUS1"].includes(scriptName)) {
        if(!shouldCaptureAFormRemoval) {
          continue;
        }

        if (tc.wouldCapture && ["PUSHSTATE", "WINDOW_LOCATION"].includes(scriptName)) {
          // Don't run scripts that should actually capture for this testcase.
          continue;
        }
      } else if (["REMOVE_TOP"].includes(scriptName)) {
        if (shouldCaptureAFormRemoval) {
          // Don't run scripts that should actually capture for this testcase.
          continue;
        }
      }

      let taskName = `testcase-${count}-${scriptName}`;
      let tmp = {
        async [taskName]() {
          info("Starting testcase with script " + scriptName + ": " + JSON.stringify(tc) + ": " + JSON.stringify(heuristic));
          await testFormlesSubmitNavigationNegative(tc, scriptName, heuristic);
        },
      };
      add_task(tmp[taskName]);
    }
  }
}


</script>

<p id="display"></p>

<div id="content">
  <iframe id="loginFrame" src="http://test1.mochi.test:8888/tests/toolkit/components/passwordmgr/test/mochitest/blank.html"></iframe>
</div>
<pre id="test"></pre>
</body>
</html>