mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 10:49:00 +00:00
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:
parent
37147fc116
commit
dd9bdb5ece
23 changed files with 2230 additions and 1707 deletions
12
.gitignore
vendored
12
.gitignore
vendored
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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}';
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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);
|
||||
|
|
54
lib/dom/templates/html/impl/impl_Document.darttemplate
Normal file
54
lib/dom/templates/html/impl/impl_Document.darttemplate
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);";
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue