Source/WebCore/ChangeLog

 12011-02-09 Andy Estes <aestes@apple.com>
 2
 3 Reviewed by NOBODY (OOPS!).
 4
 5 HTML5 TreeBuilder regressed a Peacekeeper DOM test by 40%
 6 https://bugs.webkit.org/show_bug.cgi?id=48719
 7
 8 The HTML5 fragment parsing algorithm specifies that a new Document
 9 should be created to serve as the temporary parent of fragment nodes
 10 during parsing. Document creation is expensive and accounts for ~38% of
 11 the Peacekeeper DOM performance regression. Avoid the cost of creating
 12 a dummy document by using the already-created DocumentFragment as the
 13 root node during fragment parsing.
 14
 15 With this patch, the regression in Peacekeeper from Safari 5.0.3 to ToT
 16 is ~24%.
 17
 18 Test: fast/parser/fragment-parser-doctype.html
 19
 20 * dom/ContainerNode.h:
 21 (WebCore::ContainerNode::firstElementChild): Add a method that returns
 22 the first element-typed child from a ContainerNode.
 23 * dom/Document.cpp:
 24 (WebCore::Document::cacheDocumentElement): Call
 25 ContainerNode::firstElementChild() to retrieve and cache the document
 26 element.
 27 * html/parser/HTMLConstructionSite.cpp:
 28 (WebCore::HTMLConstructionSite::HTMLConstructionSite): Initialize the
 29 root ContainerNode.
 30 (WebCore::HTMLConstructionSite::detach): Clear the reference to the
 31 root ContainerNode.
 32 (WebCore::HTMLConstructionSite::insertHTMLHtmlStartTagBeforeHTML):
 33 Attach the new element to the root ContainerNode.
 34 (WebCore::HTMLConstructionSite::insertDoctype): Ditto.
 35 (WebCore::HTMLConstructionSite::insertCommentOnDocument): Ditto.
 36 * html/parser/HTMLConstructionSite.h: Store a pointer to a
 37 ContainerNode that will be used as the root node for document parsing.
 38 This node might or might not be the same as m_document.
 39 * html/parser/HTMLTreeBuilder.cpp:
 40 (WebCore::HTMLTreeBuilder::HTMLTreeBuilder): Initialize the
 41 HTMLConstructionSite with the correct root ContainerNode based on
 42 whether or not we're parsing a fragment.
 43 (WebCore::HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext):
 44 Remove m_dummyDocumentForFragmentParsing.
 45 (WebCore::HTMLTreeBuilder::FragmentParsingContext::finished): If the
 46 fragment has a context element, store only the children of the root
 47 element (HTML5 Section 10.4, Step 7).
 48 * html/parser/HTMLTreeBuilder.h:
 49
1502011-02-09 Beth Dakin <bdakin@apple.com>
251
352 Reviewed by Maciej Stachowiak.
78171

Source/WebCore/dom/ContainerNode.h

@@public:
4343
4444 Node* firstChild() const { return m_firstChild; }
4545 Node* lastChild() const { return m_lastChild; }
 46 ContainerNode* firstElementChild() const;
4647
4748 bool insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode&, bool shouldLazyAttach = false);
4849 bool replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode&, bool shouldLazyAttach = false);

@@inline Node* Node::lastChild() const
169170 return 0;
170171 return toContainerNode(this)->lastChild();
171172}
 173
 174inline ContainerNode* ContainerNode::firstElementChild() const
 175{
 176 Node* child = firstChild();
 177 while (child && !child->isElementNode())
 178 child = child->nextSibling();
 179 return static_cast<ContainerNode*>(child);
 180}
172181
173182} // namespace WebCore
174183
77988

Source/WebCore/dom/Document.cpp

@@void Document::childrenChanged(bool chan
705705void Document::cacheDocumentElement() const
706706{
707707 ASSERT(!m_documentElement);
708  Node* n = firstChild();
709  while (n && !n->isElementNode())
710  n = n->nextSibling();
711  m_documentElement = static_cast<Element*>(n);
 708 m_documentElement = static_cast<Element*>(firstElementChild());
712709}
713710
714711PassRefPtr<Element> Document::createElement(const AtomicString& name, ExceptionCode& ec)
77988

Source/WebCore/html/parser/HTMLConstructionSite.cpp

@@void HTMLConstructionSite::attachAtSite(
129129 if (child->parentNode() && site.parent->attached() && !child->attached())
130130 child->attach();
131131}
132 
133 HTMLConstructionSite::HTMLConstructionSite(Document* document, FragmentScriptingPermission scriptingPermission, bool isParsingFragment)
 132
 133HTMLConstructionSite::HTMLConstructionSite(Document* document)
134134 : m_document(document)
 135 , m_attachmentRoot(document)
 136 , m_fragmentScriptingPermission(FragmentScriptingAllowed)
 137 , m_isParsingFragment(false)
 138 , m_redirectAttachToFosterParent(false)
 139{
 140}
 141
 142HTMLConstructionSite::HTMLConstructionSite(DocumentFragment* fragment, FragmentScriptingPermission scriptingPermission)
 143 : m_document(fragment->document())
 144 , m_attachmentRoot(fragment)
135145 , m_fragmentScriptingPermission(scriptingPermission)
136  , m_isParsingFragment(isParsingFragment)
 146 , m_isParsingFragment(true)
137147 , m_redirectAttachToFosterParent(false)
138148{
139149}

@@HTMLConstructionSite::~HTMLConstructionS
145155void HTMLConstructionSite::detach()
146156{
147157 m_document = 0;
 158 m_attachmentRoot = 0;
148159}
149160
150161void HTMLConstructionSite::setForm(HTMLFormElement* form)

@@void HTMLConstructionSite::insertHTMLHtm
170181{
171182 RefPtr<HTMLHtmlElement> element = HTMLHtmlElement::create(m_document);
172183 element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
173  m_openElements.pushHTMLHtmlElement(attach<Element>(m_document, element.get()));
 184 m_openElements.pushHTMLHtmlElement(attach<Element>(m_attachmentRoot, element.get()));
174185#if ENABLE(OFFLINE_WEB_APPLICATIONS)
175186 element->insertedByParser();
176187#endif

@@void HTMLConstructionSite::insertHTMLBod
205216void HTMLConstructionSite::insertDoctype(AtomicHTMLToken& token)
206217{
207218 ASSERT(token.type() == HTMLToken::DOCTYPE);
208  attach(m_document, DocumentType::create(m_document, token.name(), String::adopt(token.publicIdentifier()), String::adopt(token.systemIdentifier())));
 219 attach(m_attachmentRoot, DocumentType::create(m_document, token.name(), String::adopt(token.publicIdentifier()), String::adopt(token.systemIdentifier())));
 220
 221 // A fragment DOCTYPE shouldn't modify the compatibility mode of the owning document.
 222 if (m_isParsingFragment)
 223 return;
209224
210225 if (token.forceQuirks())
211226 m_document->setCompatibilityMode(Document::QuirksMode);

@@void HTMLConstructionSite::insertComment
222237void HTMLConstructionSite::insertCommentOnDocument(AtomicHTMLToken& token)
223238{
224239 ASSERT(token.type() == HTMLToken::Comment);
225  attach(m_document, Comment::create(m_document, token.comment()));
 240 attach(m_attachmentRoot, Comment::create(m_document, token.comment()));
226241}
227242
228243void HTMLConstructionSite::insertCommentOnHTMLHtmlElement(AtomicHTMLToken& token)
77988

Source/WebCore/html/parser/HTMLConstructionSite.h

@@class Element;
4343class HTMLConstructionSite {
4444 WTF_MAKE_NONCOPYABLE(HTMLConstructionSite);
4545public:
46  HTMLConstructionSite(Document*, FragmentScriptingPermission, bool isParsingFragment);
 46 HTMLConstructionSite(Document*);
 47 HTMLConstructionSite(DocumentFragment*, FragmentScriptingPermission);
4748 ~HTMLConstructionSite();
4849
4950 void detach();

@@private:
130131 void dispatchDocumentElementAvailableIfNeeded();
131132
132133 Document* m_document;
 134
 135 // This is the root ContainerNode to which the parser attaches all newly
 136 // constructed nodes. It points to a DocumentFragment when parsing fragments
 137 // and a Document in all other cases.
 138 ContainerNode* m_attachmentRoot;
 139
133140 RefPtr<Element> m_head;
134141 RefPtr<HTMLFormElement> m_form;
135142 mutable HTMLElementStack m_openElements;
77988

Source/WebCore/html/parser/HTMLTreeBuilder.cpp

@@private:
341341HTMLTreeBuilder::HTMLTreeBuilder(HTMLDocumentParser* parser, HTMLDocument* document, bool reportErrors, bool usePreHTML5ParserQuirks)
342342 : m_framesetOk(true)
343343 , m_document(document)
344  , m_tree(document, FragmentScriptingAllowed, false)
 344 , m_tree(document)
345345 , m_reportErrors(reportErrors)
346346 , m_isPaused(false)
347347 , m_insertionMode(InitialMode)

@@HTMLTreeBuilder::HTMLTreeBuilder(HTMLDoc
359359HTMLTreeBuilder::HTMLTreeBuilder(HTMLDocumentParser* parser, DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission, bool usePreHTML5ParserQuirks)
360360 : m_framesetOk(true)
361361 , m_fragmentContext(fragment, contextElement, scriptingPermission)
362  , m_document(m_fragmentContext.document())
363  , m_tree(m_document, scriptingPermission, true)
 362 , m_document(fragment->document())
 363 , m_tree(fragment, scriptingPermission)
364364 , m_reportErrors(false) // FIXME: Why not report errors in fragments?
365365 , m_isPaused(false)
366366 , m_insertionMode(InitialMode)

@@HTMLTreeBuilder::HTMLTreeBuilder(HTMLDoc
374374 if (contextElement) {
375375 // Steps 4.2-4.6 of the HTML5 Fragment Case parsing algorithm:
376376 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#fragment-case
377  m_document->setCompatibilityMode(contextElement->document()->compatibilityMode());
378377 processFakeStartTag(htmlTag);
379378 resetInsertionModeAppropriately();
380379 m_tree.setForm(closestFormAncestor(contextElement));

@@HTMLTreeBuilder::FragmentParsingContext:
403402}
404403
405404HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext(DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission)
406  : m_dummyDocumentForFragmentParsing(HTMLDocument::create(0, KURL(), fragment->document()->baseURI()))
407  , m_fragment(fragment)
 405 : m_fragment(fragment)
408406 , m_contextElement(contextElement)
409407 , m_scriptingPermission(scriptingPermission)
410408{
411  m_dummyDocumentForFragmentParsing->setCompatibilityMode(fragment->document()->compatibilityMode());
412 }
413 
414 Document* HTMLTreeBuilder::FragmentParsingContext::document() const
415 {
416  ASSERT(m_fragment);
417  return m_dummyDocumentForFragmentParsing.get();
 409 ASSERT(!fragment->hasChildNodes());
418410}
419411
420412void HTMLTreeBuilder::FragmentParsingContext::finished()
421413{
422  // Populate the DocumentFragment with the parsed content now that we're done.
423  ContainerNode* root = m_dummyDocumentForFragmentParsing.get();
424  if (m_contextElement)
425  root = m_dummyDocumentForFragmentParsing->documentElement();
426  m_fragment->takeAllChildrenFrom(root);
 414 if (!m_contextElement)
 415 return;
 416
 417 // The HTML5 spec says to return the children of the fragment's document
 418 // element when there is a context element (10.4.7).
 419 RefPtr<ContainerNode> documentElement = m_fragment->firstElementChild();
 420 m_fragment->removeChildren();
 421 ASSERT(documentElement);
 422 m_fragment->takeAllChildrenFrom(documentElement.get());
427423}
428424
429425HTMLTreeBuilder::FragmentParsingContext::~FragmentParsingContext()
77988

Source/WebCore/html/parser/HTMLTreeBuilder.h

@@private:
212212 FragmentParsingContext(DocumentFragment*, Element* contextElement, FragmentScriptingPermission);
213213 ~FragmentParsingContext();
214214
215  Document* document() const;
216215 DocumentFragment* fragment() const { return m_fragment; }
217216 Element* contextElement() const { ASSERT(m_fragment); return m_contextElement; }
218217 FragmentScriptingPermission scriptingPermission() const { ASSERT(m_fragment); return m_scriptingPermission; }

@@private:
220219 void finished();
221220
222221 private:
223  RefPtr<Document> m_dummyDocumentForFragmentParsing;
224222 DocumentFragment* m_fragment;
225223 Element* m_contextElement;
226224
77988

LayoutTests/ChangeLog

 12011-02-09 Andy Estes <aestes@apple.com>
 2
 3 Reviewed by NOBODY (OOPS!).
 4
 5 HTML5 TreeBuilder regressed a Peacekeeper DOM test by 40%
 6 https://bugs.webkit.org/show_bug.cgi?id=48719
 7
 8 * fast/parser/fragment-parser-doctype-expected.txt: Added.
 9 * fast/parser/fragment-parser-doctype.html: Added.
 10
1112011-02-09 Ryosuke Niwa <rniwa@webkit.org>
212
313 Reviewed by Darin Adler.
78171

LayoutTests/fast/parser/fragment-parser-doctype-expected.txt

 1Verifying that a fragment's DOCTYPE does not affect parsing:
 2PASS container.firstChild.nextSibling is null
 3Verifying that a fragment's DOCTYPE does not change the compatibility mode of the owner document:
 4PASS container.firstChild.nextSibling is null
 5PASS successfullyParsed is true
 6
 7TEST COMPLETE
 8
 9
0

LayoutTests/fast/parser/fragment-parser-doctype.html

 1<html>
 2<head>
 3<link rel="stylesheet" href="../js/resources/js-test-style.css">
 4<script src="../js/resources/js-test-pre.js"></script>
 5</head>
 6<body>
 7<p id="description"></p>
 8<div id="console"></div>
 9<script>
 10 debug("Verifying that a fragment's DOCTYPE does not affect parsing:");
 11 var container = document.createElement("div");
 12 document.body.appendChild(container);
 13 container.innerHTML = "<!DOCTYPE html><p><table>"
 14 shouldBeNull("container.firstChild.nextSibling");
 15</script>
 16<p id="test"><table></table>
 17<script>
 18 debug ("Verifying that a fragment's DOCTYPE does not change the compatibility mode of the owner document:");
 19 var test = document.getElementById("test");
 20 shouldBeNull("container.firstChild.nextSibling");
 21 var successfullyParsed = true;
 22</script>
 23<script src="../js/resources/js-test-post.js"></script>
 24</body>
 25</html>
0