summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/fetch/redirect-navigate/preserve-fragment.html
blob: 682539a7445f2da6ec27d4b6a217d743c5c91915 (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
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Ensure fragment is kept across redirects</title>
    <meta name="timeout" content="long">
    <link rel=help href="https://www.w3.org/TR/cuap/#uri">
    <link rel=help href="https://tools.ietf.org/html/rfc7231#section-7.1.2">
    <link rel=help href="https://bugs.webkit.org/show_bug.cgi?id=158420">
    <link rel=help href="https://bugs.webkit.org/show_bug.cgi?id=24175">
    <script src="/common/get-host-info.sub.js"></script>
    <script src="/resources/testharness.js"></script>
    <script src="/resources/testharnessreport.js"></script>
    <script>
      let frame;
      let message;

      const HTTP_SAME_ORIGIN = "HTTP - SameOrigin";
      const HTTPS_SAME_ORIGIN = "HTTPS - SameOrigin";
      const HTTP_CROSS_ORIGIN = "HTTP - CrossOrigin";
      const HTTPS_CROSS_ORIGIN = "HTTPS - CrossOrigin";

      function messageReceived(f) {
        return new Promise((resolve) => {
          window.addEventListener("message", (e) => {
            message = e.data;
            resolve();
          }, {once: true});
          f();
        });
      }

      function getHostname(navigation_type) {
        switch (navigation_type) {
          case HTTP_SAME_ORIGIN:
            return get_host_info().HTTP_ORIGIN;
          case HTTPS_SAME_ORIGIN:
            return get_host_info().HTTPS_ORIGIN
          case HTTP_CROSS_ORIGIN:
            return get_host_info().HTTP_REMOTE_ORIGIN
          case HTTPS_CROSS_ORIGIN:
            return get_host_info().HTTPS_REMOTE_ORIGIN
        }

        return 'nonexistent'
      }

      // Turns |path| from a relative to this file path into a full URL, with
      // the host being determined by one of the ORIGIN strings above.
      function relativePathToFull(path,  navigation_type) {
        let host = getHostname(navigation_type);

        const pathname = window.location.pathname;
        const base_path = pathname.substring(0, pathname.lastIndexOf('/') + 1);

        return host + base_path + path;
      }

      // Constructs a URL to redirect.py which will respond with the given
      // redirect status |code| to the provided |to_url|. Optionally adds on a
      // |fragment|, if provided, to use in the initial request to redirect.py
      function buildRedirectUrl(to_url, code, fragment) {
        to_url = encodeURIComponent(to_url);
        let dest = `/common/redirect.py?status=${code}&location=${to_url}`;
        if (fragment)
          dest = dest + '#' + fragment;
        return dest;
      }

      async function redirectTo(url, code, navigation_type, fragment) {
        const dest = buildRedirectUrl(url, code, fragment);
        await messageReceived( () => {
          frame.contentWindow.location = getHostname(navigation_type) + dest;
        });
      }

      async function doubleRedirectTo(url, code, navigation_type, fragment, intermediate_fragment) {
        const second_redirection = buildRedirectUrl(url, code, intermediate_fragment);
        const first_redirection = buildRedirectUrl(second_redirection, code, fragment);
        await messageReceived( () => {
          frame.contentWindow.location = getHostname(navigation_type) + first_redirection;
        });
      }

      onload = () => {
        frame = document.getElementById("frame");

        // The tests in this file verify fragments are correctly propagated in
        // a number of HTTP redirect scenarios. Each test is run for every
        // relevant redirect status code. We also run each scenario under each
        // combination of navigating to cross/same origin and using http/https.
        const status_codes = [301, 302, 303, 307, 308];
        const navigation_types = [HTTP_SAME_ORIGIN,
                                  HTTPS_SAME_ORIGIN,
                                  HTTP_CROSS_ORIGIN,
                                  HTTPS_CROSS_ORIGIN];

        for (let navigation_type of navigation_types) {
          // Navigate to a URL with a fragment. The URL redirects to a different
          // page. Ensure we land on the redirected page with the fragment
          // specified in the initial navigation's URL.
          //
          // Redirect chain: urlA#target -> urlB
          //
          for (let code of status_codes) {
            promise_test(async () => {
              const to_url = relativePathToFull('resources/destination.html', navigation_type);
              await redirectTo(to_url, code, navigation_type, "target");
              assert_true(message.url.endsWith('#target'));
              assert_equals(message.scrollY, 2000, "scrolls to fragment from initial navigation.");
            }, `[${navigation_type}] Preserve fragment in ${code} redirect`);
          }

          // Navigate to a URL with a fragment. The URL redirects to a different
          // URL that also contains a fragment. Ensure we land on the redirected
          // page using the fragment specified in the redirect response and not
          // the one in the initial navigation.
          //
          // Redirect chain: urlA#target -> urlB#fromRedirect
          //
          for (let code of status_codes) {
            promise_test(async () => {
              const to_url = relativePathToFull('resources/destination.html#fromRedirect', navigation_type);
              await redirectTo(to_url, code, navigation_type, "target");
              assert_true(message.url.endsWith('#fromRedirect'), `Unexpected fragment: ${message.url}`);
              assert_equals(message.scrollY, 4000, "scrolls to fragment from redirect.");
            }, `[${navigation_type}] Redirect URL fragment takes precedence in ${code} redirect`);
          }

          // Perform two redirects. The initial navigation has a fragment and
          // will redirect to a URL that also responds with a redirect. Ensure we
          // land on the final page with the fragment from the original
          // navigation.
          //
          // Redirect chain: urlA#target -> urlB -> urlC
          //
          for (let code of status_codes) {
            promise_test(async () => {
              const to_url = relativePathToFull('resources/destination.html', navigation_type);
              await doubleRedirectTo(to_url, code, navigation_type, "target");
              assert_true(message.url.endsWith('#target'), `Unexpected fragment: ${message.url}`);
              assert_equals(message.scrollY, 2000, "scrolls to fragment from initial navigation.");
            }, `[${navigation_type}] Preserve fragment in multiple ${code} redirects`);
          }

          // Perform two redirects. The initial navigation has a fragment and
          // will redirect to a URL that also responds with a redirect. The
          // second redirection to the final page also has a fragment. Ensure we
          // land on the final page with the fragment from the redirection
          // response URL.
          //
          // Redirect chain: urlA#target -> urlB -> urlC#fromRedirect
          //
          for (let code of status_codes) {
            promise_test(async () => {
              const to_url = relativePathToFull('resources/destination.html#fromRedirect', navigation_type);
              await doubleRedirectTo(to_url, code, navigation_type, "target");
              assert_true(message.url.endsWith('#fromRedirect'), `Unexpected fragment: ${message.url}`);
              assert_equals(message.scrollY, 4000, "scrolls to fragment from redirect.");
            }, `[${navigation_type}] Destination URL fragment takes precedence in multiple ${code} redirects`);
          }

          // Perform two redirects. The initial navigation has a fragment and
          // will redirect to a URL that also responds with a redirect. This
          // time, both redirect response have a fragment. Ensure we land on the
          // final page with the fragment from the last redirection response URL.
          //
          // Redirect chain: urlA#target -> urlB#intermediate -> urlC#fromRedirect
          //
          for (let code of status_codes) {
            promise_test(async () => {
              const to_url = relativePathToFull('resources/destination.html#fromRedirect', navigation_type);
              await doubleRedirectTo(to_url, code, navigation_type, "target", "intermediate");
              assert_true(message.url.endsWith('#fromRedirect'), `Unexpected fragment: ${message.url}`);
              assert_equals(message.scrollY, 4000, "scrolls to fragment from redirect.");
            }, `[${navigation_type}] Final redirect fragment takes precedence over intermediate in multiple ${code} redirects`);
          }

          // Perform two redirects. The initial navigation has a fragment and
          // will redirect to a URL that also responds with a redirect. The first
          // redirect response has a fragment but the second doesn't. Ensure we
          // land on the final page with the fragment from the first redirection
          // response URL.
          //
          // Redirect chain: urlA#target -> urlB#fromRedirect -> urlC
          //
          for (let code of status_codes) {
            promise_test(async () => {
              const to_url = relativePathToFull('resources/destination.html', navigation_type);
              await doubleRedirectTo(to_url, code, navigation_type, "target", "fromRedirect");
              assert_true(message.url.endsWith('#fromRedirect'), `Unexpected fragment: ${message.url}`);
              assert_equals(message.scrollY, 4000, "scrolls to fragment from redirect.");
            }, `[${navigation_type}] Preserve intermediate fragment in multiple ${code} redirects`);
          }
        }
      }
    </script>
  </head>
  <body>
    <iframe id="frame" src=""></iframe>
  </body>
</html>