Книга: JavaScript. Подробное руководство, 6-е издание
22.3. Взаимодействие документов с разным происхождением
22.3. Взаимодействие документов с разным происхождением
Как отмечалось в разделе 14.8, некоторые окна и вкладки броузера полностью изолированы друг от друга, и сценарий, выполняющийся в одном окне или вкладке, ничего не знает о существовании других окон и вкладок. В других случаях, когда сценарий сам открывает новые окна или работает с вложенными фреймами, сценарии в этих окнах и фреймах могут быть осведомлены друг о друге. Если эти окна или фреймы содержат документы, полученные от одного и того же веб-сервера, сценарии в этих окнах и фреймах смогут взаимодействовать друг с другом и манипулировать документами друг друга.
Однако иногда сценарий имеет возможность сослаться на другой объект Window
, но, поскольку содержимое этого окна имеет иное происхождение, веб-броузер (следующий требованиям политики общего происхождения) не позволит сценарию просматривать содержимое документа в этом другом окне. Броузер не позволит сценарию читать значения свойств или вызывать методы другого окна. Единственный метод окна, который может вызвать сценарий из документа с другим происхождением, называется postMessage(),
и этот метод обеспечивает возможность ограниченных взаимодействий - в форме асинхронных сообщений - между сценариями из документов с разным происхождением. Этот вид взаимодействий определяется стандартом HTML5 и реализован во всех текущих броузерах (включая ІЕ8 и более поздние версии). Этот прием известен как «обмен сообщениями между документами с разным происхождением», но, так как прикладной интерфейс поддержки определен в объекте Window
, а не в объекте документа, возможно, лучше было бы назвать этот прием «обменом сообщениями между окнами».
Метод postMessage()
принимает два обязательных аргумента. Первый - передаваемое сообщение. Согласно спецификации HTML5, это может быть любое простое значение или объект, который можно скопировать (смотрите врезку «Структурированные копии» выше), но некоторые текущие броузеры (включая Firefox 4 beta) принимают только строки, поэтому, если в сообщении потребуется передать объект или массив, его необходимо будет предварительно сериализовать с помощью функции JSON.stringify
() (раздел 6.9).
Во втором аргументе должна передаваться строка, определяющая ожидаемое происхождение окна, принимающего сообщение. Эта строка должна включать протокол, имя хоста и (необязательно) номер порта (можно передать полный URL-адрес, но все, кроме протокола, хоста и порта будет игнорироваться). Это предусмотрено из соображений безопасности: злонамеренный программный код или обычные пользователи могут открывать новые окна с документами, взаимодействие с которыми вы не предполагали, поэтому postMessage()
не будет доставлять ваши сообщения, если окно будет содержать документ с происхождением, отличным от указанного. Если передаваемое сообщение не содержит конфиденциальной информации и вы готовы передать ее сценарию из документа с любым происхождением, то во втором аргументе можно передать строку "*", которая будет играть роль шаблонного символа. Если необходимо указать, что документ должен иметь то же происхождение, что и документ в текущем окне, можно передать строку "/".
Если происхождение документа совпадет с указанным в аргументе, вызов метода postMessage()
приведет к возбуждению события «message» в целевом объекте Window
. Сценарий в этом окне может определить обработчик для обработки событий «message», которому будет передан объект события со следующими свойствами:
data
Копия сообщения, переданного методу postMessage() в первом аргументе.
source
Объект Window
, из которого было отправлено сообщение.
origin
Строка, определяющая происхождение (в виде URL) документа, отправившего сообщение.
Большинство обработчиков onmessage()
должно сначала проверить свойство origin
своего аргумента и должно игнорировать сообщения из неподдерживаемых доменов.
Обмен сообщениями с документами из сторонних доменов посредством метода postMessage()
и обработки события «message» может пригодиться, например, когда необходимо подключить к веб-странице модуль, или «гаджет», с другого сайта. Если модуль достаточно прост и автономен, его можно просто загрузить в элемент <iframe>.
Однако представьте, что это более сложный модуль, который определяет свой прикладной интерфейс, и ваша веб-страница должна управлять этим модулем или как-то иначе взаимодействовать с ним. Если модуль определен в виде элемента <script>,
он сможет предложить полноценный прикладной интерфейс на языке JavaScript, но, с другой стороны, включение его в страницу даст ему полный контроль над страницей и ее содержимым. Такой подход широко используется в современной Всемирной паутине (особенно в веб-рекламе), но в действительности это не самое лучшее решение, даже если вы доверяете другому сайту.
Альтернативой является обмен сообщениями между документами с разным происхождением: автор модуля может упаковать его в HTML-файл, который принимает события «message» и передает их для обработки соответствующим функциям на языке JavaScript. В этом случае веб-страница, подключившая модуль, сможет взаимодействовать с ним, отправляя сообщения с помощью метода postMessage().
Этот прием демонстрируется в примерах 22.4 и 22.5. В примере 22.4 приводится реализация простого модуля, подключаемого с помощью элемента <iframe>
, который выполняет поиск на сайте Twitter и отображает сообщения, соответствующие указанной фразе. Чтобы выполнить поиск с помощью этого модуля, подключившая его страница должна просто отправить ему сообщение с требуемой фразой.
Пример 22.4. Модуль поиска на сайте Twitter с помощью метода postMessage()
<!DOCTYPE html>
<! —
Это модуль поиска на сайте Twitter. Модуль можно подключить к любой странице внутри элемента <iframe> и запросить его выполнить поиск, отправив ему строку запроса с помощью метода postMessage(). Поскольку модуль подключается в элементе <iframe>, а не <script>, он не сможет получить доступ к содержимому вмещающего документа.
-->
<html>
<head>
<style>body { font: 9pt sans-serif: }</style>
<!-- Подключить библиотеку jQuery ради ее утилиты jQuery.getJSON() -->
<script src="http://code.jquery.com/jquery-1.4.4.min.js"/></script>
<script>
// Можно было бы просто использовать свойство window.onmessage,
// но некоторые старые броузеры (такие как Firefox 3) не поддерживают его,
// поэтому обработчик устанавливается таким способом,
if (window.addEventListener)
window.addEventListener("message", handleMessage, false);
else
window.attachEvent("onmessage", handleMessage); // Для IE8
function handleMessage(e) {
// Нас не волнует происхождение документа, отправившего сообщение:
// мы готовы выполнить поиск на сайте Twitter для любой страницы.
// Однако сообщение должно поступить от окна, вмещающего этот модуль,
if (е.source !== window.parent) return;
var searchterm = e.data; // Это фраза, которую требуется отыскать
// С помощью утилит поддержки Ajax из библиотеки jQuery и прикладного
// интерфейса Twitter отыскать сообщения, соответствующие фразе.
jQuery.getJS0N("http://search.twitter.com/search.json?callback=?",
{ q: searchterm },
function(data) { // Вызывается с результатами запроса
var tweets = data.results;
// Создать HTML-документ для отображения результатов
var escaped = searchterm.replace("<", "<”);
var html = "<h2>" + escaped + "</h2>";
if (tweets.length == 0) {
html += "No tweets found";
}
else {
html += "<dl>"; // <dl> list of results
for(var і = 0; і < tweets.length; i++) {
var tweet = tweets[i];
var text = tweet.text;
var from = tweet.from_user;
var tweeturl = "http://twitter.eom/#!/" + from +
"/status/" + tweet.id_str;
html += "<dt><a target='_blank‘ href="' +
tweeturl + "'>" + tweet.from_user +
"</a></dt><dd>" + tweet.text + "</dd>";
}
html += "</dl>";
}
// Вставить документ в <iframe>
document.body.innerHTML = html;
});
}
$(function() {
// Сообщить вмещающему документу, что модуль готов к поиску. Вмещающий документ
// не может отправлять модулю сообщения до получения этого сообщения, потому что
// модуль еще не готов принимать сообщения. Вмещающий документ может просто
// дождаться события onload, чтобы определить момент, кода будут загружены
// все фреймы. Но мы предусматриваем отправку этого сообщения, чтобы известить
// вмещающий документ, что можно начать выполнять поиск еще до получения
// события onload. Модулю неизвестно происхождение вмещающего документа,
// поэтому мы используем *, чтобы броузер доставил сообщение любому документу,
window.parent.postMessage("Twitter Search v0.1", "*");
});
</script>
</head>
<body>
</body>
</html>
В примере 22.5 представлен простой сценарий на языке JavaScript, который может быть вставлен в любую веб-страницу, где требуется использовать модуль поиска по сайту Twitter. Он вставляет модуль в документ и затем устанавливает обработчики событий во все ссылки в документе, чтобы при наведении указателя мыши на ссылку производился вызов метода postMessage()
модуля, заставляющий его выполнить поиск по строке URL ссылки. Это позволит узнать, что говорят люди о веб-сайте перед тем, как перейти к нему.
Пример 22.5. Использование модуля поиска по сайту Twitter с помощью метода postMessage()
// Этот сценарий JS вставляет Twitter Search Gadget в документ и добавляет обработчик
// событий ко всем ссылкам в документе, чтобы при наведении указателя мыши на них
// с помощью модуля поиска отыскать упоминания URL-адресов в ссылках.
// Это позволяет узнать, что говорят другие о сайтах, прежде чем щелкнуть на ссылке,
window.addEventListener("load", function() { // He работает в IE < 9
var origin = "http://davidflanagan.com”; // Происхождение модуля
var gadget = "/demos/TwitterSearch.html"; // Путь к модулю
var ifгате = document.createElement("ifгате"); // Создать iframe
iframe.src = origin + gadget; // Установить его URL
iframe.width = "250"; // Ширина 250 пикселов
iframe.height = ”100%"; // Во всю высоту документа
iframe.style.cssFloat = "right"; // Разместить справа
// Вставить фрейм в начало документа
document.body.insertBefore(iframe, document.body.firstChild);
// Отыскать все ссылки и связать их с модулем
var links = document.getElementsByTagName("а");
for(var і = 0; і < links.length; i++) {
// addEventListener не работает в версии ІЕ8 и ниже
links[i].addEventListener("mouseover", function() {
// Отправить url как искомую фразу и доставлять его, только если
// фрейм все еще отображает документ с сайта davidflanagan.com
iframe.contentWindow.postMessage(this.href, origin);
}, false);
}
}, false);
- 22.1. Геопозиционирование
- 22.2. Управление историей посещений
- 22.3. Взаимодействие документов с разным происхождением
- 22.4. Фоновые потоки выполнения
- 22.5. Типизированные массивы и буферы
- 22.6. Двоичные объекты
- 22.7. Прикладной интерфейс к файловой системе
- 22.8. Базы данных на стороне клиента
- 22.9. Веб-сокеты
- 22 Прикладные интерфейсы HTML5
- Эффективное взаимодействие процессов архитектуры Classic Server
- 6.1.6. Печать документов
- 6.2. Создание и автоматическое заполнение бланков стандартных документов
- Просмотр библиотек документов
- Создание библиотек документов
- Добавление документов
- Извлечение и возврат документов
- Извлечение и возврат документов в приложениях Microsoft Office 2007
- Удаление документов
- Использование параметров библиотек документов
- Создание рабочей области для документов