APIS

The Sky core API

module 'sky:core' {

  // EVENTS

  interface Event {
    constructor (String type, Boolean bubbles, any data); // O(1)
    readonly attribute String type; // O(1)
    readonly attribute Boolean bubbles; // O(1)
    attribute any data; // O(1)

    readonly attribute EventTarget target; // O(1)
    void preventDefault(); // O(1)
    attribute any result; // O(1) // defaults to undefined

    // TODO(ianh): do events get blocked at scope boundaries, e.g. focus events when both sides are in the scope?
    // TODO(ianh): do events ger retargetted, e.g. focus when leaving a custom element?
  }

  callback EventListener any (Event event); // return value is assigned to Event.result

  interface EventTarget {
    any dispatchEvent(Event event); // O(N) in total number of listeners for this type in the chain // returns Event.result
    void addEventListener(String type, EventListener listener); // O(1)
    void removeEventListener(String type, EventListener listener); // O(N) in event listeners with that type
  }

  interface CustomEventTarget : EventTarget {
    constructor (); // O(1)
    attribute EventTarget parentNode; // getter O(1), setter O(N) in height of tree, throws if this would make a loop

    // you can inherit from this to make your object into an event target
  }



  // DOM

  typedef ChildNode (Element or Text);
  typedef ChildArgument (Element or Text or String);

  abstract interface Node : EventTarget {
    readonly attribute TreeScope? ownerScope; // O(1)
    
    readonly attribute ParentNode? parentNode; // O(1)
    readonly attribute Element? parentElement; // O(1) // if parentNode isn't an element, returns null
    readonly attribute ChildNode? previousSibling; // O(1)
    readonly attribute ChildNode? nextSibling; // O(1)
    
    // the following all throw if parentNode is null
    void insertBefore(ChildArgument... nodes); // O(N) in number of arguments plus all their descendants
    void insertAfter(ChildArgument... nodes); // O(N) in number of arguments plus all their descendants
    void replaceWith(ChildArgument... nodes); // O(N) in number of descendants plus arguments plus all their descendants
    void remove(); // O(N) in number of descendants
    Node cloneNode(Boolean deep); // O(1) if deep=false, O(N) in the number of descendants if deep=true

    // called when parentNode changes
    virtual void parentChangeCallback(ParentNode? oldParent, ParentNode? newParent, ChildNode? previousSibling, ChildNode? nextSibling); // O(N) in descendants (calls attached/detached)
    virtual void attachedCallback(); // noop
    virtual void detachedCallback(); // noop
  }

  abstract interface ParentNode : Node {
    readonly attribute ChildNode? firstChild; // O(1)
    readonly attribute ChildNode? lastChild; // O(1)
    
    // Returns a new Array every time.
    Array<ChildNode> getChildNodes(); // O(N) in number of child nodes
    Array<Element> getChildElements(); // O(N) in number of child nodes // TODO(ianh): might not be necessary if we have the parser drop unnecessary whitespace text nodes

    void append(ChildArgument... nodes); // O(N) in number of arguments plus all their descendants
    void prepend(ChildArgument... nodes); // O(N) in number of arguments plus all their descendants
    void replaceChildrenWith(ChildArgument... nodes); // O(N) in number of descendants plus arguments plus all their descendants
    
    Element? findId(String id); // O(1)
  }

  interface Attr {
    constructor (String name, String value); // O(1)
    readonly attribute String name; // O(1)
    readonly attribute String value; // O(1)
  }

  interface Element : ParentNode {
    readonly attribute String tagName; // O(1)

    Boolean hasAttribute(String name); // O(N) in arguments
    String getAttribute(String name); // O(N) in arguments
    void setAttribute(String name, String value); // O(N) in arguments
    void removeAttribute(String name); // O(N) in arguments
    
    // Returns a new Array and new Attr instances every time.
    Array<Attr> getAttributes(); // O(N) in arguments

    readonly attribute ShadowRoot? shadowRoot; // O(1) // returns the youngest shadow root
    void addShadowRoot(ShadowRoot root); // O(N) in descendants of argument
    Array<ContentElement> getDestinationInsertionPoints(); // O(N) in number of insertion points the node is in

    virtual void attributeChangeCallback(String name, String? oldValue, String? newValue); // noop
    virtual void shadowRootChangeCallback(ShadowRoot root); // noop
    // TODO(ianh): does a node ever need to know when it's been redistributed?
  }
  Element createElement(String tagName, Dictionary attributes, ChildArguments... nodes); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants
  Element createElement(String tagName, Dictionary attributes); // shorthand
  Element createElement(String tagName, ChildArguments... nodes); // shorthand
  Element createElement(String tagName); // shorthand

  Object registerElement(String tagName, Object interfaceObject); // O(N) in number of outstanding elements with that tag name to be upgraded

  interface Text : Node {
    constructor (String value); // O(1)
    attribute String value; // O(1)

    void replaceWith(String node); // O(1) // special case override of Node.replaceWith()

    virtual void valueChangeCallback(String? oldValue, String? newValue); // noop
  }

  interface DocumentFragment : ParentNode {
    constructor (ChildArguments... nodes); // O(N) in number of arguments plus all their descendants
  }

  abstract interface TreeScope : ParentNode {
    readonly attribute Document? ownerDocument; // O(1)
    readonly attribute TreeScope? parentScope; // O(1)
  }

  interface ShadowRoot : TreeScope {
    constructor (ChildArguments... nodes); // O(N) in number of arguments plus all their descendants
    readonly attribute Element? host; // O(1)
    readonly attribute ShadowRoot? olderShadowRoot; // O(1)
    void removeShadowRoot(); // O(N) in descendants
  }

  interface Document : TreeScope {
    constructor (ChildArguments... nodes); // O(N) in number of arguments plus all their descendants
  }
  
  interface SelectorQuery {
    constructor (String selector); // O(F()) where F() is the complexity of the selector

    Boolean matches(Element element); // O(F())
    Element? find(ParentNode root); // O(N*F()) where N is the number of descendants
    Array<Element> findAll(ParentNode root); // O(N*F()) where N is the number of descendants
  }

  // Built-in Elements
  interface ImportElement : Element { }
  interface TemplateElement : Element {
    readonly attribute DocumentFragment content; // O(1)
  }
  interface ScriptElement : Element { }
  interface StyleElement : Element { }
  interface ContentElement : Element {
    Array<Node> getDistributedNodes(); // O(N) in distributed nodes
  }
  interface ShadowElement : Element {
    Array<Node> getDistributedNodes(); // O(N) in distributed nodes
  }
  interface ImgElement : Element { }
  interface IframeElement : Element { }
  interface TElement : Element { }
  interface AElement : Element { }
  interface TitleElement : Element { }



  // MODULES

  interface Module : EventTarget {
    constructor (Application application, Document document); // O(1)
    attribute any exports; // O(1) // defaults to the module's document
    readonly attribute Document document; // O(1) // the module's document
    readonly attribute Application application; // O(1)
  }

  interface Application : EventTarget {
    constructor (Document document); // O(1)
    attribute String title; // O(1)
    readonly attribute Document document; // O(1) // the application's document
  }

  // see script.md for a description of the global object, though note that
  // the sky core module doesn't use it or affect it in any way.

}

TODO(ianh): event loop

TODO(ianh): define the DOM APIs listed above, including firing the change callbacks

TODO(ianh): schedule microtask, schedule task, requestAnimationFrame, custom element callbacks...

Appendices

Sky IDL

The Sky IDL language is used to describe JS APIs found in Sky, in particular, the JS APIs exposed by the four magical imports defined in this document.

Sky IDL definitions are typically compiled to C++ that exposes the C++ implementations of the APIs to JavaScript.

Sky IDL works more or less the same as Web IDL but the syntax is a bit different.

module 'sky:modulename' {

  // this is a comment

  typedef NewType OldType; // useful when OldType is a commonly-used union

  interface InterfaceName {
    // an interface corresponds to a JavaScript prototype
  }

  abstract interface Superinterface {
    // an abstract interface can't have a constructor
    // in every other respect it is the same as a regular interface
  }

  interface Subinterface : Superinterface {
    // properties
    readonly attribute ReturnType attributeName; // getter
    attribute ReturnType attributeName; // getter and setter

    // methods and constructors
    constructor ();
    ReturnType method();
      // When the platform calls this method, it always invokes the "real" method, even if it's been
      // deleted from the prototypes (as if it took a reference to the method at startup, and stored
      // state using Symbols)
    virtual ReturnType methodCallback();
      // when the platform calls this, it actually calls it the way JS would, so author overrides do
      // affect what gets called. Make sure if you override it that you call the superclass implementation!
      // The default implementations of 'virtual' methods all end by calling the identically named method
      // on the superclass, if there is such a method.

    // arguments and overloading are done as follows
    // note that the argument names are only for documentation purposes
    ReturnType method(ArgumentType argumentName1, ArgumentType argumentName2);
    // the last argument's type can have "..." appended to it to indicate a varargs-like situation
    ReturnType method(ArgumentType argumentName1, ArgumentType... allSubsequentArguments);
  }

  // the module can have properties and methods also
  attribute String Foo;
  void method();

}

The following types are available:

  • Integer - WebIDL long long
  • Float - WebIDL double
  • String - WebIDL USVString
  • Boolean - WebIDL boolean

Object - WebIDL object

  • InterfaceName - an instance of the interface InterfaceName
  • Promise<Type> - WebIDL Promise<T>
  • Array<Type> - WebIDL sequence<T>
  • Dictionary - unordered set of name-value String-String pairs with no duplicate names
  • Type? - union of Type and the singleton type with value “null” (WebIDL nullable)
  • (Type1 or Type2) - union of Type1 and Type2 (WebIDL union)
  • any - union of all types (WebIDL any)

Methods that return nothing (undefined, in JS) use the keyword “void” instead of a type.

TODO(ianh): Figure out what should happen with omitted and extraneous parameters

TODO(ianh): Define in detail how this actually works

Mojom IDL

The Mojom IDL language is used to describe the APIs exposed over Mojo pipes.

Mojom IDL definitions are typically compiled to wrappers in each language, which are then used as imports.

TODO(ianh): Define in detail how this actually works

Notes

global object = {} // with Math, RegExp, etc

magical imports:
  the core mojo fabric JS API   sky:mojo:fabric:core
  the asyncWait/cancelWait mojo fabric JS API (interface to IPC thread)  sky:mojo:fabric:ipc
  the mojom for the shell, proxying through C++ so that the shell pipe isn't exposed  sky:mojo:shell
  the sky API  sky:core

TODO(ianh): determine if we want to separate the “this” from the Document, especially for Modules, so that exposing a module‘s element doesn’t expose the module's exports attribute.