[Перевод] Опенсорсный набор утилит PayPal для кроссдоменного взаимодействия

Автор материала, разработчик из PayPal, описывает набор утилит для кроссдоменного взаимодействия.

59ef6004210be159039868.png

Мы в PayPal пишем много яваскриптового кода, который в конечном счете начинает работать на других веб-сайтах и других доменах. Яркий пример — checkout.js, интеграционный скрипт, полностью охватывающий процесс оформления и оплаты заказа с помощью нашего сервиса, позволяющий мерчантам очень просто и непринужденно встраивать нашу кнопку и механизм оформления заказа в свои сайты.

Запуск нашего кода на сторонних сайтах сопряжен с огромным количество рисков и подводных камней, но есть некоторые ключевые моменты:

  • Мы совершенно точно никак не можем сломать какие-либо сайты. Это означает, что мы никак не модифицируем глобальные переменные, которых мы не создавали, включая полизаполнения для браузерных API. Мы не делаем этого, поскольку такой подход почти всегда делает отслеживание багов практически невозможным.
  • А вот сайты очень даже могут нас сломать, что они делают довольно часто. Один из недавних примеров: сайт перезаписал Array.prototype.toJSON что привело к поломке JSON.stringify. Многие веб-сайты включают полизаполнение WeakMap, которое в отличие от ее нативной версии не работает на междоменных оконных объектах. Поэтому без дополнительных предварительных проверок мы можем доверять только собственному коду.
  • Междоменные ограничения (описывающие что можно и нельзя делать с айфреймом или всплывающим окном, содержащими контент другого домена внутри) — предмет до конца неясный, и правила, к тому же, довольно часто меняются в зависимости от того, в каком браузере вы находитесь. Например в IE и Edge вы в буквальном смысле не можете отправить сообщение между всплывающим и родительским окнами если у них отличаются домены. Сделать это можно лишь прибегнув к унизительным хакам.
  • Некоторые браузеры выдают реально странные вещи. Например, единственный действенный метод проверки домен окна — выполнение try/catch с целью получения доступа или задания свойства оконного объекта. Но даже с учетом этого Safari настаивает на том, чтобы в консоли для этого действия появлялась соответствующая ошибка происхождения. Эта проблема превратилась в генератор неразберихи: наши мерчанты писали баг репорты, а мы говорили им что эту ошибку нужно игнорировать.
  • Ограничения, задуманные в качестве средства борьбы с навязчивой рекламой, часто мешают нормальной работе нашего кода на других сайтах. К примеру, открыть всплывающее окно возможно только в тот же самый момент, когда пользователь кликает на нашу кнопку. Попробуй мы сделать какое-нибудь асинхронное взаимодействие, нам конец: браузер заблокирует всплывающее окно, даже несмотря на то, что его появление формально было результатом действия пользователя. Вдобавок к этому, ограничения по сторонним кукам, задуманные чтобы предотвратить сбор информации по рекламе, почти всегда препятствуют нашим попыткам отслеживать сессии пользователей в айфреймах.


В прошлом году мы решили сосредоточиться на том, чтобы собрать вместе по-настоящему мощный набор инструментов, помогающий избегать подобных ловушек и создавать отличный пользовательский опыт без необходимости постоянно беспокоиться о том, доходят ли сообщения и все ли окна отрисовываются.

Мы открыли весь исходный код набора. Если вы желаете создать взаимодействия, которые работают с вашей системой с других сайтов или просто между разными доменами внутри вашей организации, то поиски заканчиваются здесь.

grumbler


image

Сначала о главном. Мы хотели создать толковую заготовку, на базе которой работали бы все запланированные нами инструменты. У нас есть формальный список технологий, которые нам нравятся, но объединить их в некое качественное целое задача не из простых. Мы решили максимально упростить себе работу в будущем и потому всякий раз, когда мы хотим опубликовать новую кросс доменную библиотеку или инструмент, мы форкаем grumbler и начинаем кодить, не беспокоясь о предварительной настройки какой-либо среды или инструментов.

→ Подробнее по ссылке

post-robot


image

Первой настоящей технической проблемой, которую мы хотели решить был обмен сообщениями между окнами и айфреймами. Думаете можно просто взять window.postMessage и начать сыпать сообщениями направо и налево? Подумайте еще раз:

  • Нет никаких ответов или обратных вызовов, что превращает запрос данных из другого окна или получение данных из него в ответ в весьма хитрое дело.
  • IE/Edge не позволяют вам пользоваться postMessage между всплывающим и родительским окном если их домены отличаются.
  • Нет почти никакой обработки ошибок. Как, впрочем, и способа определения того, что другое окно вообще получило ваше сообщение.
  • Нельзя отправлять более интересные типы данных, такие, например, как функции, промисы, объекты ошибок и другие.


post-robot призван решить все эти проблемы одним махом, предоставляя стабильный и надежный способ отправки сообщений и получения ответов.

→ Подробнее по ссылке

xcomponent


image

Мы любим пользоваться React, и нам нравится великолепная в своей простоте идея компонентов, работающих по принципу «данные вниз, действия вверх». При этом концепция React нацелена на рендеринг пользовательского интерфейса непосредственно на вашей странице, без каких-либо фреймов или междоменных ограничений. Но есть одно существенное «но»: мы бы никогда не стали даже пытаться загрузить React на сайте, который нам не принадлежит.

Мы спросили себя:, а что если включить айфреймы и всплывающие окна в React-подобные компоненты, которым мы могли бы напрямую передавать свойства и обратные вызовы и позволить дочерним окнам напрямую вызывать функции, переданные родителем?

Айфреймы, как правило, для этого не подходят. То есть в обычных ситуациях с помощью window.postMessage вы можете только отправлять простые объекты и строки вниз айфрейму и обратно вверх его родителю. Но с xcomponent у вас появляется возможность отрисовывать айфрейм вообще без необходимости настраивать какие-либо обработчики сообщений: вы будете просто передавать данные и функции напрямую в айфрейм, и он будет получать напрямую любые свойства, которые вы передадите ему в объекте window.xprops.

Кроме того, он автоматически настраивает привязки к популярным фреймворкам, таким как React, Angular, Vue и другим, что позволяет вам отрисовывать айфреймы в своем приложении нативным способом без какой-либо необходимости беспокоиться о том, насколько ужасно обычно ведут себя айфреймы.

→ Подробнее по ссылке

→ А вот так мы пользуемся xcomponent в PayPal

xcomponent-demo


image

Руководствуясь теми же соображениями, что и в случае с grumbler«ом, мы хотели максимально упростить работу над компонентами xcomponent, сделав так, чтобы настройка среды и инструментов не замедляла работу. Все любят, когда можно просто начать кодить.

Поэтому мы выпустили xcomponent-demo — шаблон, который вы можете форкнуть и начать работать. В нем есть все что нужно для компоновки, тестирования, издания и даже статической типизации вашего кода и публикации x-компонентов для общего использования.

cross-domain-utils


image

Работа с междоменными окнами — сложная штука. Возникает много вопросов. Например, как узнать наверняка, что окно другого домена закрыто? Что его домен совпадает с вашим? Какие у него дочерние элементы? Браузеры предоставляют простые API для некоторых из этих задач, но в каждом браузере все получается по своему.

С cross-domain-utils мы попытались создать библиотеку вспомогательных методов, призванных помочь в работе с распространенными кейсами, упрощающих работу с междоменными окнами и избавляющих вас от необходимости постоянно беспокоиться о получении ошибок или предупреждений в консоли. Инструмент, в числе прочего, позволяет избежать реально странных экстремальных кейсов, наподобие вот такого:

59ef5cdad8b24430736271.png

Edge, как всегда, в своем репертуаре.

cross-domain-safe-weakmap


image

Хранение оконных объектов — процесс затратный с точки зрения оперативной памяти, даже если окно, референс которого вы храните, закрыто.

WeakMaps идеально подходит для хранения данных об окнах, поскольку:

  • Они не хранят референс оконного объекта, когда им перестают пользоваться где-либо еще.
  • Задать свойства междоменного окна напрямую нельзя, поэтому ассоциация данных с окном (например, коллбэк, который нужно вызвать когда окно закрыто) реализуется с трудом. Применение WeakMap упрощает этот процесс. Для этого достаточно просто воспользоваться окном как weakmap-ключом, и тогда можно хранить любые данные, какие только захотите.


Единственная проблема заключается в том, что многие существующие WeakMap прокладки пытаются вызвать Object.defineProperty по ключу, что сразу же приводит к неудаче в случаях, когда ключ представляет собой междоменное окно с ограниченным доступом.

Cross-domain-safe-weakmap, напротив, разработана так, чтобы сделать работу с междоменными окнами с помощью их ключей и нативной реализации WeakMap (если она доступна) максимально предсказуемой и надежной.

zalgo-promise


image

Многие браузеры просто лишают приоритета все, что отправляется в setTimeout, если вкладка браузера, в которой отрабатывает ваш код находится не в фокусе. Поэтому когда вы работаете над чем-то, что должно работать во всплывающем окне, и вам при этом нужно отправить сообщение родителю и запросить его что-либо сделать… В таком случае любой setTimeout конкретно затормозит работу родителя.

Поэтому выход простой: отказываемся от setTimeout для любых критичных путей выполнения кода, верно?

Проблема в том, что многие из существующих промежуточных промис-оболочек в отсутствие помощников вроде setImmediate прибегают к использованию setTimeout, чтобы гарантировать, что переданная .then функция вызывается асинхронно. Поэтому если вы хотите воспользоваться промисами в самых разных браузерах и делать при этом так, чтобы код выполнялся в окнах без фокус, тогда вам немного не повезло.

zalgo-promise пытается решить эту проблему путем перевода всех промисов в синхронный режим по умолчанию. Это значит, что если они будут разрешены синхронно, то передаваемая .then() функция будет также вызвана синхронно. Она называется zalgo-promise потому что мы в буквальном смысле решили выпустить zalgo (после тщательного рассмотрения всех за и против).

→ Подробнее по ссылке

beaver-logger


image

Один из важных моментов, связанных с запуском кода на других доменах, заключается в том, что вам необходимо знать, что происходит и как люди пользуются вашими продуктами. И уж по крайней мере вам нужно знать о тех случаях, когда код вызывает какие-либо ошибочные сценарии или ломает чей-то сайт.

С другой стороны, нет никакой необходимости отправлять один сигнал за другим или писать в журнал сотню запросов подряд для каждого события, для которого вы хотели зафиксировать.

beaver-logger пытается решить эту проблему путем отбора и компоновки логов и периодического их сбрасывания на ваш сервер. В его комплект даже входит серверный компонент узла для получения логов, но вы можете свободно приспосабливать его как хотите для любой любимой платформы.

fetch-robot


image

CORS — головная боль. Получение корректных заголовков требует настройки на сервере каждой отдельной конечной точки. А если речь идет не о GET-запросе, то CORS заставляет делать дополнительные операции обмена, чтобы определить, безопасно ли делать тот или иной конечный запрос.

fetch-robot — новый экспериментальный модуль, нацеленный на решение этой проблемы. Он позволяет вам публиковать в одном месте манифест, определяющий URL-ы, которые могут быть вызваны, а также то, кем они могут быть вызваны и какие заголовки и другие поля при этом могут быть использованы.

Он показывает содержимое URL-ов с другим доменом через айфрейм. С помощью post-robot этот же айфрейм проводит через себя все сообщения, что позволяет избежать проблемы дополнительных взаимодействий (поскольку все проверки выполняются на стороне клиента, внутри доверенного фрейма).

→ Подробнее по ссылке

Спасибо всем за чтение, и если у вас есть какие-нибудь идеи по поводу того, каких инструментов не хватает в наборе по кроссдоменному взаимодействию или что в них можно улучшить, пожалуйста, напишите автору!

— Дэниел, команда разработки чекаут-инструментов PayPal

image

© Geektimes