summaryrefslogtreecommitdiffstats
path: root/dom/base/test/test_urgent_start.html
blob: 6241bacc6bfe3dafc8610ae5b3bb94ea90e31b71 (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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
<!DOCTYPE HTML>
<!--
  https://bugzilla.mozilla.org/show_bug.cgi?id=1348050
  Test for fetch and xhr to guarantee we only mark channel as urgent-start when
  it is triggered by user input events.

  For { Fetch, SRC-*, XHR }, do the test as following:
  Step 1: Verify them not mark the channel when there is no any input event.
  Step 2: Verify them mark the channel there is a user input event.
  Step 3: Verify them not mark the channel when there is a non input event.

  In each steps, it shows that we only mark channel on direct triggering task.
  We won't mark the channel for additional task(setTimeout) or
  micro-task(promise).
-->
<html>
<head>
  <title>Test for urgent-start on Fetch and XHR</title>
  <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet"
        type="text/css"
        href="chrome://mochikit/content/tests/SimpleTest/test.css">
  <script type="text/javascript" src="manifest.js"></script>
</head>
<body>
<img id="image"></body>
<audio autoplay id="audio"></audio>
<iframe id="iframe"></iframe>
<input type="image" id="input"></input>
<embed id="embed"></embed>
<pre id="test">
<script class="testbody" type="text/javascript">

SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(runTest);

const topic_request = "http-on-opening-request";
const topic_response = "http-on-examine-response";
const topic_cachedResponse = "http-on-examine-cached-response";
const scope = "http://mochi.test:8888/chrome/dom/base/test/"
const url = scope + "file_empty.html";

let expectedResults = [];
let testcases = [
  "fetch",
  "src-embed",
  "src-img",
  "src-input",
  "src-media",
  "xhr",
];
let testcase;

function isUrgentStart(aClassFlags) {
  if (!aClassFlags) {
    return false;
  }

  const urgentStartFlag = 1 << 6;
  return !!(urgentStartFlag & aClassFlags);
}

// Test for setTimeout (task)
function testSetTimeout() {
  return new Promise(aResolve =>
    setTimeout(function() {
      testSimple().then(aResolve);
    }, 0));
}

// Test for promise chain (micro-task)
function testPromise() {
  return Promise.resolve().then(testSimple);
}

function testSimple() {
  let testUrl = url + "?" + expectedResults.length;

  if (testcase == "fetch") {
    return fetch(testUrl);
  } else if (testcase == "src-embed") {
    document.getElementById('embed').src = testUrl;
    return Promise.resolve();
  } else if (testcase == "src-img") {
    document.getElementById('image').src = testUrl;
    return Promise.resolve();
  } else if (testcase == "src-input") {
    document.getElementById('input').src = testUrl;
    return Promise.resolve();
  } else if (testcase == "src-media") {
    document.getElementById('audio').src = testUrl;
    return Promise.resolve();
  } else if (testcase == "xhr") {
    let xhr = new XMLHttpRequest();
    xhr.open("GET", testUrl, true);
    xhr.send(null);
    return Promise.resolve();
  }

  ok(false, "Shouldn't go here.");
  return Promise.reject();
}

function sendRequsetAndCheckUrgentStart(aEventToTest) {
  info("SendRequsetAndCheckUrgentStart");

  let promise1, promise2;
  let promise1_resolve, promise2_resolve;

  function checkUrgentStart(aSubject) {
    var channel = aSubject.QueryInterface(Ci.nsIChannel);
    if (!channel.URI.spec.includes(scope) ) {
      return;
    }

    info("CheckUrgentStart");

    let cos = channel.QueryInterface(Ci.nsIClassOfService);

    let expectedResult = expectedResults.shift();
    is(isUrgentStart(cos.classFlags), expectedResult,
       "Expect get: " + expectedResult + ", get: " +
       isUrgentStart(cos.classFlags) + " in the " +
       (9 - expectedResults.length) + " test of " + testcase);

    // Make sure we've run the check.
    promise1_resolve();
  }

  // Resolve this after we've gotten response to prevent from sending too many
  // requests to Necko in a short time.
  function getResponse(aSubject) {
    var channel = aSubject.QueryInterface(Ci.nsIChannel);
    if (!channel.URI.spec.includes(scope) ) {
      return;
    }
    info("GetResponse");

    promise2_resolve();
  }

  SpecialPowers.addObserver(checkUrgentStart, topic_request);
  SpecialPowers.addObserver(getResponse, topic_response);
  SpecialPowers.addObserver(getResponse, topic_cachedResponse);

  return Promise.resolve()
    .then(() => {
      promise1 = new Promise(aResolve => { promise1_resolve = aResolve; });
      promise2 = new Promise(aResolve => { promise2_resolve = aResolve; });
      return Promise.all([addListenerAndSendEvent(testSimple, aEventToTest),
                          promise1,
                          promise2]);
    })
    .then(() => {
      promise1 = new Promise(aResolve => { promise1_resolve = aResolve; });
      promise2 = new Promise(aResolve => { promise2_resolve = aResolve; });
      return Promise.all([addListenerAndSendEvent(testSetTimeout, aEventToTest),
                          promise1,
                          promise2]);
    })
    .then(() => {
      promise1 = new Promise(aResolve => { promise1_resolve = aResolve; });
      promise2 = new Promise(aResolve => { promise2_resolve = aResolve; });
      return Promise.all([addListenerAndSendEvent(testPromise, aEventToTest),
                          promise1,
                          promise2]);
    })
    .then(() => {
      // remove obs if we've tested each three conditions
      // (simple, promise, setTimeout).
      SpecialPowers.removeObserver(checkUrgentStart, topic_request);
      SpecialPowers.removeObserver(getResponse,topic_response);
      SpecialPowers.removeObserver(getResponse, topic_cachedResponse);
      return Promise.resolve();
    });
}

function addListenerAndSendEvent(aFunction, aEventToTest) {
  info("AddListenerAndSendEvent:" + aEventToTest);

  let eventHandle = function () {
    return aFunction();
  };

  if (aEventToTest === TestEvent.USER_INPUT_EVENT) {
    // User Input Event
    window.addEventListener("mousedown", eventHandle, {once: true});
  } else if (aEventToTest === TestEvent.NONUSER_INPUT_EVENT) {
    window.addEventListener("message", eventHandle, {once: true});
  }

  if (aEventToTest === TestEvent.USER_INPUT_EVENT) {
    // User Input Event
    var utils = SpecialPowers.getDOMWindowUtils(window);
    utils.sendMouseEvent("mousedown", 1, 1, 0, 1, 0);
  } else if (aEventToTest === TestEvent.NONUSER_INPUT_EVENT) {
    window.postMessage("hello", "*");
  } else if (aEventToTest === TestEvent.NOEVENT) {
    eventHandle();
  }
}

const TestEvent = {
  NOEVENT: 0,
  USER_INPUT_EVENT: 1,
  NONUSER_INPUT_EVENT: 2,
};

function executeTest() {
  is(expectedResults.length, 0, "expectedResults should be 0 be executeTest.");

  // We will test fetch first and then xhr.
  testcase = testcases.shift();
  info("Verify " + testcase);

  expectedResults = [
    /* SimpleTest without any events */ false,
    /* PromiseTest without any events */ false,
    /* SetTimeoutTest without any events */ false,
    /* SimpleTest with a user input event */ true,
    /* PromiseTest with a user input event */ false,
    /* SetTimeoutTest with user input event */ false,
    /* SimpleTest with a non user input event */ false,
    /* PromiseTest with a non user input event */ false,
    /* SetTimeoutTest with a non user input event */ false,
  ];

  return Promise.resolve()
  // Verify urgent-start is not set when the request is not triggered by any
  // events.
    .then(() => sendRequsetAndCheckUrgentStart(TestEvent.NOEVENT))

  // Verify urgent-start is set only when the request is triggered by a user
  // input event. (not for another microtask (e.g. promise-chain) and
  // task (e.g. setTimeout)).
    .then(() => sendRequsetAndCheckUrgentStart(TestEvent.USER_INPUT_EVENT))

  // Verify urgent-start is not set when the request is triggered by a non user
  // input event.
    .then(() => sendRequsetAndCheckUrgentStart(TestEvent.NONUSER_INPUT_EVENT))
    .then(_ => {
      if (testcases.length !== 0) {
        // Run the other test if we still have tests needed to be run.
        return executeTest();
      }

      return Promise.resolve();
    });
}

function endCheck() {
  info("End Check: make sure that we've done all the tests.");

  is(testcases.length, 0, "All the tests should be executed.");
  is(expectedResults.length, 0, "All the tests should be executed.");

  return Promise.resolve();
}

function runTest() {// Disable mixed-content upgrading as this test is expecting an HTTP load
  return SpecialPowers.pushPrefEnv(
    {"set": [
      ["security.mixed_content.upgrade_display_content", false],
      ["network.http.rcwn.enabled", false],
    ]}
    )
    .then(executeTest)
    .then(endCheck)
    .catch(aError => ok(false, "Some test failed with error " + aError))
    .then(SimpleTest.finish);
}
</script>
</pre>
</body>
</html>