summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/service-workers/service-worker/fetch-waits-for-activate.https.html
blob: 7c888450f0d5f5fc72ec38909487877f38a1c024 (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
<!DOCTYPE html>
<title>Service Worker: Fetch Event Waits for Activate Event</title>
<meta name=timeout content=long>
<script src="/resources/testharness.js"></script>
<script src="resources/testharness-helpers.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>

const worker_url = 'resources/fetch-waits-for-activate-worker.js';
const normalized_worker_url = normalizeURL(worker_url);
const worker_scope = 'resources/fetch-waits-for-activate/';

// Resolves with the Service Worker's registration once it's reached the
// "activating" state. (The Service Worker should remain "activating" until
// explicitly told advance to the "activated" state).
async function registerAndWaitForActivating(t) {
  const registration = await service_worker_unregister_and_register(
      t, worker_url, worker_scope);
  t.add_cleanup(() => service_worker_unregister(t, worker_scope));

  await wait_for_state(t, registration.installing, 'activating');

  return registration;
}

// Attempts to ensure that the "Handle Fetch" algorithm has reached the step
//
//   "If activeWorker’s state is "activating", wait for activeWorker’s state to
//    become "activated"."
//
// by waiting for some time to pass.
//
// WARNING: whether the algorithm has reached that step isn't directly
// observable, so this is best effort and can race. Note that this can only
// result in false positives (where the algorithm hasn't reached that step yet
// and any functional events haven't actually been handled by the Service
// Worker).
async function ensureFunctionalEventsAreWaiting(registration) {
  await (new Promise(resolve => { setTimeout(resolve, 1000); }));

  assert_equals(registration.active.scriptURL, normalized_worker_url,
                'active worker should be present');
  assert_equals(registration.active.state, 'activating',
                'active worker should be in activating state');
}

promise_test(async t => {
  const registration = await registerAndWaitForActivating(t);

  let frame = null;
  t.add_cleanup(() => {
    if (frame) {
      frame.remove();
    }
  });

  // This should block until we message the worker to tell it to complete
  // the activate event.
  const frameLoadPromise = with_iframe(worker_scope).then(function(f) {
    frame = f;
  });

  await ensureFunctionalEventsAreWaiting(registration);
  assert_equals(frame, null, 'frame should not be loaded');

  registration.active.postMessage('ACTIVATE');

  await frameLoadPromise;
  assert_equals(frame.contentWindow.navigator.serviceWorker.controller.scriptURL,
                normalized_worker_url,
                'frame should now be loaded and controlled');
  assert_equals(registration.active.state, 'activated',
                'active worker should be in activated state');
}, 'Navigation fetch events should wait for the activate event to complete.');

promise_test(async t => {
  const frame = await with_iframe(worker_scope);
  t.add_cleanup(() => { frame.remove(); });

  const registration = await registerAndWaitForActivating(t);

  // Make the Service Worker control the frame so the frame can perform an
  // intercepted fetch.
  await (new Promise(resolve => {
    navigator.serviceWorker.onmessage = e => {
      assert_equals(
        frame.contentWindow.navigator.serviceWorker.controller.scriptURL,
        normalized_worker_url, 'frame should be controlled');
      resolve();
    };

    registration.active.postMessage('CLAIM');
  }));

  const fetch_url = `${worker_scope}non/existent/path`;
  const expected_fetch_result = 'Hello world';
  let fetch_promise_settled = false;

  // This should block until we message the worker to tell it to complete
  // the activate event.
  const fetchPromise = frame.contentWindow.fetch(fetch_url, {
    method: 'POST',
    body: expected_fetch_result,
  }).then(response => {
    fetch_promise_settled = true;
    return response;
  });

  await ensureFunctionalEventsAreWaiting(registration);
  assert_false(fetch_promise_settled,
               "fetch()-ing a Service Worker-controlled scope shouldn't have " +
               "settled yet");

  registration.active.postMessage('ACTIVATE');

  const response = await fetchPromise;
  assert_equals(await response.text(), expected_fetch_result,
                "Service Worker should have responded to request to" +
                fetch_url)
  assert_equals(registration.active.state, 'activated',
                'active worker should be in activated state');
}, 'Subresource fetch events should wait for the activate event to complete.');

</script>
</body>