dart:html perf optimization based on runing Dromaeo benchmarks

BUG=
TEST=

Review URL: https://chromiumcodereview.appspot.com//9732019

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@5955 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
jacobr@google.com 2012-03-28 22:23:39 +00:00
parent 37147fc116
commit dd9bdb5ece
23 changed files with 2230 additions and 1707 deletions

12
.gitignore vendored
View file

@ -12,9 +12,9 @@ xcodebuild
*.sln
*.ncb
# Stuff the appengine server needs copied locally from third_party
/client/samples/swarm/appengine/httplib2/
/client/samples/swarm/appengine/oauth2client/
# Generated files
/samples/third_party/dromaeo/tests/frog/
/samples/third_party/dromaeo/index-js.html
# Build output directories
out/
@ -28,10 +28,10 @@ Debug_*/
/client/outcode
# Generated dom idl database
/client/dom/database
/lib/dom/database
# Generated dom and html classes
/client/dom/generated
/client/html/generated
/lib/dom/generated
/lib/html/generated
# Not sure what creates this
fixed_length_file_out

View file

@ -13,48 +13,63 @@ from systeminterface import *
# but need to be exposed internally to implement dart:html on top of a standard
# browser.
_private_html_members = set([
'Element.clientLeft',
'Element.clientTop',
'Element.clientWidth',
'Element.clientHeight',
'Element.offsetLeft',
'Element.offsetTop',
'Element.offsetWidth',
'Element.offsetHeight',
'Element.scrollLeft',
'Element.scrollTop',
'Element.scrollWidth',
'Element.scrollHeight',
'Element.childElementCount',
'Element.firstElementChild',
'Element.hasAttribute',
'Element.getAttribute',
'Element.removeAttribute',
'Element.setAttribute',
'Element.className',
'Element.children',
'Element.querySelectorAll',
'NodeSelector.querySelectorAll',
'Document.querySelectorAll',
'DocumentFragment.querySelectorAll',
'Element.getBoundingClientRect',
'Element.getClientRects',
'Node.appendChild',
'Node.removeChild',
'Node.replaceChild',
'Node.attributes',
'Node.childNodes',
'Document.createElement',
'Document.createElementNS',
'Document.createEvent',
'Document.createTextNode',
'Document.createTouchList',
'Window.getComputedStyle',
'EventTarget.removeEventListener',
'Document.getElementById',
'Document.getElementsByClassName',
'Document.getElementsByName',
'Document.getElementsByTagName',
'Document.querySelectorAll',
'DocumentFragment.querySelectorAll',
'Element.childElementCount',
'Element.children',
'Element.className',
'Element.clientHeight',
'Element.clientLeft',
'Element.clientTop',
'Element.clientWidth',
'Element.firstElementChild',
'Element.getAttribute',
'Element.getBoundingClientRect',
'Element.getClientRects',
'Element.getElementsByClassName',
'Element.getElementsByTagName',
'Element.hasAttribute',
'Element.lastElementChild',
'Element.offsetHeight',
'Element.offsetLeft',
'Element.offsetTop',
'Element.offsetWidth',
'Element.querySelectorAll',
'Element.removeAttribute',
'Element.scrollHeight',
'Element.scrollLeft',
'Element.scrollTop',
'Element.scrollWidth',
'Element.setAttribute',
'Event.initEvent',
'EventTarget.addEventListener',
'EventTarget.dispatchEvent',
'Event.initEvent',
'EventTarget.removeEventListener',
'MouseEvent.initMouseEvent',
'Node.appendChild',
'Node.attributes',
'Node.childNodes',
'Node.firstChild',
'Node.lastChild',
"Node.nodeType",
'Node.removeChild',
'Node.replaceChild',
'NodeSelector.querySelectorAll',
'Window.getComputedStyle',
])
_manually_generated_html_members = set([
'Document.querySelectorAll',
'Document.querySelector',
])
# Members from the standard dom that exist in the dart:html library with
@ -66,16 +81,15 @@ _html_library_renames = {
'Element.querySelector': 'query',
'Element.webkitMatchesSelector' : 'matchesSelector',
'Element.scrollIntoViewIfNeeded': 'scrollIntoView',
'Document.querySelector': 'query',
'Node.cloneNode': 'clone',
'Node.nextSibling': 'nextNode',
'Node.ownerDocument': 'document',
'Node.parentNode': 'parent',
'Node.previousSibling': 'previousNode',
'Node.textContent': 'text',
'SVGElement.className': '_svgClassName',
'SVGAnimatedString.className': '_svgClassName',
'SVGStylable.className': '_svgClassName',
'SVGElement.className': '$dom_svgClassName',
'SVGAnimatedString.className': '$dom_svgClassName',
'SVGStylable.className': '$dom_svgClassName',
}
#TODO(jacobr): inject annotations into the interfaces based on this table and
@ -100,29 +114,22 @@ _html_library_remove = set([
# "CDATASection.*",
# "Comment.*",
# "DOMImplementation.*",
"Document.get:documentElement",
"Document.get:forms",
# "Document.get:selectedStylesheetSet",
# "Document.set:selectedStylesheetSet",
# "Document.get:preferredStylesheetSet",
"Document.get:links",
"Document.getElementsByTagName",
"Document.set:domain",
"Document.get:implementation",
"Document.createAttributeNS",
"Document.get:inputEncoding",
"Document.getElementById",
"Document.getElementsByClassName",
"Document.get:height",
"Document.get:width",
"Element.getElementsByClassName",
"Element.getElementsByTagNameNS",
"Element.getElementsByTagName",
"Document.get:compatMode",
"Document.importNode",
"Document.evaluate",
"Document.get:images",
"Document.querySelector",
"Document.createExpression",
"Document.getOverrideStyle",
"Document.xmlStandalone",
@ -130,7 +137,6 @@ _html_library_remove = set([
"Document.adoptNode",
"Document.get:characterSet",
"Document.createAttribute",
"Document.querySelectorAll",
"Document.get:URL",
"Document.createEntityReference",
"Document.get:documentURI",
@ -138,7 +144,6 @@ _html_library_remove = set([
"Document.createNodeIterator",
"Document.createProcessingInstruction",
"Document.get:doctype",
"Document.getElementsByName",
"Document.createTreeWalker",
"Document.location",
"Document.createNSResolver",
@ -237,9 +242,7 @@ _html_library_remove = set([
"Node.get:COMMENT_NODE",
"Node.get:ENTITY_REFERENCE_NODE",
"Node.isSupported",
"Node.get:firstChild",
"Node.get:DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC",
"Node.get:lastChild",
"Node.get:NOTATION_NODE",
"Node.normalize",
"Node.get:parentElement",
@ -249,7 +252,6 @@ _html_library_remove = set([
"Node.get:prefix",
"Node.set:prefix",
"Node.get:DOCUMENT_POSITION_PRECEDING",
"Node.get:nodeType",
"Node.removeEventListener",
"Node.get:nodeValue",
"Node.set:nodeValue",
@ -426,26 +428,13 @@ class HtmlSystemShared(object):
return True
return False
def MaybeReturnDocument(self, return_type):
"""
To make it appear that there are not a distinct Document and
HTMLHtmlElement (document.documentElement) objects we always use
documentElement instead of the regular document object so must not
allow a regular document to leak out.
"""
# TODO(jacobr): any method that returns a Node could also theoretically
# really return a Document but there are alot of methods that return nodes
# and they all appear to be safe. Consider the alternate strategy of
# whitelisting just the known safe methods that return Nodes.
return (DartType(return_type) == 'EventTarget' or
DartType(return_type) == 'Document')
def _AllAncestorInterfaces(self, interface):
interfaces = ([interface.id] +
self._generator._AllImplementedInterfaces(interface))
return interfaces
def RenameInHtmlLibrary(self, interface, member, member_prefix=''):
def RenameInHtmlLibrary(self, interface, member, member_prefix='',
implementation_class=False):
"""
Returns the name of the member in the HTML library or None if the member is
suppressed in the HTML library
@ -453,24 +442,33 @@ class HtmlSystemShared(object):
if not self._AllowInHtmlLibrary(interface, member, member_prefix):
return None
target_name = member
for interface_name in self._AllAncestorInterfaces(interface):
name = interface_name + '.' + member
if name in _html_library_renames:
return _html_library_renames[name]
target_name = _html_library_renames[name]
name = interface.id + '.' + member_prefix + member
if name in _html_library_renames:
return _html_library_renames[name]
target_name = _html_library_renames[name]
if not target_name.startswith('_'):
if self._PrivateInHtmlLibrary(interface, member, member_prefix):
return '_' + member
target_name = '$dom_' + target_name
elif implementation_class and self._ManuallyGeneratedInHtmlLibrary(
interface, member, member_prefix):
target_name = '_' + target_name
# No rename required
return member
return target_name
def _PrivateInHtmlLibrary(self, interface, member, member_prefix):
return self._Matches(interface, member, member_prefix,
_private_html_members)
def _ManuallyGeneratedInHtmlLibrary(self, interface, member, member_prefix):
return self._Matches(interface, member, member_prefix,
_manually_generated_html_members)
# TODO(jacobr): this already exists
def _TraverseParents(self, interface, callback):
for parent in interface.parents:
@ -701,7 +699,8 @@ class HtmlDartInterfaceGenerator(DartInterfaceGenerator):
operations - contains the overloads, one or more operations with the same
name.
"""
html_name = self._shared.RenameInHtmlLibrary(self._interface, info.name)
html_name = self._shared.RenameInHtmlLibrary(
self._interface, info.name)
if html_name and not self._shared.IsPrivate(html_name):
self._members_emitter.Emit('\n'
' $TYPE $NAME($PARAMS);\n',
@ -875,9 +874,11 @@ class HtmlFrogClassGenerator(FrogInterfaceGenerator):
def AddAttribute(self, getter, setter):
html_getter_name = self._shared.RenameInHtmlLibrary(
self._interface, DartDomNameOfAttribute(getter), 'get:')
self._interface, DartDomNameOfAttribute(getter), 'get:',
implementation_class=True)
html_setter_name = self._shared.RenameInHtmlLibrary(
self._interface, DartDomNameOfAttribute(getter), 'set:')
self._interface, DartDomNameOfAttribute(getter), 'set:',
implementation_class=True)
if not html_getter_name:
getter = None
@ -887,11 +888,8 @@ class HtmlFrogClassGenerator(FrogInterfaceGenerator):
if not getter and not setter:
return
if ((getter and (html_getter_name != getter.id or
self._shared.MaybeReturnDocument(getter.type.id))) or
(setter and (html_setter_name != setter.id or
self._shared.MaybeReturnDocument(setter.type.id))) or
self._interface.id == 'Document'):
if ((getter and html_getter_name != getter.id) or
(setter and html_setter_name != setter.id)):
if getter:
self._AddRenamingGetter(getter, html_getter_name)
if setter:
@ -951,55 +949,35 @@ class HtmlFrogClassGenerator(FrogInterfaceGenerator):
def _AddRenamingGetter(self, attr, html_name):
return_type = self._NarrowOutputType(attr.type.id)
if self._shared.MaybeReturnDocument(attr.type.id):
self._members_emitter.Emit(
'\n $TYPE get $(HTML_NAME)() => '
'_FixHtmlDocumentReference(_$(HTML_NAME));\n',
HTML_NAME=html_name,
TYPE=return_type)
html_name = '_' + html_name
# For correctness this needs to be the return type of the native helper
# method due to the fact that the real HTMLDocument object is not typed
# as a document. TODO(jacobr): we could simplify this.
return_type = '_EventTargetImpl'
self._members_emitter.Emit(
'\n $TYPE get $(HTML_NAME)() native "return $(THIS).$NAME;";\n',
'\n $TYPE get $(HTML_NAME)() native "return this.$NAME;";\n',
HTML_NAME=html_name,
NAME=attr.id,
TYPE=return_type,
THIS='this.parentNode' if self._interface.id == 'Document' else 'this')
TYPE=return_type)
def _AddRenamingSetter(self, attr, html_name):
self._members_emitter.Emit(
'\n void set $HTML_NAME($TYPE value)'
' native "$(THIS).$NAME = value;";\n',
' native "this.$NAME = value;";\n',
HTML_NAME=html_name,
NAME=attr.id,
TYPE=self._NarrowInputType(attr.type.id),
THIS='this.parentNode' if self._interface.id == 'Document' else 'this')
TYPE=self._NarrowInputType(attr.type.id))
def AddOperation(self, info):
"""
Arguments:
info: An OperationInfo object.
"""
html_name = self._shared.RenameInHtmlLibrary(self._interface, info.name)
html_name = self._shared.RenameInHtmlLibrary(
self._interface, info.name, implementation_class=True)
if not html_name:
return
maybe_return_document = self._shared.MaybeReturnDocument(info.type_name)
# Do we need a native body?
if (self._interface.id == 'Document' or # Need alternate 'this'
html_name != info.name or # renamed operation
maybe_return_document): # need to wrap value
# For example: use window.document instead of his.parentNode.
if (html_name != info.name):
return_type = self._NarrowOutputType(info.type_name)
operation_emitter = self._members_emitter.Emit('$!SCOPE',
THIS=('this.parentNode' if self._interface.id == 'Document'
else 'this'),
TYPE=return_type,
HTML_NAME=html_name,
NAME=info.name,
@ -1008,20 +986,10 @@ class HtmlFrogClassGenerator(FrogInterfaceGenerator):
PARAMS=info.ParametersImplementationDeclaration(
lambda type_name: self._NarrowInputType(type_name)))
if maybe_return_document:
assert len(info.overloads) == 1
operation_emitter.Emit(
'\n'
' $TYPE $(HTML_NAME)($PARAMS) => '
'_FixHtmlDocumentReference(_$(HTML_NAME)($PARAMNAMES));\n'
'\n'
' _EventTargetImpl _$(HTML_NAME)($PARAMS)'
' native "return $(THIS).$NAME($PARAMNAMES);";\n')
else:
operation_emitter.Emit(
'\n'
' $TYPE $(HTML_NAME)($PARAMS)'
' native "$(RETURN)$(THIS).$NAME($PARAMNAMES);";\n')
' native "$(RETURN)this.$NAME($PARAMNAMES);";\n')
else:
self._members_emitter.Emit(
'\n'
@ -1063,10 +1031,8 @@ class HtmlFrogClassGenerator(FrogInterfaceGenerator):
def _EmitEventGetter(self, events_class):
self._members_emitter.Emit(
'\n $TYPE get on() =>\n new $TYPE($EVENTTARGET);\n',
TYPE=events_class,
EVENTTARGET='_jsDocument' if self._interface.id == 'Document'
else 'this')
'\n $TYPE get on() =>\n new $TYPE(this);\n',
TYPE=events_class)
# ------------------------------------------------------------------------------
@ -1205,7 +1171,7 @@ class HtmlDartiumInterfaceGenerator(object):
self._template = template
def DomObjectName(self):
return '_documentPtr' if self._interface.id == 'Document' else '_ptr'
return '_ptr'
# TODO(jacobr): these 3 methods are duplicated.
def _NarrowToImplementationType(self, type_name):
@ -1248,10 +1214,8 @@ class HtmlDartiumInterfaceGenerator(object):
# TODO(jacobr): this is fragile. There isn't a guarantee that dart:dom
# will continue to exactly match the IDL names.
dom_name = interface.javascript_binding_name
# We hard code the cases for these classes
if dom_name != 'HTMLHtmlElement' and dom_name != 'Document':
self._system._wrap_cases.append(
' case "%s": return new %s._wrap(domObject);' %
" case '%s': return new %s._wrap(domObject);" %
(dom_name, self._class_name))
extends = ' extends ' + base if base else ' extends _DOMTypeBase'
@ -1274,8 +1238,6 @@ class HtmlDartiumInterfaceGenerator(object):
EXTENDS=extends,
IMPLEMENTS=' implements ' + implements_str)
# Document requires a custom wrapper.
if dom_name != 'Document':
self._members_emitter.Emit(
' $(CLASSNAME)._wrap(ptr) : super._wrap(ptr);\n',
CLASSNAME=self._class_name)
@ -1362,9 +1324,9 @@ class HtmlDartiumInterfaceGenerator(object):
def AddAttribute(self, getter, setter):
dom_name = DartDomNameOfAttribute(getter or setter)
html_getter_name = self._shared.RenameInHtmlLibrary(
self._interface, dom_name, 'get:')
self._interface, dom_name, 'get:', implementation_class=True)
html_setter_name = self._shared.RenameInHtmlLibrary(
self._interface, dom_name, 'set:')
self._interface, dom_name, 'set:', implementation_class=True)
if getter and html_getter_name:
self._AddGetter(getter, html_getter_name)
@ -1372,16 +1334,6 @@ class HtmlDartiumInterfaceGenerator(object):
self._AddSetter(setter, html_setter_name)
def _AddGetter(self, attr, html_name):
if self._shared.MaybeReturnDocument(attr.type.id):
self._members_emitter.Emit(
'\n'
' $TYPE get $(HTML_NAME)() => '
'_FixHtmlDocumentReference(_wrap($(THIS).$DOM_NAME));\n',
HTML_NAME=html_name,
DOM_NAME=DartDomNameOfAttribute(attr),
TYPE=DartType(attr.type.id),
THIS=self.DomObjectName())
else:
self._members_emitter.Emit(
'\n'
' $TYPE get $(HTML_NAME)() => _wrap($(THIS).$DOM_NAME);\n',
@ -1622,7 +1574,8 @@ class HtmlDartiumInterfaceGenerator(object):
Arguments:
info: An OperationInfo object.
"""
html_name = self._shared.RenameInHtmlLibrary(self._interface, info.name)
html_name = self._shared.RenameInHtmlLibrary(
self._interface, info.name, implementation_class=True)
if not html_name:
return
@ -1662,14 +1615,6 @@ class HtmlDartiumInterfaceGenerator(object):
# We could place the logic for handling Document directly in _wrap
# but we chose to place it here so that bugs in the wrapper and
# wrapperless implementations are more consistent.
if self._shared.MaybeReturnDocument(info.type_name):
emitter.Emit('$(INDENT)return _FixHtmlDocumentReference('
'_wrap($(THIS).$NAME($ARGS)));\n',
INDENT=indent,
THIS=self.DomObjectName(),
NAME=info.name,
ARGS=argument_expressions)
else:
emitter.Emit('$(INDENT)return _wrap($(THIS).$NAME($ARGS));\n',
INDENT=indent,
THIS=self.DomObjectName(),

View file

@ -32,7 +32,7 @@ _DocumentImpl __document;
void _initialize() {
__window = _wrap(dom.window);
__document = _wrap(dom.document.documentElement);
__document = _wrap(dom.document);
}
Window get window() {
@ -74,13 +74,11 @@ _wrap(raw) {
if (domObject.dartObjectLocalStorage != null)
return domObject.dartObjectLocalStorage;
switch(domObject.typeName) {
case 'HTMLDocument':
throw 'A document should never be wrapped directly. TODO(jacobr) XXX';
case 'HTMLHtmlElement':
return new _DocumentImpl._wrap(domObject);
case 'HTMLElement':
return new _UnknownElementImpl._wrap(domObject);
$WRAPCASES
case 'HTMLDocument':
return new _DocumentImpl._wrap(domObject);
default:
throw 'Unrecognized object $domObject. Name=${domObject.typeName}';
}

View file

@ -1,47 +0,0 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
class $CLASSNAME extends _ElementImpl
implements Document {
$!MEMBERS
final dom.HTMLDocument _documentPtr;
final _NodeImpl _wrappedDocumentPtr;
_DocumentImpl._wrap(ptr) :
super._wrap(ptr),
_documentPtr = ptr.parentNode,
_wrappedDocumentPtr = ptr.parentNode != null ?
new _SecretHtmlDocumentImpl._wrap(ptr.parentNode) : null;
// For efficiency and simplicity, we always use the HtmlElement as the
// Document but sometimes internally we need the real JS document object.
_NodeImpl get _rawDocument() => _wrappedDocumentPtr;
// The document doesn't have a parent element.
_ElementImpl get parent() => null;
}
// This class should not be externally visible. If a user ever gets access to
// a _SecretHtmlDocumentImpl object that is a bug. This object is hidden by
// adding checks to all methods that could an HTMLDocument. We believe that
// list is limited to Event.target, and HTMLHtmlElement.parent.
// In a wrapper based world there isn't a need for this complexity but we
// use this design for consistency with the wrapperless implementation so
// that bugs show up in both cases.
class _SecretHtmlDocumentImpl extends _NodeImpl implements Node {
_SecretHtmlDocumentImpl._wrap(ptr) : super._wrap(ptr);
_DocumentImpl get _documentElement() => _wrap(_ptr.documentElement);
}
EventTarget _FixHtmlDocumentReference(EventTarget eventTarget) {
if (eventTarget is _SecretHtmlDocumentImpl) {
_SecretHtmlDocumentImpl secretDocument = eventTarget;
return secretDocument._documentElement;
} else {
return eventTarget;
}
}

View file

@ -50,11 +50,11 @@ class _EventListenerListImpl implements EventListenerList {
// TODO(jacobr): what is the correct behavior here. We could alternately
// force the event to have the expected type.
assert(evt.type == _type);
return _ptr._dispatchEvent(evt);
return _ptr.$dom_dispatchEvent(evt);
}
void _add(EventListener listener, bool useCapture) {
_ptr._addEventListener(_type,
_ptr.$dom_addEventListener(_type,
_findOrAddWrapper(listener, useCapture),
useCapture);
}
@ -62,7 +62,7 @@ class _EventListenerListImpl implements EventListenerList {
void _remove(EventListener listener, bool useCapture) {
Function wrapper = _removeWrapper(listener, useCapture);
if (wrapper !== null) {
_ptr._removeEventListener(_type, wrapper, useCapture);
_ptr.$dom_removeEventListener(_type, wrapper, useCapture);
}
}

View file

@ -24,45 +24,13 @@ $!GENERATED_DART_FILES
#source('../../dom/src/_ListIterators.dart');
#source('../../dom/src/_Lists.dart');
_WindowImpl _cachedWindow;
_DocumentImpl _cachedDocument;
void _init() {
_cachedDocument = _document;
_cachedWindow = _window;
// Feature detect that dart:dom and dart:html are not both loaded by
// checking for the presence of a bug that manifests itself when both
// libraries are loaded.
// TODO(jacobr): remove this code once b/1911 is fixed and the frog compiler
// is changed to generate compile time errors if two libraries that define
// the same native types in conflicting ways are imported.
var element = new Element.tag('body');
element.innerHTML = 'f';
if (element.text == '') {
_cachedWindow.console.error(
'Cannot import dart:html and dart:dom within the same application.');
throw new UnsupportedOperationException(
'Cannot import dart:html and dart:dom within the same application.');
}
}
Window get window() {
if (_cachedWindow == null) {
_init();
}
return _cachedWindow;
}
Window get window() native "return window;";
_WindowImpl get _window() native "return window;";
Document get document() {
if (_cachedDocument == null) {
_init();
}
return _cachedDocument;
}
Document get document() native "return document;";
_DocumentImpl get _document() native "return window.document.documentElement;";
_DocumentImpl get _document() native "return document;";
// Workaround for tags like <cite> that lack their own Element subclass --
// Dart issue 1990.

View file

@ -1,34 +0,0 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
class $CLASSNAME extends _ElementImpl
implements Document
native "*HTMLHtmlElement" {
$!MEMBERS
// For efficiency and simplicity, we always use the HtmlElement as the
// Document but sometimes internally we need the real JS document object.
_NodeImpl get _jsDocument() native "return this.parentNode;";
// The document doesn't have a parent element.
_ElementImpl get parent() => null;
}
// This class should not be externally visible. If a user ever gets access to
// a _SecretHtmlDocumentImpl object that is a bug. This object is hidden by
// adding checks to all methods that could an HTMLDocument. We believe that
// list is limited to Event.target, and HTMLHtmlElement.parent.
class _SecretHtmlDocumentImpl extends _NodeImpl implements Node
native "*HTMLDocument" {
_DocumentImpl get _documentElement() native "return this.documentElement;";
}
EventTarget _FixHtmlDocumentReference(EventTarget eventTarget) {
if (eventTarget is _SecretHtmlDocumentImpl) {
_SecretHtmlDocumentImpl secretDocument = eventTarget;
return secretDocument._documentElement;
} else {
return eventTarget;
}
}

View file

@ -43,15 +43,15 @@ class _EventListenerListImpl implements EventListenerList {
// TODO(jacobr): what is the correct behavior here. We could alternately
// force the event to have the expected type.
assert(evt.type == _type);
return _ptr._dispatchEvent(evt);
return _ptr.$dom_dispatchEvent(evt);
}
void _add(EventListener listener, bool useCapture) {
_ptr._addEventListener(_type, listener, useCapture);
_ptr.$dom_addEventListener(_type, listener, useCapture);
}
void _remove(EventListener listener, bool useCapture) {
_ptr._removeEventListener(_type, listener, useCapture);
_ptr.$dom_removeEventListener(_type, listener, useCapture);
}
}

View file

@ -1,8 +0,0 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
class $CLASSNAME$EXTENDS$IMPLEMENTS
native "*IntentionallyInvalid" {
$!MEMBERS
}

View file

@ -4,7 +4,7 @@
class $CLASSNAME$EXTENDS$IMPLEMENTS native "@*DOMWindow" {
_DocumentImpl get document() native "return this.document.documentElement;";
_DocumentImpl get document() native "return this.document;";
void requestLayoutFrame(TimeoutHandler callback) {
_addMeasurementFrameCallback(callback);

View file

@ -0,0 +1,54 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
class $CLASSNAME extends _NodeImpl
implements Document
$if FROG
native "*HTMLDocument"
$endif
{
$!MEMBERS
// TODO(jacobr): implement all Element methods not on Document.
_ElementImpl query(String selectors) {
// It is fine for our RegExp to detect element id query selectors to have
// false negatives but not false positives.
if (const RegExp("^#[_a-zA-Z]\\w*\$").hasMatch(selectors)) {
return $dom_getElementById(selectors.substring(1));
}
return $dom_querySelector(selectors);
}
// TODO(jacobr): autogenerate this method.
$if FROG
_ElementImpl $dom_querySelector(String selectors) native "return this.querySelector(selectors);";
$else
_ElementImpl $dom_querySelector(String selectors) =>
_wrap(_.querySelector(selectors));
$endif
ElementList queryAll(String selectors) {
if (const RegExp("""^\\[name=["'][^'"]+['"]\\]\$""").hasMatch(selectors)) {
final mutableMatches = $dom_getElementsByName(
selectors.substring(7,selectors.length - 2));
int len = mutableMatches.length;
final copyOfMatches = new List<Element>(len);
for (int i = 0; i < len; ++i) {
copyOfMatches[i] = mutableMatches[i];
}
return new _FrozenElementList._wrap(copyOfMatches);
} else if (const RegExp("^[*a-zA-Z0-9]+\$").hasMatch(selectors)) {
final mutableMatches = $dom_getElementsByTagName(selectors);
int len = mutableMatches.length;
final copyOfMatches = new List<Element>(len);
for (int i = 0; i < len; ++i) {
copyOfMatches[i] = mutableMatches[i];
}
return new _FrozenElementList._wrap(copyOfMatches);
} else {
return new _FrozenElementList._wrap($dom_querySelectorAll(selectors));
}
}
}

View file

@ -146,7 +146,7 @@ class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
}
ElementList queryAll(String selectors) =>
new _FrozenElementList._wrap(_querySelectorAll(selectors));
new _FrozenElementList._wrap($dom_querySelectorAll(selectors));
String get innerHTML() {
final e = new Element.tag("div");
@ -215,8 +215,8 @@ class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
String get tagName() => "";
String get webkitdropzone() => "";
String get webkitRegionOverflow() => "";
Element get firstElementChild() => elements.first();
Element get lastElementChild() => elements.last();
Element get $dom_firstElementChild() => elements.first();
Element get $dom_lastElementChild() => elements.last();
Element get nextElementSibling() => null;
Element get previousElementSibling() => null;
Element get offsetParent() => null;

View file

@ -10,7 +10,7 @@ class _ChildrenElementList implements ElementList {
final _HTMLCollectionImpl _childElements;
_ChildrenElementList._wrap(_ElementImpl element)
: _childElements = element._children,
: _childElements = element.$dom_children,
_element = element;
List<Element> _toList() {
@ -22,7 +22,7 @@ class _ChildrenElementList implements ElementList {
}
_ElementImpl get first() {
return _element._firstElementChild;
return _element.$dom_firstElementChild;
}
void forEach(void f(Element element)) {
@ -68,7 +68,7 @@ class _ChildrenElementList implements ElementList {
}
bool isEmpty() {
return _element._firstElementChild == null;
return _element.$dom_firstElementChild == null;
}
int get length() {
@ -80,7 +80,7 @@ class _ChildrenElementList implements ElementList {
}
void operator []=(int index, _ElementImpl value) {
_element._replaceChild(value, _childElements[index]);
_element.$dom_replaceChild(value, _childElements[index]);
}
void set length(int newLength) {
@ -89,7 +89,7 @@ class _ChildrenElementList implements ElementList {
}
Element add(_ElementImpl value) {
_element._appendChild(value);
_element.$dom_appendChild(value);
return value;
}
@ -99,7 +99,7 @@ class _ChildrenElementList implements ElementList {
void addAll(Collection<Element> collection) {
for (_ElementImpl element in collection) {
_element._appendChild(element);
_element.$dom_appendChild(element);
}
}
@ -144,13 +144,13 @@ class _ChildrenElementList implements ElementList {
Element removeLast() {
final last = this.last();
if (last != null) {
_element._removeChild(last);
_element.$dom_removeChild(last);
}
return last;
}
Element last() {
return _element.lastElementChild;
return _element.$dom_lastElementChild;
}
}
@ -312,7 +312,7 @@ class ElementAttributeMap implements Map<String, String> {
ElementAttributeMap._wrap(this._element);
bool containsValue(String value) {
final attributes = _element._attributes;
final attributes = _element.$dom_attributes;
for (int i = 0, len = attributes.length; i < len; i++) {
if(value == attributes[i].value) {
return true;
@ -322,15 +322,15 @@ class ElementAttributeMap implements Map<String, String> {
}
bool containsKey(String key) {
return _element._hasAttribute(key);
return _element.$dom_hasAttribute(key);
}
String operator [](String key) {
return _element._getAttribute(key);
return _element.$dom_getAttribute(key);
}
void operator []=(String key, String value) {
_element._setAttribute(key, value);
_element.$dom_setAttribute(key, value);
}
String putIfAbsent(String key, String ifAbsent()) {
@ -340,18 +340,18 @@ class ElementAttributeMap implements Map<String, String> {
}
String remove(String key) {
_element._removeAttribute(key);
_element.$dom_removeAttribute(key);
}
void clear() {
final attributes = _element._attributes;
final attributes = _element.$dom_attributes;
for (int i = attributes.length - 1; i >= 0; i--) {
remove(attributes[i].name);
}
}
void forEach(void f(String key, String value)) {
final attributes = _element._attributes;
final attributes = _element.$dom_attributes;
for (int i = 0, len = attributes.length; i < len; i++) {
final item = attributes[i];
f(item.name, item.value);
@ -360,7 +360,7 @@ class ElementAttributeMap implements Map<String, String> {
Collection<String> getKeys() {
// TODO(jacobr): generate a lazy collection instead.
final attributes = _element._attributes;
final attributes = _element.$dom_attributes;
final keys = new List<String>(attributes.length);
for (int i = 0, len = attributes.length; i < len; i++) {
keys[i] = attributes[i].name;
@ -370,7 +370,7 @@ class ElementAttributeMap implements Map<String, String> {
Collection<String> getValues() {
// TODO(jacobr): generate a lazy collection instead.
final attributes = _element._attributes;
final attributes = _element.$dom_attributes;
final values = new List<String>(attributes.length);
for (int i = 0, len = attributes.length; i < len; i++) {
values[i] = attributes[i].value;
@ -382,7 +382,7 @@ class ElementAttributeMap implements Map<String, String> {
* The number of {key, value} pairs in the map.
*/
int get length() {
return _element._attributes.length;
return _element.$dom_attributes.length;
}
/**
@ -429,20 +429,20 @@ class _ElementRectImpl implements ElementRect {
final _ClientRectListImpl _clientRects;
_ElementRectImpl(_ElementImpl element) :
client = new _SimpleClientRect(element._clientLeft,
element._clientTop,
element._clientWidth,
element._clientHeight),
offset = new _SimpleClientRect(element._offsetLeft,
element._offsetTop,
element._offsetWidth,
element._offsetHeight),
scroll = new _SimpleClientRect(element._scrollLeft,
element._scrollTop,
element._scrollWidth,
element._scrollHeight),
_boundingClientRect = element._getBoundingClientRect(),
_clientRects = element._getClientRects();
client = new _SimpleClientRect(element.$dom_clientLeft,
element.$dom_clientTop,
element.$dom_clientWidth,
element.$dom_clientHeight),
offset = new _SimpleClientRect(element.$dom_offsetLeft,
element.$dom_offsetTop,
element.$dom_offsetWidth,
element.$dom_offsetHeight),
scroll = new _SimpleClientRect(element.$dom_scrollLeft,
element.$dom_scrollTop,
element.$dom_scrollWidth,
element.$dom_scrollHeight),
_boundingClientRect = element.$dom_getBoundingClientRect(),
_clientRects = element.$dom_getClientRects();
_ClientRectImpl get bounding() => _boundingClientRect;
@ -491,7 +491,7 @@ class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
ElementList get elements() => new _ChildrenElementList._wrap(this);
ElementList queryAll(String selectors) =>
new _FrozenElementList._wrap(_querySelectorAll(selectors));
new _FrozenElementList._wrap($dom_querySelectorAll(selectors));
Set<String> get classes() {
if (_cssClassSet === null) {
@ -534,8 +534,70 @@ class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
Future<CSSStyleDeclaration> getComputedStyle(String pseudoElement) {
return _createMeasurementFuture(
() => _window._getComputedStyle(this, pseudoElement),
() => _window.$dom_getComputedStyle(this, pseudoElement),
new Completer<CSSStyleDeclaration>());
}
$!MEMBERS
}
final _START_TAG_REGEXP = const RegExp('<(\\w+)');
class _ElementFactoryProvider {
static final _CUSTOM_PARENT_TAG_MAP = const {
'body' : 'html',
'head' : 'html',
'caption' : 'table',
'td': 'tr',
'colgroup': 'table',
'col' : 'colgroup',
'tr' : 'tbody',
'tbody' : 'table',
'tfoot' : 'table',
'thead' : 'table',
'track' : 'audio',
};
/** @domName Document.createElement */
factory Element.html(String html) {
// TODO(jacobr): this method can be made more robust and performant.
// 1) Cache the dummy parent elements required to use innerHTML rather than
// creating them every call.
// 2) Verify that the html does not contain leading or trailing text nodes.
// 3) Verify that the html does not contain both <head> and <body> tags.
// 4) Detatch the created element from its dummy parent.
String parentTag = 'div';
String tag;
final match = _START_TAG_REGEXP.firstMatch(html);
if (match !== null) {
tag = match.group(1).toLowerCase();
if (_CUSTOM_PARENT_TAG_MAP.containsKey(tag)) {
parentTag = _CUSTOM_PARENT_TAG_MAP[tag];
}
}
final _ElementImpl temp = new Element.tag(parentTag);
temp.innerHTML = html;
Element element;
if (temp.elements.length == 1) {
element = temp.elements.first;
} else if (parentTag == 'html' && temp.elements.length == 2) {
// Work around for edge case in WebKit and possibly other browsers where
// both body and head elements are created even though the inner html
// only contains a head or body element.
element = temp.elements[tag == 'head' ? 0 : 1];
} else {
throw new IllegalArgumentException('HTML had ${temp.elements.length} ' +
'top level elements but 1 expected');
}
element.remove();
return element;
}
/** @domName Document.createElement */
$if FROG
// Optimization to improve performance until the frog compiler inlines this
// method.
factory Element.tag(String tag) native "return document.createElement(tag)";
$else
factory Element.tag(String tag) => _document.$dom_createElement(tag);
$endif
}

View file

@ -2,11 +2,115 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/**
* Lazy implementation of the child nodes of an element that does not request
* the actual child nodes of an element until strictly necessary greatly
* improving performance for the typical cases where it is not required.
*/
class _ChildNodeListLazy implements NodeList {
final _NodeImpl _this;
_ChildNodeListLazy(this._this);
$if FROG
_NodeImpl get first() native "return this._this.firstChild;";
_NodeImpl last() native "return this._this.lastChild;";
$else
_NodeImpl get first() => _this.$dom_firstChild;
_NodeImpl last() => _this.$dom_lastChild;
$endif
void add(_NodeImpl value) {
_this.$dom_appendChild(value);
}
void addLast(_NodeImpl value) {
_this.$dom_appendChild(value);
}
void addAll(Collection<_NodeImpl> collection) {
for (_NodeImpl node in collection) {
_this.$dom_appendChild(node);
}
}
_NodeImpl removeLast() {
final last = last();
if (last != null) {
_this.$dom_removeChild(last);
}
return last;
}
void clear() {
_this.text = '';
}
void operator []=(int index, _NodeImpl value) {
_this.$dom_replaceChild(value, this[index]);
}
Iterator<Node> iterator() => _this.$dom_childNodes.iterator();
// TODO(jacobr): We can implement these methods much more efficiently by
// looking up the nodeList only once instead of once per iteration.
void forEach(void f(Node element)) => _Collections.forEach(this, f);
Collection map(f(Node element)) => _Collections.map(this, [], f);
Collection<Node> filter(bool f(Node element)) =>
new _NodeListWrapper(_Collections.filter(this, <Node>[], f));
bool every(bool f(Node element)) => _Collections.every(this, f);
bool some(bool f(Node element)) => _Collections.some(this, f);
bool isEmpty() => this.length == 0;
// From List<Node>:
// TODO(jacobr): this could be implemented for child node lists.
// The exception we throw here is misleading.
void sort(int compare(Node a, Node b)) {
throw new UnsupportedOperationException("Cannot sort immutable List.");
}
int indexOf(Node element, [int start = 0]) =>
_Lists.indexOf(this, element, start, this.length);
int lastIndexOf(Node element, [int start = 0]) =>
_Lists.lastIndexOf(this, element, start);
// FIXME: implement thesee.
void setRange(int start, int length, List<Node> from, [int startFrom]) {
throw new UnsupportedOperationException(
"Cannot setRange on immutable List.");
}
void removeRange(int start, int length) {
throw new UnsupportedOperationException(
"Cannot removeRange on immutable List.");
}
void insertRange(int start, int length, [Node initialValue]) {
throw new UnsupportedOperationException(
"Cannot insertRange on immutable List.");
}
NodeList getRange(int start, int length) =>
new _NodeListWrapper(_Lists.getRange(this, start, length, <Node>[]));
// -- end List<Node> mixins.
// TODO(jacobr): benchmark whether this is more efficient or whether caching
// a local copy of $dom_childNodes is more efficient.
int get length() => _this.$dom_childNodes.length;
_NodeImpl operator[](int index) => _this.$dom_childNodes[index];
}
class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
_NodeListImpl get nodes() {
final list = _childNodes;
list._parent = this;
return list;
_ChildNodeListLazy get nodes() {
return new _ChildNodeListLazy(this);
}
void set nodes(Collection<Node> value) {
@ -15,7 +119,7 @@ class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
List copy = new List.from(value);
text = '';
for (Node node in copy) {
_appendChild(node);
$dom_appendChild(node);
}
}
@ -23,7 +127,7 @@ class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
_NodeImpl remove() {
if (this.parent != null) {
final _NodeImpl parent = this.parent;
parent._removeChild(this);
parent.$dom_removeChild(this);
}
return this;
}
@ -31,7 +135,7 @@ class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
_NodeImpl replaceWith(Node otherNode) {
try {
final _NodeImpl parent = this.parent;
parent._replaceChild(otherNode, this);
parent.$dom_replaceChild(otherNode, this);
} catch(var e) {
};

View file

@ -95,23 +95,23 @@ class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
// From Collection<Node>:
void add(_NodeImpl value) {
_parent._appendChild(value);
_parent.$dom_appendChild(value);
}
void addLast(_NodeImpl value) {
_parent._appendChild(value);
_parent.$dom_appendChild(value);
}
void addAll(Collection<_NodeImpl> collection) {
for (_NodeImpl node in collection) {
_parent._appendChild(node);
_parent.$dom_appendChild(node);
}
}
_NodeImpl removeLast() {
final last = this.last();
if (last != null) {
_parent._removeChild(last);
_parent.$dom_removeChild(last);
}
return last;
}
@ -121,7 +121,7 @@ class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
}
void operator []=(int index, _NodeImpl value) {
_parent._replaceChild(value, this[index]);
_parent.$dom_replaceChild(value, this[index]);
}
void forEach(void f(Node element)) => _Collections.forEach(this, f);

View file

@ -5,7 +5,7 @@
class _AttributeClassSet extends _CssClassSet {
_AttributeClassSet(element) : super(element);
String _className() => _element.attributes['class'];
String $dom_className() => _element.attributes['class'];
void _write(Set s) {
_element.attributes['class'] = _formatSet(s);

View file

@ -10,21 +10,21 @@
*/
class _DataAttributeMap implements Map<String, String> {
final Map<String, String> _attributes;
final Map<String, String> $dom_attributes;
_DataAttributeMap(this._attributes);
_DataAttributeMap(this.$dom_attributes);
// interface Map
// TODO: Use lazy iterator when it is available on Map.
bool containsValue(String value) => getValues().some((v) => v == value);
bool containsKey(String key) => _attributes.containsKey(_attr(key));
bool containsKey(String key) => $dom_attributes.containsKey(_attr(key));
String operator [](String key) => _attributes[_attr(key)];
String operator [](String key) => $dom_attributes[_attr(key)];
void operator []=(String key, String value) {
_attributes[_attr(key)] = value;
$dom_attributes[_attr(key)] = value;
}
String putIfAbsent(String key, String ifAbsent()) {
@ -34,7 +34,7 @@ class _DataAttributeMap implements Map<String, String> {
return this[key];
}
String remove(String key) => _attributes.remove(_attr(key));
String remove(String key) => $dom_attributes.remove(_attr(key));
void clear() {
// Needs to operate on a snapshot since we are mutatiting the collection.
@ -44,7 +44,7 @@ class _DataAttributeMap implements Map<String, String> {
}
void forEach(void f(String key, String value)) {
_attributes.forEach((String key, String value) {
$dom_attributes.forEach((String key, String value) {
if (_matches(key)) {
f(_strip(key), value);
}
@ -53,7 +53,7 @@ class _DataAttributeMap implements Map<String, String> {
Collection<String> getKeys() {
final keys = new List<String>();
_attributes.forEach((String key, String value) {
$dom_attributes.forEach((String key, String value) {
if (_matches(key)) {
keys.add(_strip(key));
}
@ -63,7 +63,7 @@ class _DataAttributeMap implements Map<String, String> {
Collection<String> getValues() {
final values = new List<String>();
_attributes.forEach((String key, String value) {
$dom_attributes.forEach((String key, String value) {
if (_matches(key)) {
values.add(value);
}
@ -194,7 +194,7 @@ class _CssClassSet implements Set<String> {
Set<String> _read() {
// TODO(mattsh) simplify this once split can take regex.
Set<String> s = new Set<String>();
for (String name in _className().split(' ')) {
for (String name in $dom_className().split(' ')) {
String trimmed = name.trim();
if (!trimmed.isEmpty()) {
s.add(trimmed);
@ -207,14 +207,14 @@ class _CssClassSet implements Set<String> {
* Read the class names as a space-separated string. This is meant to be
* overridden by subclasses.
*/
String _className() => _element._className;
String $dom_className() => _element.$dom_className;
/**
* Join all the elements of a set into one string and write
* back to the element.
*/
void _write(Set s) {
_element._className = _formatSet(s);
_element.$dom_className = _formatSet(s);
}
String _formatSet(Set<String> s) {

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -3,17 +3,19 @@
// BSD-style license that can be found in the LICENSE file.
// TODO(rnystrom): add a way to supress public classes from DartDoc output.
// TODO(jacobr): we can remove this class now that we are using the $dom_
// convention for deprecated methods rather than truly private methods.
/**
* This class is intended for testing purposes only.
*/
class Testing {
static void addEventListener(EventTarget target, String type, EventListener listener, bool useCapture) {
final _EventTargetImpl targetImpl = target;
targetImpl._addEventListener(type, listener, useCapture);
targetImpl.$dom_addEventListener(type, listener, useCapture);
}
static void removeEventListener(EventTarget target, String type, EventListener listener, bool useCapture) {
final _EventTargetImpl targetImpl = target;
targetImpl._removeEventListener(type, listener, useCapture);
targetImpl.$dom_removeEventListener(type, listener, useCapture);
}
}

View file

@ -66,3 +66,7 @@ class _WebSocketFactoryProvider {
factory WebSocket(String url) => _wrap(new dom.WebSocket(url));
}
class _TextFactoryProvider {
factory Text(String data) => _document.$dom_createTextNode(data);
}

View file

@ -70,3 +70,7 @@ class _WebSocketFactoryProvider {
factory WebSocket(String url) native '''return new WebSocket(url);''';
}
class _TextFactoryProvider {
factory Text(String data) native "return document.createTextNode(data);";
}

View file

@ -2,16 +2,11 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
class _TextFactoryProvider {
factory Text(String data) => _document._createTextNode(data);
}
class _EventFactoryProvider {
factory Event(String type, [bool canBubble = true,
bool cancelable = true]) {
final _EventImpl e = _document._createEvent("Event");
e._initEvent(type, canBubble, cancelable);
final _EventImpl e = _document.$dom_createEvent("Event");
e.$dom_initEvent(type, canBubble, cancelable);
return e;
}
}
@ -22,8 +17,8 @@ class _MouseEventFactoryProvider {
[bool canBubble = true, bool cancelable = true, bool ctrlKey = false,
bool altKey = false, bool shiftKey = false, bool metaKey = false,
EventTarget relatedTarget = null]) {
final e = _document._createEvent("MouseEvent");
e._initMouseEvent(type, canBubble, cancelable, view, detail,
final e = _document.$dom_createEvent("MouseEvent");
e.$dom_initMouseEvent(type, canBubble, cancelable, view, detail,
screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
button, relatedTarget);
return e;
@ -42,62 +37,6 @@ class _CSSStyleDeclarationFactoryProvider {
}
}
final _START_TAG_REGEXP = const RegExp('<(\\w+)');
class _ElementFactoryProvider {
static final _CUSTOM_PARENT_TAG_MAP = const {
'body' : 'html',
'head' : 'html',
'caption' : 'table',
'td': 'tr',
'colgroup': 'table',
'col' : 'colgroup',
'tr' : 'tbody',
'tbody' : 'table',
'tfoot' : 'table',
'thead' : 'table',
'track' : 'audio',
};
/** @domName Document.createElement */
factory Element.html(String html) {
// TODO(jacobr): this method can be made more robust and performant.
// 1) Cache the dummy parent elements required to use innerHTML rather than
// creating them every call.
// 2) Verify that the html does not contain leading or trailing text nodes.
// 3) Verify that the html does not contain both <head> and <body> tags.
// 4) Detatch the created element from its dummy parent.
String parentTag = 'div';
String tag;
final match = _START_TAG_REGEXP.firstMatch(html);
if (match !== null) {
tag = match.group(1).toLowerCase();
if (_CUSTOM_PARENT_TAG_MAP.containsKey(tag)) {
parentTag = _CUSTOM_PARENT_TAG_MAP[tag];
}
}
final _ElementImpl temp = new Element.tag(parentTag);
temp.innerHTML = html;
Element element;
if (temp.elements.length == 1) {
element = temp.elements.first;
} else if (parentTag == 'html' && temp.elements.length == 2) {
// Work around for edge case in WebKit and possibly other browsers where
// both body and head elements are created even though the inner html
// only contains a head or body element.
element = temp.elements[tag == 'head' ? 0 : 1];
} else {
throw new IllegalArgumentException('HTML had ${temp.elements.length} ' +
'top level elements but 1 expected');
}
element.remove();
return element;
}
/** @domName Document.createElement */
factory Element.tag(String tag) => _document._createElement(tag);
}
class _DocumentFragmentFactoryProvider {
/** @domName Document.createDocumentFragment */
factory DocumentFragment() => document.createDocumentFragment();
@ -135,7 +74,7 @@ class _DocumentFragmentFactoryProvider {
class _SVGElementFactoryProvider {
factory SVGElement.tag(String tag) {
final Element temp =
_document._createElementNS("http://www.w3.org/2000/svg", tag);
_document.$dom_createElementNS("http://www.w3.org/2000/svg", tag);
return temp;
}