module 'sky:core' { // EVENTS class Event { constructor (String type, Boolean bubbles = true, any data = null); // 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) attribute Boolean handled; // O(1) attribute any result; // O(1) // TODO(ianh): do events get blocked at scope boundaries, e.g. focus events when both sides are in the scope? // TODO(ianh): do events get retargetted, e.g. focus when leaving a custom element? } callback EventListener any (Event event); // if the return value is not undefined: // assign it to event.result // set event.handled to true abstract class EventTarget { any dispatchEvent(Event event); // O(N) in total number of listeners for this type in the chain // sets event.handled to false and event.result to undefined // makes a record of the event target chain by calling getEventDispatchChain() // invokes all the handlers on the chain in turn // returns event.result virtual Array<EventTarget> getEventDispatchChain(); // O(1) // returns [] void addEventListener(String type, EventListener listener); // O(1) void removeEventListener(String type, EventListener listener); // O(N) in event listeners with that type private Array<String> getRegisteredEventListenerTypes(); // O(N) private Array<EventListener> getRegisteredEventListenersForType(String type); // O(N) } class CustomEventTarget : EventTarget { // implemented in JS constructor (); // O(1) attribute EventTarget parentNode; // getter O(1), setter O(N) in height of tree, throws if this would make a loop virtual Array<EventTarget> getEventDispatchChain(); // O(N) in height of tree // implements EventTarget.getEventDispatchChain() // let result = []; // let node = this; // while (node) { // result.push(node); // node = node.parentNode; // } // return result; // you can inherit from this to make your object into an event target // or you can inherit from EventTarget and implement your own getEventDispatchChain() } // DOM typedef ChildNode (Element or Text); typedef ChildArgument (Element or Text or String); abstract class Node : EventTarget { // implemented in C++ 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) virtual Array<EventTarget> getEventDispatchChain(); // O(N) in number of ancestors across shadow trees // implements EventTarget.getEventDispatchChain() // returns the event dispatch chain (including handling shadow trees) // 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 = false); // 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 class 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 } class Attr { constructor (String name, String value = ''); // O(1) readonly attribute String name; // O(1) readonly attribute String value; // O(1) } abstract class Element : ParentNode { readonly attribute String tagName; // O(1) Boolean hasAttribute(String name); // O(N) in number of attributes String getAttribute(String name); // O(N) in number of attributes void setAttribute(String name, String value = ''); // O(N) in number of attributes void removeAttribute(String name); // O(N) in number of attributes // Returns a new Array and new Attr instances every time. Array<Attr> getAttributes(); // O(N) in number of attributes readonly attribute ShadowRoot? shadowRoot; // O(1) // returns the shadow root Array<ContentElement> getDestinationInsertionPoints(); // O(N) in number of insertion points the node is in virtual void endTagParsedCallback(); // noop virtual void attributeChangeCallback(String name, String? oldValue, String? newValue); // noop // TODO(ianh): does a node ever need to know when it's been redistributed? } class 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 } class DocumentFragment : ParentNode { constructor (ChildArguments... nodes); // O(N) in number of arguments plus all their descendants } abstract class TreeScope : ParentNode { readonly attribute Document? ownerDocument; // O(1) readonly attribute TreeScope? parentScope; // O(1) Element? findId(String id); // O(1) } class ShadowRoot : TreeScope { constructor (Element host); // O(1) // note that there is no way in the API to use a newly created ShadowRoot readonly attribute Element host; // O(1) } class Document : TreeScope { constructor (ChildArguments... nodes); // O(N) in number of arguments plus all their descendants } class SelectorQuery { constructor (String selector); // O(F()) where F() is the complexity of the selector Boolean matches(Element element); // O(F()) Element? find(Element root); // O(N*F())+O(M) where N is the number of descendants and M the average depth of the tree Element? find(DocumentFragment root); // O(N*F())+O(M) where N is the number of descendants and M the average depth of the tree Element? find(TreeScope root); // O(N*F()) where N is the number of descendants Array<Element> findAll(Element root); // O(N*F())+O(N*M) where N is the number of descendants and M the average depth of the tree Array<Element> findAll(DocumentFragment root); // O(N*F())+O(N*M) where N is the number of descendants and M the average depth of the tree Array<Element> findAll(TreeScope root); // O(N*F()) where N is the number of descendants } // BUILT-IN ELEMENTS class ImportElement : Element { constructor (Dictionary<String> attributes, ChildArguments... nodes); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants constructor (ChildArguments... nodes); // shorthand constructor (Dictionary<String> attributes); // shorthand constructor (); // shorthand constructor attribute String tagName; // O(1) // "import" constructor attribute Boolean shadow; // O(1) // false } class TemplateElement : Element { constructor (Dictionary<String> attributes, ChildArguments... nodes); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants constructor (ChildArguments... nodes); // shorthand constructor (Dictionary<String> attributes); // shorthand constructor (); // shorthand constructor attribute String tagName; // O(1) // "template" constructor attribute Boolean shadow; // O(1) // false readonly attribute DocumentFragment content; // O(1) } class ScriptElement : Element { constructor (Dictionary<String> attributes, ChildArguments... nodes); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants constructor (ChildArguments... nodes); // shorthand constructor (Dictionary<String> attributes); // shorthand constructor (); // shorthand constructor attribute String tagName; // O(1) // "script" constructor attribute Boolean shadow; // O(1) // false } class StyleElement : Element { constructor (Dictionary<String> attributes, ChildArguments... nodes); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants constructor (ChildArguments... nodes); // shorthand constructor (Dictionary<String> attributes); // shorthand constructor (); // shorthand constructor attribute String tagName; // O(1) // "style" constructor attribute Boolean shadow; // O(1) // false } class ContentElement : Element { constructor (Dictionary<String> attributes, ChildArguments... nodes); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants constructor (ChildArguments... nodes); // shorthand constructor (Dictionary<String> attributes); // shorthand constructor (); // shorthand constructor attribute String tagName; // O(1) // "content" constructor attribute Boolean shadow; // O(1) // false Array<Node> getDistributedNodes(); // O(N) in distributed nodes } class ImgElement : Element { constructor (Dictionary<String> attributes, ChildArguments... nodes); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants constructor (ChildArguments... nodes); // shorthand constructor (Dictionary<String> attributes); // shorthand constructor (); // shorthand constructor attribute String tagName; // O(1) // "img" constructor attribute Boolean shadow; // O(1) // false } class DivElement : Element { constructor (Dictionary<String> attributes, ChildArguments... nodes); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants constructor (ChildArguments... nodes); // shorthand constructor (Dictionary<String> attributes); // shorthand constructor (); // shorthand constructor attribute String tagName; // O(1) // "div" constructor attribute Boolean shadow; // O(1) // false } class SpanElement : Element { constructor (Dictionary<String> attributes, ChildArguments... nodes); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants constructor (ChildArguments... nodes); // shorthand constructor (Dictionary<String> attributes); // shorthand constructor (); // shorthand constructor attribute String tagName; // O(1) // "span" constructor attribute Boolean shadow; // O(1) // false } class IframeElement : Element { constructor (Dictionary<String> attributes, ChildArguments... nodes); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants constructor (ChildArguments... nodes); // shorthand constructor (Dictionary<String> attributes); // shorthand constructor (); // shorthand constructor attribute String tagName; // O(1) // "iframe" constructor attribute Boolean shadow; // O(1) // false } class TElement : Element { constructor (Dictionary<String> attributes, ChildArguments... nodes); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants constructor (ChildArguments... nodes); // shorthand constructor (Dictionary<String> attributes); // shorthand constructor (); // shorthand constructor attribute String tagName; // O(1) // "t" constructor attribute Boolean shadow; // O(1) // false } class AElement : Element { constructor (Dictionary<String> attributes, ChildArguments... nodes); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants constructor (ChildArguments... nodes); // shorthand constructor (Dictionary<String> attributes); // shorthand constructor (); // shorthand constructor attribute String tagName; // O(1) // "a" constructor attribute Boolean shadow; // O(1) // false } class TitleElement : Element { constructor (Dictionary<String> attributes, ChildArguments... nodes); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants constructor (ChildArguments... nodes); // shorthand constructor (Dictionary<String> attributes); // shorthand constructor (); // shorthand constructor attribute String tagName; // O(1) // "title" constructor attribute Boolean shadow; // O(1) // false } class ErrorElement : Element { constructor (Dictionary<String> attributes, ChildArguments... nodes); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants constructor (ChildArguments... nodes); // shorthand constructor (Dictionary<String> attributes); // shorthand constructor (); // shorthand constructor attribute String tagName; // O(1) // "error" constructor attribute Boolean shadow; // O(1) // false } // MODULES callback InternalElementConstructor void (Module module); dictionary ElementRegistration { String tagName; Boolean shadow = false; InternalElementConstructor? constructor = null; } interface ElementConstructor { constructor (Dictionary<String> attributes, ChildArguments... nodes); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants constructor (ChildArguments... nodes); // shorthand constructor (Dictionary<String> attributes); // shorthand constructor (); // shorthand constructor attribute String tagName; constructor attribute Boolean shadow; } abstract class AbstractModule : EventTarget { readonly attribute Document document; // O(1) // the Documentof the module or application Promise<any> import(String url); // O(Yikes) // returns the module's exports private Array<Module> getImports(); O(N) // returns the Module objects of all the imported modules readonly attribute String url; ElementConstructor registerElement(ElementRegistration options); // O(1) // if you call registerElement() with an object that was created by // registerElement(), it just returns the object after registering it, // rather than creating a new constructor // otherwise, it proceeds as follows: // 1. let constructor be the constructor passed in, if any // 2. let prototype be the constructor's prototype; if there is no // constructor, let prototype be Element // 3. create a new Function that: // 1. throws if not called as a constructor // 2. creates an actual Element object // 3. initialises the shadow tree if shadow on the options is true // 4. calls constructor, if it's not null, with the module as the argument // 4. let that new Function's prototype be the aforementioned prototype // 5. let that new Function have tagName and shadow properties set to // the values passed in on options // 6. register the new element readonly attribute ScriptElement? currentScript; // O(1) // returns the <script> element currently being executed if any, and if it's in this module; else null } class Module : AbstractModule { constructor (Application application, Document document, String url); // O(1) readonly attribute Application application; // O(1) attribute any exports; // O(1) // defaults to {} } class Application : AbstractModule { constructor (Document document, String url); // O(1) attribute String title; // O(1) } // 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...
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 callback CallbackName ReturnType (ArgumentType argumentName); class ClassName { // a class corresponds to a JavaScript prototype // corresponds to a WebIDL 'interface' } abstract class Superclass { // an abstract class can't have a constructor // in every other respect it is the same as a regular class } class Subclass : Superclass { // 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) // Calling a method with fewer arguments than defined will throw. // Calling a method with more arguments ignores the extra arguments. 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. // properties on the constructor constructor readonly attribute ReturnType staticName; // private APIs - see below private void 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); // trailing arguments can have a default value, which must be a literal of the given type ReturnType method(ArgumentType argumentName1, ArgumentType argumentName2 = defaultValue); } dictionary Options { String foo; // if there's no default, the property must be specified or it's a TypeError Integer bar = 4; // properties can have default values } // the module can have properties and methods also attribute String Foo; void method(); interface InterfaceName { // describes a template of a prototype, in the same syntax as a class // not actually exposed in the runtime } }
Private APIs are only accessible via Symbol objects, which are then exposed on the sky:debug module's exports object as the name of the member given in the IDL.
For example, consider:
class Foo { private void Bar(); }
In a script with a foo
object of type Foo
, foo.Bar
is undefined. However, it can be obtained as follows:
<import src="sky:debug" as="debug"/> <!-- ... import whatever defines 'foo' ... --> <script> foo[debug.Bar] </script>
The following types are available:
Integer
- WebIDL long long
Float
- WebIDL double
Infinity
- singleton type with value Infinity
String
- WebIDL USVString
Boolean
- WebIDL boolean
Object
- WebIDL object
(ClassName
can be used as a literal for this type)ClassName
- an instance of the class ClassNameDictionaryName
- an instance of the dictionary DictionaryNamePromise<Type>
- WebIDL Promise<T>
Generator<Type>
- An ECMAScript generator function that returns data of the given typeArray<Type>
- WebIDL sequence<T>
Dictionary<Type>
- unordered set of name-value String-Type pairs with no duplicate namesType?
- 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): Define in detail how this actually works
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
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 the sky debug symbols for private APIs sky:debug
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.