/* * Copyright (C) 2010 Google, Inc. All Rights Reserved. * Copyright (C) 2011, 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "sky/engine/config.h" #include "sky/engine/core/html/parser/HTMLTreeBuilder.h" #include "gen/sky/core/HTMLNames.h" #include "sky/engine/bindings/core/v8/ExceptionStatePlaceholder.h" #include "sky/engine/core/dom/DocumentFragment.h" #include "sky/engine/core/html/HTMLTemplateElement.h" #include "sky/engine/core/html/parser/AtomicHTMLToken.h" #include "sky/engine/core/html/parser/HTMLDocumentParser.h" #include "sky/engine/core/html/parser/HTMLParserIdioms.h" #include "sky/engine/core/html/parser/HTMLToken.h" #include "sky/engine/core/html/parser/HTMLTokenizer.h" namespace blink { static TextPosition uninitializedPositionValue1() { return TextPosition(OrdinalNumber::fromOneBasedInt(-1), OrdinalNumber::first()); } HTMLTreeBuilder::HTMLTreeBuilder(HTMLDocumentParser* parser, Document* document, bool) : #if ENABLE(ASSERT) m_isAttached(true), #endif m_tree(document) , m_insertionMode(HTMLMode) , m_originalInsertionMode(HTMLMode) , m_parser(parser) , m_scriptToProcessStartPosition(uninitializedPositionValue1()) { m_tree.openElements()->pushRootNode(document); } HTMLTreeBuilder::~HTMLTreeBuilder() { } void HTMLTreeBuilder::detach() { #if ENABLE(ASSERT) // This call makes little sense in fragment mode, but for consistency // DocumentParser expects detach() to always be called before it's destroyed. m_isAttached = false; #endif // HTMLConstructionSite might be on the callstack when detach() is called // otherwise we'd just call m_tree.clear() here instead. m_tree.detach(); } HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext() : m_fragment(nullptr) { } HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext(DocumentFragment* fragment, Element* contextElement) : m_fragment(fragment) { ASSERT(!fragment->hasChildren()); } HTMLTreeBuilder::FragmentParsingContext::~FragmentParsingContext() { } PassRefPtr HTMLTreeBuilder::takeScriptToProcess(TextPosition& scriptStartPosition) { ASSERT(m_scriptToProcess); ASSERT(!m_tree.hasPendingTasks()); // Unpause ourselves, callers may pause us again when processing the script. // The HTML5 spec is written as though scripts are executed inside the tree // builder. We pause the parser to exit the tree builder, and then resume // before running scripts. scriptStartPosition = m_scriptToProcessStartPosition; m_scriptToProcessStartPosition = uninitializedPositionValue1(); return m_scriptToProcess.release(); } void HTMLTreeBuilder::constructTree(AtomicHTMLToken* token) { const HTMLToken::Type type = token->type(); if (type == HTMLToken::Character) { processCharacter(token); return; } // Any non-character token needs to cause us to flush any pending text immediately. // NOTE: flush() can cause any queued tasks to execute, possibly re-entering the parser. m_tree.flush(); if (type == HTMLToken::StartTag) { processStartTag(token); } else if (type == HTMLToken::EndTag) { processEndTag(token); } else if (type == HTMLToken::EndOfFile) { processEndOfFile(token); } else { ASSERT_NOT_REACHED(); } m_tree.executeQueuedTasks(); // We might be detached now. } void HTMLTreeBuilder::processStartTag(AtomicHTMLToken* token) { ASSERT(token->type() == HTMLToken::StartTag); const AtomicString& name = token->name(); if (name == HTMLNames::styleTag) { processGenericRawTextStartTag(token); } else if (name == HTMLNames::scriptTag) { processScriptStartTag(token); } else if (token->selfClosing()) { m_tree.insertSelfClosingHTMLElement(token); } else { m_tree.insertHTMLElement(token); } } void HTMLTreeBuilder::processEndTag(AtomicHTMLToken* token) { ASSERT(token->type() == HTMLToken::EndTag); const InsertionMode mode = insertionMode(); if (mode == HTMLMode) { HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord(); while (record->next()) { RefPtr element = record->element(); if (element->hasLocalName(token->name())) { m_tree.openElements()->popUntilPopped(element.get()); ASSERT(m_tree.openElements()->topNode()); return; } record = record->next(); } return; } ASSERT(mode == TextMode); if (token->name() == HTMLNames::scriptTag) { // Pause ourselves so that parsing stops until the script can be processed by the caller. ASSERT(m_tree.currentElement()->hasLocalName(HTMLNames::scriptTag.localName())); m_scriptToProcess = m_tree.currentElement(); m_tree.openElements()->pop(); setInsertionMode(m_originalInsertionMode); return; } m_tree.openElements()->pop(); setInsertionMode(m_originalInsertionMode); } void HTMLTreeBuilder::processCharacter(AtomicHTMLToken* token) { ASSERT(token->type() == HTMLToken::Character); m_tree.insertTextNode(token->characters()); } void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken* token) { ASSERT(token->type() == HTMLToken::EndOfFile); m_tree.processEndOfFile(); } void HTMLTreeBuilder::processGenericRawTextStartTag(AtomicHTMLToken* token) { ASSERT(token->type() == HTMLToken::StartTag); m_tree.insertHTMLElement(token); m_originalInsertionMode = m_insertionMode; setInsertionMode(TextMode); } void HTMLTreeBuilder::processScriptStartTag(AtomicHTMLToken* token) { ASSERT(token->type() == HTMLToken::StartTag); m_tree.insertScriptElement(token); m_originalInsertionMode = m_insertionMode; TextPosition position = m_parser->textPosition(); m_scriptToProcessStartPosition = position; setInsertionMode(TextMode); } void HTMLTreeBuilder::finished() { if (isParsingFragment()) return; ASSERT(m_isAttached); // Warning, this may detach the parser. Do not do anything else after this. m_tree.finishedParsing(); } } // namespace blink