提交 f31e6ec0 编写于 作者: H Hixie

[Specs] Remove all the obsolete specs.

TBR=abarth

Review URL: https://codereview.chromium.org/1142853006
上级 c20a7f9c
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.
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.
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>
```
Best Practices and Conventions for Sky Frameworks
=================================================
* elements should not expose convenience property accessors that just
reflect content attributes.
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
}
```
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;
}
```
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)
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;
}
```
此差异已折叠。
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
```
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.
此差异已折叠。
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)));
}
```
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);
}
```
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册