Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Third Party Hell
Search
Matthias Le Brun
March 28, 2018
Technology
580
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Third Party Hell
Matthias Le Brun
March 28, 2018
More Decks by Matthias Le Brun
See All by Matthias Le Brun
GraphQL, Pothos & SQLite: a perfect match
bloodyowl
0
120
(why the hell did I) build a GraphQL client for the browser
bloodyowl
0
160
Boxed: bringing algebraic types to TypeScript
bloodyowl
0
190
leveraging (algebraic data) types to make your UI rock @ jsheroes
bloodyowl
0
340
Leveraging (algebraic data) types to make your UI rock solid
bloodyowl
0
510
La drôle d'histoire de JavaScript
bloodyowl
0
400
Healthy Code Collaboration
bloodyowl
0
370
Simplify your UI management with (algebraic data) types
bloodyowl
0
860
Simplify your UI management with (algebraic data) types
bloodyowl
1
580
Other Decks in Technology
See All in Technology
AI時代こそ、スケールしないことをしよう -「作る人」から「なぜ作るか」を考える人へ / Do Things That Don't Scale in the AI Era — From How to Why
kaminashi
1
140
テックカンファレンス三大ステークホルダーの文化人類学 ─ 違いを認め合う関係性作り
bash0c7
2
600
人とエージェントが高め合う協業設計
kintotechdev
0
920
41歳でAWSが好きすぎてITエンジニアになったおっさんの話
yama3133
1
770
歴史から理解するクラウドインフラのしくみ
kizawa2020
0
140
「待ち時間」の消滅と「自我消耗」の加速:生成AI時代のエンジニアを救うメンタル・リソース管理
poropinai1966
0
160
それでも、技術なブログを書く理由 #kichijojipm / Why I Still Write Tech Blogs Even Now
shinkufencer
0
870
Claude Mythos、Fable...フロンティアAIの最新動向と企業のセキュリティ対策
flatt_security
0
150
文字起こし基盤の信頼性
abnoumaru
0
130
AIで楽になるはずが、なぜ疲れる?
kinopeee
0
100
クラウドを使う側から、作る側へ / 大吉祥寺.pm 2026前夜祭
fujiwara3
6
1.4k
AI研修(Day2)【MIXI 26新卒技術研修】
mixi_engineers
PRO
1
540
Featured
See All Featured
Amusing Abliteration
ianozsvald
1
240
Why Our Code Smells
bkeepers
PRO
340
58k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
120k
Technical Leadership for Architectural Decision Making
baasie
3
450
Art, The Web, and Tiny UX
lynnandtonic
304
22k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.1k
Speed Design
sergeychernyshev
33
1.9k
Game over? The fight for quality and originality in the time of robots
wayneb77
1
230
The Language of Interfaces
destraynor
162
27k
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
220
Max Prin - Stacking Signals: How International SEO Comes Together (And Falls Apart)
techseoconnect
PRO
0
320
New Earth Scene 8
popppiees
3
2.4k
Transcript
Third-party hell Making code for hostile environments
Matthias Le Brun @bloodyowl Be
third-party?
First party page You
None
use cases: widgets, players
not your page not your domain not controlled
strategy?
Inject DOM elements in the first party page?
First party CSS resets & defaults will fuck your page
up in unexpected ways
What you wrote Nice Nice What shows up Nice Nice
-> Nice Nice
Inline styles or ensure your stylesheet is always last in
the DOM
• unset all properties • unset all pseudo elements properties
• unset weird vendor selectors (some use them) You need to:
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) #--* NOPE
you need isolation
Inject DOM elements in the first party page?
Web Components?
scoped styles isolation
Not enough browser support + unstable when it works
Web Components?
iframe?
if you have a fixed ratio or height? sure
iframe adds two requests (HTML + inner JS) and limited
for communication
iframe?
sourceless iframe?
just use the iframe as a rendering target
let iframe = document.createElement("iframe"); iframe.src = "javascript:;";
Browser issues • IE can't access contentWindow without a domain
• Firefox doesn't always fire a load event
iframe.src = isTrident ? "javascript:" + `'<script> window.onload = function()
{ document.domain = "first-party.domain" } %</script>'` : "javascript:;";
setTimeout( renderWithoutLoadEvent, ARBITRARY_TIMEOUT );
an iframe has a fixed height Design issue
you need a JS running on the first party to
set the iframe height
watch the iframe body size?
body content content iframe
watch the iframe body size?
watch a container div in the body size
let lastHeight = 0; function tick() { let height =
root.getBoundingClientRect().height; if(height ##!== lastHeight) { lastHeight = height; iframe.style.height = height + "px"; } requestAnimationFrame(tick); } requestAnimationFrame(tick);
Try not to use global window & document
node.ownerDocument document.defaultView Use these
arrayFromIframe instanceof Array Array.isArray(arrayFromIframe) ({}).toString.call(arrayFromIframe) !!=== "[object Array]" Don’t trust
inheritance
Layers? Modals?
Dropdown ▼
Dropdown ▼ solution 1: expand iframe contents of the first-party
page
Dropdown ▼ solution 2: scroll view
Dropdown ▼ solution 3: more iframes™ r + pageYOffset
let layerManager: (module LayerManager) = environment )== "THIRD_PARTY" ? ((module
ThirdPartyLayerManager): (module LayerManager)) : ((module DefaultLayerManager): (module LayerManager)); module LayerManager = (val layerManager);
ReactDOM.createPortal
No persistent storage No service workers ...
SDK needs to be small
no (or very few) polyfills
Can't break MooTools or Prototype.js
Bundler conflicts { +/* !!... #*/ output: { +/* !!...
#*/ jsonpFunction: "namespaced__define", } }
Booting
domReady?
when DOMContentLoaded or load fire, you don't know if your
booting script is ready
async loop
function checkSdk() { if(window.myBootingFunction) { window.myBootingFunction() } else { setTimeout(checkSdk,
40) } } checkSdk()
third-party 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");
register events before your actual script has loaded
If you're going to make a third-party widget: keep all
that in mind
the code you made your client install on their page
needs to work forever
you can only add non-breaking changes
tl;dr; styles: you're fucked scripts: you're fucked API: no breaking
change
Thank you Got any questions? ✋