Практическое применение
HTML5 в Я.Почте
Алексей Андросов
Старший разработчик интерфейсов
Будущее? Реальность!
3
Как мы тестируем новые
технологии?




4
Перед внедрением

1. После реализации новой фичи в каком-либо
   браузере смотрим где она может быть
   полезна в Почте
2. Изучаем фичу на живом прототипе, смотрим на
   границы возможностей, проблемы и баги
3. Принимаем решение о внедрении




6
После внедрения
    function errorLog(msg) {
      new Image().src = '/monitoring.txt?' +
        'msg=' + encodeURIComponent(msg)
    }

    window.onerror =function(msg, url, line){
      errorLog(msg, url, line)
    }

    try {
      // опасное или непонятное место
    } catch(e) {
      errorLog(e)
    }
7
После внедрения




8
WebSocket/SSE,
или как работает
автообновление ящика в
Я.Почте


9
Автообновление почты
Задача:
— Поддерживать ящик в актуальном состоянии без
  перезагрузки страницы
— Надо ходить за данными в другой домен




10
Автообновление почты
Решение:
— WebSocket
— Server-Sent Events
— flash-websocket
— В качестве запасного варианта iframe + long-
  polling + postMessage




11
Что передаётся?
 {
     "operation": "тип операции",
     "data": "xml-данные",
     "lcn": "номер ревизии",
     "connection_id": "id соединения"
 }

— lcn нужен, чтобы понимать, не пропустили ли мы
  сообщений, например, при разрыве соединения
— connecton_id нужен, чтобы понимать, откуда
  произошло изменение




12
Что получилось?
— Простая реализация поддержки ящика в
  актуальном состоянии на современных
  клиентских технологиях
— Постоянное соединение с сервером, а значит —
  минимальные задержки в доставке




13
WebSocket




14
WebSocket
— Постоянное соединение
— Честный двухсторонний обмен данными без
  лишних телодвижений
— Не учитывается в ограничениях на количество
  одновременных подключений, т.к. другой
  протокол
— Возможность кроссдоменных запросов
— Chrome 4+, Firefox 6+, IE 10, Safari 5+




15
Автообновление почты
Недостатки
— Протокол и JS API до сих пор не утверждены как
  стандарт
— В JS API нельзя получить заголовки ответа.
  Например, вместо 403 надо отвечать
  специальным сообщением
— При потере интернета не всегда закрывается
  соединение. Нужны ping-pong события для
  проверки соединения
— Надо поддерживать много версий протокола: 75,
  76 (до принятия в IETF), IETF 07-17


16
WebSocket
FAIL
— В ноябре 2010 года были опубликованы
  исследования протокола. Из-за багов в
  реализации прозрачных прокси-серверов с
  помощью протокола можно подменять кэш
  сервера
— Реализован в Fx 4+ и Opera 11+. По-умолчанию
  выключен. В Fx 6+ включен по-умолчанию
  (MozWebSocket)
— Блокируется некоторыми антивирусами
  (например, Avast)


17
Server-Sent Events
(EventSource)




18
Server-Sent Events

— Одностороннее постоянное соединение
  server→client
— Простейший text-based протокол поверх HTTP
— Обработчики onopen, onmessage, onerror
— Умеет сам переустанавливать соединение
— Поддержка: Chrome 6+, Fx 6+, Opera 11+, Safari 5+




19
Server-Sent Events
 HTTP/1.1 200 OK
 Cache-Control: no-cache
 Content-Type: text/event-stream

 : this is commentnn

 data: some textnn

 data: another messagen
 data: with two linesnn

 event: named_eventn
 data: yet another messagenn

20
Server-Sent Events
Проблемы:
— Стандарт находится в статусе «черновик»
— Нет поддержки CORS
— Если сервер не ответил HTTP 200 или Content-
  type: text/event-stream → onerror без
  дополнительных сведений
— В Fx и Opera onopen случается не сразу, надо
  отправить фейковые данные. Можно, например,
  комментарий
— Надо следить за readyState, чтобы отличить
  переподключение от закрытия соединения

21
Server-Sent Events
Проблемы:
— Именованное событие приходит в обработчик
  sse.addEventListener('event_name', cb)
— Если имени нет, пустое или 'message', то
  приходит в sse.addEventListener('message', cb)
— Как получать все именованные события в один
  обработчик - непонятно




22
Cross-site XHR + localStorage,
 или как грузится Я.Почта



23
Кэширование статики
Проблема
— Для инициализации надо загрузить 1Мб (gzip -
  350кб) статики (css+js+xsl)
— Браузерное кэширование работает, но возникают
  задержи, т. к. браузер пытается понять, надо ли
  обновить файл
— Браузерный кэш может вытесниться другими
  файлами




24
Кэширование статики
Решение
— Выносим логику инвалидации кэша на сторону
  JavaScript
— Для этого нужны кроссдоменный XHR, чтобы
  получить содержимое файлов, и localStorage,
  чтобы их хранить




25
Кэширование статики
Получаем
— Данные из localStorage не удалятся, пока мы этого
  не захотим или пользователь их не очистит
— Нет задержек при обращении к кэшу
— Разгружаем статический кластер




26
localStorage




27
localStorage
— Локальное постоянное key-value хранилище
  текстовых данных
— Простое API: getItem(), setItem(), key(), clear()
— Chrome 4+, Firefox 3.5+, IE8+, Opera 10.5+, Safari
  4+.




28
localStorage
Главное правило




29
localStorage
Главное правило

 Все обращения к методам и
 свойствам надо оборачивать
         в try-catch!


     github.com/doochik/SafeLS
30
localStorage
… а в IPad
 try {
     ls.setItem(key, data);
 } catch(e) {
     ls.removeItem(key);
     ls.setItem(key, data)
 }




31
localStorage
Ограничения на домен
 A mostly arbitrary limit of fve megabytes per origin is
recommended.
     dev.w3.org/html5/webstorage/#disk-space


          Все браузеры следуют этой
             рекомендации, но…



32
localStorage
Ограничения на домен
— Chrome, Safari используют SQLite и UTF-16,
  поэтому записать можно ~2,5 млн. символов
— Fx, — SQLite + UTF-8, ~5 млн. символов
— IE — xml, ~5 млн. символов
— Opera — xml + base64, ~2 млн. символов,
  появляется сообщение с просьбой увеличить
  размер


— Если сделать hosted app для Chrome, то можно
  получить разрешение на безлимитный storage :)
33
localStorage
Глобальные ограничения
 User agents should guard against sites storing data
under the origins other afliated sites, e.g. storing up to
the limit in a1.example.com, a2.example.com, etc,
circumventing the main example.com storage limit.


    dev.w3.org/html5/webstorage/#disk-space
— Chrome, Safari — нет
— Fx, IE ~5 млн. символов на домен 2-го уровня
— Opera — ~35 млн. символов на всё


34
Кроссдоменный XHR




35
Cross-site XHR with CORS
— Обычный XMLHtpRequest, но с возможностью
  делать запросы в другой домен
— В ответе сервера должен появиться HTTP-
  заголовок «Access-Control-Allow-Origin: *»
— Chrome 3+, Firefox 3.5+, IE8+ (XDomainRequest),
  Safari 4+

 <!–- Определение поддержки -->
  !!(window['XDomainRequest'] ||
  (window['XMLHttpRequest'] && 'withCredentials' in new
 XMLHttpRequest()));




36
Cross-site XHR with CORS
Проблемы
— Не все прокси-сервера пропускают заголовки
  Access-Control-Allow-Origin

 xdr['onerror'] = function(){
    // fallback до обычный системы загрузки
 }

— XdomainRequest в IE не является полноценной
  заменой XHR, имеет ограниченный функционал
  (htp://clck.ru/QBRe)
— В IE9 в режиме IE9 виснут запросы
 xdr['onprogress'] = function() {};


37
postMessage




38
postMessage
— Асинхронный обмен данными между разными
  window и origin (IE8 синхронный sic!)
— Поддержка в Chrome 4+, Firefox 3+, IE8+, Opera
  9.5+, Safari 4+
— В Chrome, Fx 6+, Opera 10.5+, Safari можно
  отправлять объекты, в остальных — только строки
— Быстрее, чем window.setTimeout(cb, 0)




39
HTML5 data-* атрибуты




40
data-*
 <div
     data-name="alexey"
     data-ids="2080000000089839302">
        <!--- ... --->
 </div>

— element.dataset['name'] в 10 раз медленнее
  element.getAtribute('data-name')
— $('div').data('ids') === 2080000000089839400.
  Потому что слишком большое число :)


41
online/offline events




42
online/offline
— Браузер может говорить, что поддерживает
  события, а событий отсылать не будет
— Браузер может говорить, что поддерживает
  события, а события отправит только при ручном
  переключении в «Автономный режим»
— События могут быть в window и document
— Нужно полить свойство navigator.onLine




44
online/offline
— Если ofine, значит нет ни одного активного
  сетевого соединения
— Если online, есть хотя бы одно любое активное
  сетевое соединение. Это не означает, что есть
  доступ в интернет или до нужного ресурса
— Надо проверять реальный доступ до нужного
  ресурса (ajax, img, ...)




46
Cache manifest




47
Cache manifest
— Нужен для ofine-кэширования веб-приложения
— Можно рассказать браузерам, какие именно
  файлы можно закэшировать, а за какими надо
  всегда ходить на сайт
— Обновили manifest — значит, вышла новая версия
— Все бы хорошо, но … Firefox до 4 версии не
  реагирует на обновление manifest


— дальше можно не тестировать :)


48
А если серьезно, то...
— После обновления версии надо перезагружать
  страницу
— Кэшируется страница, в которой указан manifest.
  А у нас там логин! Но это можно обойти с
  помощью Cache-Control: no-store
— В Firefox есть предупреждение о том, что сайт
  собирается что-то кэшировать
— Fallback-секция не работает




49
Вопросы?




50
Алексей Андросов
Старший разработчик интерфейсов

aandrosov@yandex-team.ru
@doochik

Практическое применение HTML5 в Я.Почте

  • 2.
    Практическое применение HTML5 вЯ.Почте Алексей Андросов Старший разработчик интерфейсов
  • 3.
  • 4.
    Как мы тестируемновые технологии? 4
  • 6.
    Перед внедрением 1. Послереализации новой фичи в каком-либо браузере смотрим где она может быть полезна в Почте 2. Изучаем фичу на живом прототипе, смотрим на границы возможностей, проблемы и баги 3. Принимаем решение о внедрении 6
  • 7.
    После внедрения function errorLog(msg) { new Image().src = '/monitoring.txt?' + 'msg=' + encodeURIComponent(msg) } window.onerror =function(msg, url, line){ errorLog(msg, url, line) } try { // опасное или непонятное место } catch(e) { errorLog(e) } 7
  • 8.
  • 9.
  • 10.
    Автообновление почты Задача: — Поддерживатьящик в актуальном состоянии без перезагрузки страницы — Надо ходить за данными в другой домен 10
  • 11.
    Автообновление почты Решение: — WebSocket —Server-Sent Events — flash-websocket — В качестве запасного варианта iframe + long- polling + postMessage 11
  • 12.
    Что передаётся? { "operation": "тип операции", "data": "xml-данные", "lcn": "номер ревизии", "connection_id": "id соединения" } — lcn нужен, чтобы понимать, не пропустили ли мы сообщений, например, при разрыве соединения — connecton_id нужен, чтобы понимать, откуда произошло изменение 12
  • 13.
    Что получилось? — Простаяреализация поддержки ящика в актуальном состоянии на современных клиентских технологиях — Постоянное соединение с сервером, а значит — минимальные задержки в доставке 13
  • 14.
  • 15.
    WebSocket — Постоянное соединение —Честный двухсторонний обмен данными без лишних телодвижений — Не учитывается в ограничениях на количество одновременных подключений, т.к. другой протокол — Возможность кроссдоменных запросов — Chrome 4+, Firefox 6+, IE 10, Safari 5+ 15
  • 16.
    Автообновление почты Недостатки — Протоколи JS API до сих пор не утверждены как стандарт — В JS API нельзя получить заголовки ответа. Например, вместо 403 надо отвечать специальным сообщением — При потере интернета не всегда закрывается соединение. Нужны ping-pong события для проверки соединения — Надо поддерживать много версий протокола: 75, 76 (до принятия в IETF), IETF 07-17 16
  • 17.
    WebSocket FAIL — В ноябре2010 года были опубликованы исследования протокола. Из-за багов в реализации прозрачных прокси-серверов с помощью протокола можно подменять кэш сервера — Реализован в Fx 4+ и Opera 11+. По-умолчанию выключен. В Fx 6+ включен по-умолчанию (MozWebSocket) — Блокируется некоторыми антивирусами (например, Avast) 17
  • 18.
  • 19.
    Server-Sent Events — Одностороннеепостоянное соединение server→client — Простейший text-based протокол поверх HTTP — Обработчики onopen, onmessage, onerror — Умеет сам переустанавливать соединение — Поддержка: Chrome 6+, Fx 6+, Opera 11+, Safari 5+ 19
  • 20.
    Server-Sent Events HTTP/1.1200 OK Cache-Control: no-cache Content-Type: text/event-stream : this is commentnn data: some textnn data: another messagen data: with two linesnn event: named_eventn data: yet another messagenn 20
  • 21.
    Server-Sent Events Проблемы: — Стандартнаходится в статусе «черновик» — Нет поддержки CORS — Если сервер не ответил HTTP 200 или Content- type: text/event-stream → onerror без дополнительных сведений — В Fx и Opera onopen случается не сразу, надо отправить фейковые данные. Можно, например, комментарий — Надо следить за readyState, чтобы отличить переподключение от закрытия соединения 21
  • 22.
    Server-Sent Events Проблемы: — Именованноесобытие приходит в обработчик sse.addEventListener('event_name', cb) — Если имени нет, пустое или 'message', то приходит в sse.addEventListener('message', cb) — Как получать все именованные события в один обработчик - непонятно 22
  • 23.
    Cross-site XHR +localStorage, или как грузится Я.Почта 23
  • 24.
    Кэширование статики Проблема — Дляинициализации надо загрузить 1Мб (gzip - 350кб) статики (css+js+xsl) — Браузерное кэширование работает, но возникают задержи, т. к. браузер пытается понять, надо ли обновить файл — Браузерный кэш может вытесниться другими файлами 24
  • 25.
    Кэширование статики Решение — Выносимлогику инвалидации кэша на сторону JavaScript — Для этого нужны кроссдоменный XHR, чтобы получить содержимое файлов, и localStorage, чтобы их хранить 25
  • 26.
    Кэширование статики Получаем — Данныеиз localStorage не удалятся, пока мы этого не захотим или пользователь их не очистит — Нет задержек при обращении к кэшу — Разгружаем статический кластер 26
  • 27.
  • 28.
    localStorage — Локальное постоянноеkey-value хранилище текстовых данных — Простое API: getItem(), setItem(), key(), clear() — Chrome 4+, Firefox 3.5+, IE8+, Opera 10.5+, Safari 4+. 28
  • 29.
  • 30.
    localStorage Главное правило Всеобращения к методам и свойствам надо оборачивать в try-catch! github.com/doochik/SafeLS 30
  • 31.
    localStorage … а вIPad try { ls.setItem(key, data); } catch(e) { ls.removeItem(key); ls.setItem(key, data) } 31
  • 32.
    localStorage Ограничения на домен A mostly arbitrary limit of fve megabytes per origin is recommended. dev.w3.org/html5/webstorage/#disk-space Все браузеры следуют этой рекомендации, но… 32
  • 33.
    localStorage Ограничения на домен —Chrome, Safari используют SQLite и UTF-16, поэтому записать можно ~2,5 млн. символов — Fx, — SQLite + UTF-8, ~5 млн. символов — IE — xml, ~5 млн. символов — Opera — xml + base64, ~2 млн. символов, появляется сообщение с просьбой увеличить размер — Если сделать hosted app для Chrome, то можно получить разрешение на безлимитный storage :) 33
  • 34.
    localStorage Глобальные ограничения Useragents should guard against sites storing data under the origins other afliated sites, e.g. storing up to the limit in a1.example.com, a2.example.com, etc, circumventing the main example.com storage limit. dev.w3.org/html5/webstorage/#disk-space — Chrome, Safari — нет — Fx, IE ~5 млн. символов на домен 2-го уровня — Opera — ~35 млн. символов на всё 34
  • 35.
  • 36.
    Cross-site XHR withCORS — Обычный XMLHtpRequest, но с возможностью делать запросы в другой домен — В ответе сервера должен появиться HTTP- заголовок «Access-Control-Allow-Origin: *» — Chrome 3+, Firefox 3.5+, IE8+ (XDomainRequest), Safari 4+ <!–- Определение поддержки --> !!(window['XDomainRequest'] || (window['XMLHttpRequest'] && 'withCredentials' in new XMLHttpRequest())); 36
  • 37.
    Cross-site XHR withCORS Проблемы — Не все прокси-сервера пропускают заголовки Access-Control-Allow-Origin xdr['onerror'] = function(){ // fallback до обычный системы загрузки } — XdomainRequest в IE не является полноценной заменой XHR, имеет ограниченный функционал (htp://clck.ru/QBRe) — В IE9 в режиме IE9 виснут запросы xdr['onprogress'] = function() {}; 37
  • 38.
  • 39.
    postMessage — Асинхронный обменданными между разными window и origin (IE8 синхронный sic!) — Поддержка в Chrome 4+, Firefox 3+, IE8+, Opera 9.5+, Safari 4+ — В Chrome, Fx 6+, Opera 10.5+, Safari можно отправлять объекты, в остальных — только строки — Быстрее, чем window.setTimeout(cb, 0) 39
  • 40.
  • 41.
    data-* <div data-name="alexey" data-ids="2080000000089839302"> <!--- ... ---> </div> — element.dataset['name'] в 10 раз медленнее element.getAtribute('data-name') — $('div').data('ids') === 2080000000089839400. Потому что слишком большое число :) 41
  • 42.
  • 44.
    online/offline — Браузер можетговорить, что поддерживает события, а событий отсылать не будет — Браузер может говорить, что поддерживает события, а события отправит только при ручном переключении в «Автономный режим» — События могут быть в window и document — Нужно полить свойство navigator.onLine 44
  • 46.
    online/offline — Если ofine,значит нет ни одного активного сетевого соединения — Если online, есть хотя бы одно любое активное сетевое соединение. Это не означает, что есть доступ в интернет или до нужного ресурса — Надо проверять реальный доступ до нужного ресурса (ajax, img, ...) 46
  • 47.
  • 48.
    Cache manifest — Нужендля ofine-кэширования веб-приложения — Можно рассказать браузерам, какие именно файлы можно закэшировать, а за какими надо всегда ходить на сайт — Обновили manifest — значит, вышла новая версия — Все бы хорошо, но … Firefox до 4 версии не реагирует на обновление manifest — дальше можно не тестировать :) 48
  • 49.
    А если серьезно,то... — После обновления версии надо перезагружать страницу — Кэшируется страница, в которой указан manifest. А у нас там логин! Но это можно обойти с помощью Cache-Control: no-store — В Firefox есть предупреждение о том, что сайт собирается что-то кэшировать — Fallback-секция не работает 49
  • 50.
  • 51.
    Алексей Андросов Старший разработчикинтерфейсов aandrosov@yandex-team.ru @doochik