|
1 <!DOCTYPE html> |
|
2 <html xmlns="http://www.w3.org/1999/xhtml"> |
|
3 <!-- |
|
4 https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage |
|
5 --> |
|
6 <head> |
|
7 <title>postMessage from about:blank, data URLs</title> |
|
8 <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> |
|
9 <script type="text/javascript" src="browserFu.js"></script> |
|
10 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> |
|
11 </head> |
|
12 <body> |
|
13 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage">Mozilla Bug 387706</a> |
|
14 <p id="display"></p> |
|
15 <div id="content" style="display: none"></div> |
|
16 |
|
17 <pre id="test"> |
|
18 <script class="testbody" type="application/javascript"><![CDATA[ |
|
19 /** Test for Bug 387706 **/ |
|
20 |
|
21 SimpleTest.waitForExplicitFinish(); |
|
22 |
|
23 var B64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
|
24 |
|
25 /** |
|
26 * Encodes an array of bytes into a string using the base 64 encoding scheme. |
|
27 * |
|
28 * @param bytes |
|
29 * An array of bytes to encode. |
|
30 */ |
|
31 function b64(str) |
|
32 { |
|
33 var byteArray = new Array(str.length); |
|
34 for (var i = 0, sz = str.length; i < sz; i++) |
|
35 byteArray[i] = str.charCodeAt(i); |
|
36 |
|
37 var index = 0; |
|
38 function get3Bytes() |
|
39 { |
|
40 if (byteArray.length - index < 3) |
|
41 return null; // Less than three bytes remaining |
|
42 |
|
43 // Return the next three bytes in the array, and increment index for our |
|
44 // next invocation |
|
45 return byteArray.slice(index, index += 3); |
|
46 } |
|
47 |
|
48 var out = ""; |
|
49 var bytes = null; |
|
50 while ((bytes = get3Bytes())) |
|
51 { |
|
52 var bits = 0; |
|
53 for (var i = 0; i < 3; i++) |
|
54 bits = (bits << 8) | bytes[i]; |
|
55 for (var j = 18; j >= 0; j -= 6) |
|
56 out += B64_CHARS[(bits>>j) & 0x3F]; |
|
57 } |
|
58 |
|
59 // Get the remaining bytes |
|
60 bytes = byteArray.slice(index); |
|
61 |
|
62 switch (bytes.length) |
|
63 { |
|
64 case 2: |
|
65 out += B64_CHARS[(bytes[0]>>2) & 0x3F] + |
|
66 B64_CHARS[((bytes[0] & 0x03) << 4) | ((bytes[1] >> 4) & 0x0F)] + |
|
67 B64_CHARS[((bytes[1] & 0x0F) << 2)] + |
|
68 "="; |
|
69 break; |
|
70 case 1: |
|
71 out += B64_CHARS[(bytes[0]>>2) & 0x3F] + |
|
72 B64_CHARS[(bytes[0] & 0x03) << 4] + |
|
73 "=="; |
|
74 break; |
|
75 } |
|
76 |
|
77 return out; |
|
78 } |
|
79 |
|
80 |
|
81 var aboutBlankWindow = null; |
|
82 var aboutBlank2Window = null; |
|
83 var dataWindow = null; |
|
84 |
|
85 /** Convert a nullable string to a pretty representation */ |
|
86 function sourceify(v) |
|
87 { |
|
88 if (typeof v == "string") |
|
89 return "'" + v + "'"; |
|
90 return String(v); |
|
91 } |
|
92 |
|
93 /** Receives MessageEvents to this window. */ |
|
94 function messageReceiver(evt) |
|
95 { |
|
96 // It's not clear what the security model is for data: URLs and whether they |
|
97 // can access their parents; WebKit denies access, while Gecko currently |
|
98 // allows it. We work around this problem by using postMessage (surprise!) |
|
99 // to start the round of tests when each iframe loads. |
|
100 if (evt.data === "next-test") |
|
101 { |
|
102 setTimeout(nextTest, 0); |
|
103 return; |
|
104 } |
|
105 |
|
106 |
|
107 try |
|
108 { |
|
109 ok(evt instanceof MessageEvent, "umm, how did we get this?"); |
|
110 is(evt.type, "message", "expected events of type 'message'"); |
|
111 |
|
112 if (isMozilla) |
|
113 { |
|
114 ok(evt.isTrusted === false, "shouldn't have been a trusted event"); |
|
115 } |
|
116 |
|
117 if (evt.data === "about:blank-response") |
|
118 { |
|
119 // This isn't clarified in HTML5 yet, but the origin for a document which |
|
120 // has been open()ed is the origin of the calling code, somewhat loosely |
|
121 // speaking. For the specific case of about:blank it's also possible |
|
122 // that the origin is determined by the code that opens the window. It's |
|
123 // not codified yet which of these two causes the identifier tokens on |
|
124 // the event generated by the new window to be those of this window, but |
|
125 // in either case this is what they should be. |
|
126 is(evt.origin, "http://mochi.test:8888", |
|
127 "wrong origin for event from about:blank"); |
|
128 is(evt.source, aboutBlankWindow, "wrong source"); |
|
129 |
|
130 // ...and onto the next test |
|
131 setupBlank2(); |
|
132 } |
|
133 else if (evt.data === "about:blank2-response") |
|
134 { |
|
135 is(evt.origin, "http://mochi.test:8888", |
|
136 "wrong origin for event from about:blank #2"); |
|
137 is(evt.source, aboutBlank2Window, "wrong source"); |
|
138 |
|
139 setupData(); |
|
140 } |
|
141 else if (evt.data === "data-response") |
|
142 { |
|
143 // HTML5 defines the origin of a data: URI as the origin of the window or |
|
144 // script that opened the data: URI. |
|
145 is(evt.origin, "http://mochi.test:8888", |
|
146 "wrong origin for event from data URL (should be the origin of the " + |
|
147 "window/script that opened the URL, in this case the origin of this " + |
|
148 "file)"); |
|
149 is(evt.source, dataWindow, "wrong source"); |
|
150 |
|
151 finish(); |
|
152 } |
|
153 else |
|
154 { |
|
155 ok(false, "unexpected message: " + evt.data); |
|
156 } |
|
157 } |
|
158 catch (e) |
|
159 { |
|
160 ok(false, "error processing event with data '" + evt.data + "': " + e); |
|
161 } |
|
162 } |
|
163 |
|
164 function getContents(description, responseText) |
|
165 { |
|
166 var contents = |
|
167 "<!DOCTYPE html>\n" + |
|
168 "<html>\n" + |
|
169 "<head>\n" + |
|
170 " <title>about:blank</title>\n" + |
|
171 " <script type='application/javascript'>\n" + |
|
172 "function receive(evt)\n" + |
|
173 "{\n" + |
|
174 " var response = '" + responseText + "';\n" + |
|
175 "\n" + |
|
176 " if (evt.source !== window.parent)\n" + |
|
177 " response += ' wrong-source';\n" + |
|
178 " if (evt.origin !== 'http://mochi.test:8888')\n" + |
|
179 " response += ' wrong-origin(' + evt.origin + ')';\n" + |
|
180 " if (evt.data !== 'from-opener')\n" + |
|
181 " response += ' wrong-data(' + evt.data + ')';\n" + |
|
182 "\n" + |
|
183 " window.parent.postMessage(response, 'http://mochi.test:8888');\n" + |
|
184 "}\n" + |
|
185 "\n" + |
|
186 "function ready()\n" + |
|
187 "{\n" + |
|
188 " window.parent.postMessage('next-test', 'http://mochi.test:8888');\n" + |
|
189 "}\n" + |
|
190 "\n" + |
|
191 "window.addEventListener('load', ready, false);\n" + |
|
192 "window.addEventListener('message', receive, false);\n" + |
|
193 " </script>\n" + |
|
194 "</head>\n" + |
|
195 "<body><p>" + description + "</p></body>\n" + |
|
196 "</html>"; |
|
197 |
|
198 return contents; |
|
199 } |
|
200 |
|
201 function finish() |
|
202 { |
|
203 SimpleTest.finish(); |
|
204 } |
|
205 |
|
206 var xhtmlns = "http://www.w3.org/1999/xhtml"; |
|
207 |
|
208 function insert(el) |
|
209 { |
|
210 var content = $("content"); |
|
211 content.parentNode.insertBefore(el, content); |
|
212 } |
|
213 |
|
214 function setupBlank() |
|
215 { |
|
216 var aboutBlankFrame = document.createElementNS(xhtmlns, "iframe"); |
|
217 aboutBlankFrame.setAttribute("src", "about:blank"); |
|
218 insert(aboutBlankFrame); |
|
219 |
|
220 aboutBlankWindow = aboutBlankFrame.contentWindow; |
|
221 var doc = aboutBlankWindow.document; |
|
222 doc.open(); |
|
223 doc.write(getContents("This was about:blank #1", "about:blank-response")); |
|
224 doc.close(); |
|
225 |
|
226 // I don't believe anything guarantees sync parsing, so we have to wait for |
|
227 // the new window to poke us to actually do the test. :-\ |
|
228 } |
|
229 |
|
230 function setupBlank2() |
|
231 { |
|
232 var aboutBlank2Frame = document.createElementNS(xhtmlns, "iframe"); |
|
233 aboutBlank2Frame.addEventListener("load", nextTest, false); |
|
234 aboutBlank2Frame.setAttribute("src", "about:blank"); |
|
235 |
|
236 insert(aboutBlank2Frame); |
|
237 } |
|
238 |
|
239 // Could use window.btoa here, but that's not standardized, and we want to be |
|
240 // able to run these tests against browsers that don't support it. |
|
241 var dataURI = "data:text/html;base64," + |
|
242 b64(getContents("A data: URL", "data-response")); |
|
243 |
|
244 function setupData() |
|
245 { |
|
246 var dataFrame = document.createElementNS(xhtmlns, "iframe"); |
|
247 dataFrame.setAttribute("src", dataURI); |
|
248 insert(dataFrame); |
|
249 |
|
250 dataWindow = dataFrame.contentWindow; |
|
251 |
|
252 // ...and wait again for the window to load... |
|
253 } |
|
254 |
|
255 var count = 0; |
|
256 function nextTest() |
|
257 { |
|
258 switch (count++) |
|
259 { |
|
260 case 0: |
|
261 testBlank(); |
|
262 break; |
|
263 |
|
264 case 1: |
|
265 testBlank2(); |
|
266 break; |
|
267 |
|
268 case 2: |
|
269 testData(); |
|
270 break; |
|
271 |
|
272 default: |
|
273 ok(false, "unreached"); |
|
274 break; |
|
275 } |
|
276 } |
|
277 |
|
278 function testBlank() |
|
279 { |
|
280 aboutBlankWindow.postMessage("from-opener", "http://mochi.test:8888"); |
|
281 } |
|
282 |
|
283 function testBlank2() |
|
284 { |
|
285 // For some reason we can't access this across browsers prior to the iframe |
|
286 // loading, so set its value here. |
|
287 aboutBlank2Window = window.frames[1]; |
|
288 |
|
289 var doc = aboutBlank2Window.document; |
|
290 |
|
291 doc.body.textContent = "This was about:blank #2"; |
|
292 |
|
293 var script = doc.createElement("script"); |
|
294 script.textContent = |
|
295 "window.parent.postMessage('about:blank2-response', " + |
|
296 " 'http://mochi.test:8888');"; |
|
297 doc.body.appendChild(script); |
|
298 } |
|
299 |
|
300 function testData() |
|
301 { |
|
302 dataWindow.postMessage("from-opener", "http://mochi.test:8888"); |
|
303 } |
|
304 |
|
305 window.addEventListener("message", messageReceiver, false); |
|
306 |
|
307 addLoadEvent(setupBlank); |
|
308 ]]></script> |
|
309 </pre> |
|
310 </body> |
|
311 </html> |