Weird APIs

BrazilJS, August 26, 2017

E aí, sou Mike.


Live in Austin, TX.


Lead the Web Compatibility Team, Mozilla.

I have a web log.

I work on webcompat.com.

And sometimes on the Compatibility Standard.

ANYWAYS

Weird Browser APIs.

Why do I care about weird APIs?

Why do I care about weird APIs?

  • Browser Interoperability

Why do I care about weird APIs?

  • Browser Interoperability
  • Web Platform Predictability

Why do I care about weird APIs?

  • Browser Interoperability
  • Web Platform Predictability
  • Web Compatibility

raise ur hand if you work on a browser

raise ur hand if you work on a web site or application

Why should you care about weird APIs?

  • Let's come back to this

okay

Side effects

Observable side effects

Side Effects

A function or expression is said to have a side effect if it modifies some state outside its scope or has an observable interaction with its calling functions or the outside world besides returning a value.

some smart person (probably), wikipedia

window.showModalDialog()

    // a.html
    var ret = window.showModalDialog("b.html" [, args][, opts]);
    callDHTMLFunction(ret)
     
    // b.html
    var args = window.dialogArguments();
    window.returnValue = "lol";
    

The issue is that showModalDialog spins a nested event loop. That leads to all manner of craziness because V8 stores the current execution context in a static. That means the V8 execution context for the caller is always on the stack while the modal dialog is running... tl;dr: showModalDialog's security is broken beyond repair. Good thing we're deleting it. :-/

abarth@chromium.org

weird, but dead

Math.random()

    >> Math.random()
    0.5984321348460281
     
    >> Math.random()
    0.3374764713511752
    

CSSOM scrollTop, scrollLeft, scrollWidth, scrollHeight

    // which one??
    document.documentElement.scrollTop
    document.body.scrollTop
     
    // i forget
    var y = document.documentElement.scrollTop || 
            document.body.scrollTop;
    

quirks mode

Google Closure dom.js

    goog.dom.getDocumentScrollElement_ = function(doc) {
      // WebKit needs body.scrollLeft in both quirks and strict mode.
      if (!goog.userAgent.WEBKIT && goog.dom.isCss1CompatMode_(doc)) {
        return doc.documentElement;
      }
      return doc.body || doc.documentElement;
    };
    

document.scrollingElement

Do what the spec said to do, before.

CSSOM scrollTop, scrollLeft, scrollWidth, scrollHeight, redux

    // which one??
    document.documentElement.scrollTop
    document.body.scrollTop
     
    // who cares (in a perfectly supported world) 
    var y = document.scrollingElement.scrollTop;
    

Consistent casing rules

Platform casing rules (theory)

Casing rule Examples
Methods & Props Camel case document.createAttribute(), document.compatMode
Classes & Mixins Pascal case NonElementParentNode, NamedNodeMap
Initialisms in APIs All caps, except when first word HTMLCollection, elm.innerHTML
Repeated initialisms same as above HTMLHRElement, RTCDTMFSender

RTCDTMFSender

Real Time Communication Dual-Tone Multi-Frequency Sender

Platform casing weirdness

  • XMLHttpRequest

Platform casing weirdness

  • XMLHttpRequest
  • HTMLHtmlElement

Platform casing weirdness

  • XMLHttpRequest
  • HTMLHtmlElement
  • Vendor Prefixes....................................................................................
  • ..........................................................................................................................................................
  • ..........................................................................................................................................................
  • ..........................................................................................................................................................
  • ..........................................................................................................................................................
  • ..........................................................................................................................................................

it gets weird

spot the bug?

good luck unprefixing :(

CSSOM getters

What do getters for the following CSS props look like in CSSOM?


  • font-size
  • -moz-transform
  • -webkit-transform

put on your algorithm hats

CSSStyleDeclaration interface

        partial interface CSSStyleDeclaration {
         attribute [TreatNullAs=EmptyString] CSSOMString _camel_cased_attribute;
        };
      

The camel-cased attribute attribute, on getting, must return the result of invoking getPropertyValue() with the argument being the result of running the IDL attribute to CSS property algorithm for camel-cased attribute.

CSS Property to IDL attribute

font-size ->
fontSize

-moz-transform ->
MozTransform

-webkit-transform ->
WebkitTransform

CSSStyleDeclaration interface

        partial interface CSSStyleDeclaration {
         attribute [TreatNullAs=EmptyString] CSSOMString _webkit_cased_attribute;
        };
      

For each CSS property property that is a supported CSS property and that begins with the string -webkit-, the following partial interface applies where webkit-cased attribute is obtained by running the CSS property to IDL attribute algorithm for property, with the lowercase first flag set.

-webkit-transform ->
webkitTransform

But why?

CSSStyleDeclaration interface

        partial interface CSSStyleDeclaration {
         attribute [TreatNullAs=EmptyString] CSSOMString _dashed_attribute;
        };
      

The dashed attribute attribute, on getting, must return the result of invoking getPropertyValue() with the argument being dashed attribute.

-webkit-transform ->
-webkit-transform

element.style["-webkit-transform"]

Avoid Boolean traps

initMouseEvent()

        event.initMouseEvent("click", true, true, window,
                             0, 0, 0, 0, 0,
                             false, false, false, false,
                             0, null);
      

initMouseEvent()

        event.initMouseEvent("click", true, true, window,
                             0, 0, 0, 0, 0,
                             false, false, false, false,
                             0, null);
      

What's the 13th false??

initMouseEvent()

        event.initMouseEvent("click", true, true, window,
                             0, 0, 0, 0, 0,
                             false, false, false, metaKey,
                             0, null);
      

initMouseEvent()

        event.initMouseEvent(type, canBubble, cancelable, view,
                             detail, screenX, screenY, clientX, clientY,
                             ctrlKey, altKey, shiftKey, metaKey,
                             button, relatedTarget);
      

gross

MouseEvent() constructor

        new MouseEvent("click", {
           "metaKey": true",
           "button": 0
           ...
        });
      

Prefer dictionary parameters that default to false.

Stringy APIs

document.execCommand()

  document.designMode = "on";
  document.execCommand("selectall");
  document.execCommand("InsertLineBreak")
  document.execCommand("InsertLineBreak");
  document.execCommand("InsertText", false, "hello");
  

DOMImplementation.hasFeature()

    document.implementation.hasFeature("Core", "2.0")
  

true

DOMImplementation.hasFeature()

    document.implementation.hasFeature("LOL", "14.0")
  

...true

navigator.userAgent

Why should you care about weird APIs?

Why should you care about weird APIs?

  • More predictable codebase
  • Less bugs
  • Less technical debt

Why should you care about weird APIs?

  • More predictable codebase
  • Less bugs

Why should you care about weird APIs?

  • More predictable codebase
  • Less bugs
  • Less technical debt

PAS


https://miketaylr.com
miket@mozilla.com
@miketaylr