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
110
(why the hell did I) build a GraphQL client for the browser
bloodyowl
0
150
Boxed: bringing algebraic types to TypeScript
bloodyowl
0
190
leveraging (algebraic data) types to make your UI rock @ jsheroes
bloodyowl
0
330
Leveraging (algebraic data) types to make your UI rock solid
bloodyowl
0
500
La drôle d'histoire de JavaScript
bloodyowl
0
400
Healthy Code Collaboration
bloodyowl
0
360
Simplify your UI management with (algebraic data) types
bloodyowl
0
850
Simplify your UI management with (algebraic data) types
bloodyowl
1
580
Other Decks in Technology
See All in Technology
Claude Codeをどのように キャッチアップしているか
oikon48
13
8.8k
AWS Security Agent といっしょに脅威モデリングをやってみよう
amarelo_n24
1
200
GitHub Copilot 最新アップデート – 「一歩先」の実践活用術
moulongzhang
5
1.6k
Kiro Ambassador を目指す話
k_adachi_01
0
120
[チョークトーク資料]AWS DevOps Agent を使いこなす / AWS Dev Ops Agent Chalk Talk AWS Summit Japan 2026
kinunori
4
750
IaC コードを資産へ:AWS CDK 社内ライブラリと横断展開 / aws-summit-japan-2026
gotok365
10
1.5k
現場のトークンマネジメント
dak2
1
170
FPGAの開発コンペでZephyrを使ってみた
iotengineer22
0
190
【2026年版】 ベクトル検索とEmbedding最前線
mocobeta
23
7.3k
AWS Security Hub CSPMの成功・失敗体験
cmusudakeisuke
0
510
徹底討論!ECS vs EKS!
daitak
3
1.4k
2026年6月23日 Syncable Tech + Start Python Club にて
hamukazu
0
150
Featured
See All Featured
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
240
Claude Code のすすめ
schroneko
67
230k
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.7k
Code Reviewing Like a Champion
maltzj
528
40k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
330
A better future with KSS
kneath
240
18k
Large-scale JavaScript Application Architecture
addyosmani
515
110k
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
340
A Tale of Four Properties
chriscoyier
163
24k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.3k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
2.9k
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
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? ✋