Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Third Party Hell

Third Party Hell

Matthias Le Brun

March 28, 2018
Tweet

More Decks by Matthias Le Brun

Other Decks in Technology

Transcript

  1. Third-party hell

    Making code for hostile environments

    View Slide

  2. Matthias Le Brun
    @bloodyowl
    Be

    View Slide

  3. third-party?

    View Slide

  4. First party page
    You

    View Slide

  5. View Slide

  6. use cases:
    widgets, players

    View Slide

  7. not your page
    not your domain
    not controlled

    View Slide

  8. strategy?

    View Slide

  9. Inject DOM elements
    in the first party
    page?

    View Slide

  10. First party CSS resets
    & defaults will fuck
    your page up in
    unexpected ways

    View Slide

  11. What you wrote
    Nice
    Nice
    What shows up
    Nice
    Nice
    -> Nice
    Nice

    View Slide

  12. Inline styles
    or
    ensure your stylesheet
    is always last in the DOM

    View Slide

  13. • unset all properties
    • unset all pseudo elements
    properties
    • unset weird vendor
    selectors (some use them)
    You need to:

    View Slide

  14. Alpha() AlphaImageLoader() :active additive-symbols (@counter-style) !::after (:after) align-content align-items align-self all 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 shape> !::before (:before) 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 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-increment counter-reset @counter-style cross-fade() cubic-bezier() !::cue
    cursor 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 :first :first-child !::first-letter (:first-
    letter) !::first-line (:first-line) :first-of-type fit-content() 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() :fullscreen Glow()
    Gradient() GradientWipe() Gray() grad 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() image() image-orientation image-rendering image-resolution image-set() @import in :in-range :indeterminate inherit initial inline-
    size inset() :invalid invert() isolation justify-content kHz @keyframes Light() :lang :last-child :last-of-type leader() :left left @left-bottom
    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 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 perspective perspective() perspective-origin place-content !::placeholder pointer-events polygon()
    position prefix (@counter-style) pt px Q quotes RadialWipe() RandomBars() RandomDissolve() Redirect() RevealTrans() rad radial-gradient() range
    (@counter-style) :read-only :read-write rect() rem repeat() repeating-linear-gradient() repeating-radial-gradient() :required resize
    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-image-threshold shape-margin shape-outside skew() skewX() skewY() speak-as (@counter-style) src (@font-
    face) steps() @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 top @top-
    center touch-action transform transform-box 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() 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

    View Slide

  15. you need isolation

    View Slide

  16. Inject DOM elements
    in the first party
    page?

    View Slide

  17. Web Components?

    View Slide

  18. scoped styles
    isolation

    View Slide

  19. Not enough browser
    support + unstable
    when it works

    View Slide

  20. Web Components?

    View Slide

  21. iframe?

    View Slide

  22. if you have a fixed
    ratio or height? sure

    View Slide

  23. iframe adds two
    requests (HTML +
    inner JS) and limited
    for communication

    View Slide

  24. iframe?

    View Slide

  25. sourceless iframe?

    View Slide

  26. just use the iframe as
    a rendering target

    View Slide

  27. let iframe = document.createElement("iframe");
    iframe.src = "javascript:;";

    View Slide

  28. Browser issues
    • IE can't access
    contentWindow
    without a domain
    • Firefox doesn't always
    fire a load event

    View Slide

  29. iframe.src = isTrident ?
    "javascript:" +
    `'<br/>window.onload = function() {<br/>document.domain = "first-party.domain"<br/>}<br/>%'`
    : "javascript:;";

    View Slide

  30. setTimeout(
    renderWithoutLoadEvent,
    ARBITRARY_TIMEOUT
    );

    View Slide

  31. an iframe has a fixed
    height
    Design issue

    View Slide

  32. you need a JS running
    on the first party to
    set the iframe height

    View Slide

  33. watch the iframe
    body size?

    View Slide

  34. body
    content
    content
    iframe

    View Slide

  35. watch the iframe
    body size?

    View Slide

  36. watch a container div
    in the body size

    View Slide

  37. let lastHeight = 0;
    function tick() {
    let height = root.getBoundingClientRect().height;
    if(height ##!== lastHeight) {
    lastHeight = height;
    iframe.style.height = height + "px";
    }
    requestAnimationFrame(tick);
    }
    requestAnimationFrame(tick);

    View Slide

  38. Try not to use global
    window & document

    View Slide

  39. node.ownerDocument
    document.defaultView
    Use these

    View Slide

  40. arrayFromIframe instanceof Array
    Array.isArray(arrayFromIframe)
    ({}).toString.call(arrayFromIframe)
    !!=== "[object Array]"
    Don’t trust inheritance

    View Slide

  41. Layers? Modals?

    View Slide

  42. Dropdown ▼

    View Slide

  43. Dropdown ▼
    solution 1:
    expand iframe
    contents of the first-party page

    View Slide

  44. Dropdown ▼
    solution 2:
    scroll view

    View Slide

  45. Dropdown ▼
    solution 3:
    more iframes™
    r
    + pageYOffset

    View Slide

  46. let layerManager: (module LayerManager) =
    environment )== "THIRD_PARTY" ?
    ((module ThirdPartyLayerManager): (module LayerManager)) :
    ((module DefaultLayerManager): (module LayerManager));
    module LayerManager = (val layerManager);

    View Slide

  47. ReactDOM.createPortal

    View Slide

  48. No persistent storage
    No service workers
    ...

    View Slide

  49. SDK needs to be small

    View Slide

  50. no (or very few)
    polyfills

    View Slide

  51. Can't break MooTools
    or Prototype.js

    View Slide

  52. Bundler conflicts
    {
    +/* !!... #*/
    output: {
    +/* !!... #*/
    jsonpFunction: "namespaced__define",
    }
    }

    View Slide

  53. Booting

    View Slide

  54. domReady?

    View Slide

  55. when
    DOMContentLoaded or
    load fire, you don't know
    if your booting script is
    ready

    View Slide

  56. async loop

    View Slide

  57. function checkSdk() {
    if(window.myBootingFunction) {
    window.myBootingFunction()
    } else {
    setTimeout(checkSdk, 40)
    }
    }
    checkSdk()

    View Slide

  58. third-party analytics?

    View Slide

  59. (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");

    View Slide

  60. register events
    before your actual
    script has loaded

    View Slide

  61. If you're going to
    make a third-party
    widget:
    keep all that in mind

    View Slide

  62. the code you made
    your client install on
    their page needs to
    work forever

    View Slide

  63. you can only add
    non-breaking
    changes

    View Slide

  64. tl;dr;
    styles: you're fucked
    scripts: you're fucked
    API: no breaking change

    View Slide

  65. Thank you
    Got any questions?

    View Slide