You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 3-frames-and-windows/03-cross-window-communication/article.md
+57-68
Original file line number
Diff line number
Diff line change
@@ -26,82 +26,66 @@ The "Same Origin" policy states that:
26
26
- if we have a reference to another window, e.g. a popup created by `window.open` or a window inside `<iframe>`, and that window comes from the same origin, then we have full access to that window.
27
27
- otherwise, if it comes from another origin, then we can't access the content of that window: variables, document, anything. The only exception is `location`: we can change it (thus redirecting the user). But we cannot *read* location (so we can't see where the user is now, no information leak).
28
28
29
-
Now let's see some examples. First, we'll look at pages that come from the same origin and do not conflict with the "Same Origin" policy, and afterwards we'll cover cross-window messaging that allows to work around the "Same Origin" policy.
29
+
### In action: iframe
30
30
31
+
An `<iframe>` tag hosts a separate embedded window, with its own separate `document` and `window` objects.
31
32
32
-
````warn header="Subdomains may be same-origin"
33
-
There's a small exclusion in the "Same Origin" policy.
33
+
We can access them using properties:
34
34
35
-
If windows share the same second-level domain, for instance `john.site.com`, `peter.site.com` and `site.com` (so that their common second-level domain is `site.com`), they can be treated as coming from the "same origin".
35
+
-`iframe.contentWindow` to get the window inside the `<iframe>`.
36
+
-`iframe.contentDocument` to get the document inside the `<iframe>`, shorthand for `iframe.contentWindow.document`.
36
37
37
-
To make it work, all such pages (including the one from `site.com`) should run the code:
38
+
When we access something inside the embedded window, the browser checks if the iframe has the same origin. If that's not so then the access is denied (writing to `location` is an exception, it's still permitted).
38
39
39
-
```js
40
-
document.domain = 'site.com';
41
-
```
42
-
43
-
That's all. Now they can interact without limitations. Again, that's only possible for pages with the same second-level domain.
44
-
````
45
-
46
-
## Accessing an iframe contents
47
-
48
-
Our first example covers iframes. An `<iframe>` is a two-faced beast. From one side it's a tag, just like `<script>` or `<img>`. From the other side it's a window-in-window.
49
-
50
-
The embedded window has a separate `document` and `window` objects.
51
-
52
-
We can access them like using the properties:
53
-
54
-
-`iframe.contentWindow` is a reference to the window inside the `<iframe>`.
55
-
-`iframe.contentDocument` is a reference to the document inside the `<iframe>`.
56
-
57
-
When we access an embedded window, the browser checks if the iframe has the same origin. If that's not so then the access is denied (with exclusions noted above).
58
-
59
-
For instance, here's an `<iframe>` from another origin:
40
+
For instance, let's try reading and writing to `<iframe>` from another origin:
// also we can't READ the URL of the page in iframe
77
61
try {
78
-
alert(iframe.contentWindow.location);
62
+
// Can't read URL from the Location object
63
+
*!*
64
+
let href =iframe.contentWindow.location.href; // ERROR
65
+
*/!*
79
66
} catch(e) {
80
67
alert(e); // Security Error
81
68
}
82
69
83
-
// ...but we can change it (and thus load something else into the iframe)!
84
-
iframe.contentWindow.location='/'; // works
70
+
// ...we can WRITE into location (and thus load something else into the iframe)!
71
+
*!*
72
+
iframe.contentWindow.location='/'; // OK
73
+
*/!*
85
74
86
-
iframe.onload=null; // clear the handler, to run this code only once
75
+
iframe.onload=null; // clear the handler, not to run it after the location change
87
76
};
88
77
</script>
89
78
```
90
79
91
80
The code above shows errors for any operations except:
92
81
93
-
- Getting the reference to the inner window `iframe.contentWindow`
94
-
-Changing its`location`.
82
+
- Getting the reference to the inner window `iframe.contentWindow` - that's allowed.
83
+
-Writing to`location`.
95
84
96
-
```smart header="`iframe.onload` vs `iframe.contentWindow.onload`"
97
-
The `iframe.onload` event is actually the same as `iframe.contentWindow.onload`. It triggers when the embedded window fully loads with all resources.
98
-
99
-
...But `iframe.onload` is always available, while `iframe.contentWindow.onload` needs the same origin.
100
-
```
101
-
102
-
And now an example with the same origin. We can do anything with the embedded window:
85
+
Contrary to that, if the `<iframe>` has the same origin, we can do anything with it:
103
86
104
87
```html run
88
+
<!-- iframe from the same site -->
105
89
<iframesrc="/"id="iframe"></iframe>
106
90
107
91
<script>
@@ -112,7 +96,13 @@ And now an example with the same origin. We can do anything with the embedded wi
112
96
</script>
113
97
```
114
98
115
-
### Please wait until the iframe loads
99
+
```smart header="`iframe.onload` vs `iframe.contentWindow.onload`"
100
+
The `iframe.onload` event (on the `<iframe>` tag) is essentially the same as `iframe.contentWindow.onload` (on the embedded window object). It triggers when the embedded window fully loads with all resources.
101
+
102
+
...But we can't access `iframe.contentWindow.onload` for an iframe from another origin, so using `iframe.onload`.
103
+
```
104
+
105
+
## Windows on subdomains: document.domain
116
106
117
107
By definition, two URLs with different domains have different origins.
118
108
@@ -159,11 +149,13 @@ Here, look:
159
149
</script>
160
150
```
161
151
162
-
That's actually a well-known pitfall for developers. We shouldn't work with the document immediately, because that's the *wrong document*. If we set any event handlers on it, they will be ignored.
152
+
We shouldn't work with the document of a not-yet-loaded iframe, because that's the *wrong document*. If we set any event handlers on it, they will be ignored.
153
+
154
+
How to detect the moment when the document is there?
163
155
164
-
...But the `onload`event triggerswhen the whole iframe with all resources is loaded. What if we want to act sooner, on `DOMContentLoaded` of the embedded document?
156
+
The right document is definitely at place when `iframe.onload` triggers. But it only triggers when the whole iframe with all resources is loaded.
165
157
166
-
That's not possible if the iframe comes from another origin. But for the same origin we can try to catch the moment when a new document appears, and then setup necessary handlers, like this:
158
+
We can try to catch the moment earlier using checks in `setInterval`:
167
159
168
160
```html run
169
161
<iframesrc="/"id="iframe"></iframe>
@@ -173,21 +165,17 @@ That's not possible if the iframe comes from another origin. But for the same or
173
165
174
166
// every 100 ms check if the document is the new one
clearInterval(timer); // cancel setInterval, don't need it any more
184
174
}, 100);
185
175
</script>
186
176
```
187
177
188
-
Let me know in comments if you know a better solution here.
189
-
190
-
## window.frames
178
+
## Collection: window.frames
191
179
192
180
An alternative way to get a window object for `<iframe>` -- is to get it from the named collection `window.frames`:
193
181
@@ -229,11 +217,11 @@ if (window == top) { // current window == window.top?
229
217
}
230
218
```
231
219
232
-
## The sandbox attribute
220
+
## The "sandbox" iframe attribute
233
221
234
222
The `sandbox` attribute allows for the exclusion of certain actions inside an `<iframe>` in order to prevent it executing untrusted code. It "sandboxes" the iframe by treating it as coming from another origin and/or applying other limitations.
235
223
236
-
By default, for `<iframe sandbox src="...">` the "default set" of restrictions is applied to the iframe. But we can provide a space-separated list of "excluded" limitations as a value of the attribute, like this: `<iframe sandbox="allow-forms allow-popups">`. The listed limitations are not applied.
224
+
There's a "default set" of restrictions applied for `<iframe sandbox src="...">`. But it can be relaxed if we provide a space-separated list of restrictions that should not be applied as a value of the attribute, like this: `<iframe sandbox="allow-forms allow-popups">`.
237
225
238
226
In other words, an empty `"sandbox"` attribute puts the strictest limitations possible, but we can put a space-delimited list of those that we want to lift.
239
227
@@ -287,9 +275,9 @@ Arguments:
287
275
`targetOrigin`
288
276
: Specifies the origin for the target window, so that only a window from the given origin will get the message.
289
277
290
-
The `targetOrigin` is a safety measure. Remember, if the target window comes from another origin, we can't read it's`location`. So we can't be sure which site is open in the intended window right now: the user could navigate away.
278
+
The `targetOrigin` is a safety measure. Remember, if the target window comes from another origin, we can't read its`location` in the sender window. So we can't be sure which site is open in the intended window right now: the user could navigate away, and the sender window has no idea about it.
291
279
292
-
Specifying `targetOrigin` ensures that the window only receives the data if it's still at that site. Good when the data is sensitive.
280
+
Specifying `targetOrigin` ensures that the window only receives the data if it's still at the right site. Important when the data is sensitive.
293
281
294
282
For instance, here `win` will only receive the message if it has a document from the origin `http://example.com`:
295
283
@@ -331,7 +319,7 @@ The event object has special properties:
331
319
: The origin of the sender, for instance `http://javascript.info`.
332
320
333
321
`source`
334
-
: The reference to the sender window. We can immediately `postMessage` back if we want.
322
+
: The reference to the sender window. We can immediately `source.postMessage(...)` back if we want.
335
323
336
324
To assign that handler, we should use `addEventListener`, a short syntax `window.onmessage` does not work.
// can message back using event.source.postMessage(...)
348
338
});
349
339
```
350
340
@@ -356,9 +346,9 @@ The full example:
356
346
357
347
To call methods and access the content of another window, we should first have a reference to it.
358
348
359
-
For popups we have two properties:
360
-
-`window.open` -- opens a new window and returns a reference to it,
361
-
-`window.opener` -- a reference to the opener window from a popup
349
+
For popups we have these references:
350
+
-From the opener window: `window.open` -- opens a new window and returns a reference to it,
351
+
-From the popup: `window.opener` -- is a reference to the opener window from a popup.
362
352
363
353
For iframes, we can access parent/children windows using:
364
354
-`window.frames` -- a collection of nested window objects,
@@ -368,18 +358,17 @@ For iframes, we can access parent/children windows using:
368
358
If windows share the same origin (host, port, protocol), then windows can do whatever they want with each other.
369
359
370
360
Otherwise, only possible actions are:
371
-
- Change the location of another window (write-only access).
361
+
- Change the `location` of another window (write-only access).
372
362
- Post a message to it.
373
363
374
-
375
-
Exclusions are:
364
+
Exceptions are:
376
365
- Windows that share the same second-level domain: `a.site.com` and `b.site.com`. Then setting `document.domain='site.com'` in both of them puts them into the "same origin" state.
377
366
- If an iframe has a `sandbox` attribute, it is forcefully put into the "different origin" state, unless the `allow-same-origin` is specified in the attribute value. That can be used to run untrusted code in iframes from the same site.
378
367
379
-
The `postMessage` interface allows two windows to talk with security checks:
368
+
The `postMessage` interface allows two windows with any origins to talk:
380
369
381
370
1. The sender calls `targetWin.postMessage(data, targetOrigin)`.
382
-
2. If `targetOrigin` is not `'*'`, then the browser checks if window `targetWin` has the URL from `targetWin` site.
371
+
2. If `targetOrigin` is not `'*'`, then the browser checks if window `targetWin` has the origin `targetOrigin`.
383
372
3. If it is so, then `targetWin` triggers the `message` event with special properties:
384
373
-`origin` -- the origin of the sender window (like `http://my.site.com`)
0 commit comments