blob: 63c33d6b7325e00245540d84623e32be0e406e0e [file] [log] [blame]
/*
* Copyright (C) 2011 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 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 "NewXMLDocumentParser.h"
#include "DocumentFragment.h"
#include "Element.h"
#include "ScriptElement.h"
#include "ScriptSourceCode.h"
#include "SegmentedString.h"
#include "XMLTreeBuilder.h"
#include "XMLTreeViewer.h"
namespace WebCore {
NewXMLDocumentParser::NewXMLDocumentParser(Document* document)
: ScriptableDocumentParser(document)
, m_tokenizer(XMLTokenizer::create())
, m_parserPaused(false)
, m_finishWasCalled(false)
, m_pendingScript(0)
, m_scriptElement(0)
, m_treeBuilder(XMLTreeBuilder::create(this, document))
{
}
NewXMLDocumentParser::NewXMLDocumentParser(DocumentFragment* fragment, Element* parent, FragmentScriptingPermission)
: ScriptableDocumentParser(fragment->document())
, m_tokenizer(XMLTokenizer::create())
, m_parserPaused(false)
, m_finishWasCalled(false)
, m_pendingScript(0)
, m_scriptElement(0)
, m_treeBuilder(XMLTreeBuilder::create(this, fragment, parent))
{
}
bool NewXMLDocumentParser::parseDocumentFragment(const String& chunk, DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission)
{
if (!chunk.length())
return true;
RefPtr<NewXMLDocumentParser> parser = NewXMLDocumentParser::create(fragment, contextElement, scriptingPermission);
parser->append(SegmentedString(chunk));
// Do not call finish(). Current finish() implementation touches the main Document/loader
// and can cause crashes in the fragment case.
parser->detach(); // Allows ~DocumentParser to assert it was detached before destruction.
// FIXME: return false if not well-formed
return true;
}
NewXMLDocumentParser::~NewXMLDocumentParser()
{
}
void NewXMLDocumentParser::resumeParsing()
{
m_parserPaused = false;
append(m_input);
}
void NewXMLDocumentParser::processScript(ScriptElement* scriptElement)
{
if (scriptElement->prepareScript(TextPosition(), ScriptElement::AllowLegacyTypeInTypeAttribute)) {
if (scriptElement->readyToBeParserExecuted())
scriptElement->executeScript(ScriptSourceCode(scriptElement->scriptContent(), document()->url(), TextPosition()));
else if (scriptElement->willBeParserExecuted()) {
m_pendingScript = scriptElement->cachedScript();
m_scriptElement = scriptElement->element();
m_pendingScript->addClient(this);
// m_pendingScript will be 0 if script was already loaded and addClient() executed it.
if (m_pendingScript)
pauseParsing();
} else
m_scriptElement = 0;
}
}
TextPosition NewXMLDocumentParser::textPosition() const
{
return TextPosition::minimumPosition();
}
OrdinalNumber NewXMLDocumentParser::lineNumber() const
{
return OrdinalNumber::first();
}
void NewXMLDocumentParser::insert(const SegmentedString&)
{
ASSERT_NOT_REACHED();
}
void NewXMLDocumentParser::append(const SegmentedString& string)
{
m_input = string;
while (!m_input.isEmpty() && isParsing() && !m_parserPaused) {
if (!m_tokenizer->nextToken(m_input, m_token))
continue;
#ifndef NDEBUG
m_token.print();
#endif
AtomicXMLToken token(m_token);
m_treeBuilder->processToken(token);
if (m_token.type() == XMLTokenTypes::EndOfFile)
break;
m_token.clear();
ASSERT(m_token.isUninitialized());
}
}
void NewXMLDocumentParser::finish()
{
ASSERT(!m_finishWasCalled);
if (m_parserPaused)
return;
m_treeBuilder->finish();
m_finishWasCalled = true;
if (isParsing()) {
#if ENABLE(XSLT)
XMLTreeViewer xmlTreeViewer(document());
if (xmlTreeViewer.hasNoStyleInformation())
xmlTreeViewer.transformDocumentToTreeView();
#endif // ENABLE(XSLT)
prepareToStopParsing();
}
document()->setReadyState(Document::Interactive);
document()->finishedParsing();
}
bool NewXMLDocumentParser::hasInsertionPoint()
{
return false;
}
bool NewXMLDocumentParser::finishWasCalled()
{
return m_finishWasCalled;
}
bool NewXMLDocumentParser::isWaitingForScripts() const
{
return false;
}
bool NewXMLDocumentParser::isExecutingScript() const
{
return false;
}
void NewXMLDocumentParser::executeScriptsWaitingForStylesheets()
{
}
void NewXMLDocumentParser::notifyFinished(CachedResource* unusedResource)
{
ASSERT_UNUSED(unusedResource, unusedResource == m_pendingScript);
ASSERT(m_pendingScript->accessCount() > 0);
ScriptSourceCode sourceCode(m_pendingScript.get());
bool errorOccurred = m_pendingScript->errorOccurred();
bool wasCanceled = m_pendingScript->wasCanceled();
m_pendingScript->removeClient(this);
m_pendingScript = 0;
RefPtr<Element> element = m_scriptElement;
ScriptElement* scriptElement = toScriptElement(m_scriptElement.get());
m_scriptElement = 0;
ASSERT(scriptElement);
// JavaScript can detach this parser, make sure it's kept alive even if detached.
RefPtr<NewXMLDocumentParser> protect(this);
if (errorOccurred)
scriptElement->dispatchErrorEvent();
else if (!wasCanceled) {
scriptElement->executeScript(sourceCode);
scriptElement->dispatchLoadEvent();
}
if (!isDetached() && m_parserPaused)
resumeParsing();
}
}