mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Specs] Remove all the obsolete specs.
TBR=abarth Review URL: https://codereview.chromium.org/1142853006
This commit is contained in:
parent
c20a7f9c28
commit
f31e6ec087
@ -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.
|
||||
|
||||
@ -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.
|
||||
@ -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>
|
||||
```
|
||||
@ -1,6 +0,0 @@
|
||||
Best Practices and Conventions for Sky Frameworks
|
||||
=================================================
|
||||
|
||||
* elements should not expose convenience property accessors that just
|
||||
reflect content attributes.
|
||||
|
||||
@ -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
|
||||
}
|
||||
```
|
||||
@ -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;
|
||||
}
|
||||
```
|
||||
222
specs/markup.md
222
specs/markup.md
@ -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)
|
||||
148
specs/modules.md
148
specs/modules.md
@ -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;
|
||||
}
|
||||
```
|
||||
846
specs/parsing.md
846
specs/parsing.md
@ -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...
|
||||
|
||||
- '``&``: Emit Run the _emitting operation_ for the character
|
||||
'``&``'.
|
||||
|
||||
- '``'``: Emit Run the _emitting operation_ for the character
|
||||
'``'``'.
|
||||
|
||||
- '``>``: Emit Run the _emitting operation_ for the character
|
||||
'``>``'.
|
||||
|
||||
- '``<``: Emit Run the _emitting operation_ for the character
|
||||
'``<``'.
|
||||
|
||||
- '``"``: 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.
|
||||
144
specs/runloop.md
144
specs/runloop.md
@ -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
|
||||
```
|
||||
101
specs/script.md
101
specs/script.md
@ -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.
|
||||
1082
specs/style.md
1082
specs/style.md
File diff suppressed because it is too large
Load Diff
348
specs/style2.md
348
specs/style2.md
@ -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)));
|
||||
}
|
||||
```
|
||||
|
||||
@ -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);
|
||||
}
|
||||
```
|
||||
Loading…
x
Reference in New Issue
Block a user