- Window: message event
- Syntax
- Event type
- Event properties
- Examples
- Specifications
- Browser compatibility
- See also
- MessageEvent
- Constructor
- Instance properties
- Instance methods
- Examples
- Specifications
- Browser compatibility
- See also
- Общение между окнами
- Политика «Одинакового источника»
- Доступ к содержимому ифрейма
- Окна на поддоменах: document.domain
- Ифрейм: подождите документ
- Коллекция window.frames
- Атрибут ифрейма sandbox
Window: message event
The message event is fired on a Window object when the window receives a message, for example from a call to Window.postMessage() from another browsing context.
This event is not cancellable and does not bubble.
Syntax
Use the event name in methods like addEventListener() , or set an event handler property.
addEventListener("message", (event) => >); onmessage = (event) => >;
Event type
Event properties
This interface also inherits properties from its parent, Event . MessageEvent.data Read only The data sent by the message emitter. MessageEvent.origin Read only A string representing the origin of the message emitter. MessageEvent.lastEventId Read only A string representing a unique ID for the event. MessageEvent.source Read only A MessageEventSource (which can be a WindowProxy, MessagePort , or ServiceWorker object) representing the message emitter. MessageEvent.ports Read only An array of MessagePort objects representing the ports associated with the channel the message is being sent through (where appropriate, e.g. in channel messaging or when sending a message to a shared worker).
Examples
Suppose a script sends a message to a different browsing context, such as another , using code like this:
const targetFrame = window.top.frames[1]; const targetOrigin = "https://example.org"; const windowMessageButton = document.querySelector("#window-message"); windowMessageButton.addEventListener("click", () => targetFrame.postMessage("hello there", targetOrigin); >);
.addEventListener("message", (event) => console.log(`Received message: $event.data>`); >);
.onmessage = (event) => console.log(`Received message: $event.data>`); >;
Specifications
Browser compatibility
See also
MessageEvent
The MessageEvent interface represents a message received by a target object.
This is used to represent messages in:
- Server-sent events (see EventSource.message_event ).
- Web sockets (see the onmessage property of the WebSocket interface).
- Cross-document messaging (see Window.postMessage() and Window.message_event ).
- Channel messaging (see MessagePort.postMessage() and MessagePort.message_event ).
- Cross-worker/document messaging (see the above two entries, but also Worker.postMessage() , Worker.message_event , ServiceWorkerGlobalScope.message_event , etc.)
- Broadcast channels (see Broadcastchannel.postMessage() ) and BroadcastChannel.message_event ).
- WebRTC data channels (see onmessage ).
The action triggered by this event is defined in a function set as the event handler for the relevant message event (e.g. using an onmessage handler as listed above).
Note: This feature is available in Web Workers
Constructor
Instance properties
This interface also inherits properties from its parent, Event . MessageEvent.data Read only The data sent by the message emitter. MessageEvent.origin Read only A string representing the origin of the message emitter. MessageEvent.lastEventId Read only A string representing a unique ID for the event. MessageEvent.source Read only A MessageEventSource (which can be a WindowProxy, MessagePort , or ServiceWorker object) representing the message emitter. MessageEvent.ports Read only An array of MessagePort objects representing the ports associated with the channel the message is being sent through (where appropriate, e.g. in channel messaging or when sending a message to a shared worker).
Instance methods
This interface also inherits methods from its parent, Event . initMessageEvent() Deprecated Initializes a message event. Do not use this anymore — use the MessageEvent() constructor instead.
Examples
In our Basic shared worker example (run shared worker), we have two HTML pages, each of which uses some JavaScript to perform a simple calculation. The different scripts are using the same worker file to perform the calculation — they can both access it, even if their pages are running inside different windows. The following code snippet shows creation of a SharedWorker object using the SharedWorker() constructor. Both scripts contain this:
const myWorker = new SharedWorker("worker.js");
Both scripts then access the worker through a MessagePort object created using the SharedWorker.port property. If the onmessage event is attached using addEventListener, the port is manually started using its start() method:
When the port is started, both scripts post messages to the worker and handle messages sent from it using port.postMessage() and port.onmessage , respectively:
.onchange = () => myWorker.port.postMessage([first.value, second.value]); console.log("Message posted to worker"); >; second.onchange = () => myWorker.port.postMessage([first.value, second.value]); console.log("Message posted to worker"); >; myWorker.port.onmessage = (e) => result1.textContent = e.data; console.log("Message received from worker"); >;
Inside the worker we use the onconnect handler to connect to the same port discussed above. The ports associated with that worker are accessible in the connect event’s ports property — we then use MessagePort start() method to start the port, and the onmessage handler to deal with messages sent from the main threads.
onconnect = (e) => const port = e.ports[0]; port.addEventListener("message", (e) => const workerResult = `Result: $e.data[0] * e.data[1]>`; port.postMessage(workerResult); >); port.start(); // Required when using addEventListener. Otherwise called implicitly by onmessage setter. >;
Specifications
Browser compatibility
See also
- ExtendableMessageEvent — similar to this interface but used in interfaces that needs to give more flexibility to authors.
Общение между окнами
Политика «Одинакового источника» (Same Origin) ограничивает доступ окон и фреймов друг к другу.
Идея заключается в том, что если у пользователя открыто две страницы: john-smith.com и gmail.com , то у скрипта со страницы john-smith.com не будет возможности прочитать письма из gmail.com . Таким образом, задача политики «Одинакового источника» – защитить данные пользователя от возможной кражи.
Политика «Одинакового источника»
Два URL имеют «одинаковый источник» в том случае, если они имеют совпадающие протокол, домен и порт.
Эти URL имеют одинаковый источник:
- http://www.site.com (другой домен: www. важен)
- http://site.org (другой домен: .org важен)
- https://site.com (другой протокол: https )
- http://site.com:8080 (другой порт: 8080 )
Политика «Одинакового источника» говорит, что:
- если у нас есть ссылка на другой объект window , например, на всплывающее окно, созданное с помощью window.open или на window из и у этого окна тот же источник, то к нему будет полный доступ.
- в противном случае, если у него другой источник, мы не сможем обращаться к его переменным, объекту document и так далее. Единственное исключение – объект location : его можно изменять (таким образом перенаправляя пользователя). Но нельзя читать location (нельзя узнать, где находится пользователь, чтобы не было никаких утечек информации).
Доступ к содержимому ифрейма
Внутри находится по сути отдельное окно с собственными объектами document и window .
Мы можем обращаться к ним, используя свойства:
- iframe.contentWindow ссылка на объект window внутри .
- iframe.contentDocument – ссылка на объект document внутри , короткая запись для iframe.contentWindow.document .
Когда мы обращаемся к встроенному в ифрейм окну, браузер проверяет, имеет ли ифрейм тот же источник. Если это не так, тогда доступ будет запрещён (разрешена лишь запись в location , это исключение).
Для примера давайте попробуем чтение и запись в ифрейм с другим источником:
catch(e) < alert(e); // Security Error >// также мы не можем прочитать URL страницы в ифрейме try < // Нельзя читать из объекта Location let href = iframe.contentWindow.location.href; // ОШИБКА >catch(e) < alert(e); // Security Error >// . но можно писать в него (и загрузить что-то другое в ифрейм)! iframe.contentWindow.location = '/'; // OK iframe.onload = null; // уберём обработчик, чтобы не срабатывал после изменения location >;
Код выше выведет ошибку для любых операций, кроме:
- Получения ссылки на внутренний объект window из iframe.contentWindow
- Изменения location .
С другой стороны, если у ифрейма тот же источник, то с ним можно делать всё, что угодно:
;
Событие iframe.onload – по сути то же, что и iframe.contentWindow.onload . Оно сработает, когда встроенное окно полностью загрузится со всеми ресурсами.
…Но iframe.onload всегда доступно извне ифрейма, в то время как доступ к iframe.contentWindow.onload разрешён только из окна с тем же источником.
Окна на поддоменах: document.domain
По определению, если у двух URL разный домен, то у них разный источник.
Но если в окнах открыты страницы с поддоменов одного домена 2-го уровня, например john.site.com , peter.site.com и site.com (так что их общий домен site.com ), то можно заставить браузер игнорировать это отличие. Так что браузер сможет считать их пришедшими с одного источника при проверке возможности доступа друг к другу.
Для этого в каждом таком окне нужно запустить:
После этого они смогут взаимодействовать без ограничений. Ещё раз заметим, что это доступно только для страниц с одинаковым доменом второго уровня.
Ифрейм: подождите документ
Когда ифрейм – с того же источника, мы имеем доступ к документу в нём. Но есть подвох. Не связанный с кросс-доменными особенностями, но достаточно важный, чтобы о нём знать.
Когда ифрейм создан, в нём сразу есть документ. Но этот документ – другой, не тот, который в него будет загружен!
Так что если мы тут же сделаем что-то с этим документом, то наши изменения, скорее всего, пропадут.
;
Нам не следует работать с документом ещё не загруженного ифрейма, так как это не тот документ. Если мы поставим на него обработчики событий – они будут проигнорированы.
Как поймать момент, когда появится правильный документ?
Можно проверять через setInterval :
< let newDoc = iframe.contentDocument; if (newDoc == oldDoc) return; alert("New document is here!"); clearInterval(timer); // отключим setInterval, он нам больше не нужен >, 100);
Коллекция window.frames
Другой способ получить объект window из – забрать его из именованной коллекции window.frames :
- По номеру: window.frames[0] – объект window для первого фрейма в документе.
- По имени: window.frames.iframeName – объект window для фрейма со свойством name=»iframeName» .
Ифрейм может иметь другие ифреймы внутри. Таким образом, объекты window создают иерархию.
Навигация по ним выглядит так:
- window.frames – коллекция «дочерних» window (для вложенных фреймов).
- window.parent – ссылка на «родительский» (внешний) window .
- window.top – ссылка на самого верхнего родителя.
window.frames[0].parent === window; // true
Можно использовать свойство top , чтобы проверять, открыт ли текущий документ внутри ифрейма или нет:
Атрибут ифрейма sandbox
Атрибут sandbox позволяет наложить ограничения на действия внутри , чтобы предотвратить выполнение ненадёжного кода. Атрибут помещает ифрейм в «песочницу», отмечая его как имеющий другой источник и/или накладывая на него дополнительные ограничения.
Существует список «по умолчанию» ограничений, которые накладываются на . Их можно уменьшить, если указать в атрибуте список исключений (специальными ключевыми словами), которые не нужно применять, например: .
Другими словами, если у атрибута «sandbox» нет значения, то браузер применяет максимум ограничений, но через пробел можно указать те из них, которые мы не хотим применять.
allow-same-origin «sandbox» принудительно устанавливает «другой источник» для ифрейма. Другими словами, он заставляет браузер воспринимать iframe , как пришедший из другого источника, даже если src содержит тот же сайт. Со всеми сопутствующими ограничениями для скриптов. Эта опция отключает это ограничение. allow-top-navigation Позволяет ифрейму менять parent.location . allow-forms Позволяет отправлять формы из ифрейма. allow-scripts Позволяет запускать скрипты из ифрейма. allow-popups Позволяет открывать всплывающие окна из ифрейма с помощью window.open .
Пример ниже демонстрирует ифрейм, помещённый в песочницу со стандартным набором ограничений: . На странице содержится JavaScript и форма.
Обратите внимание, что ничего не работает. Таким образом, набор ограничений по умолчанию очень строгий: