Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
Third Party Hell (BestOfWeb)
Matthias Le Brun
June 07, 2019
Technology
0
240
Third Party Hell (BestOfWeb)
Matthias Le Brun
June 07, 2019
Tweet
Share
More Decks by Matthias Le Brun
See All by Matthias Le Brun
Healthy Code Collaboration
bloodyowl
0
48
Simplify your UI management with (algebraic data) types
bloodyowl
0
480
Simplify your UI management with (algebraic data) types
bloodyowl
1
290
Migrating a large Reason+React codebase to hooks
bloodyowl
0
310
Best practices
bloodyowl
0
210
A good Reason for typing
bloodyowl
1
380
Third Party Hell
bloodyowl
1
320
ReasonML @ OCaml Users in Paris
bloodyowl
0
230
Reason or How I Learned to Stop Worrying and Learnt a New and Safer Language
bloodyowl
0
780
Other Decks in Technology
See All in Technology
Oracle Database Technology Night #55 Oracle Autonomous Database 再入門
oracle4engineer
PRO
1
140
プロダクション環境の信頼性を損ねず観測する技術
egmc
4
940
Unity Package Managerで自作パッケージを配布する方法
yunoda
0
270
1年間のポストモーテム運用とそこから生まれたツール sre-advisor / SRE NEXT 2022
fujiwara3
6
3.8k
Kubernetesの上に作る、統一されたマイクロサービス運用体験
tkuchiki
1
1.4k
新規ゲームのリリース(開発)前からのSRE活動
tmkoikee
1
660
A Conditional Point Diffusion-Refinement Paradigm for 3D Point Cloud Completion
takmin
0
230
開発者のための GitHub Organization の安全な運用と 継続的なモニタリング
flatt_security
3
4k
Agile and Requirement : アジャイルな要件定義について考える
kawaguti
PRO
11
3.7k
Contrastive Self-Supervised Learning
yenlung
PRO
0
110
YAMLを書くだけで構築できる分散ストレージ
sat
PRO
0
230
What's new in Spring Cloud?
olgamaciaszek
0
170
Featured
See All Featured
We Have a Design System, Now What?
morganepeng
35
2.9k
Creatively Recalculating Your Daily Design Routine
revolveconf
207
10k
A Tale of Four Properties
chriscoyier
149
20k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
349
27k
Testing 201, or: Great Expectations
jmmastey
21
5.4k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
268
11k
Stop Working from a Prison Cell
hatefulcrawdad
261
17k
Designing for Performance
lara
596
63k
The Mythical Team-Month
searls
208
39k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
15
920
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
103
16k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
38
12k
Transcript
third party hell a journey coding in hostile environments
Matthias Le Brun @bloodyowl lead front-end developer beop.io co-founder &
podcast host putaindecode.io
third party?
example.first-party first party page
example.first-party first party page <script src="service.third-party" >!</script> #// or <iframe
src="service.third-party" %/>
None
use case?
use case? • widgets • players • analytics
why not integrate with the back-ends of our customers?
• ∞ impl. • updates • UX et capabilities
strategy
let's make a widget
None
approach #1 inject our DOM elements directly in the first
party page
approach #1 Nice what we wrote
approach #1 Nice Nice Nice -> Nice Nice what shows
up Nice
approach #1 CSS™
CSS™ • Cascading • Inheritance
approach #1 first party CSS resets & defaults will f*ck
things in unexpected ways
approach #1 inline CSS or ensure your stylesheet is always
the last one in the DOM
approach #1 • unset all properties • unset all pseudo-elements
• unset weird vendors selectors • use highest-pri selectors • !important
Alpha() AlphaImageLoader() :active additive-symbols (@counter-style) '::after (:after) align-content align-items align-self
all <an-plus-b> <angle> animation animation-delay animation-direction animation-duration animation-fill-mode animation-iteration-count animation-name animation-play-state animation-timing- function @annotation annotation() attr() Barn() BasicImage() BlendTrans() Blinds() Blur() '::backdrop backface-visibility background background-attachment background-blend-mode background-clip background-color background-image background-origin background-position background-repeat background-size <basic- shape> '::before (:before) <blend-mode> block-size blur() border border-block-end border-block-end-color border-block-end-style border-block-end-width border-block-start border-block-start-color border-block-start-style border-block-start-width border-bottom border-bottom-color border-bottom-left-radius border-bottom-right-radius border-bottom-style border-bottom-width border-collapse border-color border-image border-image-outset border-image-repeat border- image-slice border-image-source border-image-width border-inline-end border-inline-end-color border-inline-end-style border-inline-end-width border-inline- start border-inline-start-color border-inline-start-style border-inline-start-width border-left border-left-color border-left-style border-left-width border-radius border-right border-right-color border-right-style border-right-width border-spacing border-style border-top border-top-color border-top-left- radius border-top-right-radius border-top-style border-top-width border-width bottom @bottom-center box-decoration-break box-shadow box-sizing break-after break-before break-inside brightness() CheckerBoard() Chroma() Compositor() calc() caption-side caret-color ch @character-variant character-variant() @charset :checked circle() clear clip clip-path cm <color> color column-count column-fill column-gap column-rule column-rule-color column-rule-style column- rule-width column-span column-width columns content contrast() <counter> counter-increment counter-reset @counter-style cross-fade() cubic-bezier() '::cue cursor <custom-ident> DropShadow() :default deg :dir direction :disabled display dpcm dpi dppx drop-shadow() Emboss() Engrave() element() ellipse() em :empty empty-cells :enabled ex Fade() FlipH() FlipV() fallback (@counter-style) filter <filter-function> :first :first-child '::first-letter (:first- letter) '::first-line (:first-line) :first-of-type fit-content() <flex> flex flex-basis flex-direction flex-flow flex-grow flex-shrink flex-wrap float :focus font @font-face font-family font-family (@font-face) font-feature-settings font-feature-settings (@font-face) @font-feature-values font-kerning font- language-override font-size font-size-adjust font-stretch font-stretch (@font-face) font-style font-style (@font-face) font-synthesis font-variant font- variant (@font-face) font-variant-alternates font-variant-caps font-variant-east-asian font-variant-ligatures font-variant-numeric font-variant-position font-variation-settings (@font-face) font-weight font-weight (@font-face) format() format() (@font-face) fr frames() <frequency> :fullscreen Glow() Gradient() GradientWipe() Gray() grad <gradient> grayscale() grid grid-area grid-auto-columns grid-auto-flow grid-auto-rows grid-column grid-column-end grid-column-gap grid-column-start grid-gap grid-row grid-row-end grid-row-gap grid-row-start grid-template grid-template-areas grid-template-columns grid- template-rows Hz hanging-punctuation height height (@viewport) @historical-forms :hover hsl() hsla() hue-rotate() hyphens ICMFilter() Inset() Invert() Iris() <ident> <image> image() image-orientation image-rendering image-resolution image-set() @import in :in-range :indeterminate inherit initial inline- size inset() <integer> :invalid invert() isolation justify-content kHz @keyframes Light() :lang :last-child :last-of-type leader() :left left @left-bottom <length> letter-spacing line-break line-height linear-gradient() :link list-style list-style-image list-style-position list-style-type local() MaskFilter() Matrix() MotionBlur() margin margin-block-end margin-block-start margin-bottom margin-inline-end margin-inline-start margin-left margin-right margin-top mask mask-clip mask-composite mask-image mask-mode mask-origin mask-position mask-repeat mask-size mask-type matrix() matrix3d() max-height max-height (@viewport) max-width max-width (@viewport) max-zoom (@viewport) @media min-block-size min-height min-height (@viewport) min-inline-size min-width min-width (@viewport) min-zoom (@viewport) minmax() mix-blend-mode mm ms ms-filter-alpha() ms-filter-basicimage() ms-filter-blendtrans() ms-filter-blur() ms-filter- chroma() ms-filter-compositor() ms-filter-dropshadow() ms-filter-emboss() ms-filter-engrave() ms-filter-fliph() ms-filter-flipv() ms-filter-glow() ms- filter-gray() ms-filter-icmfilter() ms-filter-invert() ms-filter-light() ms-filter-maskfilter() ms-filter-matrix() ms-filter-motionblur() ms-filter- redirect() ms-filter-revealtrans() ms-filter-shadow() ms-filter-wave() ms-filter-xray() ms-proceduralsurface-alphaimageloader() ms-proceduralsurface- gradient() ms-transition-barn() ms-transition-blinds() ms-transition-checkerboard() ms-transition-fade() ms-transition-gradientwipe() ms-transition-inset() ms-transition-iris() ms-transition-pixelate() ms-transition-radialwipe() ms-transition-randombars() ms-transition-randomdissolve() ms-transition-slide() ms- transition-spiral() ms-transition-stretch() ms-transition-strips() ms-transition-wheel() ms-transition-zigzag() @namespace negative (@counter- style) :not :nth-child :nth-last-child :nth-last-of-type :nth-of-type <number> object-fit object-position offset-block-end offset-block-start offset-inline- end offset-inline-start :only-child :only-of-type opacity opacity() :optional order orientation (@viewport) @ornaments ornaments() orphans :out-of-range outline outline-color outline-offset outline-style outline-width overflow overflow-wrap overflow-x overflow-y Pixelate() pad (@counter-style) padding padding-block-end padding-block-start padding-bottom padding-inline-end padding-inline-start padding-left padding-right padding-top @page page-break-after page-break-before page-break-inside pc <percentage> perspective perspective() perspective-origin place-content '::placeholder pointer-events polygon() <position> position prefix (@counter-style) pt px Q quotes RadialWipe() RandomBars() RandomDissolve() Redirect() RevealTrans() rad radial-gradient() range (@counter-style) <ratio> :read-only :read-write rect() rem repeat() repeating-linear-gradient() repeating-radial-gradient() :required resize <resolution> revert rgb() rgba() :right right @right-bottom :root rotate() rotate3d() rotateX() rotateY() rotateZ() ruby-align ruby-merge ruby-position Shadow() Slide() Spiral() Stretch() Strips() s saturate() scale() scale3d() scaleX() scaleY() scaleZ() :scope scroll-behavior scroll-snap-coordinate scroll-snap-destination scroll-snap-type '::selection sepia() <shape> shape-image-threshold shape-margin shape-outside skew() skewX() skewY() speak-as (@counter-style) src (@font- face) steps() <string> @styleset styleset() @stylistic stylistic() suffix (@counter-style) @supports @swash swash() symbols (@counter-style) symbols() system (@counter-style) tab-size table-layout :target target-counter() target-counters() target-text() text-align text-align-last text-combine-upright text- decoration text-decoration-color text-decoration-line text-decoration-style text-emphasis text-emphasis-color text-emphasis-position text-emphasis-style text-indent text-justify text-orientation text-overflow text-rendering text-shadow text-transform text-underline-position <time> <timing-function> top @top- center touch-action transform transform-box <transform-function> transform-origin transform-style transition transition-delay transition-duration transition-property transition-timing-function translate() translate3d() translateX() translateY() translateZ() turn unicode-bidi unicode-range (@font-face) unset <url> url() user-zoom (@viewport) :valid var() vertical-align vh @viewport visibility :visited vmax vmin vw Wave() Wheel() white-space widows width width (@viewport) will-change word-break word-spacing word-wrap writing-mode Xray() Zigzag() z-index zoom (@viewport) )--* approach #1 NOPE
approach #1 we need isolation
approach #1 inject DOM elements directly in the first party
page
approach #1 inject DOM elements directly in the first party
page
approach #2 web components
approach #2 scoped styles!
approach #2 • Not enough browser support • Polyfills are
sloooooow* *ever tried YouTube on a non-chrome browser?
approach #2
approach #2 web components
approach #2 web components *for now *
approach #3 iframes
approach #3 iframes add one or two requests (HTML +
JS)
approach #3 limited for communication
approach #3 iframes
approach #3 iframes
approach #4 ✨ sourceless ✨ iframes
approach #4 just use the iframe as a rendering target
approach #4 let iframe = document.createElement("iframe"); iframe.src = "about:blank"; console.log(iframe.contentWindow)
approach #4 browser issues!
browser issues! • IE can't access contentWindow without a domain
• Firefox doesn't always fire a load event
approach #4 iframe.src = isIE ? "javascript:" + `'<script> window.onload
= function() { document.domain = "first-party.domain" } !</script>'` : "about:blank";
approach #4 setTimeout( renderWithoutLoadEvent, ARBITRARY_TIMEOUT );
approach #4 design issues!
design issues! An iframe has a fixed height
set the iframe height dynamically from the first party page
approach #4
watch the iframe's body height? approach #4
approach #4 My third-party elements iframe
approach #4 My third-party elements have grown iframe
approach #4 My third-party elements have grown iframe body grows
approach #4 My third-party shrank iframe but the body didn't
shrink
watch the iframe's body height? approach #4
watch the iframe's body height? approach #4
watch a div container in the body approach #4
how do we watch? approach #4
IntersectionObserver? approach #4
approach #4
IntersectionObserver? approach #4
approach #4 IntersectionObserver?
approach #4 IntersectionObserver?
good old client rect! approach #4
approach #4 let lastHeight = 0; function tick() { let
height = root.getBoundingClientRect().height; if(height ))!== lastHeight) { lastHeight = height; iframe.style.height = height + "px"; } requestAnimationFrame(tick); } requestAnimationFrame(tick);
only use window & document when you know what you're
doing takeovers
use node.ownerDocument and document.defaultView takeovers
don't trust inheritance takeovers
don't trust inheritance arrayFromIframe instanceof Array #// false
don't trust inheritance Array.isArray(arrayFromIframe) #// true Object.prototype.toString .call(arrayFromIframe) ''=== "[object
Array]" #// true
browsers! takeovers
3rd party devs: hey, <animate /> doesn't work in sourceless
iframes firefox: you want to use like ChromiumSVGs LOL wontfix takeovers
takeovers W3C their own APIs
3rd party devs: hey, can we get a setting to
let our logged customers say they want us to access their cookie so that we know if we need to show their admin interface? safari: no fuck off all third-party is trash takeovers
chrome: takeovers
how do we do layers/modals ? takeovers
let's build a dropdown Dropdown iframe Some first party page
contents
approach #1: grow the iframe Dropdown iframe Some first party
page contents Contents
approach #2: scrollviews Dropdown iframe Some first party page contents
Dropdown iframe Some first party page contents approach #3
Dropdown iframe Some first party page contents : more iframes™
Contents approach #3
React.createPortal is your friend takeovers
takeovers <iframe %/> Frame Loading
takeovers <iframe %/> Frame createPortal(el) Done
Environment.isThirdParty ? <Frame> child !</Frame> : child; takeovers
read-only storage no service workers takeovers
takeovers read-only storage no storage at all* ** no service
workers *in safari and firefox **only fails after some time
your SDK needs to be small takeovers
no (or very few) polyfills takeovers
because MooTools or Prototype.js are still a thing takeovers
because MooTools or Prototype.js are still a thing takeovers
avoid bundler conflicts takeovers
avoid bundler conflicts { -/* ''... )*/ output: { -/*
''... )*/ jsonpFunction: "YOUR_OWN_NAMESPACE", } }
booting takeovers
domReady? onLoad? takeovers
async loop takeovers
async loop function checkSdk() { if(window.myBootingFunction) { window.myBootingFunction() } else
{ setTimeout(checkSdk, 40) } } checkSdk()
analytics? takeovers
analytics? • needs to load the actual script • needs
to register events before the script is loaded
analytics? (function(_, b, e, o, p) { if (_.myTracker) {
return; } _.myTracker = function myTracker() { myTracker.events.push([].slice.call(arguments)); }; myTracker.events = []; o = b.createElement(e); o.src = "https:#//example.com/path-to-real-script.js"; p = b.querySelector(e); p.parentNode.insertBefore(o, p); })(window, document, "script");
analytics? (function(_, b, e, o, p) { if (_.myTracker) {
return; } _.myTracker = function myTracker() { myTracker.events.push([].slice.call(arguments)); }; myTracker.events = []; o = b.createElement(e); o.src = "https:#//example.com/path-to-real-script.js"; p = b.querySelector(e); p.parentNode.insertBefore(o, p); })(window, document, "script");
analytics? (function(_, b, e, o, p) { if (_.myTracker) {
return; } _.myTracker = function myTracker() { myTracker.events.push([].slice.call(arguments)); }; myTracker.events = []; o = b.createElement(e); o.src = "https:#//example.com/path-to-real-script.js"; p = b.querySelector(e); p.parentNode.insertBefore(o, p); })(window, document, "script"); dedupe
analytics? (function(_, b, e, o, p) { if (_.myTracker) {
return; } _.myTracker = function myTracker() { myTracker.events.push([].slice.call(arguments)); }; myTracker.events = []; o = b.createElement(e); o.src = "https:#//example.com/path-to-real-script.js"; p = b.querySelector(e); p.parentNode.insertBefore(o, p); })(window, document, "script"); dedupe register
analytics? (function(_, b, e, o, p) { if (_.myTracker) {
return; } _.myTracker = function myTracker() { myTracker.events.push([].slice.call(arguments)); }; myTracker.events = []; o = b.createElement(e); o.src = "https:#//example.com/path-to-real-script.js"; p = b.querySelector(e); p.parentNode.insertBefore(o, p); })(window, document, "script"); dedupe register load
API takeovers
API • existing installations need to work forever • never
introduce a breaking change
when making a third- party widget: we need to keep
all that in mind, at every step takeovers
because reverting a design decision is hard takeovers
• not your page • not your domain • not
a controlled env
tl;dr; takeovers • styles: f*cked, iframes • scripts: f*cked, limitations
• API: f*cked, retrocompat • storage: f*cked, do without
thank you!
thank you! Matthias Le Brun @bloodyowl questions?