[Specs] Remove all the obsolete specs.

TBR=abarth

Review URL: https://codereview.chromium.org/1142853006
This commit is contained in:
Hixie 2015-05-19 16:39:32 -07:00
parent c20a7f9c28
commit f31e6ec087
14 changed files with 2 additions and 3445 deletions

View File

@ -1,37 +1,2 @@
The Sky Environment
===================
The main files loaded by the Sky environment are Sky files, though
they can refer to binary resources like images and fonts.
Sky files
---------
Conventional MIME type: ``text/sky``, though this type is neither
necessary nor sufficient to indicate that a file is a Sky file; only
the signature matters for type dispatch of Sky files.
Conventional extension: ``.sky``
Signatures:
For application files, one of the following:
* ``23 21 6d 6f 6a 6f 20 6d 6f 6a 6f 3a 73 6b 79 0a`` ("``#!mojo mojo:sky\n``")
* ``23 21 6d 6f 6a 6f 20 6d 6f 6a 6f 3a 73 6b 79 0d`` ("``#!mojo mojo:sky\r``")
* ``23 21 6d 6f 6a 6f 20 6d 6f 6a 6f 3a 73 6b 79 20`` ("``#!mojo mojo:sky ``")
For module files, one of the following:
* ``53 4b 59 20 4d 4f 44 55 4c 45 0a`` ("``SKY MODULE\n``")
* ``53 4b 59 20 4d 4f 44 55 4c 45 0d`` ("``SKY MODULE\r``")
* ``53 4b 59 20 4d 4f 44 55 4c 45 20`` ("``SKY MODULE ``")
Notes
-----
```
magical imports:
the mojo fabric API dart:mojo
the mojom for the shell, proxying through C++ so that the shell pipe isn't exposed dart:mojo-shell
the Sky API dart:sky
```
This file contains documentation for what we hope Sky to support in due course.
It's mostly proposals. It's not intended to be comprehensive.

View File

@ -1,58 +0,0 @@
Animation API
=============
```dart
typedef void TimerCallback();
external void _addTask({ TimerCallback callback, Duration budget, int bits, int priority, Queue queue });
// see (runloop.md)[runloop.md] for the semantics of tasks and queues
// _addTask() does the zone magic on callback
external final Queue get _idleQueue;
external final Queue get _paintQueue;
external final Queue get _nextPaintQueue;
class AnimationTimer extends Timer {
factory whenIdle(TimerCallback callback, { Duration budget: 1.0 }) {
if (budget.inMilliseconds > 1.0)
budget = new Duration(milliseconds: 1.0);
_addTask(callback: callback, budget: budget, bits: IdleTask, priority: IdlePriority, queue: _idleQueue);
}
factory beforePaint(TimerCallback callback, { int priority: 0 }) {
_addTask(callback: callback, budget: new Duration(milliseconds: 1.0), bits: PaintTask, priority: priority, queue: _paintQueue);
}
factory nextFrame(TimerCallback callback, { int priority: 0 }) {
_addTask(callback: callback, budget: new Duration(milliseconds: 1.0), bits: PaintTask, priority: priority, queue: _nextPaintQueue);
}
}
```
Easing Functions
----------------
```dart
// part of the framework, not dart:sky
typedef void AnimationCallback();
abstract class EasingFunction {
EasingFunction({double duration: 0.0, AnimationCallback completionCallback: null });
double getFactor(Float time);
// calls completionCallback if time >= duration
// then returns a number ostensibly in the range 0.0 to 1.0
// (but it could in practice go outside this range, e.g. for
// animation styles that overreach then come back)
}
```
If you want to have two animations simultaneously, e.g. two
transforms, then you can add to the RenderNode's overrideStyles a
StyleValue that combines other StyleValues, e.g. a
"TransformStyleValueCombinerStyleValue", and then add to it the
regular animated StyleValues, e.g. multiple
"AnimatedTransformStyleValue" objects. A framework API could make
setting all that up easy, given the right underlying StyleValue
classes.

View File

@ -1,112 +0,0 @@
Built-In Elements
=================
```dart
SKY MODULE
<script>
import 'dart:sky';
class ImportElement extends Element {
ImportElement = Element;
@override
Type getLayoutManager() => null; // O(1)
}
class TemplateElement extends Element {
TemplateElement = Element;
// TODO(ianh): convert <template> to using a token stream instead of a Fragment
external Fragment get content; // O(1)
@override
Type getLayoutManager() => null; // O(1)
}
class ScriptElement extends Element {
ScriptElement = Element;
@override
Type getLayoutManager() => null; // O(1)
}
class StyleElement extends Element {
StyleElement = Element;
external List<Rule> getRules(); // O(N) in rules
@override
Type getLayoutManager() => null; // O(1)
}
class ContentElement extends Element {
ContentElement = Element;
external List<Node> getDistributedNodes(); // O(N) in distributed nodes
@override
Type getLayoutManager() => null; // O(1)
}
class ImgElement extends Element {
ImgElement = Element;
@override
Type getLayoutManager() => ImgElementLayoutManager; // O(1)
}
class DivElement extends Element {
DivElement = Element;
}
class SpanElement extends Element {
SpanElement = Element;
}
class IframeElement extends Element {
IframeElement = Element;
@override
Type getLayoutManager() => IframeElementLayoutManager; // O(1)
}
class TElement extends Element {
TElement = Element;
}
class AElement extends Element {
AElement = Element;
}
class TitleElement extends Element {
TitleElement = Element;
@override
Type getLayoutManager() => null; // O(1)
}
class _ErrorElement extends Element {
_ErrorElement._create();
@override
Type getLayoutManager() => _ErrorElementLayoutManager; // O(1)
}
void _init(script) {
module.registerElement('import', ImportElement);
module.registerElement('template', TemplateElement);
module.registerElement('script', ScriptElement);
module.registerElement('style', StyleElement);
module.registerElement('content', ContentElement);
module.registerElement('img', ImgElement);
module.registerElement('div', DivElement);
module.registerElement('span', SpanElement);
module.registerElement('iframe', IframeElement);
module.registerElement('t', TElement);
module.registerElement('a', AElement);
module.registerElement('title', TitleElement);
}
</script>
```

View File

@ -1,6 +0,0 @@
Best Practices and Conventions for Sky Frameworks
=================================================
* elements should not expose convenience property accessors that just
reflect content attributes.

View File

@ -1,288 +0,0 @@
Sky DOM APIs
============
```dart
// ELEMENT TREE API
abstract class Node extends EventTarget {
@override
external List<EventTarget> getEventDispatchChain(); // O(N) in number of ancestors across shadow trees
// implements EventTarget.getEventDispatchChain()
// returns the event dispatch chain (including handling shadow trees)
external Root get owner; // O(1)
external ParentNode get parentNode; // O(1)
Element get parentElement {
if (parentNode is Element)
return parentNode as Element;
return null;
}
external Node get previousSibling; // O(1)
Element get previousElementSibling {
var result = previousSibling;
while (result != null && result is! Element)
result = result.previousSibling;
return result as Element;
}
external Node get nextSibling; // O(1)
Element get nextElementSibling {
var result = nextSibling;
while (result != null && result is! Element)
result = result.nextSibling;
return result as Element;
}
// TODO(ianh): rename insertBefore() and insertAfter() since the Web
// has an insertBefore() that means something else. What's a good
// name, though?
external void _insertBefore(Node node); // O(N) in number of descendants
// node must be Text or Element, parentNode must be non-null
void insertBefore(List nodes) {
List.forEach((node) {
if (node is String)
node = new Text(node);
_insertBefore(node);
});
}
external void _insertAfter(Node node); // O(N) in number of arguments plus all their descendants
// node must be Text or Element, parentNode must be non-null
void insertAfter(List nodes) {
var lastNode = this;
List.forEach((node) {
if (node is String)
node = new Text(node);
lastNode._insertAfter(node);
lastNode = node;
});
}
void replaceWith(List nodes) {
if (nextSibling != null) {
var anchor = nextSibling;
remove(); // parentNode can't be null here, so this won't throw
anchor.insertBefore(nodes);
} else {
var anchor = parentNode;
remove(); // throws if parentNode is null
anchor.append(nodes);
}
}
external void remove(); // O(N) in number of descendants
// parentNode must be non-null
// called when parentNode changes
// this is why insertBefore(), append(), et al, are O(N) -- the whole affected subtree is walked
// mutating the element tree from within this is strongly discouraged, since it will result in the
// callbacks being invoked while the element tree is in a different state than implied by the callbacks
external void parentChangedCallback(ParentNode oldParent, ParentNode newParent); // O(N) in descendants
// default implementation calls attached/detached
void attachedCallback() { }
void detachedCallback() { }
external List<ContentElement> getDestinationInsertionPoints(); // O(N) in number of insertion points the node is in
// returns the <content> elements to which this element was distributed
external Node cloneNode({bool deep: false}); // O(1) if deep=false, O(N) in the number of descendants if deep=true
external ElementStyleDeclarationList get style; // O(1)
// for nodes that aren't in the ApplicationRoot's composed tree,
// returns null (so in particular orphaned subtrees and nodes in
// module Roots don't have one, nor do shadow tree Roots)
// also always returns null for ContentElement elements
// -- should be (lazily) updated when the node's parent chain
// changes (same time as, e.g., the id hashtable is marked
// dirty)
external RenderNode get renderNode; // O(1)
// this will be null until the first time it is rendered
// it becomes null again when it is taken out of the rendering (see style.md)
Type getLayoutManager() => null; // O(1)
void resetLayoutManager() { // O(1)
if (renderNode != null) {
renderNode._layoutManager = null;
renderNode._needsManager = true;
}
}
}
abstract class ParentNode extends Node {
external Node get firstChild; // O(1)
Element get firstElementChild {
var result = firstChild;
while (result != null && result is! Element)
result = result.nextSibling;
return result as Element;
}
external Node get lastChild; // O(1)
Element get lastElementChild {
var result = lastChild;
while (result != null && result is! Element)
result = result.previousSibling;
return result as Element;
}
// Returns a new List every time.
external List<Node> getChildren(); // O(N) in number of child nodes
List<Element> getChildElements() {
// that the following works without a cast is absurd
return getChildren().where((node) => node is Element).toList();
}
external void _appendChild(Node node); // O(N) in number of descendants
// node must be Text or Element
void appendChild(node) {
if (node is String)
node = new Text(node);
_appendChild(node);
}
void append(List nodes) {
nodes.forEach(appendChild);
}
external void _prependChild(Node node); // O(N) in number of descendants
// node must be Text or Element
void prependChild(node) {
if (node is String)
node = new Text(node);
_prependChild(node);
}
void prepend(List nodes) {
// note: not implemented in terms of _prependChild()
if (firstChild != null)
firstChild.insertBefore(nodes);
else
append(nodes);
}
external void removeChildren(); // O(N) in number of descendants
void setChild(node) {
removeChildren();
appendChild(node);
}
void setChildren(List nodes) {
removeChildren();
append(nodes);
}
}
class Attr {
const Attr (this.name, [this.value = '']); // O(1)
final String name; // O(1)
final String value; // O(1)
}
// @hasShadow annotation for registering elements
class _HasShadow {
const _HasShadow();
}
const hasShadow = const _HasShadow();
abstract class Element extends ParentNode {
Element({Map<String, String> attributes: null,
List children: null,
Module hostModule: null}) { // O(M+N), M = number of attributes, N = number of children nodes plus all their descendants
var shadowClass = reflectClass(hasShadow.runtimeType);
var shadowAnnotations = reflect(this).type.metadata.where((mirror) => mirror.type == shadowClass);
if (shadowAnnotations.length > 2)
throw new StateError('@hasShadow specified multiple times on ' + currentMirrorSystem().getName(reflectClass(this.runtimeType).simpleName));
bool needsShadow = shadowAnnotations.length == 1;
if (children != null)
children = children.map((node) => node is String ? new Text(node) : node).toList();
this._initElement(attributes, children, hostModule, needsShadow);
}
external void _initElement(Map<String, String> attributes, List children, Module hostModule, bool needsShadow);
// initialises the internal attributes table, which is a ordered list
// appends the given children nodes
// children must be Text or Element
// if needsShadow is true, creates a shadow tree
external bool hasAttribute(String name); // O(N) in number of attributes
external String getAttribute(String name); // O(N) in number of attributes
external void setAttribute(String name, [String value = '']); // O(N) in number of attributes
external void removeAttribute(String name); // O(N) in number of attributes
// calling setAttribute() with a null value removes the attribute
// (calling it without a value sets it to the empty string)
// Returns a new Array and new Attr instances every time.
external List<Attr> getAttributes(); // O(N) in number of attributes
external Root get shadowRoot; // O(1)
// returns the shadow root
void endTagParsedCallback() { }
void attributeChangedCallback(String name, String oldValue, String newValue) { }
// name will never be null when this is called by sky
// TODO(ianh): does a node ever need to know when it's been redistributed?
@override
Type getLayoutManager() { // O(1)
if (renderNode)
return renderNode.getProperty(phDisplay);
return super.getLayoutManager();
}
}
class Text extends Node {
external Text([String value = '']); // O(1)
external String get value; // O(1)
external void set (String value); // O(1)
void valueChangedCallback(String oldValue, String newValue) { }
@override
Type getLayoutManager() => TextLayoutManager; // O(1)
}
class Fragment extends ParentNode {
Fragment({List children}); // O(N) in number of arguments plus all their descendants
// children must be String, Text, or Element
}
class Root extends ParentNode {
Root({List children: null, this.host}) { // O(N) in number of children nodes plus all their descendants
if (children != null)
children = children.map((node) => node is String ? new Text(node) : node).toList();
this._initRoot(children);
}
external void _initRoot(List children);
// appends the given children nodes
// children must be Text or Element
final Element host;
external Element findId(String id); // O(1)
// throws if id is null
}
class ApplicationRoot extends Root {
ApplicationRoot ({List children}) : super(children: children); // O(N) in number of children nodes arguments plus all their descendants
@override
Type getLayoutManager() => rootLayoutManager; // O(1)
}
Type rootLayoutManager = BlockLayoutManager; // O(1)
class SelectorQuery {
external SelectorQuery(String selector); // O(F()) where F() is the complexity of the selector
external bool matches(Element element); // O(F())
external Element find(Node root); // O(N*F())+O(M) where N is the number of descendants and M the average depth of the tree
external List<Element> findAll(Node root); // O(N*F())+O(N*M) where N is the number of descendants and M the average depth of the tree
// find() and findAll() throw if the root is not one of the following:
// - Element
// - Fragment
// - Root
}
```

View File

@ -1,31 +0,0 @@
Frameworks
----------
Sky is intended to support multiple frameworks. Here is one way you
could register a custom element using Dart annotations:
```dart
// @tagname annotation for registering elements
// only useful when placed on classes that inherit from Element
class tagname extends AutomaticMetadata {
const tagname(this.name);
final String name;
void init(DeclarationMirror target, Module module, ScriptElement script) {
assert(target is ClassMirror);
if (!(target as ClassMirror).isSubclassOf(reflectClass(Element)))
throw new UnsupportedError('@tagname can only be used on descendants of Element');
module.registerElement(name, (target as ClassMirror).reflectedType);
}
}
```
A framework that used the above code could use the following code to
get the tag name of an element:
```dart
String getTagName(Element element) { // O(N) in number of annotations on the class
// throws a StateError if the class doesn't have an @tagname annotation
var tagnameClass = reflectClass(tagname);
return (reflectClass(element.runtimeType).metadata.singleWhere((mirror) => mirror.type == tagnameClass).reflectee as tagname).name;
}
```

View File

@ -1,222 +0,0 @@
Sky Markup: Syntax
==================
A Sky file must consist of the following components:
1. If the file is intended to be a top-level Sky application, the
string "``#!mojo mojo:sky``" followed by a U+0020, U+000A or
U+000D character.
If the file is intended to be a module, then the string "SKY", a
U+0020 (space) character, the string "MODULE", and a U+0020,
U+000A or U+000D character.
These signatures make it more difficult to e.g. embed some Sky
markup into a PNG and then cause someone to import that image as a
module.
2. Zero or more of the following, in any order:
- comments
- text
- escapes
- elements
Sky files must be encoded using UTF-8.
A file that doesn't begin with the "``#!mojo mojo:sky``" signature
isn't a Sky application file. For example:
#!mojo https://example.com/runtimes/sky.asmjs
Hello World
...is not a Sky file, even if ``https://example.com/runtimes/sky.asmjs``
is an implementation of the Sky runtime: it's just a file intended
specifically for that runtime.
The ``mojo:sky`` URL represents the generic Sky runtime provided by
your Mojo runtime vendor.
Comments
--------
Comments start with the sequence "``<!--``" and end with the
sequence "``-->``", where the start and end hyphens don't overlap.
In between these characters, any sequence of characters is allowed
except "``-->``", which terminates the comment. Comments cannot,
therefore, be nested.
Text
----
Any sequence of Unicode characters other than ``<``, ``&``, and
U+0000.
Escapes
-------
There are three kinds of escapes:
### Hex
They begin with the sequence ``&#x`` or ``&#X``, followed by a
sequence of hex characters (lowercase or uppercase), followed by a
semicolon. The number 0 is not allowed.
### Decimal
They begin with the sequence ``&#`` or ``&#``, followed by a
sequence of decimal characters, followed by a semicolon. The number 0
is not allowed.
### Named
They begin with the sequence ``&``, followed by any characters,
followed by a semicolon.
The following names work:
| Name | Character | Unicode |
| ---- | --------- | ------- |
| `lt` | `<` | U+003C LESS-THAN SIGN character |
| `gt` | `>` | U+003E GREATER-THAN SIGN character |
| `amp` | `&` | U+0026 AMPERSAND character |
| `apos` | `'` | U+0027 APOSTROPHE character |
| `quot` | `"` | U+0022 QUOTATION MARK character |
Elements
--------
An element consists of the following:
1. ``<``
2. Tag name: A sequence of characters other than ``/``, ``>``,
U+0020, U+000A, U+000D (whitespace).
3. Zero or more of the following:
1. One or more U+0020, U+000A, U+000D (whitespace).
2. Attribute name: A sequence of characters other than ``/``,
``=``, ``>``, U+0020, U+000A, U+000D (whitespace).
3. Optionally:
1. Zero or more U+0020, U+000A, U+000D (whitespace) characters.
2. ``=``
3. Zero or more U+0020, U+000A, U+000D (whitespace) characters.
4. Attribute value: Either:
- ``'`` followed by attribute text other than ``'``
followed by a terminating ``'``.
- ``"`` followed by attribute text other than ``'``
followed by a terminating ``"``.
- attribute text other than ``/``, ``>``,
U+0020, U+000A, U+000D (whitespace).
"Attribute text" is escapes or any unicode characters other
than U+0000.
4. Either:
- For a void element:
1. ``/``, indicating an empty element.
2. ``>``
- For a non-void element:
2. ``>``
3. The element's contents:
- If the element's tag name is ``script``, then any sequence of
characters other than U+0000, but there must not be the
substring ``</script``. The sequence must be valid sky script.
- If the element's tag name is ``style``, then any sequence of
characters other than U+0000, but there must not be the
substring ``</style``. The sequence must be valid sky style.
- Otherwise, zero or more of the following, in any order:
- comments
- text
- escapes
- elements
4. Finally, the end tag, which may be omitted if the element's tag
name is not ``template``, consisting of:
1. ``<``
2. ``/``
3. Same sequence of characters as "tag name" above; this may
be omitted if no start tags have had their end tag omitted
since this element's start tag, unless this element's tag
name is ``script`` or ``style``.
4. ``>``
Sky Markup: Elements
====================
The Sky language consists of very few elements, since it is expected
that everything of note would be provided by frameworks.
The following elements are implicitly registered by default, even if
you haven't imported anything. You can get to their constructors if
you import dart:sky (basically, dart:sky is always imported by defaul;
it's the runtime library). None of these elements have shadow trees.
``<import src="foo.sky">``
- Downloads and imports foo.sky in the background.
``<import src="foo.sky" as="foo">``
- Downloads and imports foo.sky in the background, using "foo" as its
local name (see ``<script>``).
``<template>``
- The contents of the element aren't placed in the Element itself.
They are instead placed into a Fragment that you can obtain from
the element's "content" attribute.
``<script>``
- Blocks until all previous imports have been loaded, then loads the
library given in the script block, as described in
[scripts.md](scripts.md).
``<style>``
- Adds the contents to the module's styles.
``<content>``
``<content select="...">``
- In a shadow tree, acts as an insertion point for distributed nodes.
The select="" attribute gives the selector to use to pick the nodes
to place in this insertion point; it defaults to everything.
``<img src="foo.bin">``
- Sky fetches the bits for foo.bin, looks for a decoder for those
bits, and renders the bits that the decoder returns.
``<div>``
- Element that does nothing.
``<span>``
- Element that does nothing.
``<iframe src="foo.bin">``
- Sky tells mojo to open an application for foo.bin, and hands that
application a view so that the application can render appropriately.
``<t>``
- Within a ``<t>`` section, whitespace is not trimmed from the start and
end of text nodes by the parser.
TOOD(ianh): figure out if the authoring aesthetics of this are ok
``<a href="foo.bin">``
- A widget that, when invoked, causes mojo to open a new application
for "foo.bin".
``<title>``
- Sets the contents as the application's title (as provided by Sky to
the view manager). (Actually just ensures that any time the element
is mutated, module.application.title is set to the element's
contents.)
Sky Markup: Global Attributes
=============================
The following attributes are available on all elements:
* ``id=""`` (any value)
* ``class=""`` (any value, space-separated)
* ``style=""`` (declaration part of a Sky style rule)
* ``lang=""`` (language code)
* ``dir=""`` (ltr or rtl only)
* ``contenteditable=""`` (subject to future developments)
* ``tabindex=""`` (subject to future developments)

View File

@ -1,148 +0,0 @@
Sky Module System
=================
This document describes the Sky module system.
Overview
--------
The Sky module system is based on the ``import`` element. In its
most basic form, you import a module as follows:
```html
<import src="path/to/module.sky" />
```
As these ``import`` elements are inserted into a module's element
tree, the module's list of outstanding dependencies grows. When an
imported module completes, it is removed from the importing module's
list of outstanding dependencies.
Before compiling script or inserting an element that is not already
registered, the parser waits until the list of outstanding
dependencies is empty. After the parser has finished parsing, the
module waits until its list of outstanding dependencies is empty
before marking itself complete.
The ``as`` attribute on the ``import`` element binds a name to the
imported module:
```html
<import src="path/to/chocolate.sky" as="chocolate" />
```
Each module implicitly imports the [Built-In Elements
Module](builtins.md).
When a module imports another, and the ``import`` element has no
``as`` attribute, then any elements registered in that module whose
tag names do not begin with an underscore must be registered on the
importing module. (If multiple elements are registered with the same
name, that name gets marked as dead for that module and all the
registrations for that name are discarded.)
TODO(ianh): decide if elements imported with "as" should be imported
but with the "as" name prefixed, as in ``<foo.button>``
Module API
----------
Each module consists of one or more libraries. The first library in a
module is the *element tree library*, which consists of the following
code for a Sky module:
```dart
import 'dart:sky';
final Module module = new Module();
```
...and the following code for a Sky application:
```dart
import 'dart:sky';
final Module module = new Application();
```
The ``<script>`` elements found in the module's element tree create
the subsequent libraries. Each one first imports the ``dart:mirror``
library, then the ``dart:sky`` module, then the first library
described above, then all the modules referenced by ``<import>``
element up to that ``<script>`` element and all the libraries defined
by ``<script>`` elements up to that point, interleaved so as to
maintain the same relative order as those elements were first seen by
the parser.
When a library imports a module, it actually imports all the libraries
that were declared by that module except the aforementioned element
tree library. If the ``as`` attribute is present on the ``import``
element, all the libraries are bound to the same name.
At the end of the ``<script>`` block's source, if it parsed correctly
and completely, the conceptual equivalent of the following code is
appended (but without affecting the library's list of declarations and
without any possibility of it clashing with identifiers described in
the library itself):
```dart
class _ { }
void main(ScriptElement script) {
LibraryMirror library = reflectClass(_).owner as LibraryMirror;
if (library.declarations.containsKey(#_init) && library.declarations[#_init] is MethodMirror)
_init(script);
AutomaticMetadata.runLibrary(library, module, script);
}
```
Then, that ``main(script)`` function is called, with ``script`` set to
the ``ScriptElement`` object representing the relevant ``<script>``
element.
TODO(ianh): decide what URL and name we should give the libraries, as
exposed in MirrorSystem.getName(libraryMirror.qualifiedName) etc
The ``Module`` class is defined in ``dart:sky`` as follows:
```dart
abstract class AbstractModule extends EventTarget {
AbstractModule({this.url, this.elements});
final String url;
final Root elements; // O(1)
// the Root node of the module or application's element tree
external Future<Module> import(String url); // O(Yikes)
// load and return the URL at the given Module
// if it's already loaded, the future will resolve immediately
// if loading fails, the future will have an error
external List<Module> getImports(); // O(N)
// returns the Module objects of all the imported modules
external void registerElement(String tagname, Type elementClass); // O(1)
// registers a tag name with the parser
// only useful during parse time
// verify that tagname isn't null or empty
// verify that elementClass is the Type of a class that extends Element (directly or indirectly, but not via "implements" or "with")
// (see the @tagname code for an example of how to verify that from dart)
// verify that there's not already a class registered for this tag name
// if there is, then mark this tagname is broken, so that it acts as if it's not registered in the parser,
// and, if this is the first time it was marked broken, log a console message regarding the issue
// (mention the tag name but not the classes, so that it's not observable that this currently happens out of order)
}
class Module extends AbstractModule {
Module({String url, Root elements, this.application}) :
super(url: url, elements: elements); // O(1)
final Application application; // O(1)
}
class Application extends AbstractModule {
Application({String url, Root elements, this.gestureManager}) :
super(url: url, elements: elements); // O(1)
external String get title; // O(1)
external void set title(String newValue); // O(1)
final GestureManager gestureManager;
}
```

View File

@ -1,846 +0,0 @@
Parsing
=======
Parsing in Sky is a strict pipeline consisting of five stages:
- decoding, which converts incoming bytes into Unicode characters
using UTF-8.
- normalising, which manipulates the sequence of characters.
- tokenising, which converts these characters into four kinds of
tokens: character tokens, start tag tokens, end tag tokens, and
automatic end tag tokens. Character tokens have a single character
value. Start and end tag tokens have a tag name, and a list of
name/value pairs known as attributes.
- token cleanup, which converts sequences of character tokens into
string tokens, and removes duplicate attributes in tag tokens.
- tree construction, which converts these tokens into a tree of nodes.
Later stages cannot affect earlier stages.
When a sequence of bytes is to be parsed, there is always a defined
_parsing context_, which is either an Application object or a Module
object.
Decoding stage
--------------
To decode a sequence of bytes _bytes_ for parsing, the [utf-8
decode](https://encoding.spec.whatwg.org/#utf-8-decode) algorithm must
be used to transform _bytes_ into a sequence of characters
_characters_.
Note: The decoder will strip a leading BOM if any.
This sequence must then be passed to the normalisation stage.
Normalisation stage
-------------------
To normalise a sequence of characters, apply the following rules:
* Any U+000D character followed by a U+000A character must be removed.
* Any U+000D character not followed by a U+000A character must be
converted to a U+000A character.
* Any U+0000 character must be converted to a U+FFFD character.
The converted sequence of characters must then be passed to the
tokenisation stage.
Tokenisation stage
------------------
To tokenise a sequence of characters, a state machine is used.
Initially, the state machine must begin in the **signature** state.
Each character in turn must be processed according to the rules of the
state at the time the character is processed. A character is processed
once it has been _consumed_. This produces a stream of tokens; the
tokens must be passed to the token cleanup stage.
When the last character is consumed, the tokeniser ends.
### Expecting a string ###
When the user agent is to _expect a string_, it must run these steps:
1. Let _expectation_ be the string to expect. When this string is
indexed, the first character has index 0.
2. Assertion: The first character in _expectation_ is the current
character, and _expectation_ has more than one character.
3. Consume the current character.
4. Let _index_ be 1.
5. Let _success_ and _failure_ be the states specified for success and
failure respectively.
6. Switch to the **expect a string** state.
### Tokeniser states ###
#### **Signature** state ####
If the current character is...
* '``#``': If the _parsing context_ is not an Application, switch to
the _failed signature_ state. Otherwise, expect the string
"``#!mojo mojo:sky``", with _after signature_ as the _success_
state and _failed signature_ as the _failure_ state.
* '``S``': If the _parsing context_ is not a Module, switch to the
_failed signature_ state. Otherwise, expect the string
"``SKY MODULE``", with _after signature_ as the _success_ state,
and _failed signature_ as the _failure_ state.
* Anything else: Jump to the **failed signature** state.
#### **Expect a string** state ####
If the current character is not the same as the <i>index</i>th character in
_expectation_, then switch to the _failure_ state.
Otherwise, consume the character, and increase _index_. If _index_ is
now equal to the length of _expectation_, then switch to the _success_
state.
#### **After signature** state ####
If the current character is...
* U+000A: Consume the character and switch to the **data** state.
* U+0020: Consume the character and switch to the **consume rest of
line** state.
* Anything else: Switch to the **failed signature** state.
#### **Failed signature** state ####
Stop parsing. No tokens are emitted. The file is not a sky file.
#### **Consume rest of line** state ####
If the current character is...
* U+000A: Consume the character and switch to the **data** state.
* Anything else: Consume the character and stay in this state.
#### **Data** state ####
If the current character is...
* '``<``': Consume the character and switch to the **tag open** state.
* '``&``': Consume the character and switch to the **character
reference** state, with the _return state_ set to the **data**
state, and the _emitting operation_ being to emit a character token
for the given character.
* Anything else: Emit the current input character as a character
token. Consume the character. Stay in this state.
#### **Script raw data** state ####
If the current character is...
* '``<``': Consume the character and switch to the **script raw
data: close 1** state.
* Anything else: Emit the current input character as a character
token. Consume the character. Stay in this state.
#### **Script raw data: close 1** state ####
If the current character is...
* '``/``': Consume the character and switch to the **script raw
data: close 2** state.
* Anything else: Emit '``<``' character tokens. Switch to the
**script raw data** state without consuming the character.
#### **Script raw data: close 2** state ####
If the current character is...
* '``s``': Consume the character and switch to the **script raw
data: close 3** state.
* Anything else: Emit '``</``' character tokens. Switch to the
**script raw data** state without consuming the character.
#### **Script raw data: close 3** state ####
If the current character is...
* '``c``': Consume the character and switch to the **script raw
data: close 4** state.
* Anything else: Emit '``</s``' character tokens. Switch to the
**script raw data** state without consuming the character.
#### **Script raw data: close 4** state ####
If the current character is...
* '``r``': Consume the character and switch to the **script raw
data: close 5** state.
* Anything else: Emit '``</sc``' character tokens. Switch to the
**script raw data** state without consuming the character.
#### **Script raw data: close 5** state ####
If the current character is...
* '``i``': Consume the character and switch to the **script raw
data: close 6** state.
* Anything else: Emit '``</scr``' character tokens. Switch to the
**script raw data** state without consuming the character.
#### **Script raw data: close 6** state ####
If the current character is...
* '``p``': Consume the character and switch to the **script raw
data: close 7** state.
* Anything else: Emit '``</scri``' character tokens. Switch to the
**script raw data** state without consuming the character.
#### **Script raw data: close 7** state ####
If the current character is...
* '``t``': Consume the character and switch to the **script raw
data: close 8** state.
* Anything else: Emit '``</scrip``' character tokens. Switch to the
**script raw data** state without consuming the character.
#### **Script raw data: close 8** state ####
If the current character is...
* U+0020, U+000A, '``/``', '``>``': Create an end tag token, and
let its tag name be the string '``script``'. Switch to the
**before attribute name** state without consuming the character.
* Anything else: Emit '``</script``' character tokens. Switch to the
**script raw data** state without consuming the character.
#### **Style raw data** state ####
If the current character is...
* '``<``': Consume the character and switch to the **style raw
data: close 1** state.
* Anything else: Emit the current input character as a character
token. Consume the character. Stay in this state.
#### **Style raw data: close 1** state ####
If the current character is...
* '``/``': Consume the character and switch to the **style raw
data: close 2** state.
* Anything else: Emit '``<``' character tokens. Switch to the
**style raw data** state without consuming the character.
#### **Style raw data: close 2** state ####
If the current character is...
* '``s``': Consume the character and switch to the **style raw
data: close 3** state.
* Anything else: Emit '``</``' character tokens. Switch to the
**style raw data** state without consuming the character.
#### **Style raw data: close 3** state ####
If the current character is...
* '``t``': Consume the character and switch to the **style raw
data: close 4** state.
* Anything else: Emit '``</s``' character tokens. Switch to the
**style raw data** state without consuming the character.
#### **Style raw data: close 4** state ####
If the current character is...
* '``y``': Consume the character and switch to the **style raw
data: close 5** state.
* Anything else: Emit '``</st``' character tokens. Switch to the
**style raw data** state without consuming the character.
#### **Style raw data: close 5** state ####
If the current character is...
* '``l``': Consume the character and switch to the **style raw
data: close 6** state.
* Anything else: Emit '``</sty``' character tokens. Switch to the
**style raw data** state without consuming the character.
#### **Style raw data: close 6** state ####
If the current character is...
* '``e``': Consume the character and switch to the **style raw
data: close 7** state.
* Anything else: Emit '``</styl``' character tokens. Switch to the
**style raw data** state without consuming the character.
#### **Style raw data: close 7** state ####
If the current character is...
* U+0020, U+000A, '``/``', '``>``': Create an end tag token, and
let its tag name be the string '``style``'. Switch to the
**before attribute name** state without consuming the character.
* Anything else: Emit '``</style``' character tokens. Switch to the
**style raw data** state without consuming the character.
#### **Tag open** state ####
If the current character is...
* '``!``': Consume the character and switch to the **comment start
1** state.
* '``/``': Consume the character and switch to the **close tag
state** state.
* '``>``': Emit character tokens for '``<>``'. Consume the current
character. Switch to the **data** state.
* '``0``'..'``9``', '``a``'..'``z``', '``A``'..'``Z``',
'``-``', '``_``', '``.``': Create a start tag token, let its
tag name be the current character, consume the current character and
switch to the **tag name** state.
* Anything else: Emit the character token for '``<``'. Switch to the
**data** state without consuming the current character.
#### **Close tag** state ####
If the current character is...
* '``>``': Emit an automatic end tag token. Switch to the **data**
state.
* '``0``'..'``9``', '``a``'..'``z``', '``A``'..'``Z``',
'``-``', '``_``', '``.``': Create an end tag token, let its
tag name be the current character, consume the current character and
switch to the **tag name** state.
* Anything else: Emit the character tokens for '``</``'. Switch to
the **data** state without consuming the current character.
#### **Tag name** state ####
If the current character is...
* U+0020, U+000A: Consume the current character. Switch to the
**before attribute name** state.
* '``/``': Consume the current character. Switch to the **void tag**
state.
* '``>``': Consume the current character. Switch to the **after
tag** state.
* Anything else: Append the current character to the tag name, and
consume the current character. Stay in this state.
#### **Void tag** state ####
If the current character is...
* '``>``': Consume the current character. Switch to the **after void
tag** state.
* Anything else: Switch to the **before attribute name** state without
consuming the current character.
#### **Before attribute name** state ####
If the current character is...
* U+0020, U+000A: Consume the current character. Stay in this state.
* '``/``': Consume the current character. Switch to the **void tag**
state.
* '``>``': Consume the current character. Switch to the **after
tag** state.
* Anything else: Create a new attribute in the tag token, and set its
name to the current character and its value to the empty string.
Consume the current character. Switch to the **attribute name**
state.
#### **Attribute name** state ####
If the current character is...
* U+0020, U+000A: Consume the current character. Switch to the **after
attribute name** state.
* '``/``': Consume the current character. Switch to the **void tag**
state.
* '``=``': Consume the current character. Switch to the **before
attribute value** state.
* '``>``': Consume the current character. Switch to the **after
tag** state.
* Anything else: Append the current character to the most recently
added attribute's name, and consume the current character. Stay in
this state.
#### **After attribute name** state ####
If the current character is...
* U+0020, U+000A: Consume the current character. Stay in this state.
* '``/``': Consume the current character. Switch to the **void tag**
state.
* '``=``': Consume the current character. Switch to the **before
attribute value** state.
* '``>``': Consume the current character. Switch to the **after
tag** state.
* Anything else: Create a new attribute in the tag token, and set its
name to the current character and its value to the empty string.
Consume the current character. Switch to the **attribute name**
state.
#### **Before attribute value** state ####
If the current character is...
* U+0020, U+000A: Consume the current character. Stay in this state.
* '``>``': Consume the current character. Switch to the **after
tag** state.
* '``'``': Consume the current character. Switch to the
**single-quoted attribute value** state.
* '``"``': Consume the current character. Switch to the
**double-quoted attribute value** state.
* Anything else: Switch to the **unquoted attribute value** state
without consuming the current character.
#### **Single-quoted attribute value** state ####
If the current character is...
* '``'``': Consume the current character. Switch to the
**before attribute name** state.
* '``&``': Consume the character and switch to the **character
reference** state, with the _return state_ set to the
**single-quoted attribute value** state and the _emitting operation_
being to append the given character to the value of the most
recently added attribute.
* Anything else: Append the current character to the value of the most
recently added attribute. Consume the current character. Stay in
this state.
#### **Double-quoted attribute value** state ####
If the current character is...
* '``"``': Consume the current character. Switch to the
**before attribute name** state.
* '``&``': Consume the character and switch to the **character
reference** state, with the _return state_ set to the
**double-quoted attribute value** state and the _emitting operation_
being to append the given character to the value of the most
recently added attribute.
* Anything else: Append the current character to the value of the most
recently added attribute. Consume the current character. Stay in
this state.
#### **Unquoted attribute value** state ####
If the current character is...
* U+0020, U+000A: Consume the current character. Switch to the
**before attribute name** state.
* '``>``': Consume the current character. Switch to the **after tag**
state.
* '``&``': Consume the character and switch to the **character
reference** state, with the _return state_ set to the **unquoted
attribute value** state, and the _emitting operation_ being to
append the given character to the value of the most recently added
attribute.
* Anything else: Append the current character to the value of the most
recently added attribute. Consume the current character. Stay in
this state.
#### **After tag** state ####
Emit the tag token.
If the tag token was a start tag token and the tag name was
'``script``', then switch to the **script raw data** state.
If the tag token was a start tag token and the tag name was
'``style``', then switch to the **style raw data** state.
Otherwise, switch to the **data** state.
#### **After void tag** state ####
Emit the tag token.
If the tag token is a start tag token, emit an end tag token with the
same tag name.
Switch to the **data** state.
#### **Comment start 1** state ####
If the current character is...
* '``-``': Consume the character and switch to the **comment start
2** state.
* Anything else: Emit character tokens for '``<!``'. Switch to the
**data** state without consuming the current character.
#### **Comment start 2** state ####
If the current character is...
* '``-``': Consume the character and switch to the **comment**
state.
* Anything else: Emit character tokens for '``<!-``'. Switch to the
**data** state without consuming the current character.
#### **Comment** state ####
If the current character is...
* '``-``': Consume the character and switch to the **comment end 1**
state.
* Anything else: Consume the character and stay in this state.
#### **Comment end 1** state ####
If the current character is...
* '``-``': Consume the character, switch to the **comment end 2**
state.
* Anything else: Consume the character, and switch to the **comment**
state.
#### **Comment end 2** state ####
If the current character is...
* '``>``': Consume the character and switch to the **data** state.
* '``-``': Consume the character, but stay in this state.
* Anything else: Consume the character, and switch to the **comment**
state.
#### **Character reference** state ####
Let _raw value_ be the string '``&``'.
Append the current character to _raw value_.
If the current character is...
* '``#``': Consume the character, and switch to the **numeric
character reference** state.
* '``0``'..'``9``', '``a``'..'``f``', '``A``'..'``F``': switch to the
**named character reference** state without consuming the current
character.
* Anything else: Run the _emitting operation_ for all but the last
character in _raw value_, and switch to the _return state_ without
consuming the current character.
#### **Numeric character reference** state ####
Append the current character to _raw value_.
If the current character is...
* '``x``', '``X``': Consume the character and switch to the **before
hexadecimal numeric character reference** state.
* '``0``'..'``9``': Let _value_ be the numeric value of the
current character interpreted as a decimal digit, consume the
character, and switch to the **decimal numeric character reference**
state.
* Anything else: Run the _emitting operation_ for all but the last
character in _raw value_, and switch to the _return state_ without
consuming the current character.
#### **Before hexadecimal numeric character reference** state ####
Append the current character to _raw value_.
If the current character is...
* '``0``'..'``9``', '``a``'..'``f``', '``A``'..'``F``':
Let _value_ be the numeric value of the current character
interpreted as a hexadecimal digit, consume the character, and
switch to the **hexadecimal numeric character reference** state.
* Anything else: Run the _emitting operation_ for all but the last
character in _raw value_, and switch to the _return state_ without
consuming the current character.
#### **Hexadecimal numeric character reference** state ####
Append the current character to _raw value_.
If the current character is...
* '``0``'..'``9``', '``a``'..'``f``', '``A``'..'``F``':
Let _value_ be sixteen times _value_ plus the numeric value of the
current character interpreted as a hexadecimal digit.
* '``;``': Consume the character. If _value_ is between 0x0001 and
0x10FFFF inclusive, but is not between 0xD800 and 0xDFFF inclusive,
run the _emitting operation_ with a unicode character having the
scalar value _value_; otherwise, run the _emitting operation_ with
the character U+FFFD. Then, in either case, switch to the _return
state_.
* Anything else: Run the _emitting operation_ for all but the last
character in _raw value_, and switch to the _return state_ without
consuming the current character.
#### **Decimal numeric character reference** state ####
Append the current character to _raw value_.
If the current character is...
* '``0``'..'``9``': Let _value_ be ten times _value_ plus the
numeric value of the current character interpreted as a decimal
digit.
* '``;``': Consume the character. If _value_ is between 0x0001 and
0x10FFFF inclusive, but is not between 0xD800 and 0xDFFF inclusive,
run the _emitting operation_ with a unicode character having the
scalar value _value_; otherwise, run the _emitting operation_ with
the character U+FFFD. Then, in either case, switch to the _return
state_.
* Anything else: Run the _emitting operation_ for all but the last
character in _raw value_, and switch to the _return state_ without
consuming the current character.
#### **Named character reference** state ####
Append the current character to _raw value_.
If the current character is...
* '``;``': Consume the character.
If the _raw value_ is...
- '``&amp;``: Emit Run the _emitting operation_ for the character
'``&``'.
- '``&apos;``: Emit Run the _emitting operation_ for the character
'``'``'.
- '``&gt;``: Emit Run the _emitting operation_ for the character
'``>``'.
- '``&lt;``: Emit Run the _emitting operation_ for the character
'``<``'.
- '``&quot;``: Emit Run the _emitting operation_ for the character
'``"``'.
Then, switch to the _return state_.
* '``0``'..'``9``', '``a``'..'``z``', '``A``'..'``Z``': Consume the
character and stay in this state.
* Anything else: Run the _emitting operation_ for all but the last
character in _raw value_, and switch to the _return state_ without
consuming the current character.
Token cleanup stage
-------------------
Replace each sequence of character tokens with a single string token
whose value is the concatenation of all the characters in the
character tokens.
For each start tag token, remove all but the first name/value pair for
each name (i.e. remove duplicate attributes, keeping only the first
one).
TODO(ianh): maybe sort the attributes?
For each end tag token, remove the attributes entirely.
If the token is a start tag token, notify the JavaScript token stream
callback of the token.
Then, pass the tokens to the tree construction stage.
Tree construction stage
-----------------------
To construct a node tree from a _sequence of tokens_ and an element
tree rooted at a `Root` node _root_ (this is implemented in JS):
1. Initialize the _stack of open nodes_ to be _root_.
2. Initialize _imported modules_ to an empty list.
3. Consider each token _token_ in the _sequence of tokens_ in turn, as
follows. If a token is to be skipped, then jump straight to the
next token, without doing any more work with the skipped token.
- If _token_ is a string token,
1. If the value of the token contains only U+0020 and U+000A
characters, and there is no ``t`` element on the _stack of
open nodes_, then skip the token.
2. Create a text node _node_ whose character data is the value of
the token.
3. Append _node_ to the top node in the _stack of open nodes_.
- If _token_ is a start tag token,
1. If the tag name isn't a registered tag name, then yield until
_imported modules_ contains no entries with unresolved
promises.
2. If the tag name is not registered, then let the ErrorElement
constructor from dart:sky be the element constructor.
Otherwise, let the element constructor be the registered
element's constructor for that tag name in this module.
3. Create an element _node_ with the attributes given by the
token by calling the constructor.
4. If _node_ is not an Element object, then let the constructor
be the ErrorElement constructor and return to the previous
step.
5. Append _node_ to the top node in the _stack of open nodes_.
6. Push _node_ onto the top of the _stack of open nodes_.
7. If _node_ is a ``template`` element, then:
1. Let _fragment_ be the ``Fragment`` object that the
``template`` element uses as its template contents
container.
2. Push _fragment_ onto the top of the _stack of open nodes_.
If _node_ is an ``import`` element, then:
1. Let ``url`` be the value of _node_'s ``src`` attribute.
2. Call ``parsing context``'s ``importModule()`` method,
passing it ``url``.
3. Add the returned promise to _imported modules_; if _node_
has an ``as`` attribute, associate the entry with that
name.
- If _token_ is an end tag token:
1. If the tag name is registered, let _tag name_ be that tag
name. Otherwise, let _tag name_ be "error".
2. Let _node_ be the topmost node in the _stack of open nodes_
whose tag name is _tag name_, if any. If there isn't one, skip
this token.
3. If there's a ``template`` element in the _stack of open
nodes_ above _node_, then skip this token.
4. Pop nodes from the _stack of open nodes_ until _node_ has been
popped.
5. If _node_'s tag name is ``script``, then yield until _imported
modules_ contains no entries with unresolved promises, then
execute the script given by the element's contents, using the
associated names as appropriate.
- If _token_ is an automatic end tag token:
1. Pop the top node from the _stack of open nodes_, unless it is
the _root_ node.
4. Yield until _imported modules_ has no promises.
5. Fire a ``load`` event at the _parsing context_ object.

View File

@ -1,144 +0,0 @@
Sky's Run Loop
==============
Sky has three task queues, named idle, frame, and nextFrame.
When a task is run, it has a time budget, and if the time budget is
exceeded, then a catchable DeadlineExceededException exception is
fired.
```dart
class DeadlineExceededException implements Exception { }
```
There is a method you can use that guards your code against these
exceptions:
```dart
typedef void Callback();
external guardAgainstDeadlineExceptions(Callback callback);
// runs callback.
// if the time budget for the _task_ expires while the callback is
// running, the callback isn't interrupted, but the method will throw
// an exception once the callback returns.
```
When Sky is to *process a task queue until a particular time*, with a
queue *relevant task queue*, bits *filter bits*, a time
*particular time*, and an *idle rule* which is either "sleep" or
"abort", it must run the following steps:
1. Let *remaining time* be the time until the given *particular time*.
2. If *remaining time* is less than or equal to zero, exit this
algorithm.
3. Let *task list* be the list of tasks in the *relevant task queue*
that have bits that, when 'and'ed with *filter bits*, are equal to
*filter bits*, whose required budget is less than or equal to
*remaining time*; and whose due time, if any, has been reached.
4. If *task list* is empty, then if *idle rule* is "sleep" then return
to step 1, otherwise, exit this algorithm.
5. Sort *task list* by the priority of each task, highest first.
6. Remove the top task from *task list* from the *relevant task
queue*, and let that be *selected task*.
7. Run *selected task*, with a budget of *remaining time* or 1ms,
whichever is shorter.
8. Return to step 1.
When Sky is to *drain a task queue for a specified time*, with a queue
*relevant task queue*, bits *filter bits*, and a duration *budget*, it
must run the following steps:
2. Let *task list* be the list of tasks in the *relevant task queue*
that have bits that, when 'or'ed with *filter bits*, are non-zero;
and whose required budget is less than or equal to *budget*.
4. If *task list* is empty, then exit.
5. Sort *task list* by the priority of each task, highest first.
6. Remove the top task from *task list* from the *relevant task
queue*, and let that be *selected task*.
7. Run *selected task*, with a budget of *budget*.
8. Decrease *budget* with the amount of time that *selected task* took
to run.
9. If *selected task* threw an uncaught DeadlineExceededException
exception, then cancel all the tasks in *relevant task queue*.
Otherwise, return to step 2.
Sky's run loop consists of running the following, at 120Hz (each loop
takes 8.333ms):
1. *Drain* the *frame task queue*, with bits
`application.frameTaskBits`, for 1ms.
2. Create a task that does the following, then run it with a budget of
1ms:
1. Update the render tree, including calling childAdded(),
childRemoved(), and getLayoutManager() as needed, catching any
exceptions other than DeadlineExceededException exceptions.
If an exception is thrown by this, then the RenderNode tree will
continue to not quite match the element tree, which is fine.
3. If there are no tasks on the *idle task queue* with bits
`LayoutKind`, create a task that tells the root node to layout if
it has needsLayout or descendantNeedsLayout, mark that with
priority 0 and bits `LayoutKind`, and add it to the *idle task
queue*.
4. *Process* the *idle task queue*, with bits `LayoutKind`, with a
target time of t-1ms, where t is the time at which we have to send
the frame to the GPU, and with an *idle rule* of "abort".
5. Create a task that does the following, then run it with a budget of
1ms:
1. If there are no RenderNodes that need paint, abort.
2. Call the `paint()` callback of the RenderNode that was least
recently marked as needing paint, catching any exceptions other
than DeadlineExceededException exceptions.
3. Jump to step 1.
If an exception is thrown by this, then some RenderNode objects
will be out-of-date during the paint.
6. Send frame to GPU.
7. Replace the frame queue with the nextFrame queue, and let the
nextFrame queue be an empty queue.
8. *Process* the *idle task queue*, with bits
`application.idleTaskBits`, with a target time of t, where t is the
time at which we have to start the next frame's layout and paint
computations, and with an *idle rule* of "sleep".
TODO(ianh): Update the timings above to have some relationship to
reality.
TODO(ianh): Define an API so that the application can adjust the
budgets.
Task kinds and priorities
-------------------------
Tasks scheduled by futures get the priority and task kind bits from
the task they are scheduled from.
```dart
int IdlePriority = 0; // tasks that can be delayed arbitrarily
int FutureLayoutPriority = 1000; // async-layout tasks
int AnimationPriority = 3000; // animation-related tasks
int InputPriority = 4000; // input events
int ScrollPriority = 5000; // framework-fired events for scrolling
// possible idle queue task bits
int IdleKind = 0x01; // tasks that should run during the idle loop
int LayoutKind = 0x02; // tasks that should run during layout
int TouchSafeKind = 0x04; // tasks that should keep running while there is a pointer down
int idleTaskBits = IdleKind; // tasks must have all these bits to run during idle loop
int layoutTaskBits = LayoutKind; // tasks must have all these bits to run during layout
// possible frame queue task bits
// (there are none at this time)
int frameTaskBits = 0x00; // tasks must have all these bits to run during the frame loop
```

View File

@ -1,101 +0,0 @@
Sky Script Language
===================
The Sky script language is Dart.
The way that Sky integrates the module system with its script language
is described in [modules.md](modules.md).
All the APIs defined in this documentation, unless explicitly called
out as being in a framework, are in the `dart:sky` built-in module.
When a method in `dart:sky` defined as ``external`` receives an
argument, it must type-check it, and, if the argument's value is the
wrong type, then it must throw an ArgumentError as follows:
throw new ArgumentError(value, name: name);
...where "name" is the name of the argument. Type checking here
includes rejecting nulls unless otherwise indicated or unless null is
argument's default value.
The following definitions are exposed in ``dart:sky``:
```dart
import 'dart:mirrors';
abstract class AutomaticMetadata {
const AutomaticMetadata();
void init(DeclarationMirror target, Module module, ScriptElement script);
static void runLibrary(LibraryMirror library, Module module, ScriptElement script) {
library.declarations.values.toList() /* ..sort((DeclarationMirror a, DeclarationMirror b) {
bool aHasLocation;
try {
aHasLocation = a.location != null;
} catch(e) {
aHasLocation = false;
}
bool bHasLocation;
try {
bHasLocation = b.location != null;
} catch(e) {
bHasLocation = false;
}
if (!aHasLocation)
return bHasLocation ? 1 : 0;
if (!bHasLocation)
return -1;
if (a.location.sourceUri != b.location.sourceUri)
return a.location.sourceUri.toString().compareTo(b.location.sourceUri.toString());
if (a.location.line != b.location.line)
return a.location.line - b.location.line;
return a.location.column - b.location.column;
}) */
..forEach((DeclarationMirror d) {
d.metadata.forEach((InstanceMirror i) {
if (i.reflectee is AutomaticMetadata)
i.reflectee.run(d, module, script);
});
});
}
}
class AutomaticFunction extends AutomaticMetadata {
const AutomaticFunction();
void init(DeclarationMirror target, Module module, ScriptElement script) {
assert(target is MethodMirror);
MethodMirror f = target as MethodMirror;
assert(!f.isAbstract);
assert(f.isRegularMethod);
assert(f.isTopLevel);
assert(f.isStatic);
assert(f.parameters.length == 1);
assert(f.parameters[0].type == ScriptElement);
assert(f.returnType == currentMirrorSystem().voidType);
(f.owner as LibraryMirror).invoke(f.simpleName, [script]);
}
}
const autorun = const AutomaticFunction();
```
Extensions
----------
The following as-yet unimplemented features of the Dart language are
assumed to exist:
* It is assumed that a subclass can define a constructor by reference
to a superclass' constructor, wherein the subclass' constructor has
the same arguments as the superclass' constructor and does nothing
but invoke that superclass' constructor with the same arguments. The
syntax for defining this is, within the class body for a class
called ClassName:
```dart
ClassName = SuperclassName;
ClassName.namedConstructor = SuperclassName.otherNamedConstructor;
```
* The reflection APIs (`dart:mirrors`) are assumed to reflect a
library's declarations in source order.

File diff suppressed because it is too large Load Diff

View File

@ -1,348 +0,0 @@
Sky Style Language
==================
This is a trimmed down version of the API in (style.md)[style.md]
that is intended to be a stepping stone to the long-term world where
there are no hard-coded properties in the engine.
The Sky style API looks like the following:
```dart
// all properties can be set as strings:
element.style['color'] = 'blue';
// some properties have dedicated APIs
// color
element.style.color.red += 1; // 0..255
element.style.color.blue += 10; // 0..255
element.style.color.green = 255; // 0..255
element.style.color.alpha = 128; // 0..255
// transform
element.style.transform..reset()
..translate(100, 100)
..rotate(PI/8)
..translate(-100, -100);
element.style.transform.translate(10, 0);
// height, width
element.style.height.auto = true;
if (element.style.height.auto)
element.style.height.pixels = 10;
element.style.height.pixels += 1;
element.style.height.em = 1;
// each property with a dedicated API defines a shorthand setter
// style.transform takes a matrix:
element.style.transform = new Matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
// style.color takes a 32bit int:
element.style.color = 0xFF009900;
// style.height and style.width takes pixels or the constant 'auto':
element.style.height = auto;
element.style.width = 100;
// all properties with a dedicated API can also be set to null, inherit, or initial:
element.style.transform = null; // unset the property
element.style.color = initial; // set it to its initial value
element.style.color = inherit; // make it get its parent's value
// you can create a blank StyleDeclaration object:
var style = new StyleDeclaration();
// you can replace an element's StyleDeclaration object wholesale:
element.style = style;
// you can clone a StyleDeclaration object:
var style2 = new StyleDeclaration.clone(style);
```
The dart:sky library contains the following to define this API:
```dart
import 'dart:mirrors';
import 'dart:math';
typedef void StringSetter(Symbol propertySymbol, StyleDeclaration declaration, String value);
typedef String StringGetter(Symbol propertySymbol, StyleDeclaration declaration);
typedef Property ObjectConstructor(Symbol propertySymbol, StyleDeclaration declaration);
class PropertyTable {
const PropertyTable({this.symbol, this.inherited, this.stringGetter, this.stringSetter, this.objectConstructor});
final Symbol symbol;
final bool inherited;
final StringSetter stringSetter;
final StringGetter stringGetter;
final ObjectConstructor objectConstructor;
}
Map<Symbol, PropertyTable> _registeredProperties = new Map<Symbol, PropertyTable>();
void registerProperty(PropertyTable data) {
assert(data.symbol is Symbol);
assert(data.inherited is bool);
assert(data.stringSetter is StringSetter);
assert(data.stringGetter is StringGetter);
assert(data.objectConstructor == null || data.objectConstructor is ObjectConstructor);
assert(!_registeredProperties.containsKey(data.symbol));
_registeredProperties[data.symbol] = data;
}
@proxy
class StyleDeclaration {
StyleDeclaration() { this._init(); }
StyleDeclaration.clone(StyleDeclaration template) { this.init(template); }
external void _init([StyleDeclaration template]); // O(1)
// This class has C++-backed internal state representing the
// properties known to the system. It's assumed that Property
// subclasses are also C++-backed and can directly manipulate this
// internal state.
// If the argument 'template' is provided, then this should be a clone
// of the styles of the template StyleDeclaration
operator [](String propertyName) {
var propertySymbol = new Symbol(propertyName);
if (_registeredProperties.containsKey(propertySymbol))
return _registeredProperties[propertySymbol].stringGetter(propertySymbol, this);
throw new ArgumentError(propertyName);
}
operator []=(String propertyName, String newValue) {
var propertySymbol = new Symbol(propertyName);
if (_registeredProperties.containsKey(propertySymbol))
return _registeredProperties[propertySymbol].stringSetter(propertySymbol, this, newValue);
throw new ArgumentError(propertyName);
}
// some properties expose dedicated APIs so you don't have to use string manipulation
MapOfWeakReferences<Symbol, Property> _properties = new MapOfWeakReferences<Symbol, Property>();
noSuchMethod(Invocation invocation) {
Symbol propertySymbol;
if (invocation.isSetter) {
// when it's a setter, the name will be "foo=" rather than "foo"
String propertyName = MirrorSystem.getName(invocation.memberName);
assert(propertyName[propertyName.length-1] == '=');
propertySymbol = new Symbol(propertyName.substring(0, propertyName.length-1));
} else {
propertySymbol = invocation.memberName;
}
Property property;
if (!_properties.containsKey(propertySymbol)) {
if (_registeredProperties.containsKey(propertySymbol)) {
var constructor = _registeredProperties[propertySymbol].objectConstructor;
if (constructor == null)
return super.noSuchMethod(invocation);
property = constructor(propertySymbol, this);
} else {
return super.noSuchMethod(invocation);
}
} else {
property = _properties[propertySymbol];
}
if (invocation.isMethod) {
if (property is Function)
return Function.apply(property as Function, invocation.positionalArguments, invocation.namedArguments);
return super.noSuchMethod(invocation);
}
if (invocation.isSetter)
return Function.apply(property.setter, invocation.positionalArguments, invocation.namedArguments);
return property;
}
}
const initial = const Object();
const inherit = const Object();
abstract class Property {
Property(this.propertySymbol, this.declaration);
final StyleDeclaration declaration;
final Symbol propertySymbol;
bool get inherited => _registeredProperties[propertySymbol].inherited;
bool get initial => _isInitial();
void set initial (value) {
if (value == true)
return _setInitial();
throw new ArgumentError(value);
}
bool get inherit => _isInherit();
void set inherit (value) {
if (value == true)
return _setInherit();
throw new ArgumentError(value);
}
void setter(dynamic newValue) {
switch (newValue) {
case initial:
_setInitial();
case inherit:
_setInherit();
case null:
_unset();
default:
throw new ArgumentError(value);
}
}
external bool _isInitial();
external void _setInitial();
external bool _isInherit();
external void _setInherit();
external void _unset();
}
```
Sky defines the following properties, currently as part of the core,
but eventually this will be moved to the framework:
```dart
class LengthProperty extends Property {
LengthProperty(Symbol propertySymbol, StyleDeclaration declaration) : super(propertySymbol, declaration);
double get pixels => _getPixels();
void set pixels (value) => _setPixels(value);
double get inches => _getPixels() / 96.0;
void set inches (value) => _setPixels(value * 96.0);
double get em => _getEm();
void set em (value) => _setEm(value);
void setter(dynamic value) {
if (value is num)
return _setPixels(value.toDouble());
return super.setter(value);
}
external double _getPixels();
// throws StateError if the value isn't in pixels
external void _setPixels(double value);
external double _getEm();
// throws StateError if the value isn't in pixels
external void _setEm(double value);
}
const auto = const Object();
class AutoLengthProperty extends LengthProperty {
AutoLengthProperty(Symbol propertySymbol, StyleDeclaration declaration) : super(propertySymbol, declaration);
bool get auto => _isAuto();
void set auto (value) {
if (value == true)
_setAuto();
throw new ArgumentError(value);
}
void setter(dynamic value) {
if (value == auto)
return _setAuto();
return super.setter(value);
}
external bool _isAuto();
external void _setAuto();
}
class ColorProperty extends Property {
ColorProperty(Symbol propertySymbol, StyleDeclaration declaration) : super(propertySymbol, declaration);
int get alpha => _getRGBA() & 0xFF000000 >> 24;
void set alpha (int value) => _setRGBA(_getRGBA() & 0x00FFFFFF + value << 24);
int get red => _getRGBA() & 0x00FF0000 >> 16;
void set red (int value) => _setRGBA(_getRGBA() & 0xFF00FFFF + value << 16);
int get green => _getRGBA() & 0x0000FF00 >> 8;
void set green (int value) => _setRGBA(_getRGBA() & 0xFFFF00FF + value << 8);
int get blue => _getRGBA() & 0x000000FF >> 0;
void set blue (int value) => _setRGBA(_getRGBA() & 0xFFFFFF00 + value << 0);
int get rgba => _getRGBA();
void set rgba (int value) => _setRGBA(value);
void setter(dynamic value) {
if (value is int)
return _setRGBA(value);
return super.setter(value);
}
external int _getRGBA();
// throws StateError if the value isn't a color
external void _setRGBA(int value);
}
class Matrix {
const Matrix(this.a, this.b, this.c, this.d, this.e, this.f);
// +- -+
// | a c e |
// | b d f |
// | 0 0 1 |
// +- -+
final double a;
final double b;
final double c;
final double d;
final double e;
final double f;
}
class TransformProperty extends Property {
TransformProperty(Symbol propertySymbol, StyleDeclaration declaration) : super(propertySymbol, declaration);
void reset() => setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
void translate(double dx, double dy) => transform(1.0, 0.0, 0.0, 1.0, dx, dy);
void scale(double dw, double dh) => transform(dw, 0.0, 0.0, dh, 0.0, 0.0);
void rotate(double theta) => transform(cos(theta), -sin(theta), sin(theta), cos(theta), 0.0, 0.0);
// there's no "transform" getter since it would always return a new Matrix
// such that foo.transform == foo.transform would never be true
// and foo.transform = bar; bar == foo.transform would also never be true
// which is bad API
external Matrix getTransform();
// throws StateError if the value isn't a matrix
// returns a new matrix each time
external void setTransform(a, b, c, d, e, f);
external void transform(a, b, c, d, e, f);
// throws StateError if the value isn't a matrix
}
external void autoLengthPropertyStringSetter(Symbol propertySymbol, StyleDeclaration declaration, String value);
external String autoLengthPropertyStringGetter(Symbol propertySymbol, StyleDeclaration declaration);
external void colorPropertyStringSetter(Symbol propertySymbol, StyleDeclaration declaration, String value);
external String colorPropertyStringGetter(Symbol propertySymbol, StyleDeclaration declaration);
external void transformPropertyStringSetter(Symbol propertySymbol, StyleDeclaration declaration, String value);
external String transformPropertyStringGetter(Symbol propertySymbol, StyleDeclaration declaration);
void _init() {
registerProperty(new PropertyTable(
symbol: #height,
inherited: false,
stringSetter: autoLengthPropertyStringSetter,
stringGetter: autoLengthPropertyStringGetter,
objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) =>
new AutoLengthProperty(propertySymbol, declaration)));
registerProperty(new PropertyTable(
symbol: #width,
inherited: false,
stringSetter: autoLengthPropertyStringSetter,
stringGetter: autoLengthPropertyStringGetter,
objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) =>
new AutoLengthProperty(propertySymbol, declaration)));
registerProperty(new PropertyTable(
symbol: #color,
inherited: false,
stringSetter: colorPropertyStringSetter,
stringGetter: colorPropertyStringGetter,
objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) =>
new ColorProperty(propertySymbol, declaration)));
registerProperty(new PropertyTable(
symbol: #transform,
inherited: false,
stringSetter: transformPropertyStringSetter,
stringGetter: transformPropertyStringGetter,
objectConstructor: (Symbol propertySymbol, StyleDeclaration declaration) =>
new TransformProperty(propertySymbol, declaration)));
}
```

View File

@ -1,22 +0,0 @@
Dart Utilities Used By dart:sky
===============================
The classes defined here are used internally by dart:sky but are
pretty generic.
```dart
class Pair<A, B> {
const Pair(this.a, this.b);
final A a;
final B b;
int get hashCode => a.hashCode ^ b.hashCode;
bool operator==(other) => other is Pair<A, B> && a == other.a && b == other.b;
}
// MapOfWeakReferences can be implemented in C, using the C Dart API, apparently
class MapOfWeakReferences<Key, Value> {
external operator[](Key key);
external operator[]=(Key key, Value value);
external bool containsKey(Key key);
}
```