renovations

This commit is contained in:
Ilya Kantor 2015-02-27 12:13:01 +03:00
parent 11f2d7352f
commit bd1d5e4305
23 changed files with 804 additions and 948 deletions

View file

@ -4,13 +4,11 @@
Нужно написать функцию, которая показывает подсказку при *наведении* на элемент, но не при *быстром проходе* над ним.
То есть, если посетитель именно навёл курсор мыши на элемент и почти остановился -- подсказку показать, а если быстро провёл над ним, то не надо (зачем излишнее мигание?).
То есть, если посетитель именно навёл курсор мыши на элемент и почти остановился -- подсказку показать, а если быстро провёл над ним, то не надо, зачем излишнее мигание?
Технически -- можно измерять скорость движения мыши над элементом, если она маленькая, то считаем, что это "наведение на элемент" (показать подсказку), если большая -- "быстрый проход мимо элемента" (не показывать).
Задача -- сделать универсальный код, который отслеживает "наведение на элемент".
Пусть это будет объект `new HoverIntent(options)`, который при создании принимает `options`:
Реализуйте это через универсальный объект `new HoverIntent(options)`, с параметрами `options`:
<ul>
<li>`elem` -- элемент, наведение на который нужно отслеживать.</li>
<li>`over` -- функция-обработчик наведения на элемент.</li>
@ -19,10 +17,6 @@
Пример использования такого объекта для подсказки:
```js
function HoverIntent(options) {
//... ваш код ...
}
// образец подсказки
var tooltip = document.createElement('div');
tooltip.className = "tooltip";
@ -48,6 +42,6 @@ new HoverIntent({
Если провести мышкой над "часиками" быстро, то ничего не будет, а если медленно или остановиться на них, то появится подсказка.
Обратите внимание -- подсказка не "мигает"" при проходе мыши внутри "часиков", по подэлементам.
Обратите внимание -- подсказка не "мигает" при проходе мыши внутри "часиков", по подэлементам.

View file

@ -13,7 +13,7 @@
window.open("http://ya.ru");
```
...При запуске откроется новое окно с данным URL.
...При запуске откроется новое окно с указанным URL.
Большинство браузеров по умолчанию создают новую вкладку вместо отдельного окна, но чуть далее мы увидим, что можно и "заказать" именно окно.
@ -25,37 +25,12 @@ window.open("http://ya.ru");
Как же браузер понимает -- посетитель вызвал открытие окна или нет?
**Для этого при работе скрипта он хранит внутренний "флаг", который говорит -- инициировал посетитель выполнение или нет.**
Для этого при работе скрипта он хранит внутренний "флаг", который говорит -- инициировал посетитель выполнение или нет. Например, при клике на кнопку весь код, который выполнится в результате, включая вложенные вызовы, будет иметь флаг "инициировано посетителем" и попапы при этом разрешены.
Например, при клике на кнопку весь код, который выполнится в результате, включая вложенные вызовы, будет иметь флаг "инициировано посетителем" и попапы при этом разрешены.
А если код был на странице и выполнился автоматически при её загрузке -- у него этого флага не будет. Попапы будут заблокированы.
А если код был на странице и выполнился автоматически при её загрузке -- у него этого флага не будет. Попапы будут заблокированы..
Здесь есть ещё ряд тонких моментов. Например: обработчик `onclick` вызвал `setTimeout(func, timeout)`. В результате код `func` выполнится уже после того, как событие `onclick` обработано. Следует ли передать ему флаг "инициировано посетителем" или нет?
Возможны разные точки зрения, например, IE блокирует такие окна (видимо, проще было реализовать), а Firefox и Chrome/Safari передают флаг, если таймаут меньше секунды. А если больше -- то не передают ;)
Вот это окно откроется в Firefox и Chrome (но не в IE):
```js
//+ run
setTimeout(function() {
window.open("http://ya.ru");
}, *!*500*/!*);
```
...А это -- будет заблокировано:
```js
//+ run
setTimeout(function() {
window.open("http://ya.ru");
}, *!*2500*/!*);
```
**Таким образом, современные браузеры расширяют область "действий посетителя" и блокируют попапы не всегда.**
## Синтаксис window.open
## Полный синтаксис window.open
Полный синтаксис:
@ -63,17 +38,19 @@ setTimeout(function() {
win = window.open(url, name, params)
```
Функция возвращает ссылку но объект `window` нового окна, либо `null`, если окно было заблокировано браузером.
Параметры:
<dl>
<dt>`url`</dt>
<dd>URL для загрузки в новое окно.</dd>
<dt>`name`</dt>
<dd>Имя нового окна. Может быть использовано в параметре `target` в формах. Если позднее вызвать `window.open()` с тем же именем, то некоторые браузеры (Firefox) заменяют существующее окно на новосозданное. А вот IE при этом откроет новое окно.</dd>
<dd>Имя нового окна. Может быть использовано в параметре `target` в формах. Если позднее вызвать `window.open()` с тем же именем, то браузеры (кроме IE) заменяют существующее окно на новое.</dd>
<dt>`params`</dt>
<dd>Строка с конфигурацией для нового окна. Состоит из параметров, перечисленных через запятую. Пробелов в ней быть не должно.</dd>
</dl>
**Можно легко узнать, было ли окно заблокировано. При этом результат `window.open` равен `null`.**
Значения параметров `params`.
<ol>
@ -108,32 +85,14 @@ win = window.open(url, name, params)
<li>Еще есть небольшое количество не кросс-браузерных свойств, которые обычно не используются. Вы можете узнать о них в документации, например MDN: [window.open](https://developer.mozilla.org/en/DOM/window.open).</li>
</ol>
Откроем окно с минимальным набором функций, чтобы посмотреть, какие из них браузер позволяет отключить:
```js
//+ run
var params = 'scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no' +
'width=0,height=0,left=-1000,top=-1000';
window.open('/', 'test', params);
```
Здесь большинство параметров отключено и окно позиционированно за пределами экрана. Запустите, чтобы увидеть, что произойдет на самом деле.
Теперь давайте добавим корректные параметры позиционирования: нормальную ширину, высоту и координаты левого верхнего угла.
Этот пример браузер отобразит "как заказано":
```js
//+ run
var params = 'scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no' +
'width=100,height=100,left=100,top=100';
window.open('/', 'test', params);
```
[warn]
Браузер подходит к этим параметрам интеллектуально. Он может проигнорировать их часть или даже все, они скорее являются "пожеланиями", нежели "требованиями".
[/warn]
Важные моменты:
<ul>
<li>Если при вызове `open` третий параметр отсутствует, то используются параметры по умолчанию. Обычно при этом будет открыто не окно, а вкладка.</li>
<li>Если при вызове `open` указан только первый параметр, параметр отсутствует, то используются параметры по умолчанию. Обычно при этом будет открыто не окно, а вкладка, что зачастую более удобно.</li>
<li>Если указана строка с параметрами, но некоторые `yes/no` параметры отсутствуют, то браузер выставляет их в `no`. Поэтому убедитесь, что все нужные вам параметры выставлены в `yes`.</li>
<li>Когда не указан `top/left`, то браузер откроет окно с небольшим смещением относительно левого верхнего угла последнего открытого окна.</li>
<li>Если не указаны `width/height`, новое окно будет такого же размера, как последнее открытое.</li>
@ -143,12 +102,23 @@ window.open('/', 'test', params);
Вызов `window.open` возвращает ссылку на новое окно. Она может быть использована для манипуляции свойствами окна, изменения URL, доступа к его переменным и т.п.
В примере ниже содержимое нового окна модифицируется после загрузки.
В примере ниже мы заполняем новое окно содержимым целиком из JavaScript:
```js
//+ run
var newWin = window.open("about:blank", "hello", "width=200,height=200");
newWin.document.write("Привет, мир!");
```
А здесь модифицируем содержимое после загрузки:
```js
//+ run
var newWin = window.open('/', 'example', 'width=600,height=400');
newWin.focus();
alert(newWin.location.href); // (*) about:blank, загрузка ещё не началась
newWin.onload = function() {
// создать div в документе нового окна
@ -156,33 +126,101 @@ newWin.onload = function() {
div.innerHTML = 'Добро пожаловать!'
div.style.fontSize = '30px'
*!*
var body = newWin.document.body;
*/!*
// вставить первым элементом в новое body
body.insertBefore(div, body.firstChild);
// вставить первым элементом в body нового окна
body.insertBefore(div, newWin.document.body.firstChild);
}
```
**Сразу после `window.open` новое окно ещё не загружено.**
Обратим внимание: сразу после `window.open` новое окно ещё не загружено. Это демонстрирует `alert` в строке `(*)`. Поэтому в примере выше окно модифицируется при `onload`. Можно было и поставить обработчик на `DOMContentLoaded` для `newWin.document`.
**Связь между окнами -- двухсторонняя.**
Родительское окно получает ссылку на новое через `window.open`, а дочернее -- ссылку на родителя `window.opener`.
Оно тоже может его модифицировать.
Если запустить пример ниже, то новое окно заменит содержимое текущего на `'Test'`:
```js
//+ run
var newWin = window.open("about:blank", "hello", "width=200,height=200");
newWin.document.write(
"<script>*!*window.opener.document.body.innerHTML = 'Test'*/!*</script>"
);
```
Поэтому в примере выше изменения начинаются при `onload`. Можно использовать и событие `DOMContentLoaded`.
[warn header="Same Origin Policy -- защита проверкой протокол-сайт-порт"]
**Большинство действий, особенно получение содержимого окна и его переменных, возможны лишь в том случае, если URL нового окна происходит из того же источника (англ. - *"Same Origin"*), т.е. совпадают домен, протокол и порт.**
Большинство действий, особенно получение содержимого окна и его переменных, возможны лишь в том случае, если URL нового окна происходит из того же источника (англ. - *"Same Origin"*), т.е. совпадают домен, протокол и порт.
Иначе говоря, если новое окно содержит документ с того же сайта.
Исключение: замена адреса. Поменять (но не прочитать) `win.location = ...` можно всегда, даже если окно с другого домена.
Больше информации об этом -- в главе [](/same-origin-policy).
Больше информации об этом будет позже, в главе [](/same-origin-policy).
[/warn]
## Связь между новым и родительским окном
Связь между окнами -- двухсторонняя. Родительское окно получает ссылку из `window.open`, а дочернее -- специальное свойство `window.opener`.
## События
Наиболее важные события при работе с окном браузера:
<ul>
<li>`onresize` -- событие изменения размера окна.</li>
<li>`onscroll` -- событие при прокрутке окна.</li>
<li>`onload` -- полностью загрузилась страница со всеми ресурсами.</li>
<li>`onfocus/onblur` -- получение/потеря фокуса.</li>
</ul>
## Методы и свойства
<dl>
<dt>`window.closed`
<dd>Свойство `window.closed` равно `true`, если окно закрыто. Может быть использовано, чтобы проверить, закрыл ли посетитель попап.</dd>
<dt>`window.close()`</dt>
<dd>Закрывает попап без предупреждений и уведомлений. Вообще, метод `close()` можно вызвать для любого окна, в том числе, текущего. Но если окно открыто не с помощью `window.open()`, то браузер может проигнорировать вызов `close` или запросить подтверждение.
</dd>
</dl>
## Перемещение и изменение размеров окна
Существует несколько методов для перемещения/изменения размеров окна.
<dl>
<dt>`win.moveBy(x,y)`</dt>
<dd>Перемещает окно относительно текущего положения на `x` пикселей вправо и `y` пикселей вниз. Допускаются отрицательные значения.</dd>
<dt>`win.moveTo(x,y)`</dt>
<dd>Передвигает окно в заданную координатами `x` и `y` точку экрана монитора.</dd>
<dt>`win.resizeBy(width,height)`</dt>
<dd>Изменяет размер окна на заданную величину `width/height` (ширина/высота). Допускаются отрицательные значения.</dd>
<dt>`win.resizeTo(width,height)`</dt>
<dd>Изменяет размер окна на заданное значение.</dd>
</dl>
[warn]
Чтобы предотвратить использование этих методов с плохими целями, браузеры часто блокируют их выполнение. Как правило, они работают, если окно `win` открыто вызовом [window.open](https://developer.mozilla.org/en-US/docs/Web/API/window.open) из JavaScript текущей страницы и в нём нет дополнительных вкладок.
[/warn]
[warn header="Ни свернуть ни развернуть"]
Заметим, что JavaScript не может ни свернуть ни развернуть ни "максимизировать" (Windows) окно.
Эти функции операционной системы от Frontend-разработчиков скрыты. Вызовы, описанные выше, в случае свёрнутого или максимизированного окна не работают.
[/warn]
## Прокрутка окна
Прокрутка окна требуется, пожалуй, чаще всего. Мы уже говорили о ней в главе [](/metrics-window):
<dl>
<dt>`win.scrollBy(x,y)`</dt>
<dd>
Прокрутка окна на заданное число пикселей вперед или назад. Допускаются отрицательные значения.</dd>
<dt>`win.scrollTo(x,y)`</dt>
<dd>Прокручивает окно к заданным координатам.</dd>
<dt>`elem.scrollIntoView(top)`</dt>
<dd>Этот метод прокрутки вызывается на элементе. При этом окно прокручивается так, чтобы элемент был полностью видим. Если параметр `top` равен `true` или не задан, то верх элемента совпадает с верхом окна. Если он равен `false`, то окно прокручивается так, чтобы нижний край элемента совпал с нижним краем окна.
</dd>
</dl>
Оно равно `null`, если открывшего окна нет.
## Итого
@ -195,11 +233,9 @@ newWin.onload = function() {
<li>Окна могут общаться между собой как угодно, если они из одного источника. Иначе действуют жёсткие ограничения безопасности.</li>
</ul>
Всплывающие окна используются нечасто. Ведь загрузить новую информацию можно динамически, с помощью технологии AJAX, а показать -- в элементе `DIV`, расположенным над страницей (`z-index`). Ещё одна альтернатива -- тег `IFRAME`.
Всплывающие окна используются нечасто. Ведь загрузить новую информацию можно динамически, с помощью технологии AJAX, а показать -- в элементе `<div>`, расположенным над страницей (`z-index`). Ещё одна альтернатива -- тег `<iframe>`.
Но в некоторых случаях всплывающие окна бывают очень даже полезны. Например, отдельное окно сервиса онлайн-консультаций. Посетитель может ходить по сайту в основном окне, а общаться в чате -- во вспомогательном.
Если вы хотите использовать всплывающее окно, предупредите посетителя об этом, так же и при использовании `target="_blank"` в ссылках или формах. Иконка открывающегося окошка на ссылке поможет посетителю понять, что происходит и не потерять оба окна из поля зрения.
<p style="text-align:right">Спасибо Марату Шагиеву за помощь в выкладке русскоязычного варианта этой главы.</p>

View file

@ -0,0 +1,160 @@
# Окно внутри ифрейма
Элемент `iframe` является обычным узлом DOM, как и любой другой. Существенное отличие -- в том, что с ним связан объект `window` внутреннего окна. Он доступен по ссылке `iframe.contentWindow`.
[cut]
Таким образом, `iframe.contentWindow.document` будет внутренним документом, `iframe.contentWindow.document.body` -- его `<body>` и так далее.
[smart header="Когда-то..."]
В старых браузерах использовались другие свойства, такие как `iframe.contentDocument` и даже `iframe.document`, но они давно не нужны.
[/smart]
## Переход внутрь ифрейма
В примере ниже JavaScript получает документ внутри ифрейма и модифицирует его:
```html
<!--+ run height=100 -->
<iframe src="javascript:'тест'" style="height:60px"></iframe>
<script>
var iframe = document.getElementsByTagName('iframe')[0];
*!*
var iframeDoc = iframe.contentWindow.document;
*/!*
iframeDoc.body.style.backgroundColor = 'green';
</script>
```
[smart header="src='javascript:...'"]
Атрибут `src` может использовать протокол `javascript:...`. При этом код выполняется и его результат будет содержимым ифрейма. Этот способ описан в стандарте и поддерживается всеми браузерами.
Атрибут `src` является обязательным, и его отсутствие может привести к проблемам, вплоть до игнорирования ифрейма браузером.
Чтобы ничего не загружать в ифрейм, можно указать `src="javascript:''"`.
[/smart]
## Кросс-доменность: ограничение доступа к окну
Элемент `<iframe>` является "двуличным". С одной стороны, это обычный узел DOM, с другой -- внутри находится окно, которое может иметь совершенно другой URL, содержать независимый документ из другого источника.
Внешний документ имеет полный доступ к `<iframe>` как к DOM-узлу. А вот к окну -- если они с одного источника.
Это приводит к забавным последствиям. Например, мы можем повесить обработчик `iframe.onload`, чтобы узнать о загрузке `<iframe>`, а вот на `iframe.contentWindow.onload` -- лишь в случае, если окно с того же источника.
```html
<!--+ run height=120 -->
<iframe src="http://example.com" style="height:100px"></iframe>
<script>
var iframe = document.getElementsByTagName('iframe')[0];
// сработает
iframe.onload = function() { alert("iframe onload"); }
// не сработает
iframe.contentWindow.onload = function() { alert("contentWindow onload"); }
</script>
```
Если бы в примере выше `<iframe src>` был с текущего сайта, то оба обработчика сработали бы.
## Иерархия window.frames
Альтернативный способ доступа к окну ифрейма -- это получить его из коллекции `window.frames`.
Есть два способа доступа:
<ol>
<li>`window.frames[0]` -- доступ по номеру.</li>
<li>`window.frames.iframeName` -- доступ по `name` ифрейма.</li>
</ol>
Обратим внимание: в коллекции хранится именно окно (`contentWindow`), а не DOM-элемент.
Демонстрация всех способов доступа к окну:
```html
<!--+ run -->
<iframe src="javascript:''" style="height:80px" name="i"></iframe>
<script>
var iframeTag = document.body.children[0];
var iframeWindow = iframe.contentWindow; // окно из тега
alert(frames[0] === iframeWindow); // true, окно из коллекции frames
alert(frames.i == iframeWindow); // true, окно из frames по имени
</script>
```
Внутри ифрейма могут быть свои вложенные ифреймы. Всё это вместе образует иерархию.
Ссылки для навигации по ней:
<ul>
<li>`window.frames` -- коллекция "детей" (вложенных ифреймов)</li>
<li>`window.parent` -- содержит ссылку на родительское окно, позволяет обратиться к нему из ифрейма.
Всегда верно:
```js
// (из окна со фреймом)
window.frames[0].parent === window; // true
```
</li>
<li>`window.top` -- содержит ссылку на самое верхнее окно (вершину иерархии).
Всегда верно (в предположении, что вложенные фреймы существуют):
```js
window.frames[0].frames[0].frames[0].top === window
```
</li>
</ul>
**Свойство `top` позволяет легко проверить, во фрейме ли находится текущий документ:**
```js
//+ run
if (window == top) {
alert('Этот скрипт является окном верхнего уровня в браузере');
} else {
alert('Этот скрипт исполняется во фрейме!');
}
```
## Песочница sandbox
Атрибут `sandbox` позволяет построить "песочницу" вокруг ифрейма, запретив ему выполнять ряд действий.
Наличие атрибута `sandbox`:
<ul>
<li>Заставляет браузер считать ифрейм загруженным с другого источника, так что он и внешнее окно больше не могут обращаться к переменным друг друга.</li>
<li>Отключает формы и скрипты в ифрейме.</li>
<li>Запрещает менять `parent.location` из ифрейма.</li>
</ul>
Пример ниже загружает в `<iframe sandbox>` документ с JavaScript и формой. Ни то ни другое не сработает:
[codetabs src="sandbox"]
Если у атрибута `sandbox` нет значения, то браузер применяет максимум ограничений.
Атрибут `sandbox` может содержать через пробел список ограничений, которые не нужны:
<dl>
<dt>allow-same-origin</dt>
<dd>Браузер может не считать документ в ифрейме пришедшим с другого же домена. Если ифрейм *и так* с другого домена, то ничего не меняется.</dd>
<dt>allow-top-navigation</dt>
<dd>Разрешает ифрейму менять `parent.location`.</dd>
<dt>allow-forms</dt>
<dd>Разрешает отправлять формы из `iframe`.</dd>
<dt>allow-scripts</dt>
<dd>Разрешает выполнение скриптов из ифрейма. Но скриптам, всё же, будет запрещено открывать попапы.</dd>
</dl>
[smart]
Цель атрибута `sandbox` -- наложить дополнительные ограничения. Он не может снять уже существующие, в частности, убрать ограничения безопасности, если ифрейм с другого источника.
[/smart]

View file

@ -0,0 +1,11 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<iframe sandbox src="sandboxed.html"></iframe>
</body>
</html>

View file

@ -0,0 +1,18 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
alert(1);
</script>
<form action="http://google.ru">
<input type="text">
<input type="submit" value="Отправить форму на http://google.ru">
</form>
</body>
</html>

View file

@ -0,0 +1,137 @@
# Кросс-доменные ограничения и их обход
Ограничение "Same Origin" ("тот же источник") ограничивает доступ окон и фреймов друг к другу, а также влияет на AJAX-запросы к серверу.
Причина, по которой оно существует -- безопасность. Если есть два окна, в одном из которых `vasya-pupkin.com`, а в другом `gmail.com`, то мы бы не хотели, чтобы скрипт из первого мог читать нашу почту.
Сама концепция проста, но есть много важных исключений и особенностей, которые нужно знать для полного понимания этого правила.
[cut]
## Концепция Same Origin [#same-origin]
Два URL считаются имеющим один источник ("same origin"), если у них одинаковый протокол, домен и порт.
Эти URL имеют один источник:
<ul>
<li>`http://site.com`</li>
<li>`http://site.com`/</li>
<li>`http://site.com/my/page.html`</li>
</ul>
А вот эти -- все из других источников:
<ul>
<li>http://<span style="color:red;font-weight:bold">www.</span>site.com (другой домен)</li>
<li>http://site.<span style="color:red;font-weight:bold">org</span> (другой домен)</li>
<li>http<span style="color:red; font-weight:bold">s</span>://site.com (другой протокол)</li>
<li>http://site.com<span style="color:red; font-weight:bold">:8080</span> (другой порт)</li>
</ul>
Существует ряд исключений, позволяющих-таки окнам с разных доменов обмениваться информацией, но прямой вызов методов друг друга и чтение свойств запрещены.
## В действии
Если одно окно попытается обратиться к другому, то браузер проверит, из одного ли они источника. Если нет -- доступ будет запрещён.
Например:
```html
<!--+ run -->
<iframe src="http://example.com"></iframe>
<script>
var iframe = document.body.children[0];
iframe.onload = function() {
try {
alert(iframe.contentWindow.document);
} catch(e) {
alert("Ошибка: " + e.message);
}
}
</script>
```
Пример выше выведет ошибку.
## Исключение: запись в location
Окна могут менять `location` друг друга, даже если они из разных источников.
Причём *читать* свойства `location` нельзя, одно окно не имеет право знать, на каком URL пользователь в другом. А вот *запись* браузеры считают безопасной.
Например, открыв на `javascript.ru` iframe с `http://example.com`, из этого ифрейма нельзя будет прочитать URL, а вот поменять его -- запросто:
```html
<!--+ run -->
<iframe src="http://example.com"></iframe>
<script>
var iframe = document.body.children[0];
iframe.onload = function() {
try {
// не сработает (чтение)
alert(iframe.contentWindow.location.href);
} catch(e) {
alert("Ошибка: " + e.message);
}
// сработает (запись)
iframe.contentWindow.location.href = 'http://wikipedia.org';
iframe.onload = null;
}
</script>
```
Если запустить код выше, то окно сначала начнёт загрузит `example.com`, а потом будет перенаправлено на `wikipedia.org`.
## Исключение: поддомен 3го уровня
Ещё одно важное исключение касается доменов третьего уровня.
Если несколько окон имеют общий домен второго уровня, к примеру `john.site.com`, `peter.site.com`, `site.com`, и присваивают в `document.domain` свой общий поддомен 2го уровня `site.com`, то все ограничения снимаются.
То есть, на всех этих сайтах должен быть код:
```js
document.domain = 'site.com';
```
Тогда между ними не будет кросс-доменных ограничений.
Обратим внимание: свойство `document.domain` должно быть присвоено на всех окнах, участвующих в коммуникации. Выглядит абсурдно, но даже на документе с `site.com` нужно вызвать: `document.domain="site.com"`. Иначе не будет работать.
Таким образом разные подсайты в рамках одного общего проекта могут взаимодействовать без ограничений.
## Исключения в IE
В браузере Internet Explorer есть два своих, дополнительных исключения из Same Origin Policy.
<ol>
<li>Порт не входит в понятие "источник" (origin).
Это означает, что окно с `http://site.com` может свободно общаться с `http://site.com:8080`.
Это иногда используют для общения серверов, использующих один IP-адрес. Но допустимо такое только в IE.</li>
<li>Если сайт находится в зоне "Надёжные узлы", то в Internet Explorer ограничения к нему не применяются.
При этом подразумевается, что для этой зоны в параметрах "Безопасность" включена опция "Доступ к источникам данных за пределами домена".</li>
</ol>
## Итого
Ограничение "одного источника" запрещает окнам и фреймам с разных источников вызывать методы друг друга и читать данные друг из друга.
При этом "из одного источника" означает "совпадают протокол, домен и порт".
У этого подхода ряд существенных исключений:
<ul>
<li>Свойства `window.location.*` нельзя читать, но можно менять.</li>
<li>Домены третьего уровня с общим наддоменом могут поменять `document.domain` на их общий домен второго уровня, и тогда они смогут взаимодействовать без ограничений.
</li>
<li>IE не включает порт в понятие источника. Кроме того, он позволяет снять ограничения для конкретного сайта включением в доверенную зону.</li>
</ul>

View file

@ -5,13 +5,9 @@
Он очень удобен, например, для взаимодействия внешних виджетов и сервисов, подключённых через ифрейм с основной страницей.
[cut]
## Интерфейс
## Отправитель: метод postMessage
Интерфейс состоит из двух частей.
### Отправитель: метод postMessage
Первая часть состоит из метода [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window.postMessage). Его вызывает окно, которое хочет отправить сообщение, в контексте окна-получателя.
Первая часть интерфейса состоит из метода [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window.postMessage). Его вызывает окно, которое хочет отправить сообщение, в контексте окна-получателя.
Проще говоря, если мы хотим отправить сообщение в окно `win`, то нужно вызвать `win.postMessage(data, targetOrigin)`.
@ -28,14 +24,25 @@
Мы ведь не можем из JavaScript узнать, на каком именно URL находится другое окно. Но иногда хочется быть уверенным, что данные передаются в доверенный документ. Для этого и нужен этот параметр. Проверку осуществляет браузер. При указании `'*'` ограничений нет.</dd>
</dl>
[warn header="В IE8 можно использовать `postMessage` только для ифреймов"]
Например:
```html
<iframe src="http://target.com" name="target">
В браузере IE8, интерфейс `postMessage` работает только с ифреймами. Он не работает между табами и окнами.
<script>
var win = window.frames.target;
win.postMessage("сообщение", "http://javascript.ru");
</script>
```
Это ошибка в данном конкретном браузере, в других -- всё в порядке.
[warn header="В IE11- можно использовать `postMessage` только для ифреймов"]
В браузере IE, интерфейс `postMessage` работает только с ифреймами. Он не работает между табами и окнами.
Это ошибка в данном конкретном браузере, в других -- всё в порядке. Детали по этой и связанным с ней ошибкам: [HTML5 Implementation Issues in IE8 and later](http://blogs.msdn.com/b/ieinternals/archive/2009/09/16/bugs-in-ie8-support-for-html5-postmessage-sessionstorage-and-localstorage.aspx).
[/warn]
### Получатель: событие onmessage
## Получатель: событие onmessage
Чтобы получить сообщение, окно должно поставить обработчик на событие `onmessage`.
@ -53,40 +60,28 @@
```js
function listener(event) {
if( event.origin != 'http://learn.javascript.ru') {
// что-то прислали с чужого домена - проигнорируем..
if( event.origin != 'http://javascript.ru') {
// что-то прислали с неизвестного домена - проигнорируем..
return;
}
document.getElementById("msg").innerHTML = "получено: " + event.data;
alert("получено: " + event.data);
}
if (window.addEventListener){
window.addEventListener("message", listener);
} else {
// IE8
window.attachEvent("onmessage", listener);
}
```
Этот код содержит ифрейм ниже, с именем `name="receiveFrame"`:
<iframe src="/files/tutorial/window/receive.html" style="width:100%; height: 60px; border:1px solid black" frameborder="0" name="receiveFrame"></iframe>
Запустите код для отправки ему сообщения:
```js
//+ run
var win = frames.receiveFrame;
win.postMessage("Привет!", "http://learn.javascript.ru");
```
## Задержка
[smart header="Задержка отсутствуют"]
Задержки между отправкой и получением нет, совсем.
Если для `setTimeout` стандарт предусматривает минимальную задержку 4мс, то для `postMessage` она равна 0мс.
Если для `setTimeout` стандарт предусматривает минимальную задержку 4мс, то для `postMessage` она равна 0мс.
Поэтому `postMessage` можно, в том числе, использовать как мгновенную альтернативу `setTimeout`.
[/smart]
## Итого

View file

@ -1,4 +1,4 @@
# Focus, привлечение внимания к окну
# Привлечение внимания к окну
Проверить, находится ли окно в фокусе, а также перевести внимание посетителя на него -- сложно.
@ -19,25 +19,22 @@
setInterval(function() { window.focus() }, 1000);
```
Что будет, если запустить этот код, и затем переключиться в другое окно или вкладку?
Можно подумать, что окно будет оказываться в фокусе раз в секунду. Но это не так.
Произойдет одно из трех:
<ol>
<li>Окно развернется (при необходимости) и выйдет на передний план. Обычно это происходит, когда метод `window.focus()` вызывается для попапа, а активно сейчас -- главное окно.</li>
<li>Заголовок окна начнет мигать. Чтобы увидеть это в действии -- откройте данную страницу в IE, запустите код и переключитесь на другое окно.</li>
Браузер попытается привлечь Ваше внимание миганием/мерцанием заголовка окна.</li>
<li>Не наблюдается никакого видимого эффекта. Откройте эту страницу в Firefox в Windows, запустите код и сверните окно. Вызов `window.focus` не сработает. Также ведёт себя и Opera.</li>
<li>Вообще никакого эффекта. Самый распространённый случай, если в окне много вкладок.</li>
<li>Окно развернется (при необходимости) и выйдет на передний план. Обычно это происходит, когда метод `window.focus()` вызывается для попапа, а активно сейчас -- главное окно. То есть, в этом случае вызов сработает.</li>
<li>Заголовок окна начнет мигать. Чтобы увидеть это в действии -- откройте данную страницу в IE, запустите код и переключитесь на другое окно. Браузер попытается привлечь Ваше внимание миганием/мерцанием заголовка окна.</li>
</ol>
Как же, всё-таки, можно гарантированно развернуть окно и поместить его на первый план?
Если это Вам действительно необходимо -- есть только одно надежное решение: плагин к браузеру или подписанный Java-апплет, который может обратиться напрямую к функциям ОС. Написать такое вполне можно. Но есть и другие способы привлечь внимание к окну.
## Мерцание заголовка
## Получение внимания -- для всплывающих окон
Один из лучших способов -- это сочетание `window.focus()` с мерцанием заголовка окна, как показано в примере ниже:
В дополнение к `window.focus()` используют мерцание заголовка окна, как показано в примере ниже:
```html
<!--+ run -->
@ -73,7 +70,7 @@ setInterval(function() { window.focus() }, 1000);
<input type="button" onclick="getAttention(win)" value="getAttention(win)">
```
Запустите код и сверните всплывющее окно. А затем -- нажмите кнопку с надписью "getAttention(win)". Браузер будет привлекать ваше внимание, как умеет ;)
Запустите код и сверните всплывающее окно. А затем -- нажмите кнопку с надписью "getAttention(win)". Браузер будет привлекать ваше внимание, как умеет ;)
Обратите внимание: в коде есть проверка на `win.closed`. Попытка манипулирования закрытым окном вызовет исключение.
@ -149,7 +146,7 @@ setInterval(function() { window.focus() }, 1000);
Но этот получает фокус только если посетитель сфокусируется где-то в документе: щелкнет или сделает еще какое-то действие в документе, а не просто посмотрит на него и проведет над ним мышкой.
Впрочем, никто не мешает использовать сочетание всех описанных методов :)
Впрочем, никто не мешает использовать сочетание всех описанных методов.
## Итого
@ -168,5 +165,3 @@ setInterval(function() { window.focus() }, 1000);
Поэтому для определения переключения на окно -- используйте его вместе с делегируемым `focus` на документе, а также `document.onmousemove`.</li>
</ul>
<p style="text-align:right">Спасибо Марату Шагиеву за помощь в выкладке русскоязычного варианта этой главы.</p>

View file

@ -0,0 +1,203 @@
# Атака Clickjacking и защита от неё
Атака "кликджекинг" (англ. Clickjacking) позволяет хакеру выполнить клик на сайте-жертве *от имени посетителя*.
В русском языке встречается дословный перевод термина clickjacking: "угон клика". Так же применительно к clickjacking-атаке можно встретить термины "перекрытие iframe" и "подмена пользовательского интерфейса".
Кликджекингу подверглись в своё время Twitter, Facebook , PayPal, YouTube и многие другие сайты. Сейчас, конечно, они уже защищены.
[cut]
## Идея атаки
В целом идея очень проста.
Вот как выглядел "угон клика" пользователя, который зарегистрирован на Facebook:
<ol>
<li>На вредоносной странице пользователю подсовывается безобидная ссылка (скажем, что-то скачать, "разбогатеть сейчас", посмотреть ролик или просто перейти по ссылке на интересный ресурс).</li>
<li>Поверх этой заманчивой ссылки помещен прозрачный iframe со страницей facebook.com, так что кнопка "Like" находится чётко над ней.</li>
<li>Кликая на ссылку, посетитель на самом деле нажимает на эту кнопку.</li>
</ol>
## Демо
Вот пример вредоносной страницы (для наглядности `iframe` -- полупрозрачный):
```html
<!--+ run height=120 -->
<style>
iframe { /* iframe с сайта-жертвы */
width: 400px;
height: 100px;
position: absolute;
top:0; left:-20px;
*!*
opacity: 0.5; /* в реальности opacity:0 */
*/!*
z-index: 1;
}
</style>
<div>Нажмите, чтобы разбогатеть сейчас:</div>
<!-- URL в реальности - с другого домена (атакуемого сайта) -->
*!*
<iframe src="facebook.html"></iframe>
<button>Жми тут!</button>
*/!*
<div>..И всё получится (хе-хе, у меня, злого хакера, получится)!</div>
```
В действии:
[codetabs src="clickjacking-visible" height=200]
Так как `<iframe src="facebook.html">` полупрозрачный, то в примере выше легко видеть, как он перекрывает кнопку. При клике на "Жми тут" на самом деле происходит клик на `<iframe>` (на "Like").
В итоге, если посетитель авторизован на facebook (а в большинстве случаев так и есть), то facebook.com получает щелчок от имени посетителя.
На Twitter это была бы кнопка "Follow".
Тот же самый пример, но ближе к реальности, с `opacity:0` для `<iframe>`. Вообще незаметно, что на самом деле посетитель кликает на `<iframe>`:
[codetabs src="clickjacking" height=200]
Итак, все, что нужно для проведения атаки -- это правильно расположить iframe на вредоносной странице. В большинстве случаев это делается средствами HTML/CSS.
[smart header="С клавиатурой так не сделаешь"]
Атака назвается "Clickjacking", то есть "угон клика", так как события клавиатуры "угнать" гораздо труднее.
Посетителя можно заставить сфокусироваться на `<input>` с невидимого `<iframe>`-жертвы, но текст в поле ввода также будет невидимым. Посетитель начнёт печатать, но, не увидев текст, прекратит свои действия.
[/smart]
## Плохая защита
Самый старый метод защиты -- это код JavaScript, не позволяющий отобразить веб-страницу внутри фрейма ("framebusting", также его называют "framekilling" и "framebreaking").
Примерно такой:
```js
if (top != window) {
top.location = window.location;
}
```
То есть, если окно обнаруживает, что оно загружено во фрейме, то оно автоматически делает себя верхним.
Увы, в настоящий момент это уже не является сколько-нибудь надежной защитой. Есть несколько способов обхода framebusting. Давайте рассмотрим некоторые из них.
### Блокировка top-навигации.
Можно заблокировать переход, инициированный сменой `top.location`, в событии [onbeforeunload](#window.onbeforeunload).
Обработчик этого события ставится на внешней (хакерской) странице и, при попытке `iframe` поменять `top.location`, спросит посетителя, хочет он покинуть данную страницу. В большинстве браузеров хакер может спросить посетителя, используя своё сообщение.
```js
window.onbeforeunload = function() {
window.onbeforeunload = null;
return "Хотите уйти с этой страницы, не узнав все её тайны (хе-хе)?";
}
```
Так что, скорее всего, посетитель ответит на такой странный вопрос отрицательно (он же не знает про ифрейм, видит только страницу, причины для ухода нет). А значит, ожидаемая смена `top.location` не произойдёт!
Пример в действии:
[codetabs src="top-location"]
### Атрибут sandbox
Современные браузеры поддерживают атрибут [sandbox](http://www.whatwg.org/specs/web-apps/current-work/multipage/the-iframe-element.html#attr-iframe-sandbox)
Он позволяет разрешить во фрейме скрипты `allow-scripts` и формы `allow-forms`, но запретить top-навигацию (не указать `allow-top-navigation`).
"Защищённый" `<iframe>` хакер может подключить, к примеру, так:
```html
<iframe *!*sandbox="allow-scripts allow-forms"*/!* src="facebook.html"></iframe>
```
Есть и другие приёмы для обхода этой простейшей защиты.
Firefox и старый IE могут активировать designMode на исходной странице, это также предотвращает framebusting, у IE есть нестандартный атрибут [security](https://msdn.microsoft.com/en-us/library/ie/ms534622.aspx) для ифреймов, который можно использовать с той же целью.
Как мы видим, эта защита не только не выдерживает реальной атаки, но и может скомпрометировать сайт (программист-то думает, что защитил его).
## Заголовок X-Frame-Options
Все современные браузеры поддерживают заголовок `X-Frame-Options`.
Он разрешает или запрещает отображение страницы, если она открыта во фрейме.
Браузеры игнорируют заголовок, если он определен в МЕТА тег. Таким образом, `<meta http-equiv="X-Frame-Options"...>` будет проигнорирован.
У заголовка может быть три значения:
<dl>
<dt>SAMEORIGIN</dt>
<dd>Рендеринг документа, при открытии во фрейме, производится только в том случае, когда верхний (top) документ -- с того же домена.</dd>
<dt>DENY</dt>
<dd>Рендеринг документа внутри фрейма запрещён.</dd>
<dt>ALLOW-FROM domain</dt>
<dd>Разрешает рендеринг, если внешний документ с данного домена (не поддерживается в Safari, Firefox).</dd>
</dl>
К примеру, Twitter использует `X-Frame-Options: SAMEORIGIN`. Результат:
```html
<iframe src="http://twitter.com"></iframe>
```
<iframe src="http://twitter.com"></iframe>
В зависимости от браузера, `iframe` выше либо пустой, либо в нём находится сообщение о невозможности отобразить его (IE).
## Показ с отключённым функционалом
Заголовок `X-Frame-Options` имеет неприятный побочный эффект. Иногда поисковики, анонимайзеры или другие сайты хотели бы отобразить страницу в `iframe`, по вполне "легальным" причинам, но не могут.
Хорошо бы показывать их посетителям не пустой `iframe`, а нечто, что может быть более интересно.
Например, можно изначально "накрывать" документ `div` с `height:100%;width:100%`, который будет перехватывать все клики. И поставить на нём ссылку, ведующую на страницу в новом окне.
```html
<style>
#iframe-protector {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 99999999;
}
</style>
<div id="iframe-protector">
<a href="/" target="_blank">Перейти на сайт</a>
</div>
<script>
if (top.document.domain == document.domain) {
убрать iframe-protector
}
</script>
```
Если страница -- не во фрейме или домен совпадает, то посетитель не увидит его.
## Заключение
Атаку "Clickjacking" легко осуществить, если на сайте есть действие, активируемое с помощью одного клика.
Злоумышленник может осуществить атаку через целенаправленно на посетителей ресурса -- опубликовав ссылку на форуме, или "счастливой рассылкой". Существует масса вариантов.
С первого взгляда, она "неглубокая": всё, что можно сделать -- это один клик. С другой стороны, если хакер знает, что после клика появляется какой-то другой управляющий элемент, то он, хитрыми сообщениями, может заставить посетителя кликнуть и по нему. А это уже не один, а два клика.
Атака особенно опасна, поскольку, проектируя интерфейс сайта, обычно никто и не задумывается о том, что клик от имени юзера может сделать хакер. Точки уязвимости могут быть в совершенно непредсказуемых местах.
<ul>
<li>Рекомендуется использовать `X-Frame-Options` на страницах, заведомо не предназначеных для запуска во фрейме и на важнейших страницах (финансовые транзакции).</li>
<li>Используйте перекрывающий `<div>`, если это допустимо вашим проектом и вы хотите разрешить безопасный показ документа во фреймах с любых доменов.</li>
</ul>

View file

@ -0,0 +1,8 @@
<!DOCTYPE HTML>
<html>
<body style="margin:10px;padding:10px">
<input type="button" onclick="alert('Нажата кнопка Like с другого домена!')" value="I LIKE IT !">
</body>
</html>

View file

@ -0,0 +1,29 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<style>
iframe { /* iframe с сайта-жертвы */
width: 400px;
height: 100px;
position: absolute;
top:0; left:-20px;
opacity: 0.5;
z-index: 1;
}
</style>
<div>Нажмите, чтобы разбогатеть сейчас:</div>
<!-- URL, в реальности - с другого домена (атакуемого сайта) -->
<iframe src="facebook.html"></iframe>
<button>Жми тут!</button>
<div>..И всё получится (хе-хе, у меня, злого хакера, получится)!</div>
</body>
</html>

View file

@ -0,0 +1,8 @@
<!DOCTYPE HTML>
<html>
<body style="margin:10px;padding:10px">
<input type="button" onclick="alert('Нажата кнопка Like с другого домена!')" value="I LIKE IT !">
</body>
</html>

View file

@ -0,0 +1,29 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<style>
iframe { /* iframe с сайта-жертвы */
width: 400px;
height: 100px;
position: absolute;
top:0; left:-20px;
opacity: 0;
z-index: 1;
}
</style>
<div>Нажмите, чтобы разбогатеть сейчас:</div>
<!-- URL, в реальности - с другого домена (атакуемого сайта) -->
<iframe src="facebook.html"></iframe>
<button>Жми тут!</button>
<div>..И всё получится (хе-хе, у меня, злого хакера, получится)!</div>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div>Меняет top.location на javascript.ru</div>
<script>
top.location = 'http://javascript.ru';
</script>
</body>
</html>

View file

@ -0,0 +1,38 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<style>
iframe { /* iframe с сайта-жертвы */
width: 400px;
height: 100px;
position: absolute;
top:0; left:-20px;
opacity: 0;
z-index: 1;
}
</style>
<script>
function attack() {
window.onbeforeunload = function() {
window.onbeforeunload = null;
return "Хотите уйти с этой страницы, не узнав все её тайны (хе-хе)?";
};
document.body.insertAdjacentHTML('beforeend', '<iframe src="iframe.html">');
}
</script>
</head>
<body>
<p>При нажатии на кнопку посетитель получит "странный" вопрос о том, не хочет ли уйти со страницы.</p>
<p>Наверно, он ответит "хочу остаться" и защита ифрейма будет провалена.</p>
<button onclick="attack()">Подключить "защищённый" iframe</button>
</body>
</html>

View file

@ -1,114 +0,0 @@
# Окно браузера: свойства и методы
Объект `window` выполняет две функции (или играет две роли):
<ol>
<li>Является глобальным объектом Javascript.</li>
<li>Предоставляет интерфейс для работы с окном браузера.</li>
</ol>
О глобальном объекте мы говорили в главе [](/closures). А здесь -- обратим внимание на то, что касается браузера.
[cut]
## События
Наиболее важные события при работе с окном браузера:
<ul>
<li>`onresize` -- событие изменения размера окна.</li>
<li>`onscroll` -- событие при прокрутке окна.</li>
<li>`onload` -- полностью загрузилась страница со всеми ресурсами.</li>
</ul>
## Методы и свойства
<dl>
<dt>`window.closed`
<dd>Свойство `window.closed` равно `true`, если окно закрыто. Может быть использовано, чтобы проверить, закрыл ли посетитель попап.</dd>
<dt>`window.close()`</dt>
<dd>Закрывает попап без предупреждений и уведомлений. Вообще, метод `close()` можно вызвать для любого окна, в том числе, текущего. Но если окно открыто не с помощью `window.open()`, то браузер может проигнорировать вызов `close` или запросить подтверждение.
Нажмите кнопку ниже, для закрытия текущего окна.
<input type="button" onclick="window.close()" value="window.close()">
Попытайтесь это сделать в разных браузерах. На момент написания статьи, Firefox, Opera и Safari проигнорируют вызов `close()`, а IE запросит подтверждение.
</dd>
<dt>`window.title`</dt>
<dd>Заголовок окна соостветствует содержимому элемента `TITLE` в `HEAD`.
**Забавно, но для *изменения* заголовка нужно использовать не это свойство, а `document.title`.**
Код, расположенный ниже, каждую секунду обновляет заголовок окна на текущее время:
```js
//+ run
setInterval(function() {
document.title = new Date();
}, 1000);
```
</dd>
</dl>
## Перемещение и изменение размеров окна
Существует несколько методов для перемещения/изменения размеров окна.
<dl>
<dt>`win.moveBy(x,y)`</dt>
<dd>Перемещает окно относительно текущего положения на `x` пикселей вправо и `y` пикселей вниз. Допускаются отрицательные значения.</dd>
<dt>`win.moveTo(x,y)`</dt>
<dd>Передвигает окно в заданную координатами `x` и `y` точку экрана монитора.</dd>
<dt>`win.resizeBy(width,height)`</dt>
<dd>Изменяет размер окна на заданную величину `width/height` (ширина/высота). Допускаются отрицательные значения.</dd>
<dt>`win.resizeTo(width,height)`</dt>
<dd>Изменяет размер окна на заданное значение.</dd>
</dl>
Чтобы предотвратить использование этих методов с плохими целями, браузеры часто блокируют их выполнение. Как правило, они работают, если окно `win` открыто вызовом [window.open](https://developer.mozilla.org/en-US/docs/Web/API/window.open) из JavaScript текущей страницы и в нём нет дополнительных вкладок.
**JavaScript не может свернуть или максимизировать окно.**
Эти функции операционной системы от Frontend-разработчиков скрыты. Вызовы, описанные выше, в случае свёрнутого или максимизированного окна не работают.
## Прокрутка окна
Прокрутка окна требуется, пожалуй, чаще всего. Мы уже говорили о ней в главе [](/metrics-window):
<dl>
<dt>`win.scrollBy(x,y)`
Прокрутка окна на заданное число пикселей вперед или назад. Допускаются отрицательные значения.</dt>
<dt>`win.scrollTo(x,y)`</dt>
<dd>Прокручивает окно к заданным координатам.</dd>
<dt>`elem.scrollIntoView(top)`</dt>
<dd>Этот метод прокрутки вызывается на элементе. При этом окно прокручивается так, чтобы элемент был полностью видим.
Если параметр `top` равен `true` или не задан, то верх элемента совпадает с верхом окна. Если он равен `false`, то окно прокручивается так, чтобы нижний край элемента совпал с нижним краем окна.
</dd>
</dl>
## Итого
Что можно сделать с окном браузера:
<ul>
<li>Открыть новое -- вызовом `open()`</li>
<li>Проверить, закрыто ли всплывающее окно -- свойство `window.closed`</li>
<li>Закрыть окно -- метод `window.close()`. Если окно открыто не через `open()`, то не сработает в Firefox, Opera и Safari).</li>
<li>Поставить свой заголовок в `document.title`.</li>
</ul>
Управление размером, положением, прокруткой окна:
<ul>
<li>Переместить вправо-вниз -- `win.moveBy(x,y)`.</li>
<li>Переместить в координату на экране монитора -- `win.moveTo(x,y)`.</li>
<li>Изменить размер на данную величину -- `win.resizeBy(width, height)`.</li>
<li>Выставить точный размер окна -- `win.resizeTo(width,height)`</li>
<li>Прокрутить документ в окне на данную величину -- `win.scrollBy(x,y)`.</li>
<li>Прокрутить документ в окне до точных координат (относительно документа) -- `win.scrollTo(x,y)`.</li>
<li>Дополнительно для прокрутки можно использовать
`elem.scrollIntoView(top)`, где top может быть true/false — прокрутка elem до совпадения нижней/верхней границы elem с нижней/верхней границей документа, так, чтобы elem был полностью видим. По умолчанию `true`.</li>
</ul>
<p style="text-align:right">Спасибо Марату Шагиеву за помощь в выкладке русскоязычного варианта этой главы.</p>

View file

@ -1,250 +0,0 @@
# Общение с окном в ифрейме
Здесь предполагается, что мы прекрасно знаем, что такое ифрейм, и как подключить внешний документ с его использованием. Иначе говоря, HTML для нас не нов.
В этой главе мы рассмотрим способы работы с ними из JavaScript.
[cut]
## Ифрейм: тонкости с атрибутами
Хоть мы и знаем, что такое ифрейм, но позвольте заострить внимание на нескольких моментах, касающихся атрибутов.
<dl>
<dt>Рамка: `frameborder="0"`</dt>
<dd>Если хочется, чтобы в IE8- вокруг ифреймов не было рамок, то поставьте атрибут `frameborder="0"`.
В IE8- все ифреймы в примере ниже, кроме последнего, будут с рамкой:
```html
<!--+ run -->
<style> iframe { width: 100px; height:40px } </style>
<ol>
<li><iframe src="javascript:'тест'"></iframe></li>
<li><iframe src="javascript:'тест'" style="border:0"></iframe></li>
*!*
<li><iframe src="javascript:'тест'" frameborder="0"></iframe></li>
*/!*
</ol>
```
</dd>
<dt>Пустой `src`</dt>
<dd>Атрибут `src` может использовать протокол `javascript:...`. При этом код выполняется и его результат будет содержимым ифрейма. Этот способ описан в стандарте и поддерживается всеми браузерами.
Атрибут `src` является обязательным, и его отсутствие может привести к проблемам, вплоть до игнорирования ифрейма браузером.
**Чтобы ничего не загружать в ифрейм, укажите `src="javascript:false"`.**
</dd>
<dt>Атрибут `name` и создание ифрейма в IE7-</dt>
<dd>В старых IE нельзя менять атрибут `name` после создания ифрейма:
```js
var iframe = document.createElement('iframe');
iframe.name = 'iName'; // в IE7- не сработает
```
Поэтому, если нужна совместимость, создавайте ифреймы через `innerHTML`:
```js
var tmp = document.createElement('div');
tmp.innerHTML = '<iframe name="iName" src="javascript:false"></iframe>';
var iframe = tmp.firstChild;
```
</dd>
</dl>
## Ифрейм: доступ к document и window
Элемент `iframe` является обычным узлом DOM, как и любой другой. Существенное отличие -- в том, что с ним связан объект `window` внутреннего окна. Он доступен по ссылке `iframe.contentWindow`.
Таким образом, `iframe.contentWindow.document` будет внутренним документом.
[smart header="Когда-то..."]
В старых браузерах использовались дополнительные свойства, такие как `iframe.contentDocument` и даже `iframe.document`, но они давно не нужны.
[/smart]
В примере ниже JavaScript получает документ внутри ифрейма и модифицирует его:
```html
<!--+ height="100" run -->
<iframe src="javascript:'тест'" style="height:60px"></iframe>
<script>
var iframe = document.getElementsByTagName('iframe')[0];
*!*
var iframeDoc = iframe.contentWindow.document;
*/!*
iframeDoc.body.style.backgroundColor = 'green';
</script>
```
[smart]
Обратите внимание, HTML `'тест'` в примере выше -- невалидный, там нет `BODY`. Поэтому ифрейм будет отображаться в режиме совместимости.
Но браузер исправляет структуру и гарантирует, что после загрузки документа у него всегда есть `document.body` и ровно одно.
[/smart]
В целях безопасности возможность доступа к документу в ифрейме ограничена. Если он с другого домена, на другом порту или протоколе, то доступ запрещён. Подробнее об этом ограничении и как его можно обойти -- далее, в главе [](/same-origin-policy).
## Иерархия window.frames
Альтернативный способ доступа к окну ифрейма -- это получить его из коллекции `window.frames`.
Есть два способа доступа:
<ol>
<li>`window.frames[0]` -- доступ по номеру.</li>
<li>`window.frames.iframeName` -- доступ по `name` ифрейма.</li>
</ol>
В коллекции хранится именно окно (`contentWindow`), а не тег.
Например:
```html
<!--+ run -->
<iframe src="javascript:''" style="height:80px" name="iframeName"></iframe>
<script>
var iframe = document.body.children[0];
var iframeWindow = iframe.contentWindow; // окно из тега
alert(frames[0] === iframeWindow); // true, окно из коллекции frames
alert(frames.iframeName == iframeWindow); // true, окно из frames по имени
</script>
```
Внутри ифрейма могут быть свои вложенные ифреймы. Всё это вместе образует иерархию.
Ссылки для навигации по ней:
<ul>
<li>**По детям:** `window.frames` -- коллекция "детей" (вложенных ифреймов)</li>
<li>**На родителя:** `window.parent` -- содержит ссылку на родительское окно, позволяет обратиться к нему из ифрейма.
Всегда верно:
```js
// (из окна со фреймом)
window.frames[0].parent === window; // true
```
</li>
<li>**На корень:** `window.top` -- содержит ссылку на самое верхнее окно.
Всегда верно:
```js
// (в предположении, что вложенные фреймы существуют)
window.frames[0].frames[0].frames[0].top === window
```
</li>
</ul>
**Свойство `top` позволяет легко проверить, во фрейме ли находится текущий документ:**
```js
//+ run
if (window == top) {
alert('Этот скрипт является окном верхнего уровня в браузере');
} else {
alert('Этот скрипт исполняется во фрейме!');
}
```
## Событие onload
У ифрейма есть своё событие `onload`, которое не связано с `onload` основного окна.
Иными словами, `onload` основного окна не ждёт, пока ифрейм догрузится.
**Событие `onload` есть и на теге `iframe` и на его окне.**
Это важно, так как обработчик на теге более универсален.
**В случае, когда документ с другого домена, внешний документ сможет отследить его загрузку только по `onload` на теге.**
Например, попытаемся отследить загрузку ифрейма с сайта `http://vk.com/`:
```html
<!--+ run -->
<iframe src="http://vk.com/" name="vk" style="height:100px"></iframe>
<script>
// поставить onload на элемент
document.getElementsByTagName('iframe')[0].onload = function() {
alert('Фрейм загрузился')
}
// set onload on window
frames.vk.onload = function() {
alert('Окно фрейма загрузилось')
}
</script>
```
Запустите пример выше. Вы увидите, что работает только `iframe.onload`. А если бы ифрейм был с того же домена, то сработали бы оба обработчика.
## Атрибуты seamless и sandbox
В стандарте HTML5 для [iframe](http://www.w3.org/wiki/HTML/Elements/iframe) предусмотрены атрибуты `seamless` и `sandbox`.
Оба они на момент написания (конец 2012) поддерживаются в Chrome, в других браузерах поддержка тоже не за горами.
### seamless
Атрибут `seamless` полностью интегрирует ифрейм в документ, убирая рамку и применяя CSS-стили внешнего окна к содержимому ифрейма, как будто это обычный элемент.
То есть, если в основном документе есть стиль `p { font-weight: bold }`, то он повлияет и на содержимое ифрейма.
Для JavaScript он не важен.
### sandbox
Атрибут `sandbox` позволяет построить "песочницу" вокруг ифрейма, запретив ему выполнять ряд действий.
Наличие атрибута `sandbox`:
<ul>
<li>Заставляет браузер считать ифрейм загруженным с другого домена, так что он и внешнее окно больше не могут обращаться к переменным друг друга.</li>
<li>Отключает формы и скрипты в ифрейме.</li>
<li>Запрещает менять `parent.location` из ифрейма.</li>
</ul>
Пример ниже загружает в такой ифрейм документ:
```html
<!--+ run height=200 -->
<iframe *!*sandbox*/!* src="/files/tutorial/window/sandboxed.html"></iframe>
```
Содержимое файла `sandboxed.html`:
```html
<script>
alert(1);
</script>
<form action="http://google.ru">
<input type="submit" value="Отправить форму на http://google.ru">
</form>
```
Запустите пример выше для просмотра. Ни форма ни скрипты не сработают.
**Атрибут `sandbox` может содержать флаги через пробел, которые убирают ограничения:**
<dl>
<dt>allow-same-origin</dt>
<dd>Браузер может не считать документ в ифрейме пришедшим с другого же домена. Если ифрейм *и так* с другого домена, то ничего не меняется.</dd>
<dt>allow-top-navigation</dt>
<dd>Разрешает ифрейму менять `parent.location`.</dd>
<dt>allow-forms</dt>
<dd>Разрешает отправлять формы из `iframe`.</dd>
<dt>allow-scripts</dt>
<dd>Разрешает выполнение скриптов из ифрейма. Но скриптам, всё же, будет запрещено открывать попапы.</dd>
</dl>
Цель атрибута `sandbox` -- наложить дополнительные ограничения. Он не может снять уже существующие, в частности, убрать ограничения безопасности, если ифрейм с другого домена.

View file

@ -1,163 +0,0 @@
# Ограничение "Same Origin"
Ограничение "Same Origin" ("тот же источник") ограничивает доступ окон и фреймов друг к другу, а также влияет на AJAX-запросы к серверу.
Причина, по которой оно существует -- безопасность. Если есть два окна, в одном из которых `vasya-pupkin.com`, а в другом `gmail.com`, то мы бы не хотели, чтобы скрипт из первого мог читать нашу почту.
Сама концепция проста, но есть много важных исключений и особенностей, которые нужно знать для полного понимания этого правила.
[cut]
## Same Origin [[#same-origin]
[summary]
Два URL считаются имеющим один источник ("same origin"), если у них одинаковый протокол, домен и порт.
[/summary]
Эти URL имеют один источник:
<ul>
<li>`http://site.com`</li>
<li>`http://site.com`/</li>
<li>`http://site.com/my/page.html`</li>
</ul>
А вот эти -- все из других источников:
<ul>
<li>http://<span style="color:red">www.</span>site.com (другой домен)</li>
<li>http://site.<span style="color:red">org</span> (другой домен)</li>
<li>http<span style="color:red">s</span>://site.com (другой протокол)</li>
<li>http://site.com<span style="color:red">:8080</span> (другой порт)</li>
</ul>
Существует ряд исключений, позволяющих-таки окнам с разных доменов обмениваться информацией, но прямой вызов методов друг друга и чтение свойств запрещены.
## Пример ограничений
Если одно окно попытается обратиться к другому, то браузер проверит, из одного ли они источника. Если нет -- доступ будет запрещён.
Например:
```html
<!--+ run -->
<iframe src="http://vk.com" name="vk" style="height:100px"></iframe>
<script>
var iframe = document.getElementsByName('vk')[0];
iframe.onload = function() {
try {
alert(iframe.contentWindow.document);
} catch(e) {
alert("Ошибка: " + e.message);
}
}
</script>
```
**При запуске примера выше Safari/Chrome могут вместо ошибки вывести `undefined`.**
Это их способ (некорректный) показать, что "чтение запрещено". Будем надеяться, исправят.
## Исключение: запись в location
Окно и вложенный в него `iframe` могут менять `location` друг друга, даже если они из разных источников.
Причём *читать* `location` нельзя, одно окно не имеет право знать, на каком URL пользователь в другом. А вот *запись* браузеры считают безопасной.
Например, открыв на `javascript.ru` ифрейм с `vk.com`, из этого ифрейма нельзя будет узнать URL, а вот поменять его -- запросто:
```html
<!--+ run -->
<iframe src="http://vk.com" name="vk"></iframe>
<script>
var iframe = document.getElementsByName('vk')[0];
iframe.onload = function () {
try { // попытка чтения
*!*
alert(iframe.contentWindow.location.href); // неудачно
*/!*
} catch(e) {
alert('Ошибка: ' + e.message);
}
try { // запись в location будет успешной
*!*
iframe.contentWindow.location = 'http://wikipedia.org';
*/!*
alert('Перенаправили на http://wikipedia.org');
}
};
</script>
```
## Исключение: поддомен 3го уровня
Ещё одно важное исключение касается доменов третьего уровня.
Например, у нас есть окно на `http://site.com` и два ифрейма: первый с источника `http://john.site.com`, а второй -- с `http://peter.site.com`.
**Если несколько окон присваивают в `document.domain` свой общий поддомен 2го уровня, то все ограничения снимаются.**
Важно:
<ol>
<li>Должен быть общий поддомен второго уровня.
Можно поставить `document.domain='site.com'` на странице с `my.site.com`, но нельзя это сделать на странице с `vaysa-pupkin.ru`.</li>
<li>Свойство `document.domain` должно быть присвоено на всех окнах, участвующих в коммуникации, в том числе на том, которое и так с этого домена.
Выглядит абсурдно, но на документе с `site.com` нужно вызвать: `document.domain=document.domain`. Тогда будет работать.</li>
</ol>
## Исключение: порт в IE
В браузере Internet Explorer порт не входит в понятие "источник" (origin).
Это означает, что окно с `http://site.com` может свободно общаться с `http://site.com:8080`.
Это иногда используют для общения разных сервисов, использующих один IP-адрес. Но допустимо такое только в IE.
## Исключение: зона в IE
Если сайт находится в зоне "Надёжные узлы", то в Internet Explorer ограничения к нему не применяются.
При этом подразумевается, что для этой зоны в параметрах "Безопасность" включена опция "Доступ к источникам данных за пределами домена".
## Общение между окнами с разных источников
Если ни одно из исключений выше не подошло, то есть три основных способа, как окнам общаться между собой:
<ol>
<li>Все современные браузеры, включая IE8+, поддерживают специальный интерфейс `postMessage` для общения между окнами с разных доменов. Мы рассмотрим его в отдельной главе [](/cross-window-messaging-with-postmessage).</li>
<li>Одно окно может поменять другому `location.hash` -- часть пути после `#`. При этом не произойдёт смены `URL`, но другое окно, увидев это, может прочитать из хэша информацию и, в свою очередь, ответить.
Этот способ испольуется там, где требуется поддержка IE7-.
</li>
</ol>
В совокупности с этим можно использовать интерфейс `localStorage` для оповещении всех окон и вкладок о событии. При сохранении данных в `localStorage` генерируется событие `onstorage`, причём сразу на всех окнах, фреймах и табах с тем же доменом.
Кроме того, если вы используете `IFRAME` вместе с AJAX, то, возможно, вам пригодятся способы, описанные в главе [](/ajax-iframe-xdomain).
## Итого
Ограничение "одного источника" запрещает окнам и фреймам с разных источников вызывать методы друг друга и читать данные друг из друга.
При этом "из одного источника" означает "совпадают протокол, домен и порт".
Как ни странно, у этого подхода ряд существенных исключений:
<ul>
<li>Свойство `window.location` нельзя читать, но можно менять.</li>
<li>Домены третьего уровня с общим наддоменом могут поменять `document.domain` на их общий домен второго уровня, и тогда они смогут взаимодействовать без ограничений.
</li>
<li>IE не включает порт в понятие источника. Кроме того, он позволяет снять ограничения для конкретного сайта включением в доверенную зону.</li>
</ul>
В современных браузерах (IE8+) кросс-доменное общение можно организовать через `location.hash` и [postMessage](/cross-window-messaging-with-postmessage). При этом IE8 не умеет передавать данные между окнами, но, если нужно, `localStorage` передаёт всё и везде :)

View file

@ -1,250 +0,0 @@
# Атака Clickjacking и защита от неё
Атака "кликджекинг" (англ. Clickjacking) позволяет хакеру выполнить клик на сайте-жертве *от имени посетителя*.
В русском языке встречается дословный перевод термина clickjacking: "угон клика". Так же применительно к clickjacking-атаке можно встретить термины "перекрытие iframe" и "подмена пользовательского интерфейса".
Кликджекингу подверглись в своё время Twitter, Facebook , PayPal, YouTube и многие другие сайты. Сейчас, конечно, они уже защищены.
[cut]
## Идея атаки
В целом идея очень проста.
Вот как выглядит «угон клика» пользователя, который зарегистрирован на facebook:
<ol>
<li>На вредоносной странице пользователю подсовывается безобидная ссылка
(скажем, что-то скачать, «разбогатеть сейчас», посмотреть ролик или просто перейти по ссылке на интересный ресурс).</li>
<li>Поверх этой заманчивой ссылки помещен прозрачный iframe со страницей facebook.com, так что кнопка "Like" находится чётко над ней.
Кликая на ссылку, посетитель на самом деле нажимает на эту кнопку.
</li>
</ol>
## Пример
Вот пример (для наглядности `iframe` -- полупрозрачный):
```html
<!--+ run height=120 -->
<style>
iframe { /* iframe с сайта-жертвы */
width: 300px;
height: 100px;
position: absolute;
top:0; left:-20px;
filter: alpha(opacity=50); /* в рельности будет opacity=50 */
opacity: 0.5;
z-index: 1;
}
</style>
<div>Нажмите, чтобы разбогатеть сейчас:</div>
<!-- URL в реальности - с другого домена (атакуемого сайта) -->
<iframe src="https://js.cx/clickjacking/facebook.html"></iframe>
<a href="http://google.com">Нажми тут!</a>
<div>..И всё получится (хе-хе, у меня, злого хакера, получится)!</div>
```
**При клике на ссылку на самом деле происходит клик на iframe (на «Like»).**
Если посетитель авторизован на facebook (а в большинстве случаев так и есть), то facebook.com получает щелчок от имени посетителя.
На Twitter это была бы кнопка «Follow».
Тот же самый код, но как это было бы в реальности, с `opacity:0` для iframe (щелкните, чтобы увидеть захваченную кнопку):
[iframe src="clickjacking" height=120 link edit]
Итак, все, что нужно для проведения атаки -- это правильно расположить iframe на вредоносной странице. В большинстве случаев это делается средствами HTML/CSS.
События клавиатуры захватить гораздо труднее, потому что, если `iframe` является невидимым, то текст в поле ввода также будет невидимым. Посетитель начнёт печатать, но, не увидев текст, прекратит свои действия.
## Защита и способы обхода.
Самый старый метод защиты -- это код JavaScript, не позволяющий отобразить веб-страницу внутри фрейма (*framebusting*, также его называют *framekilling* и *framebreaking*).
Добавьте к документу следующий код, если не хотите, чтобы документ загружался в iframe:
<script>
if (top != window) {
top.location = window.location;
}
</script>
То есть, если окно обнаруживает, что оно загружено во фрейме, то оно автоматически делает себя верхним.
В настоящий момент это уже не является сколько-нибудь надежной защитой.
Есть несколько способов обхода framebusting. Давайте рассмотрим некоторые из них.
### Блокировка top-навигации.
Можно заблокировать переход, инициированный сменой `top.location`, в событии [onbeforeunload](#window.onbeforeunload).
Обработчик этого события ставится на внешней (хакерской) странице и, при попытке `iframe` поменять `top.location`, спросит посетителя, хочет он покинуть данную страницу. В большинстве браузеров хакер может спросить посетителя, используя своё сообщение.
Так что, скорее всего, посетитель ответит на такой странный вопрос отрицательно (он же не знает про ифрейм, видит только страницу, причины для ухода нет).
В приведенном ниже примере представлен "защищенный" `iframe`:
```html
<!--+ src="cj_location.html" play -->
```
А вот -- хакерская страница с этим `iframe`, которая отменяет перезагрузку верхнего окна при помощи `onbeforeunload` (предполагается, что посетитель нажмёт «отмену»):
```html
<script>
window.onbeforeunload = function() {
window.onbeforeunload = null;
return "Хотите уйти с этой страницы, не узнав все её тайны (хе-хе)?";
}
</script>
<iframe src="/files/tutorial/window/cj_location.html" style="height:80px"></iframe>
```
[Открыть в отдельном окне (сразу спросит)](/files/tutorial/window/cj_location_hack.html).
### security = "restricted"
В IE8 есть особый атрибут `security = "restricted"` -- отключающий исполнение скриптов во фрейме.
Пример использования:
```html
<iframe security="restricted" src="/files/tutorial/window/cj_location.html" style="height:80px"></iframe>
```
При этом клик во фрейме, если он не требует JavaScript, всё равно сработает, а вот защита -- нет.
### HTML5
Одна из возможностей HTML5 -- атрибут [sandbox](http://www.whatwg.org/specs/web-apps/current-work/multipage/the-iframe-element.html#attr-iframe-sandbox)
Он позволяет разрешить во фрейме скрипты `allow-scripts` и формы `allow-forms`, но запретить top-навигацию (не указать `allow-top-navigation`).
Выглядеть это будет так:
```html
<iframe *!*sandbox="allow-scripts allow-forms"*/!* src="/files/tutorial/window/cj_location.html"></iframe>
```
Сработает в браузерах, которые поддерживают `sandbox`, например -- в Chrome.
### Ещё приёмы
Есть и еще приёмы для обхода этой простейшей защиты.
Firefox и старый IE могут активировать designMode на исходной странице, это также предотвращает framebusting (спасибо за идею owasp.org, страница clickjacking).
Как мы видим, она не только не выдерживает реальной атаки, но и может скомпрометировать сайт (программист-то думает, что защитил его).
## Надежная защита от Clickjacking
### Заголовок X-Frame-Options
Все современные браузеры поддерживают заголовок `X-Frame-Options`.
Он разрешает или запрещает отображение страницы, если она открыта во фрейме.
Браузеры игнорируют заголовок, если он определен в МЕТА тег. Таким образом, `<meta http-equiv="X-Frame-Options"...>` будет проигнорирован.
У заголовка может быть три значения:
<dl>
<dt>SAMEORIGIN</dt>
<dd>Рендеринг документа, при открытии во фрейме, производится только в том случае, когда верхний (top) документ -- с того же домена.</dd>
<dt>DENY</dt>
<dd>Рендеринг документа внутри фрейма запрещён.</dd>
<dt>ALLOW-FROM domain</dt>
<dd>Разрешает рендеринг, если внешний документ с данного домена (не поддерживается в Safari, Firefox).</dd>
</dl>
К примеру, Twitter использует `X-Frame-Options: SAMEORIGIN`. Результат:
```html
<iframe src="http://twitter.com"></iframe>
```
<iframe src="http://twitter.com"></iframe>
В зависимости от браузера, `iframe` выше либо пустой, либо в нём находится сообщение о невозможности отобразить его (IE).
### Приостановка показа документа
Если нужно поддерживать старые браузеры (IE7-), то можно и просто отменить показ документа:
```html
<head>
<style> body { display : none } </style>
</head>
<body>
<script>
if (self == top) {
document.getElementsByTagName('body').style.display = "block";
} else {
top.location = self.location;
}
</script>
```
[warn header="Почему не `document.body`?"]
В приведенном выше примере, мы используем `document.getElementsByTagName`, вместо `document.body`, потому что это способ получения `BODY` работает во всех браузерах, когда документ еще не готов.
[/warn]
Обратите внимание -- изначально документ скрыт. Мало ли, вдруг JavaScript во фрейме отключен -- защита сработает.
Он будет показан только в том случае, если заведомо всё в порядке: не отключён JavaScript и страница не во фрейме.
### Показ с отключённым функционалом
Заголовок `X-Frame-Options` имеет неприятный побочный эффект. Иногда поисковики, анонимайзеры или другие сайты хотели бы отобразить страницу в `iframe`, по вполне "легальным" причинам, но не могут.
Хорошо бы показывать их посетителям не пустой `iframe`, а нечто, что может быть более интересно.
Например, можно изначально "накрывать" документ `div` с `height:100%;width:100%`, который будет перехватывать все клики. И поставить на нём ссылку, ведующую на страницу в новом окне.
```html
<style>
#iframe-protector {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 99999999;
}
</style>
<div id="iframe-protector">
<a href="/" target="_blank">Перейти на сайт</a>
</div>
<script>
if (top.document.domain == document.domain) {
убрать iframe-protector
}
</script>
```
Если страница -- не во фрейме или домен совпадает, то посетитель не увидит его.
## Заключение
Атаку "Clickjacking" легко осуществить, если на сайте есть действие, активируемое с помощью одного клика.
Злоумышленник может осуществить атаку через целенаправленно на посетителей ресурса -- опубликовав ссылку на форуме, или «счастливой рассылкой». Существует масса вариантов.
<ul>
<li>Рекомендуется использовать X-Frame-Options на страницах, заведомо не предназначеных для запуска во фрейме и на важнейших страницах (финансовые транзакции).</li>
<li>Используйте защиту через framebusting для защиты IE7-, т.к. там не поддерживается X-Frame-Options.</li>
<li>Используйте перекрывающий DIV, если это допустимо вашим проектом и вы хотите разрешить безопасный показ документа во фреймах с любых доменов.</li>
</ul>
<div style="text-align:right; font-style:italic">При участии Марата Шагиева</div>

View file

@ -1,14 +0,0 @@
<!DOCTYPE HTML>
<html>
<body>
<div>Меняет top.location на google.com</div>
<script>
top.location = 'http://google.com';
</script>
<input type="button" value="Like" onclick="alert('Кнопка сработала')">
</body>
</html>

View file

@ -1,25 +0,0 @@
<!DOCTYPE HTML>
<html>
<body>
<style>
iframe { /* iframe с сайта-жертвы */
width:300px;
height:100px;
position:absolute;
top:0; left:-20px;
filter:alpha(opacity=0);
opacity:0;
z-index: 1;
}
</style>
<div>Нажмите, чтобы разбогатеть сейчас:</div>
<iframe src="/files/tutorial/window/facebook.html"></iframe>
<a href="http://google.com">Нажми тут!</a>
<div>..И ваша жизнь удалась!</div>
</body>
</html>

View file

@ -1,9 +1,5 @@
# Регулярные выражения [в работе]
Регулярные выражения -- мощный способ поиска и замены строк, который используется в самых разных языках, включая, конечно, JavaScript.
Регулярные выражения -- мощный способ поиска и замены для строк.
У меня здесь для вас две новости.
<ol>
<li>Первая плохая -- в JavaScript они на редкость убогие, хуже большинства существующих языков.</li>
<li>Вторая хорошая -- зато нам будет проще их изучить.</li>
</ol>
В JavaScript они поддерживаются в простом варианте, менее мощном, чем в большинстве других языков. Но зато нам будет проще их изучить.