mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Specs: update elements.md to define List-based APIs in terms of Node-based APIs, and finish dartification of modules.md (and do Document->Root there)
Review URL: https://codereview.chromium.org/926733002
This commit is contained in:
parent
5b9e4a9fbd
commit
e5500c19ac
@ -17,19 +17,64 @@ abstract class Node extends EventTarget {
|
||||
external Root get owner; // O(1)
|
||||
|
||||
external ParentNode get parentNode; // O(1)
|
||||
external Element get parentElement; // O(1) // if parentNode isn't an element, returns null
|
||||
external Node get previousSibling; // O(1)
|
||||
external Node get nextSibling; // O(1)
|
||||
Element get parentElement => {
|
||||
if (parentNode is Element)
|
||||
return parentNode as Element;
|
||||
return null;
|
||||
}
|
||||
|
||||
// the following all throw if parentNode is null
|
||||
external void insertBefore(List nodes); // O(N) in number of arguments plus all their descendants
|
||||
external void insertAfter(List nodes); // O(N) in number of arguments plus all their descendants
|
||||
// TODO(ianh): rename insertBefore() and insertAfter() since the Web has an insertBefore() that means
|
||||
// something else. What's a good name, though?
|
||||
external void replaceWith(List nodes); // O(N) in number of descendants plus arguments plus all their descendants
|
||||
// nodes must be String, Text, or Element
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
@ -70,18 +115,63 @@ abstract class Node extends EventTarget {
|
||||
|
||||
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
|
||||
external List<Element> getChildElements(); // O(N) in number of child nodes
|
||||
// TODO(ianh): might not be necessary if we have the parser drop unnecessary whitespace text nodes
|
||||
List<Element> getChildElements() {
|
||||
// that the following works without a cast is absurd
|
||||
return getChildren().where((node) => node is Element).toList();
|
||||
}
|
||||
|
||||
external void append(List nodes); // O(N) in number of arguments plus all their descendants
|
||||
external void appendChild(Node child); // O(N) in number of descandants
|
||||
external void prepend(List nodes); // O(N) in number of arguments plus all their descendants
|
||||
external void replaceChildrenWith(List nodes); // O(N) in number of descendants plus arguments plus all their descendants
|
||||
// nodes must be String, Text, or Element
|
||||
external void _appendChild(Node node); // O(N) in number of descendants
|
||||
// node must be Text or Element
|
||||
void appendChild(Node 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 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 node) {
|
||||
removeChildren();
|
||||
appendChild(node);
|
||||
}
|
||||
void setChildren(List nodes) {
|
||||
removeChildren();
|
||||
append(nodes);
|
||||
}
|
||||
}
|
||||
|
||||
class Attr {
|
||||
@ -107,7 +197,7 @@ abstract class Element extends ParentNode with Node {
|
||||
external 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
|
||||
// initialises the internal attributes table
|
||||
// initialises the internal attributes table, which is a ordered list
|
||||
// appends the given children nodes
|
||||
// children must be String, Text, or Element
|
||||
// if this.needsShadow, creates a shadow tree
|
||||
@ -129,9 +219,8 @@ abstract class Element extends ParentNode with Node {
|
||||
external List<Attr> getAttributes(); // O(N) in number of attributes
|
||||
|
||||
get bool needsShadow => false; // O(1)
|
||||
external Root get shadowRoot; // O(1)
|
||||
external final Root shadowRoot; // O(1)
|
||||
// returns the shadow root
|
||||
// TODO(ianh): Should this be mutable? It would help explain how it gets set...
|
||||
|
||||
void endTagParsedCallback() { }
|
||||
void attributeChangeCallback(String name, String oldValue, String newValue) { }
|
||||
|
||||
@ -24,6 +24,13 @@ dependencies is empty. After the parser has finished parsing, the
|
||||
document waits until its list of outstanding dependencies is empty
|
||||
before the module it represents is marked complete.
|
||||
|
||||
The ``as`` attribute on the ``import`` element binds a name to the
|
||||
imported module:
|
||||
|
||||
```html
|
||||
<import src="path/to/chocolate.sky" as="chocolate" />
|
||||
```
|
||||
|
||||
|
||||
Module API
|
||||
----------
|
||||
@ -52,7 +59,8 @@ 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.
|
||||
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
|
||||
@ -78,24 +86,23 @@ exposed in MirrorSystem.getName(libraryMirror.qualifiedName) etc
|
||||
The ``Module`` class is defined in ``sky:core`` as follows:
|
||||
|
||||
```dart
|
||||
|
||||
abstract class AbstractModule extends EventTarget {
|
||||
AbstractModule({this.document, this.url});
|
||||
|
||||
final Document document; // O(1)
|
||||
// the Document of the module or application
|
||||
AbstractModule({this.url, this.elements});
|
||||
|
||||
final String url;
|
||||
|
||||
@nonnull external Future<@nonnull Module> import(String url); // O(Yikes)
|
||||
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
|
||||
|
||||
@nonnull List</*@nonnull*/ Module> getImports(); // O(N)
|
||||
List<Module> getImports(); // O(N)
|
||||
// returns the Module objects of all the imported modules
|
||||
|
||||
external registerElement(@nonnull String tagname, @nonnull Type elementClass); // O(1)
|
||||
external 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
|
||||
@ -107,44 +114,17 @@ abstract class AbstractModule extends EventTarget {
|
||||
// (mention the tag name but not the classes, so that it's not observable that this currently happens out of order)
|
||||
}
|
||||
|
||||
class Module : AbstractModule {
|
||||
constructor (Application application, Document document, String url); // O(1)
|
||||
readonly attribute Application application; // O(1)
|
||||
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 : AbstractModule {
|
||||
constructor (Document document, GestureManager gestureManager, String url); // O(1)
|
||||
attribute String title; // O(1)
|
||||
readonly attribute GestureManager gestureManager;
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Naming modules
|
||||
--------------
|
||||
|
||||
The ``as`` attribute on the ``import`` element binds a name to the
|
||||
imported module:
|
||||
|
||||
```html
|
||||
<import src="path/to/chocolate.sky" as="chocolate" />
|
||||
```
|
||||
|
||||
The parser executes the contents of script elements inside a module as
|
||||
if they were executed as follow:
|
||||
|
||||
```javascript
|
||||
(new Function(name_1, ..., name_n, module, source_code)).call(
|
||||
value_1, ..., value_n, source_module);
|
||||
```
|
||||
|
||||
Where ``name_1`` through ``name_n`` are the names bound to the
|
||||
various named imports in the script element's document,
|
||||
``source_code`` is the text content of the script element,
|
||||
``source_module`` is the ``Module`` object of the script element's
|
||||
module, and ``value_1`` through ``value_n`` are the values
|
||||
exported by the various named imports in the script element's
|
||||
document.
|
||||
|
||||
When an import fails to load, the ``as`` name for the import gets
|
||||
bound to ``undefined``.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user