2 * Copyright (C) 2010 Google, Inc. All Rights Reserved.
3 * Copyright (C) 2011 Apple Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #ifndef HTMLConstructionSite_h
28 #define HTMLConstructionSite_h
30 #include "core/dom/Document.h"
31 #include "core/dom/ParserContentPolicy.h"
32 #include "core/html/parser/HTMLElementStack.h"
33 #include "core/html/parser/HTMLFormattingElementList.h"
34 #include "wtf/Noncopyable.h"
35 #include "wtf/PassRefPtr.h"
36 #include "wtf/RefPtr.h"
37 #include "wtf/Vector.h"
38 #include "wtf/text/StringBuilder.h"
42 struct HTMLConstructionSiteTask
{
43 ALLOW_ONLY_INLINE_ALLOCATION();
47 InsertText
, // Handles possible merging of text nodes.
48 InsertAlreadyParsedChild
, // Insert w/o calling begin/end parsing.
53 explicit HTMLConstructionSiteTask(Operation op
)
61 visitor
->trace(parent
);
62 visitor
->trace(nextChild
);
63 visitor
->trace(child
);
66 ContainerNode
* oldParent()
68 // It's sort of ugly, but we store the |oldParent| in the |child| field
69 // of the task so that we don't bloat the HTMLConstructionSiteTask
70 // object in the common case of the Insert operation.
71 return toContainerNode(child
.get());
75 RefPtrWillBeMember
<ContainerNode
> parent
;
76 RefPtrWillBeMember
<Node
> nextChild
;
77 RefPtrWillBeMember
<Node
> child
;
83 WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::HTMLConstructionSiteTask
);
87 // Note: These are intentionally ordered so that when we concatonate
88 // strings and whitespaces the resulting whitespace is ws = min(ws1, ws2).
96 // Flush pending text. Flush queued tasks.
99 // Flush pending text if node has length limit. Flush queued tasks.
103 class AtomicHTMLToken
;
106 class HTMLFormElement
;
108 class HTMLConstructionSite final
{
109 WTF_MAKE_NONCOPYABLE(HTMLConstructionSite
);
110 DISALLOW_ALLOCATION();
112 HTMLConstructionSite(Document
*, ParserContentPolicy
);
113 HTMLConstructionSite(DocumentFragment
*, ParserContentPolicy
);
114 ~HTMLConstructionSite();
119 // executeQueuedTasks empties the queue but does not flush pending text.
120 // NOTE: Possible reentrancy via JavaScript execution.
121 void executeQueuedTasks();
123 // flushPendingText turns pending text into queued Text insertions, but does not execute them.
124 void flushPendingText(FlushMode
);
126 // Called before every token in HTMLTreeBuilder::processToken, thus inlined:
127 void flush(FlushMode mode
)
129 if (!hasPendingTasks())
131 flushPendingText(mode
);
132 executeQueuedTasks(); // NOTE: Possible reentrancy via JavaScript execution.
133 ASSERT(mode
== FlushIfAtTextLimit
|| !hasPendingTasks());
136 bool hasPendingTasks()
138 return !m_pendingText
.isEmpty() || !m_taskQueue
.isEmpty();
141 void setDefaultCompatibilityMode();
142 void processEndOfFile();
143 void finishedParsing();
145 void insertDoctype(AtomicHTMLToken
*);
146 void insertComment(AtomicHTMLToken
*);
147 void insertCommentOnDocument(AtomicHTMLToken
*);
148 void insertCommentOnHTMLHtmlElement(AtomicHTMLToken
*);
149 void insertHTMLElement(AtomicHTMLToken
*);
150 void insertSelfClosingHTMLElement(AtomicHTMLToken
*);
151 void insertFormattingElement(AtomicHTMLToken
*);
152 void insertHTMLHeadElement(AtomicHTMLToken
*);
153 void insertHTMLBodyElement(AtomicHTMLToken
*);
154 void insertHTMLFormElement(AtomicHTMLToken
*, bool isDemoted
= false);
155 void insertScriptElement(AtomicHTMLToken
*);
156 void insertTextNode(const String
&, WhitespaceMode
= WhitespaceUnknown
);
157 void insertForeignElement(AtomicHTMLToken
*, const AtomicString
& namespaceURI
);
159 void insertHTMLHtmlStartTagBeforeHTML(AtomicHTMLToken
*);
160 void insertHTMLHtmlStartTagInBody(AtomicHTMLToken
*);
161 void insertHTMLBodyStartTagInBody(AtomicHTMLToken
*);
163 void reparent(HTMLElementStack::ElementRecord
* newParent
, HTMLElementStack::ElementRecord
* child
);
164 void reparent(HTMLElementStack::ElementRecord
* newParent
, HTMLStackItem
* child
);
165 // insertAlreadyParsedChild assumes that |child| has already been parsed (i.e., we're just
166 // moving it around in the tree rather than parsing it for the first time). That means
167 // this function doesn't call beginParsingChildren / finishParsingChildren.
168 void insertAlreadyParsedChild(HTMLStackItem
* newParent
, HTMLElementStack::ElementRecord
* child
);
169 void takeAllChildren(HTMLStackItem
* newParent
, HTMLElementStack::ElementRecord
* oldParent
);
171 PassRefPtrWillBeRawPtr
<HTMLStackItem
> createElementFromSavedToken(HTMLStackItem
*);
173 bool shouldFosterParent() const;
174 void fosterParent(PassRefPtrWillBeRawPtr
<Node
>);
176 bool indexOfFirstUnopenFormattingElement(unsigned& firstUnopenElementIndex
) const;
177 void reconstructTheActiveFormattingElements();
179 void generateImpliedEndTags();
180 void generateImpliedEndTagsWithExclusion(const AtomicString
& tagName
);
184 bool isEmpty() const { return !m_openElements
.stackDepth(); }
185 HTMLElementStack::ElementRecord
* currentElementRecord() const { return m_openElements
.topRecord(); }
186 Element
* currentElement() const { return m_openElements
.top(); }
187 ContainerNode
* currentNode() const { return m_openElements
.topNode(); }
188 HTMLStackItem
* currentStackItem() const { return m_openElements
.topStackItem(); }
189 HTMLStackItem
* oneBelowTop() const { return m_openElements
.oneBelowTop(); }
190 Document
& ownerDocumentForCurrentNode();
191 HTMLElementStack
* openElements() const { return &m_openElements
; }
192 HTMLFormattingElementList
* activeFormattingElements() const { return &m_activeFormattingElements
; }
193 bool currentIsRootNode() { return m_openElements
.topNode() == m_openElements
.rootNode(); }
195 Element
* head() const { return m_head
->element(); }
196 HTMLStackItem
* headStackItem() const { return m_head
.get(); }
198 void setForm(HTMLFormElement
*);
199 HTMLFormElement
* form() const { return m_form
.get(); }
200 PassRefPtrWillBeRawPtr
<HTMLFormElement
> takeForm();
202 ParserContentPolicy
parserContentPolicy() { return m_parserContentPolicy
; }
204 class RedirectToFosterParentGuard
{
206 WTF_MAKE_NONCOPYABLE(RedirectToFosterParentGuard
);
208 RedirectToFosterParentGuard(HTMLConstructionSite
& tree
)
210 , m_wasRedirectingBefore(tree
.m_redirectAttachToFosterParent
)
212 m_tree
.m_redirectAttachToFosterParent
= true;
215 ~RedirectToFosterParentGuard()
217 m_tree
.m_redirectAttachToFosterParent
= m_wasRedirectingBefore
;
221 HTMLConstructionSite
& m_tree
;
222 bool m_wasRedirectingBefore
;
226 // In the common case, this queue will have only one task because most
227 // tokens produce only one DOM mutation.
228 typedef WillBeHeapVector
<HTMLConstructionSiteTask
, 1> TaskQueue
;
230 void setCompatibilityMode(Document::CompatibilityMode
);
231 void setCompatibilityModeFromDoctype(const String
& name
, const String
& publicId
, const String
& systemId
);
233 void attachLater(ContainerNode
* parent
, PassRefPtrWillBeRawPtr
<Node
> child
, bool selfClosing
= false);
235 void findFosterSite(HTMLConstructionSiteTask
&);
237 PassRefPtrWillBeRawPtr
<HTMLElement
> createHTMLElement(AtomicHTMLToken
*);
238 PassRefPtrWillBeRawPtr
<Element
> createElement(AtomicHTMLToken
*, const AtomicString
& namespaceURI
);
240 void mergeAttributesFromTokenIntoElement(AtomicHTMLToken
*, Element
*);
241 void dispatchDocumentElementAvailableIfNeeded();
243 void executeTask(HTMLConstructionSiteTask
&);
244 void queueTask(const HTMLConstructionSiteTask
&);
246 RawPtrWillBeMember
<Document
> m_document
;
248 // This is the root ContainerNode to which the parser attaches all newly
249 // constructed nodes. It points to a DocumentFragment when parsing fragments
250 // and a Document in all other cases.
251 RawPtrWillBeMember
<ContainerNode
> m_attachmentRoot
;
253 RefPtrWillBeMember
<HTMLStackItem
> m_head
;
254 RefPtrWillBeMember
<HTMLFormElement
> m_form
;
255 mutable HTMLElementStack m_openElements
;
256 mutable HTMLFormattingElementList m_activeFormattingElements
;
258 TaskQueue m_taskQueue
;
260 class PendingText final
{
261 DISALLOW_ALLOCATION();
264 : whitespaceMode(WhitespaceUnknown
)
268 void append(PassRefPtrWillBeRawPtr
<ContainerNode
> newParent
, PassRefPtrWillBeRawPtr
<Node
> newNextChild
, const String
& newString
, WhitespaceMode newWhitespaceMode
)
270 ASSERT(!parent
|| parent
== newParent
);
272 ASSERT(!nextChild
|| nextChild
== newNextChild
);
273 nextChild
= newNextChild
;
274 stringBuilder
.append(newString
);
275 whitespaceMode
= std::min(whitespaceMode
, newWhitespaceMode
);
278 void swap(PendingText
& other
)
280 std::swap(whitespaceMode
, other
.whitespaceMode
);
281 parent
.swap(other
.parent
);
282 nextChild
.swap(other
.nextChild
);
283 stringBuilder
.swap(other
.stringBuilder
);
288 PendingText discardedText
;
294 // When the stringbuilder is empty, the parent and whitespace should also be "empty".
295 ASSERT(stringBuilder
.isEmpty() == !parent
);
296 ASSERT(!stringBuilder
.isEmpty() || !nextChild
);
297 ASSERT(!stringBuilder
.isEmpty() || (whitespaceMode
== WhitespaceUnknown
));
298 return stringBuilder
.isEmpty();
303 RefPtrWillBeMember
<ContainerNode
> parent
;
304 RefPtrWillBeMember
<Node
> nextChild
;
305 StringBuilder stringBuilder
;
306 WhitespaceMode whitespaceMode
;
309 PendingText m_pendingText
;
311 ParserContentPolicy m_parserContentPolicy
;
312 bool m_isParsingFragment
;
314 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-intable
315 // In the "in table" insertion mode, we sometimes get into a state where
316 // "whenever a node would be inserted into the current node, it must instead
317 // be foster parented." This flag tracks whether we're in that state.
318 bool m_redirectAttachToFosterParent
;