OLD | NEW |
(Empty) | |
| 1 // NOTE: This method only strips the fragment and is not in accordance to the |
| 2 // recommended draft specification: |
| 3 // https://w3c.github.io/webappsec/specs/referrer-policy/#null |
| 4 // TODO(kristijanburnik): Implement this helper as defined by spec once added |
| 5 // scenarios for URLs containing username/password/etc. |
| 6 function stripUrlForUseAsReferrer(url) { |
| 7 return url.replace(/#.*$/, ""); |
| 8 } |
| 9 |
| 10 function parseUrlQueryString(queryString) { |
| 11 var queries = queryString.replace(/^\?/, "").split("&"); |
| 12 var params = {}; |
| 13 |
| 14 for (var i in queries) { |
| 15 var kvp = queries[i].split("="); |
| 16 params[kvp[0]] = kvp[1]; |
| 17 } |
| 18 |
| 19 return params; |
| 20 }; |
| 21 |
| 22 function appendIframeToBody(url, attributes) { |
| 23 var iframe = document.createElement("iframe"); |
| 24 iframe.src = url; |
| 25 // Extend element with attributes. (E.g. "referrerPolicy" or "rel") |
| 26 if (attributes) { |
| 27 for (var attr in attributes) { |
| 28 iframe[attr] = attributes[attr]; |
| 29 } |
| 30 } |
| 31 document.body.appendChild(iframe); |
| 32 |
| 33 return iframe; |
| 34 } |
| 35 |
| 36 function loadImageInWindow(src, callback, attributes, w) { |
| 37 var image = new w.Image(); |
| 38 image.crossOrigin = "Anonymous"; |
| 39 image.onload = function() { |
| 40 callback(image); |
| 41 } |
| 42 |
| 43 // Extend element with attributes. (E.g. "referrerPolicy" or "rel") |
| 44 if (attributes) { |
| 45 for (var attr in attributes) { |
| 46 image[attr] = attributes[attr]; |
| 47 } |
| 48 } |
| 49 |
| 50 image.src = src; |
| 51 w.document.body.appendChild(image) |
| 52 } |
| 53 |
| 54 function extractImageData(img) { |
| 55 var canvas = document.createElement("canvas"); |
| 56 var context = canvas.getContext('2d'); |
| 57 context.drawImage(img, 0, 0); |
| 58 var imgData = context.getImageData(0, 0, img.clientWidth, img.clientHeight); |
| 59 return imgData.data; |
| 60 } |
| 61 |
| 62 function decodeImageData(rgba) { |
| 63 var rgb = new Uint8ClampedArray(rgba.length); |
| 64 |
| 65 // RGBA -> RGB. |
| 66 var rgb_length = 0; |
| 67 for (var i = 0; i < rgba.length; ++i) { |
| 68 // Skip alpha component. |
| 69 if (i % 4 == 3) |
| 70 continue; |
| 71 |
| 72 // Zero is the string terminator. |
| 73 if (rgba[i] == 0) |
| 74 break; |
| 75 |
| 76 rgb[rgb_length++] = rgba[i]; |
| 77 } |
| 78 |
| 79 // Remove trailing nulls from data. |
| 80 rgb = rgb.subarray(0, rgb_length); |
| 81 var string_data = (new TextDecoder("ascii")).decode(rgb); |
| 82 |
| 83 return JSON.parse(string_data); |
| 84 } |
| 85 |
| 86 function normalizePort(targetPort) { |
| 87 var defaultPorts = [80, 443]; |
| 88 var isDefaultPortForProtocol = (defaultPorts.indexOf(targetPort) >= 0); |
| 89 |
| 90 return (targetPort == "" || isDefaultPortForProtocol) ? |
| 91 "" : ":" + targetPort; |
| 92 } |
| 93 |
| 94 function wrapResult(url, server_data) { |
| 95 return { |
| 96 location: url, |
| 97 referrer: server_data.headers.referer, |
| 98 headers: server_data.headers |
| 99 } |
| 100 } |
| 101 |
| 102 function queryIframe(url, callback, referrer_policy) { |
| 103 var iframe = appendIframeToBody(url, referrer_policy); |
| 104 var listener = function(event) { |
| 105 if (event.source != iframe.contentWindow) |
| 106 return; |
| 107 |
| 108 callback(event.data, url); |
| 109 window.removeEventListener("message", listener); |
| 110 } |
| 111 window.addEventListener("message", listener); |
| 112 } |
| 113 |
| 114 function queryImage(url, callback, attributes, referrerPolicy, test) { |
| 115 // For images, we'll test: |
| 116 // - images in a `srcdoc` frame to ensure that it uses the referrer |
| 117 // policy of its parent, |
| 118 // - images in a top-level document, |
| 119 // - and images in a `srcdoc` frame with its own referrer policy to |
| 120 // override its parent. |
| 121 |
| 122 var noSrcDocPolicy = new Promise((resolve, reject) => { |
| 123 var iframeWithoutOwnPolicy = document.createElement('iframe'); |
| 124 iframeWithoutOwnPolicy.srcdoc = "Hello, world."; |
| 125 iframeWithoutOwnPolicy.onload = function () { |
| 126 var nextUrl = url + "&cache_destroyer2=" + (new Date()).getTime(); |
| 127 loadImageInWindow(nextUrl, function (img) { |
| 128 resolve(decodeImageData(extractImageData(img))); |
| 129 }, attributes, iframeWithoutOwnPolicy.contentWindow); |
| 130 }; |
| 131 document.body.appendChild(iframeWithoutOwnPolicy); |
| 132 }); |
| 133 |
| 134 // Give a srcdoc iframe a referrer policy different from the top-level page's
policy. |
| 135 var iframePolicy = (referrerPolicy === "no-referrer") ? "unsafe-url" : "no-ref
errer"; |
| 136 var srcDocPolicy = new Promise((resolve, reject) => { |
| 137 var iframeWithOwnPolicy = document.createElement('iframe'); |
| 138 iframeWithOwnPolicy.srcdoc = "<meta name='referrer' content='" + iframePolic
y + "'>Hello world."; |
| 139 |
| 140 iframeWithOwnPolicy.onload = function () { |
| 141 var nextUrl = url + "&cache_destroyer3=" + (new Date()).getTime(); |
| 142 loadImageInWindow(nextUrl, function (img) { |
| 143 resolve(decodeImageData(extractImageData(img))); |
| 144 }, null, iframeWithOwnPolicy.contentWindow); |
| 145 }; |
| 146 document.body.appendChild(iframeWithOwnPolicy); |
| 147 }); |
| 148 |
| 149 var pagePolicy = new Promise((resolve, reject) => { |
| 150 loadImageInWindow(url, function (img) { |
| 151 resolve(decodeImageData(extractImageData(img))); |
| 152 }, attributes, window); |
| 153 }); |
| 154 |
| 155 Promise.all([noSrcDocPolicy, srcDocPolicy, pagePolicy]).then(test.step_func(va
lues => { |
| 156 assert_equals(values[0].headers.referer, values[2].headers.referer, "Referre
r inside 'srcdoc' without its own policy should be the same as embedder's referr
er."); |
| 157 assert_equals((iframePolicy === "no-referrer" ? undefined : document.locatio
n.href), values[1].headers.referer, "Referrer inside 'srcdoc' should use the ifr
ame's policy if it has one"); |
| 158 callback(wrapResult(url, values[2]), url); |
| 159 })); |
| 160 } |
| 161 |
| 162 function queryXhr(url, callback) { |
| 163 var xhr = new XMLHttpRequest(); |
| 164 xhr.open('GET', url, true); |
| 165 xhr.onreadystatechange = function(e) { |
| 166 if (this.readyState == 4 && this.status == 200) { |
| 167 var server_data = JSON.parse(this.responseText); |
| 168 callback(wrapResult(url, server_data), url); |
| 169 } |
| 170 }; |
| 171 xhr.send(); |
| 172 } |
| 173 |
| 174 function queryWorker(url, callback) { |
| 175 var worker = new Worker(url); |
| 176 worker.onmessage = function(event) { |
| 177 var server_data = event.data; |
| 178 callback(wrapResult(url, server_data), url); |
| 179 }; |
| 180 } |
| 181 |
| 182 function queryFetch(url, callback) { |
| 183 fetch(url).then(function(response) { |
| 184 response.json().then(function(server_data) { |
| 185 callback(wrapResult(url, server_data), url); |
| 186 }); |
| 187 } |
| 188 ); |
| 189 } |
| 190 |
| 191 function queryNavigable(element, url, callback, attributes) { |
| 192 var navigable = element |
| 193 navigable.href = url; |
| 194 navigable.target = "helper-iframe"; |
| 195 |
| 196 var helperIframe = document.createElement("iframe") |
| 197 helperIframe.name = "helper-iframe" |
| 198 document.body.appendChild(helperIframe) |
| 199 |
| 200 // Extend element with attributes. (E.g. "referrer_policy" or "rel") |
| 201 if (attributes) { |
| 202 for (var attr in attributes) { |
| 203 navigable[attr] = attributes[attr]; |
| 204 } |
| 205 } |
| 206 |
| 207 var listener = function(event) { |
| 208 if (event.source != helperIframe.contentWindow) |
| 209 return; |
| 210 |
| 211 callback(event.data, url); |
| 212 window.removeEventListener("message", listener); |
| 213 } |
| 214 window.addEventListener("message", listener); |
| 215 |
| 216 navigable.click(); |
| 217 } |
| 218 |
| 219 function queryLink(url, callback, referrer_policy) { |
| 220 var a = document.createElement("a"); |
| 221 a.innerHTML = "Link to subresource"; |
| 222 document.body.appendChild(a); |
| 223 queryNavigable(a, url, callback, referrer_policy) |
| 224 } |
| 225 |
| 226 function queryAreaLink(url, callback, referrer_policy) { |
| 227 var area = document.createElement("area"); |
| 228 // TODO(kristijanburnik): Append to map and add image. |
| 229 document.body.appendChild(area); |
| 230 queryNavigable(area, url, callback, referrer_policy) |
| 231 } |
| 232 |
| 233 function queryScript(url, callback) { |
| 234 var script = document.createElement("script"); |
| 235 script.src = url; |
| 236 |
| 237 var listener = function(event) { |
| 238 var server_data = event.data; |
| 239 callback(wrapResult(url, server_data), url); |
| 240 window.removeEventListener("message", listener); |
| 241 } |
| 242 window.addEventListener("message", listener); |
| 243 |
| 244 document.body.appendChild(script); |
| 245 } |
| 246 |
| 247 // SanityChecker does nothing in release mode. |
| 248 function SanityChecker() {} |
| 249 SanityChecker.prototype.checkScenario = function() {}; |
| 250 SanityChecker.prototype.checkSubresourceResult = function() {}; |
| 251 |
| 252 // TODO(kristijanburnik): document.origin is supported since Chrome 41, |
| 253 // other browsers still don't support it. Remove once they do. |
| 254 document.origin = document.origin || (location.protocol + "//" + location.host); |
OLD | NEW |