summaryrefslogtreecommitdiffstats
path: root/WebCore/html/parser/HTMLDocumentParser.cpp
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /WebCore/html/parser/HTMLDocumentParser.cpp
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'WebCore/html/parser/HTMLDocumentParser.cpp')
-rw-r--r--WebCore/html/parser/HTMLDocumentParser.cpp536
1 files changed, 0 insertions, 536 deletions
diff --git a/WebCore/html/parser/HTMLDocumentParser.cpp b/WebCore/html/parser/HTMLDocumentParser.cpp
deleted file mode 100644
index 3f77b2c..0000000
--- a/WebCore/html/parser/HTMLDocumentParser.cpp
+++ /dev/null
@@ -1,536 +0,0 @@
-/*
- * Copyright (C) 2010 Google, 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 APPLE 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 APPLE 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 "config.h"
-#include "HTMLDocumentParser.h"
-
-#include "DocumentFragment.h"
-#include "Element.h"
-#include "Frame.h"
-#include "HTMLNames.h"
-#include "HTMLParserScheduler.h"
-#include "HTMLTokenizer.h"
-#include "HTMLPreloadScanner.h"
-#include "HTMLScriptRunner.h"
-#include "HTMLTreeBuilder.h"
-#include "HTMLDocument.h"
-#include "InspectorInstrumentation.h"
-#include "NestingLevelIncrementer.h"
-#include "Settings.h"
-#include "XSSAuditor.h"
-#include <wtf/CurrentTime.h>
-
-#ifdef ANDROID_INSTRUMENT
-#include "TimeCounter.h"
-#endif
-
-namespace WebCore {
-
-using namespace HTMLNames;
-
-namespace {
-
-// This is a direct transcription of step 4 from:
-// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#fragment-case
-HTMLTokenizer::State tokenizerStateForContextElement(Element* contextElement, bool reportErrors)
-{
- if (!contextElement)
- return HTMLTokenizer::DataState;
-
- const QualifiedName& contextTag = contextElement->tagQName();
-
- if (contextTag.matches(titleTag) || contextTag.matches(textareaTag))
- return HTMLTokenizer::RCDATAState;
- if (contextTag.matches(styleTag)
- || contextTag.matches(xmpTag)
- || contextTag.matches(iframeTag)
- || (contextTag.matches(noembedTag) && HTMLTreeBuilder::pluginsEnabled(contextElement->document()->frame()))
- || (contextTag.matches(noscriptTag) && HTMLTreeBuilder::scriptEnabled(contextElement->document()->frame()))
- || contextTag.matches(noframesTag))
- return reportErrors ? HTMLTokenizer::RAWTEXTState : HTMLTokenizer::PLAINTEXTState;
- if (contextTag.matches(scriptTag))
- return reportErrors ? HTMLTokenizer::ScriptDataState : HTMLTokenizer::PLAINTEXTState;
- if (contextTag.matches(plaintextTag))
- return HTMLTokenizer::PLAINTEXTState;
- return HTMLTokenizer::DataState;
-}
-
-} // namespace
-
-HTMLDocumentParser::HTMLDocumentParser(HTMLDocument* document, bool reportErrors)
- : ScriptableDocumentParser(document)
- , m_tokenizer(HTMLTokenizer::create(usePreHTML5ParserQuirks(document)))
- , m_scriptRunner(HTMLScriptRunner::create(document, this))
- , m_treeBuilder(HTMLTreeBuilder::create(m_tokenizer.get(), document, reportErrors, usePreHTML5ParserQuirks(document)))
- , m_parserScheduler(HTMLParserScheduler::create(this))
- , m_endWasDelayed(false)
- , m_writeNestingLevel(0)
-{
-}
-
-// FIXME: Member variables should be grouped into self-initializing structs to
-// minimize code duplication between these constructors.
-HTMLDocumentParser::HTMLDocumentParser(DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission)
- : ScriptableDocumentParser(fragment->document())
- , m_tokenizer(HTMLTokenizer::create(usePreHTML5ParserQuirks(fragment->document())))
- , m_treeBuilder(HTMLTreeBuilder::create(m_tokenizer.get(), fragment, contextElement, scriptingPermission, usePreHTML5ParserQuirks(fragment->document())))
- , m_endWasDelayed(false)
- , m_writeNestingLevel(0)
-{
- bool reportErrors = false; // For now document fragment parsing never reports errors.
- m_tokenizer->setState(tokenizerStateForContextElement(contextElement, reportErrors));
-}
-
-HTMLDocumentParser::~HTMLDocumentParser()
-{
- ASSERT(!m_parserScheduler);
- ASSERT(!m_writeNestingLevel);
- ASSERT(!m_preloadScanner);
-}
-
-void HTMLDocumentParser::detach()
-{
- DocumentParser::detach();
- if (m_scriptRunner)
- m_scriptRunner->detach();
- m_treeBuilder->detach();
- // FIXME: It seems wrong that we would have a preload scanner here.
- // Yet during fast/dom/HTMLScriptElement/script-load-events.html we do.
- m_preloadScanner.clear();
- m_parserScheduler.clear(); // Deleting the scheduler will clear any timers.
-}
-
-void HTMLDocumentParser::stopParsing()
-{
- DocumentParser::stopParsing();
- m_parserScheduler.clear(); // Deleting the scheduler will clear any timers.
-}
-
-// This kicks off "Once the user agent stops parsing" as described by:
-// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#the-end
-void HTMLDocumentParser::prepareToStopParsing()
-{
- ASSERT(!hasInsertionPoint());
-
- // pumpTokenizer can cause this parser to be detached from the Document,
- // but we need to ensure it isn't deleted yet.
- RefPtr<HTMLDocumentParser> protect(this);
-
- // NOTE: This pump should only ever emit buffered character tokens,
- // so ForceSynchronous vs. AllowYield should be meaningless.
- pumpTokenizerIfPossible(ForceSynchronous);
-
- if (isStopped())
- return;
-
- DocumentParser::prepareToStopParsing();
-
- // We will not have a scriptRunner when parsing a DocumentFragment.
- if (m_scriptRunner)
- document()->setReadyState(Document::Interactive);
-
- attemptToRunDeferredScriptsAndEnd();
-}
-
-bool HTMLDocumentParser::processingData() const
-{
- return isScheduledForResume() || inWrite();
-}
-
-void HTMLDocumentParser::pumpTokenizerIfPossible(SynchronousMode mode)
-{
- if (isStopped() || m_treeBuilder->isPaused())
- return;
-
- // Once a resume is scheduled, HTMLParserScheduler controls when we next pump.
- if (isScheduledForResume()) {
- ASSERT(mode == AllowYield);
- return;
- }
-
- pumpTokenizer(mode);
-}
-
-bool HTMLDocumentParser::isScheduledForResume() const
-{
- return m_parserScheduler && m_parserScheduler->isScheduledForResume();
-}
-
-// Used by HTMLParserScheduler
-void HTMLDocumentParser::resumeParsingAfterYield()
-{
- // pumpTokenizer can cause this parser to be detached from the Document,
- // but we need to ensure it isn't deleted yet.
- RefPtr<HTMLDocumentParser> protect(this);
-
- // We should never be here unless we can pump immediately. Call pumpTokenizer()
- // directly so that ASSERTS will fire if we're wrong.
- pumpTokenizer(AllowYield);
- endIfDelayed();
-}
-
-bool HTMLDocumentParser::runScriptsForPausedTreeBuilder()
-{
- ASSERT(m_treeBuilder->isPaused());
-
- TextPosition1 scriptStartPosition = TextPosition1::belowRangePosition();
- RefPtr<Element> scriptElement = m_treeBuilder->takeScriptToProcess(scriptStartPosition);
- // We will not have a scriptRunner when parsing a DocumentFragment.
- if (!m_scriptRunner)
- return true;
- return m_scriptRunner->execute(scriptElement.release(), scriptStartPosition);
-}
-
-void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode)
-{
- ASSERT(!isStopped());
- ASSERT(!m_treeBuilder->isPaused());
- ASSERT(!isScheduledForResume());
- // ASSERT that this object is both attached to the Document and protected.
- ASSERT(refCount() >= 2);
-
- // We tell the InspectorInstrumentation about every pump, even if we
- // end up pumping nothing. It can filter out empty pumps itself.
- // FIXME: m_input.current().length() is only accurate if we
- // end up parsing the whole buffer in this pump. We should pass how
- // much we parsed as part of didWriteHTML instead of willWriteHTML.
- InspectorInstrumentationCookie cookie = InspectorInstrumentation::willWriteHTML(document(), m_input.current().length(), m_tokenizer->lineNumber());
-
- HTMLParserScheduler::PumpSession session;
- // FIXME: This loop body has is now too long and needs cleanup.
- while (mode == ForceSynchronous || m_parserScheduler->shouldContinueParsing(session)) {
- // FIXME: It's wrong for the HTMLDocumentParser to reach back to the
- // Frame, but this approach is how the old parser handled
- // stopping when the page assigns window.location. What really
- // should happen is that assigning window.location causes the
- // parser to stop parsing cleanly. The problem is we're not
- // perpared to do that at every point where we run JavaScript.
- if (!m_treeBuilder->isParsingFragment()
- && document()->frame() && document()->frame()->navigationScheduler()->locationChangePending())
- break;
- if (!m_tokenizer->nextToken(m_input.current(), m_token))
- break;
-
- m_treeBuilder->constructTreeFromToken(m_token);
- m_token.clear();
-
- // JavaScript may have stopped or detached the parser.
- if (isStopped())
- return;
-
- // The parser will pause itself when waiting on a script to load or run.
- if (!m_treeBuilder->isPaused())
- continue;
-
- // If we're paused waiting for a script, we try to execute scripts before continuing.
- bool shouldContinueParsing = runScriptsForPausedTreeBuilder();
- m_treeBuilder->setPaused(!shouldContinueParsing);
-
- // JavaScript may have stopped or detached the parser.
- if (isStopped())
- return;
-
- if (!shouldContinueParsing)
- break;
- }
-
- // Ensure we haven't been totally deref'ed after pumping. Any caller of this
- // function should be holding a RefPtr to this to ensure we weren't deleted.
- ASSERT(refCount() >= 1);
-
- if (isWaitingForScripts()) {
- ASSERT(m_tokenizer->state() == HTMLTokenizer::DataState);
- if (!m_preloadScanner) {
- m_preloadScanner.set(new HTMLPreloadScanner(document()));
- m_preloadScanner->appendToEnd(m_input.current());
- }
- m_preloadScanner->scan();
- }
-
- InspectorInstrumentation::didWriteHTML(cookie, m_tokenizer->lineNumber());
-}
-
-bool HTMLDocumentParser::hasInsertionPoint()
-{
- return m_input.hasInsertionPoint();
-}
-
-void HTMLDocumentParser::insert(const SegmentedString& source)
-{
- if (isStopped())
- return;
-
-#ifdef ANDROID_INSTRUMENT
- android::TimeCounter::start(android::TimeCounter::ParsingTimeCounter);
-#endif
-
- // pumpTokenizer can cause this parser to be detached from the Document,
- // but we need to ensure it isn't deleted yet.
- RefPtr<HTMLDocumentParser> protect(this);
-
- {
- NestingLevelIncrementer nestingLevelIncrementer(m_writeNestingLevel);
-
- SegmentedString excludedLineNumberSource(source);
- excludedLineNumberSource.setExcludeLineNumbers();
- m_input.insertAtCurrentInsertionPoint(excludedLineNumberSource);
- pumpTokenizerIfPossible(ForceSynchronous);
- }
-
- endIfDelayed();
-}
-
-void HTMLDocumentParser::append(const SegmentedString& source)
-{
- if (isStopped())
- return;
-
- // pumpTokenizer can cause this parser to be detached from the Document,
- // but we need to ensure it isn't deleted yet.
- RefPtr<HTMLDocumentParser> protect(this);
-
- {
- NestingLevelIncrementer nestingLevelIncrementer(m_writeNestingLevel);
-
- m_input.appendToEnd(source);
- if (m_preloadScanner)
- m_preloadScanner->appendToEnd(source);
-
- if (m_writeNestingLevel > 1) {
- // We've gotten data off the network in a nested write.
- // We don't want to consume any more of the input stream now. Do
- // not worry. We'll consume this data in a less-nested write().
-#ifdef ANDROID_INSTRUMENT
- android::TimeCounter::record(android::TimeCounter::ParsingTimeCounter, __FUNCTION__);
-#endif
- return;
- }
-
- pumpTokenizerIfPossible(AllowYield);
- }
-
- endIfDelayed();
-#ifdef ANDROID_INSTRUMENT
- android::TimeCounter::record(android::TimeCounter::ParsingTimeCounter, __FUNCTION__);
-#endif
-}
-
-void HTMLDocumentParser::end()
-{
- ASSERT(!isDetached());
- ASSERT(!isScheduledForResume());
-
- // Informs the the rest of WebCore that parsing is really finished (and deletes this).
- m_treeBuilder->finished();
-}
-
-void HTMLDocumentParser::attemptToRunDeferredScriptsAndEnd()
-{
- ASSERT(isStopping());
- ASSERT(!hasInsertionPoint());
- if (m_scriptRunner && !m_scriptRunner->executeScriptsWaitingForParsing())
- return;
- end();
-}
-
-void HTMLDocumentParser::attemptToEnd()
-{
- // finish() indicates we will not receive any more data. If we are waiting on
- // an external script to load, we can't finish parsing quite yet.
-
- if (shouldDelayEnd()) {
- m_endWasDelayed = true;
- return;
- }
- prepareToStopParsing();
-}
-
-void HTMLDocumentParser::endIfDelayed()
-{
- // If we've already been detached, don't bother ending.
- if (isDetached())
- return;
-
- if (!m_endWasDelayed || shouldDelayEnd())
- return;
-
- m_endWasDelayed = false;
- prepareToStopParsing();
-}
-
-void HTMLDocumentParser::finish()
-{
- // FIXME: We should ASSERT(!m_parserStopped) here, since it does not
- // makes sense to call any methods on DocumentParser once it's been stopped.
- // However, FrameLoader::stop calls Document::finishParsing unconditionally
- // which in turn calls m_parser->finish().
-
- // We're not going to get any more data off the network, so we tell the
- // input stream we've reached the end of file. finish() can be called more
- // than once, if the first time does not call end().
- if (!m_input.haveSeenEndOfFile())
- m_input.markEndOfFile();
- attemptToEnd();
-}
-
-bool HTMLDocumentParser::finishWasCalled()
-{
- return m_input.haveSeenEndOfFile();
-}
-
-// This function is virtual and just for the DocumentParser interface.
-bool HTMLDocumentParser::isExecutingScript() const
-{
- return inScriptExecution();
-}
-
-// This function is non-virtual and used throughout the implementation.
-bool HTMLDocumentParser::inScriptExecution() const
-{
- if (!m_scriptRunner)
- return false;
- return m_scriptRunner->isExecutingScript();
-}
-
-int HTMLDocumentParser::lineNumber() const
-{
- return m_tokenizer->lineNumber();
-}
-
-TextPosition0 HTMLDocumentParser::textPosition() const
-{
- int lineZeroBased = m_tokenizer->lineNumber();
- int columnOneBased = m_tokenizer->columnNumber();
-
- return TextPosition0(WTF::ZeroBasedNumber::fromZeroBasedInt(lineZeroBased),
- WTF::OneBasedNumber::fromOneBasedInt(columnOneBased).convertToZeroBased());
-}
-
-bool HTMLDocumentParser::isWaitingForScripts() const
-{
- return m_treeBuilder->isPaused();
-}
-
-void HTMLDocumentParser::resumeParsingAfterScriptExecution()
-{
- ASSERT(!inScriptExecution());
- ASSERT(!m_treeBuilder->isPaused());
-
- m_preloadScanner.clear();
- pumpTokenizerIfPossible(AllowYield);
- endIfDelayed();
-}
-
-void HTMLDocumentParser::watchForLoad(CachedResource* cachedScript)
-{
- ASSERT(!cachedScript->isLoaded());
- // addClient would call notifyFinished if the load were complete.
- // Callers do not expect to be re-entered from this call, so they should
- // not an already-loaded CachedResource.
- cachedScript->addClient(this);
-}
-
-void HTMLDocumentParser::stopWatchingForLoad(CachedResource* cachedScript)
-{
- cachedScript->removeClient(this);
-}
-
-bool HTMLDocumentParser::shouldLoadExternalScriptFromSrc(const AtomicString& srcValue)
-{
- if (!xssAuditor())
- return true;
- return xssAuditor()->canLoadExternalScriptFromSrc(srcValue);
-}
-
-void HTMLDocumentParser::notifyFinished(CachedResource* cachedResource)
-{
- // pumpTokenizer can cause this parser to be detached from the Document,
- // but we need to ensure it isn't deleted yet.
- RefPtr<HTMLDocumentParser> protect(this);
-
- ASSERT(m_scriptRunner);
- ASSERT(!inScriptExecution());
- if (isStopping()) {
- attemptToRunDeferredScriptsAndEnd();
- return;
- }
-
- ASSERT(m_treeBuilder->isPaused());
- // Note: We only ever wait on one script at a time, so we always know this
- // is the one we were waiting on and can un-pause the tree builder.
- m_treeBuilder->setPaused(false);
- bool shouldContinueParsing = m_scriptRunner->executeScriptsWaitingForLoad(cachedResource);
- m_treeBuilder->setPaused(!shouldContinueParsing);
- if (shouldContinueParsing)
- resumeParsingAfterScriptExecution();
-}
-
-void HTMLDocumentParser::executeScriptsWaitingForStylesheets()
-{
- // Document only calls this when the Document owns the DocumentParser
- // so this will not be called in the DocumentFragment case.
- ASSERT(m_scriptRunner);
- // Ignore calls unless we have a script blocking the parser waiting on a
- // stylesheet load. Otherwise we are currently parsing and this
- // is a re-entrant call from encountering a </ style> tag.
- if (!m_scriptRunner->hasScriptsWaitingForStylesheets())
- return;
-
- // pumpTokenizer can cause this parser to be detached from the Document,
- // but we need to ensure it isn't deleted yet.
- RefPtr<HTMLDocumentParser> protect(this);
-
- ASSERT(!m_scriptRunner->isExecutingScript());
- ASSERT(m_treeBuilder->isPaused());
- // Note: We only ever wait on one script at a time, so we always know this
- // is the one we were waiting on and can un-pause the tree builder.
- m_treeBuilder->setPaused(false);
- bool shouldContinueParsing = m_scriptRunner->executeScriptsWaitingForStylesheets();
- m_treeBuilder->setPaused(!shouldContinueParsing);
- if (shouldContinueParsing)
- resumeParsingAfterScriptExecution();
-}
-
-ScriptController* HTMLDocumentParser::script() const
-{
- return document()->frame() ? document()->frame()->script() : 0;
-}
-
-void HTMLDocumentParser::parseDocumentFragment(const String& source, DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission)
-{
- RefPtr<HTMLDocumentParser> parser = HTMLDocumentParser::create(fragment, contextElement, scriptingPermission);
- parser->insert(source); // Use insert() so that the parser will not yield.
- parser->finish();
- ASSERT(!parser->processingData()); // Make sure we're done. <rdar://problem/3963151>
- parser->detach(); // Allows ~DocumentParser to assert it was detached before destruction.
-}
-
-bool HTMLDocumentParser::usePreHTML5ParserQuirks(Document* document)
-{
- ASSERT(document);
- return document->settings() && document->settings()->usePreHTML5ParserQuirks();
-}
-
-}