Home | History | Annotate | Download | only in parser
      1 /*
      2  * Copyright (C) 2010 Google, Inc. All Rights Reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "core/html/parser/HTMLDocumentParser.h"
     28 
     29 #include "core/HTMLNames.h"
     30 #include "core/css/MediaValuesCached.h"
     31 #include "core/dom/DocumentFragment.h"
     32 #include "core/dom/Element.h"
     33 #include "core/frame/LocalFrame.h"
     34 #include "core/html/HTMLDocument.h"
     35 #include "core/html/parser/AtomicHTMLToken.h"
     36 #include "core/html/parser/BackgroundHTMLParser.h"
     37 #include "core/html/parser/HTMLParserScheduler.h"
     38 #include "core/html/parser/HTMLParserThread.h"
     39 #include "core/html/parser/HTMLScriptRunner.h"
     40 #include "core/html/parser/HTMLTreeBuilder.h"
     41 #include "core/inspector/InspectorInstrumentation.h"
     42 #include "core/inspector/InspectorTraceEvents.h"
     43 #include "core/loader/DocumentLoader.h"
     44 #include "platform/SharedBuffer.h"
     45 #include "platform/TraceEvent.h"
     46 #include "public/platform/WebThreadedDataReceiver.h"
     47 #include "wtf/Functional.h"
     48 
     49 namespace WebCore {
     50 
     51 using namespace HTMLNames;
     52 
     53 // This is a direct transcription of step 4 from:
     54 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#fragment-case
     55 static HTMLTokenizer::State tokenizerStateForContextElement(Element* contextElement, bool reportErrors, const HTMLParserOptions& options)
     56 {
     57     if (!contextElement)
     58         return HTMLTokenizer::DataState;
     59 
     60     const QualifiedName& contextTag = contextElement->tagQName();
     61 
     62     if (contextTag.matches(titleTag) || contextTag.matches(textareaTag))
     63         return HTMLTokenizer::RCDATAState;
     64     if (contextTag.matches(styleTag)
     65         || contextTag.matches(xmpTag)
     66         || contextTag.matches(iframeTag)
     67         || (contextTag.matches(noembedTag) && options.pluginsEnabled)
     68         || (contextTag.matches(noscriptTag) && options.scriptEnabled)
     69         || contextTag.matches(noframesTag))
     70         return reportErrors ? HTMLTokenizer::RAWTEXTState : HTMLTokenizer::PLAINTEXTState;
     71     if (contextTag.matches(scriptTag))
     72         return reportErrors ? HTMLTokenizer::ScriptDataState : HTMLTokenizer::PLAINTEXTState;
     73     if (contextTag.matches(plaintextTag))
     74         return HTMLTokenizer::PLAINTEXTState;
     75     return HTMLTokenizer::DataState;
     76 }
     77 
     78 class ParserDataReceiver : public blink::WebThreadedDataReceiver {
     79 public:
     80     explicit ParserDataReceiver(WeakPtr<BackgroundHTMLParser> backgroundParser)
     81         : m_backgroundParser(backgroundParser)
     82     {
     83     }
     84 
     85     // WebThreadedDataReceiver
     86     virtual void acceptData(const char* data, int dataLength) OVERRIDE FINAL
     87     {
     88         ASSERT(backgroundThread() && backgroundThread()->isCurrentThread());
     89         if (m_backgroundParser.get())
     90             m_backgroundParser.get()->appendRawBytesFromParserThread(data, dataLength);
     91     }
     92 
     93     virtual blink::WebThread* backgroundThread() OVERRIDE FINAL
     94     {
     95         if (HTMLParserThread::shared())
     96             return &HTMLParserThread::shared()->platformThread();
     97 
     98         return 0;
     99     }
    100 
    101 private:
    102     WeakPtr<BackgroundHTMLParser> m_backgroundParser;
    103 };
    104 
    105 HTMLDocumentParser::HTMLDocumentParser(HTMLDocument& document, bool reportErrors)
    106     : ScriptableDocumentParser(document)
    107     , m_options(&document)
    108     , m_token(m_options.useThreading ? nullptr : adoptPtr(new HTMLToken))
    109     , m_tokenizer(m_options.useThreading ? nullptr : HTMLTokenizer::create(m_options))
    110     , m_scriptRunner(HTMLScriptRunner::create(&document, this))
    111     , m_treeBuilder(HTMLTreeBuilder::create(this, &document, parserContentPolicy(), reportErrors, m_options))
    112     , m_parserScheduler(HTMLParserScheduler::create(this))
    113     , m_xssAuditorDelegate(&document)
    114     , m_weakFactory(this)
    115     , m_preloader(adoptPtr(new HTMLResourcePreloader(&document)))
    116     , m_isPinnedToMainThread(false)
    117     , m_endWasDelayed(false)
    118     , m_haveBackgroundParser(false)
    119     , m_pumpSessionNestingLevel(0)
    120 {
    121     ASSERT(shouldUseThreading() || (m_token && m_tokenizer));
    122 }
    123 
    124 // FIXME: Member variables should be grouped into self-initializing structs to
    125 // minimize code duplication between these constructors.
    126 HTMLDocumentParser::HTMLDocumentParser(DocumentFragment* fragment, Element* contextElement, ParserContentPolicy parserContentPolicy)
    127     : ScriptableDocumentParser(fragment->document(), parserContentPolicy)
    128     , m_options(&fragment->document())
    129     , m_token(adoptPtr(new HTMLToken))
    130     , m_tokenizer(HTMLTokenizer::create(m_options))
    131     , m_treeBuilder(HTMLTreeBuilder::create(this, fragment, contextElement, this->parserContentPolicy(), m_options))
    132     , m_xssAuditorDelegate(&fragment->document())
    133     , m_weakFactory(this)
    134     , m_isPinnedToMainThread(true)
    135     , m_endWasDelayed(false)
    136     , m_haveBackgroundParser(false)
    137     , m_pumpSessionNestingLevel(0)
    138 {
    139     ASSERT(!shouldUseThreading());
    140     bool reportErrors = false; // For now document fragment parsing never reports errors.
    141     m_tokenizer->setState(tokenizerStateForContextElement(contextElement, reportErrors, m_options));
    142     m_xssAuditor.initForFragment();
    143 }
    144 
    145 HTMLDocumentParser::~HTMLDocumentParser()
    146 {
    147 #if ENABLE(OILPAN)
    148     if (m_haveBackgroundParser)
    149         stopBackgroundParser();
    150     // In Oilpan, HTMLDocumentParser can die together with Document, and
    151     // detach() is not called in this case.
    152 #else
    153     ASSERT(!m_parserScheduler);
    154     ASSERT(!m_pumpSessionNestingLevel);
    155     ASSERT(!m_preloadScanner);
    156     ASSERT(!m_insertionPreloadScanner);
    157     ASSERT(!m_haveBackgroundParser);
    158     // FIXME: We should be able to ASSERT(m_speculations.isEmpty()),
    159     // but there are cases where that's not true currently. For example,
    160     // we we're told to stop parsing before we've consumed all the input.
    161 #endif
    162 }
    163 
    164 void HTMLDocumentParser::trace(Visitor* visitor)
    165 {
    166     visitor->trace(m_treeBuilder);
    167     visitor->trace(m_scriptRunner);
    168     ScriptableDocumentParser::trace(visitor);
    169     HTMLScriptRunnerHost::trace(visitor);
    170 }
    171 
    172 void HTMLDocumentParser::pinToMainThread()
    173 {
    174     ASSERT(!m_haveBackgroundParser);
    175     ASSERT(!m_isPinnedToMainThread);
    176     m_isPinnedToMainThread = true;
    177     if (!m_tokenizer) {
    178         ASSERT(!m_token);
    179         m_token = adoptPtr(new HTMLToken);
    180         m_tokenizer = HTMLTokenizer::create(m_options);
    181     }
    182 }
    183 
    184 void HTMLDocumentParser::detach()
    185 {
    186     if (m_haveBackgroundParser)
    187         stopBackgroundParser();
    188     DocumentParser::detach();
    189     if (m_scriptRunner)
    190         m_scriptRunner->detach();
    191     m_treeBuilder->detach();
    192     // FIXME: It seems wrong that we would have a preload scanner here.
    193     // Yet during fast/dom/HTMLScriptElement/script-load-events.html we do.
    194     m_preloadScanner.clear();
    195     m_insertionPreloadScanner.clear();
    196     m_parserScheduler.clear(); // Deleting the scheduler will clear any timers.
    197 }
    198 
    199 void HTMLDocumentParser::stopParsing()
    200 {
    201     DocumentParser::stopParsing();
    202     m_parserScheduler.clear(); // Deleting the scheduler will clear any timers.
    203     if (m_haveBackgroundParser)
    204         stopBackgroundParser();
    205 }
    206 
    207 // This kicks off "Once the user agent stops parsing" as described by:
    208 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#the-end
    209 void HTMLDocumentParser::prepareToStopParsing()
    210 {
    211     // FIXME: It may not be correct to disable this for the background parser.
    212     // That means hasInsertionPoint() may not be correct in some cases.
    213     ASSERT(!hasInsertionPoint() || m_haveBackgroundParser);
    214 
    215     // pumpTokenizer can cause this parser to be detached from the Document,
    216     // but we need to ensure it isn't deleted yet.
    217     RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
    218 
    219     // NOTE: This pump should only ever emit buffered character tokens,
    220     // so ForceSynchronous vs. AllowYield should be meaningless.
    221     if (m_tokenizer) {
    222         ASSERT(!m_haveBackgroundParser);
    223         pumpTokenizerIfPossible(ForceSynchronous);
    224     }
    225 
    226     if (isStopped())
    227         return;
    228 
    229     DocumentParser::prepareToStopParsing();
    230 
    231     // We will not have a scriptRunner when parsing a DocumentFragment.
    232     if (m_scriptRunner)
    233         document()->setReadyState(Document::Interactive);
    234 
    235     // Setting the ready state above can fire mutation event and detach us
    236     // from underneath. In that case, just bail out.
    237     if (isDetached())
    238         return;
    239 
    240     attemptToRunDeferredScriptsAndEnd();
    241 }
    242 
    243 bool HTMLDocumentParser::isParsingFragment() const
    244 {
    245     return m_treeBuilder->isParsingFragment();
    246 }
    247 
    248 bool HTMLDocumentParser::processingData() const
    249 {
    250     return isScheduledForResume() || inPumpSession() || m_haveBackgroundParser;
    251 }
    252 
    253 void HTMLDocumentParser::pumpTokenizerIfPossible(SynchronousMode mode)
    254 {
    255     if (isStopped())
    256         return;
    257     if (isWaitingForScripts())
    258         return;
    259 
    260     // Once a resume is scheduled, HTMLParserScheduler controls when we next pump.
    261     if (isScheduledForResume()) {
    262         ASSERT(mode == AllowYield);
    263         return;
    264     }
    265 
    266     pumpTokenizer(mode);
    267 }
    268 
    269 bool HTMLDocumentParser::isScheduledForResume() const
    270 {
    271     return m_parserScheduler && m_parserScheduler->isScheduledForResume();
    272 }
    273 
    274 // Used by HTMLParserScheduler
    275 void HTMLDocumentParser::resumeParsingAfterYield()
    276 {
    277     ASSERT(!m_isPinnedToMainThread);
    278     // pumpTokenizer can cause this parser to be detached from the Document,
    279     // but we need to ensure it isn't deleted yet.
    280     RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
    281 
    282     if (m_haveBackgroundParser) {
    283         pumpPendingSpeculations();
    284         return;
    285     }
    286 
    287     // We should never be here unless we can pump immediately.  Call pumpTokenizer()
    288     // directly so that ASSERTS will fire if we're wrong.
    289     pumpTokenizer(AllowYield);
    290     endIfDelayed();
    291 }
    292 
    293 void HTMLDocumentParser::runScriptsForPausedTreeBuilder()
    294 {
    295     ASSERT(scriptingContentIsAllowed(parserContentPolicy()));
    296 
    297     TextPosition scriptStartPosition = TextPosition::belowRangePosition();
    298     RefPtrWillBeRawPtr<Element> scriptElement = m_treeBuilder->takeScriptToProcess(scriptStartPosition);
    299     // We will not have a scriptRunner when parsing a DocumentFragment.
    300     if (m_scriptRunner)
    301         m_scriptRunner->execute(scriptElement.release(), scriptStartPosition);
    302 }
    303 
    304 bool HTMLDocumentParser::canTakeNextToken(SynchronousMode mode, PumpSession& session)
    305 {
    306     if (isStopped())
    307         return false;
    308 
    309     ASSERT(!m_haveBackgroundParser || mode == ForceSynchronous);
    310 
    311     if (isWaitingForScripts()) {
    312         if (mode == AllowYield)
    313             session.didSeeScript = true;
    314 
    315         // If we don't run the script, we cannot allow the next token to be taken.
    316         if (session.needsYield)
    317             return false;
    318 
    319         // If we're paused waiting for a script, we try to execute scripts before continuing.
    320         runScriptsForPausedTreeBuilder();
    321         if (isStopped())
    322             return false;
    323         if (isWaitingForScripts())
    324             return false;
    325     }
    326 
    327     // FIXME: It's wrong for the HTMLDocumentParser to reach back to the
    328     //        LocalFrame, but this approach is how the old parser handled
    329     //        stopping when the page assigns window.location.  What really
    330     //        should happen is that assigning window.location causes the
    331     //        parser to stop parsing cleanly.  The problem is we're not
    332     //        perpared to do that at every point where we run JavaScript.
    333     if (!isParsingFragment()
    334         && document()->frame() && document()->frame()->navigationScheduler().locationChangePending())
    335         return false;
    336 
    337     if (mode == AllowYield)
    338         m_parserScheduler->checkForYieldBeforeToken(session);
    339 
    340     return true;
    341 }
    342 
    343 void HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser(PassOwnPtr<ParsedChunk> chunk)
    344 {
    345     TRACE_EVENT0("webkit", "HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser");
    346 
    347     // alert(), runModalDialog, and the JavaScript Debugger all run nested event loops
    348     // which can cause this method to be re-entered. We detect re-entry using
    349     // hasActiveParser(), save the chunk as a speculation, and return.
    350     if (isWaitingForScripts() || !m_speculations.isEmpty() || document()->activeParserCount() > 0) {
    351         m_preloader->takeAndPreload(chunk->preloads);
    352         m_speculations.append(chunk);
    353         return;
    354     }
    355 
    356     // processParsedChunkFromBackgroundParser can cause this parser to be detached from the Document,
    357     // but we need to ensure it isn't deleted yet.
    358     RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
    359 
    360     ASSERT(m_speculations.isEmpty());
    361     chunk->preloads.clear(); // We don't need to preload because we're going to parse immediately.
    362     m_speculations.append(chunk);
    363     pumpPendingSpeculations();
    364 }
    365 
    366 void HTMLDocumentParser::didReceiveEncodingDataFromBackgroundParser(const DocumentEncodingData& data)
    367 {
    368     document()->setEncodingData(data);
    369 }
    370 
    371 void HTMLDocumentParser::validateSpeculations(PassOwnPtr<ParsedChunk> chunk)
    372 {
    373     ASSERT(chunk);
    374     if (isWaitingForScripts()) {
    375         // We're waiting on a network script, just save the chunk, we'll get
    376         // a second validateSpeculations call after the script completes.
    377         // This call should have been made immediately after runScriptsForPausedTreeBuilder
    378         // which may have started a network load and left us waiting.
    379         ASSERT(!m_lastChunkBeforeScript);
    380         m_lastChunkBeforeScript = chunk;
    381         return;
    382     }
    383 
    384     ASSERT(!m_lastChunkBeforeScript);
    385     OwnPtr<HTMLTokenizer> tokenizer = m_tokenizer.release();
    386     OwnPtr<HTMLToken> token = m_token.release();
    387 
    388     if (!tokenizer) {
    389         // There must not have been any changes to the HTMLTokenizer state on
    390         // the main thread, which means the speculation buffer is correct.
    391         return;
    392     }
    393 
    394     // Currently we're only smart enough to reuse the speculation buffer if the tokenizer
    395     // both starts and ends in the DataState. That state is simplest because the HTMLToken
    396     // is always in the Uninitialized state. We should consider whether we can reuse the
    397     // speculation buffer in other states, but we'd likely need to do something more
    398     // sophisticated with the HTMLToken.
    399     if (chunk->tokenizerState == HTMLTokenizer::DataState
    400         && tokenizer->state() == HTMLTokenizer::DataState
    401         && m_input.current().isEmpty()
    402         && chunk->treeBuilderState == HTMLTreeBuilderSimulator::stateFor(m_treeBuilder.get())) {
    403         ASSERT(token->isUninitialized());
    404         return;
    405     }
    406 
    407     discardSpeculationsAndResumeFrom(chunk, token.release(), tokenizer.release());
    408 }
    409 
    410 void HTMLDocumentParser::discardSpeculationsAndResumeFrom(PassOwnPtr<ParsedChunk> lastChunkBeforeScript, PassOwnPtr<HTMLToken> token, PassOwnPtr<HTMLTokenizer> tokenizer)
    411 {
    412     m_weakFactory.revokeAll();
    413     m_speculations.clear();
    414 
    415     OwnPtr<BackgroundHTMLParser::Checkpoint> checkpoint = adoptPtr(new BackgroundHTMLParser::Checkpoint);
    416     checkpoint->parser = m_weakFactory.createWeakPtr();
    417     checkpoint->token = token;
    418     checkpoint->tokenizer = tokenizer;
    419     checkpoint->treeBuilderState = HTMLTreeBuilderSimulator::stateFor(m_treeBuilder.get());
    420     checkpoint->inputCheckpoint = lastChunkBeforeScript->inputCheckpoint;
    421     checkpoint->preloadScannerCheckpoint = lastChunkBeforeScript->preloadScannerCheckpoint;
    422     checkpoint->unparsedInput = m_input.current().toString().isolatedCopy();
    423     m_input.current().clear(); // FIXME: This should be passed in instead of cleared.
    424 
    425     ASSERT(checkpoint->unparsedInput.isSafeToSendToAnotherThread());
    426     HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::resumeFrom, m_backgroundParser, checkpoint.release()));
    427 }
    428 
    429 void HTMLDocumentParser::processParsedChunkFromBackgroundParser(PassOwnPtr<ParsedChunk> popChunk)
    430 {
    431     TRACE_EVENT0("webkit", "HTMLDocumentParser::processParsedChunkFromBackgroundParser");
    432 
    433     ASSERT_WITH_SECURITY_IMPLICATION(!document()->activeParserCount());
    434     ASSERT(!isParsingFragment());
    435     ASSERT(!isWaitingForScripts());
    436     ASSERT(!isStopped());
    437 #if !ENABLE(OILPAN)
    438     // ASSERT that this object is both attached to the Document and protected.
    439     ASSERT(refCount() >= 2);
    440 #endif
    441     ASSERT(shouldUseThreading());
    442     ASSERT(!m_tokenizer);
    443     ASSERT(!m_token);
    444     ASSERT(!m_lastChunkBeforeScript);
    445 
    446     ActiveParserSession session(contextForParsingSession());
    447 
    448     OwnPtr<ParsedChunk> chunk(popChunk);
    449     OwnPtr<CompactHTMLTokenStream> tokens = chunk->tokens.release();
    450 
    451     HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::startedChunkWithCheckpoint, m_backgroundParser, chunk->inputCheckpoint));
    452 
    453     for (XSSInfoStream::const_iterator it = chunk->xssInfos.begin(); it != chunk->xssInfos.end(); ++it) {
    454         m_textPosition = (*it)->m_textPosition;
    455         m_xssAuditorDelegate.didBlockScript(**it);
    456         if (isStopped())
    457             break;
    458     }
    459 
    460     for (Vector<CompactHTMLToken>::const_iterator it = tokens->begin(); it != tokens->end(); ++it) {
    461         ASSERT(!isWaitingForScripts());
    462 
    463         if (document()->frame() && document()->frame()->navigationScheduler().locationChangePending()) {
    464 
    465             // To match main-thread parser behavior (which never checks locationChangePending on the EOF path)
    466             // we peek to see if this chunk has an EOF and process it anyway.
    467             if (tokens->last().type() == HTMLToken::EndOfFile) {
    468                 ASSERT(m_speculations.isEmpty()); // There should never be any chunks after the EOF.
    469                 prepareToStopParsing();
    470             }
    471             break;
    472         }
    473 
    474         m_textPosition = it->textPosition();
    475 
    476         constructTreeFromCompactHTMLToken(*it);
    477 
    478         if (isStopped())
    479             break;
    480 
    481         if (isWaitingForScripts()) {
    482             ASSERT(it + 1 == tokens->end()); // The </script> is assumed to be the last token of this bunch.
    483             runScriptsForPausedTreeBuilder();
    484             validateSpeculations(chunk.release());
    485             break;
    486         }
    487 
    488         if (it->type() == HTMLToken::EndOfFile) {
    489             ASSERT(it + 1 == tokens->end()); // The EOF is assumed to be the last token of this bunch.
    490             ASSERT(m_speculations.isEmpty()); // There should never be any chunks after the EOF.
    491             prepareToStopParsing();
    492             break;
    493         }
    494 
    495         ASSERT(!m_tokenizer);
    496         ASSERT(!m_token);
    497     }
    498 
    499     // Make sure any pending text nodes are emitted before returning.
    500     if (!isStopped())
    501         m_treeBuilder->flush();
    502 }
    503 
    504 void HTMLDocumentParser::pumpPendingSpeculations()
    505 {
    506     // FIXME: Share this constant with the parser scheduler.
    507     const double parserTimeLimit = 0.500;
    508 
    509 #if !ENABLE(OILPAN)
    510     // ASSERT that this object is both attached to the Document and protected.
    511     ASSERT(refCount() >= 2);
    512 #endif
    513     // If this assert fails, you need to call validateSpeculations to make sure
    514     // m_tokenizer and m_token don't have state that invalidates m_speculations.
    515     ASSERT(!m_tokenizer);
    516     ASSERT(!m_token);
    517     ASSERT(!m_lastChunkBeforeScript);
    518     ASSERT(!isWaitingForScripts());
    519     ASSERT(!isStopped());
    520 
    521     // FIXME: Pass in current input length.
    522     TRACE_EVENT_BEGIN1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ParseHTML", "beginData", InspectorParseHtmlEvent::beginData(document(), lineNumber().zeroBasedInt()));
    523     TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
    524     // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
    525     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willWriteHTML(document(), lineNumber().zeroBasedInt());
    526 
    527     double startTime = currentTime();
    528 
    529     while (!m_speculations.isEmpty()) {
    530         processParsedChunkFromBackgroundParser(m_speculations.takeFirst());
    531 
    532         // Always check isStopped first as m_document may be null.
    533         if (isStopped() || isWaitingForScripts())
    534             break;
    535 
    536         if (currentTime() - startTime > parserTimeLimit && !m_speculations.isEmpty()) {
    537             m_parserScheduler->scheduleForResume();
    538             break;
    539         }
    540     }
    541 
    542     TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ParseHTML", "endLine", lineNumber().zeroBasedInt());
    543     // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
    544     InspectorInstrumentation::didWriteHTML(cookie, lineNumber().zeroBasedInt());
    545     TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateCounters", "data", InspectorUpdateCountersEvent::data());
    546 }
    547 
    548 void HTMLDocumentParser::forcePlaintextForTextDocument()
    549 {
    550     if (shouldUseThreading()) {
    551         // This method is called before any data is appended, so we have to start
    552         // the background parser ourselves.
    553         if (!m_haveBackgroundParser)
    554             startBackgroundParser();
    555 
    556         HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::forcePlaintextForTextDocument, m_backgroundParser));
    557     } else
    558         m_tokenizer->setState(HTMLTokenizer::PLAINTEXTState);
    559 }
    560 
    561 Document* HTMLDocumentParser::contextForParsingSession()
    562 {
    563     // The parsing session should interact with the document only when parsing
    564     // non-fragments. Otherwise, we might delay the load event mistakenly.
    565     if (isParsingFragment())
    566         return 0;
    567     return document();
    568 }
    569 
    570 static PassRefPtr<MediaValues> createMediaValues(Document* document)
    571 {
    572     ASSERT(document);
    573     RefPtr<MediaValues> mediaValues = MediaValuesCached::create(*document);
    574     ASSERT(mediaValues->isSafeToSendToAnotherThread());
    575     return mediaValues;
    576 }
    577 
    578 void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode)
    579 {
    580     ASSERT(!isStopped());
    581     ASSERT(!isScheduledForResume());
    582 #if !ENABLE(OILPAN)
    583     // ASSERT that this object is both attached to the Document and protected.
    584     ASSERT(refCount() >= 2);
    585 #endif
    586     ASSERT(m_tokenizer);
    587     ASSERT(m_token);
    588     ASSERT(!m_haveBackgroundParser || mode == ForceSynchronous);
    589 
    590     PumpSession session(m_pumpSessionNestingLevel, contextForParsingSession());
    591 
    592     // We tell the InspectorInstrumentation about every pump, even if we
    593     // end up pumping nothing.  It can filter out empty pumps itself.
    594     // FIXME: m_input.current().length() is only accurate if we
    595     // end up parsing the whole buffer in this pump.  We should pass how
    596     // much we parsed as part of didWriteHTML instead of willWriteHTML.
    597     TRACE_EVENT_BEGIN1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ParseHTML", "beginData", InspectorParseHtmlEvent::beginData(document(), m_input.current().currentLine().zeroBasedInt()));
    598     TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
    599     // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
    600     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willWriteHTML(document(), m_input.current().currentLine().zeroBasedInt());
    601 
    602     m_xssAuditor.init(document(), &m_xssAuditorDelegate);
    603 
    604     while (canTakeNextToken(mode, session) && !session.needsYield) {
    605         if (!isParsingFragment())
    606             m_sourceTracker.start(m_input.current(), m_tokenizer.get(), token());
    607 
    608         if (!m_tokenizer->nextToken(m_input.current(), token()))
    609             break;
    610 
    611         if (!isParsingFragment()) {
    612             m_sourceTracker.end(m_input.current(), m_tokenizer.get(), token());
    613 
    614             // We do not XSS filter innerHTML, which means we (intentionally) fail
    615             // http/tests/security/xssAuditor/dom-write-innerHTML.html
    616             if (OwnPtr<XSSInfo> xssInfo = m_xssAuditor.filterToken(FilterTokenRequest(token(), m_sourceTracker, m_tokenizer->shouldAllowCDATA())))
    617                 m_xssAuditorDelegate.didBlockScript(*xssInfo);
    618         }
    619 
    620         constructTreeFromHTMLToken(token());
    621         ASSERT(token().isUninitialized());
    622     }
    623 
    624 #if !ENABLE(OILPAN)
    625     // Ensure we haven't been totally deref'ed after pumping. Any caller of this
    626     // function should be holding a RefPtr to this to ensure we weren't deleted.
    627     ASSERT(refCount() >= 1);
    628 #endif
    629 
    630     if (isStopped())
    631         return;
    632 
    633     // There should only be PendingText left since the tree-builder always flushes
    634     // the task queue before returning. In case that ever changes, crash.
    635     if (mode == ForceSynchronous)
    636         m_treeBuilder->flush();
    637     RELEASE_ASSERT(!isStopped());
    638 
    639     if (session.needsYield)
    640         m_parserScheduler->scheduleForResume();
    641 
    642     if (isWaitingForScripts()) {
    643         ASSERT(m_tokenizer->state() == HTMLTokenizer::DataState);
    644         if (!m_preloadScanner) {
    645             m_preloadScanner = adoptPtr(new HTMLPreloadScanner(m_options, document()->url(), createMediaValues(document())));
    646             m_preloadScanner->appendToEnd(m_input.current());
    647         }
    648         m_preloadScanner->scan(m_preloader.get(), document()->baseElementURL());
    649     }
    650 
    651     TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ParseHTML", "endLine", m_input.current().currentLine().zeroBasedInt());
    652     // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
    653     InspectorInstrumentation::didWriteHTML(cookie, m_input.current().currentLine().zeroBasedInt());
    654 }
    655 
    656 void HTMLDocumentParser::constructTreeFromHTMLToken(HTMLToken& rawToken)
    657 {
    658     AtomicHTMLToken token(rawToken);
    659 
    660     // We clear the rawToken in case constructTreeFromAtomicToken
    661     // synchronously re-enters the parser. We don't clear the token immedately
    662     // for Character tokens because the AtomicHTMLToken avoids copying the
    663     // characters by keeping a pointer to the underlying buffer in the
    664     // HTMLToken. Fortunately, Character tokens can't cause us to re-enter
    665     // the parser.
    666     //
    667     // FIXME: Stop clearing the rawToken once we start running the parser off
    668     // the main thread or once we stop allowing synchronous JavaScript
    669     // execution from parseAttribute.
    670     if (rawToken.type() != HTMLToken::Character)
    671         rawToken.clear();
    672 
    673     m_treeBuilder->constructTree(&token);
    674 
    675     if (!rawToken.isUninitialized()) {
    676         ASSERT(rawToken.type() == HTMLToken::Character);
    677         rawToken.clear();
    678     }
    679 }
    680 
    681 void HTMLDocumentParser::constructTreeFromCompactHTMLToken(const CompactHTMLToken& compactToken)
    682 {
    683     AtomicHTMLToken token(compactToken);
    684     m_treeBuilder->constructTree(&token);
    685 }
    686 
    687 bool HTMLDocumentParser::hasInsertionPoint()
    688 {
    689     // FIXME: The wasCreatedByScript() branch here might not be fully correct.
    690     //        Our model of the EOF character differs slightly from the one in
    691     //        the spec because our treatment is uniform between network-sourced
    692     //        and script-sourced input streams whereas the spec treats them
    693     //        differently.
    694     return m_input.hasInsertionPoint() || (wasCreatedByScript() && !m_input.haveSeenEndOfFile());
    695 }
    696 
    697 void HTMLDocumentParser::insert(const SegmentedString& source)
    698 {
    699     if (isStopped())
    700         return;
    701 
    702     TRACE_EVENT1("webkit", "HTMLDocumentParser::insert", "source_length", source.length());
    703 
    704     // pumpTokenizer can cause this parser to be detached from the Document,
    705     // but we need to ensure it isn't deleted yet.
    706     RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
    707 
    708     if (!m_tokenizer) {
    709         ASSERT(!inPumpSession());
    710         ASSERT(m_haveBackgroundParser || wasCreatedByScript());
    711         m_token = adoptPtr(new HTMLToken);
    712         m_tokenizer = HTMLTokenizer::create(m_options);
    713     }
    714 
    715     SegmentedString excludedLineNumberSource(source);
    716     excludedLineNumberSource.setExcludeLineNumbers();
    717     m_input.insertAtCurrentInsertionPoint(excludedLineNumberSource);
    718     pumpTokenizerIfPossible(ForceSynchronous);
    719 
    720     if (isWaitingForScripts()) {
    721         // Check the document.write() output with a separate preload scanner as
    722         // the main scanner can't deal with insertions.
    723         if (!m_insertionPreloadScanner)
    724             m_insertionPreloadScanner = adoptPtr(new HTMLPreloadScanner(m_options, document()->url(), createMediaValues(document())));
    725 
    726         m_insertionPreloadScanner->appendToEnd(source);
    727         m_insertionPreloadScanner->scan(m_preloader.get(), document()->baseElementURL());
    728     }
    729 
    730     endIfDelayed();
    731 }
    732 
    733 void HTMLDocumentParser::startBackgroundParser()
    734 {
    735     ASSERT(!isStopped());
    736     ASSERT(shouldUseThreading());
    737     ASSERT(!m_haveBackgroundParser);
    738     m_haveBackgroundParser = true;
    739 
    740     RefPtr<WeakReference<BackgroundHTMLParser> > reference = WeakReference<BackgroundHTMLParser>::createUnbound();
    741     m_backgroundParser = WeakPtr<BackgroundHTMLParser>(reference);
    742 
    743     // TODO(oysteine): Disabled due to crbug.com/398076 until a full fix can be implemented.
    744     if (RuntimeEnabledFeatures::threadedParserDataReceiverEnabled())
    745         document()->loader()->attachThreadedDataReceiver(adoptPtr(new ParserDataReceiver(m_backgroundParser)));
    746 
    747     OwnPtr<BackgroundHTMLParser::Configuration> config = adoptPtr(new BackgroundHTMLParser::Configuration);
    748     config->options = m_options;
    749     config->parser = m_weakFactory.createWeakPtr();
    750     config->xssAuditor = adoptPtr(new XSSAuditor);
    751     config->xssAuditor->init(document(), &m_xssAuditorDelegate);
    752     config->preloadScanner = adoptPtr(new TokenPreloadScanner(document()->url().copy(), createMediaValues(document())));
    753     config->decoder = takeDecoder();
    754 
    755     ASSERT(config->xssAuditor->isSafeToSendToAnotherThread());
    756     ASSERT(config->preloadScanner->isSafeToSendToAnotherThread());
    757     HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::start, reference.release(), config.release()));
    758 }
    759 
    760 void HTMLDocumentParser::stopBackgroundParser()
    761 {
    762     ASSERT(shouldUseThreading());
    763     ASSERT(m_haveBackgroundParser);
    764     m_haveBackgroundParser = false;
    765 
    766     HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::stop, m_backgroundParser));
    767     m_weakFactory.revokeAll();
    768 }
    769 
    770 void HTMLDocumentParser::append(PassRefPtr<StringImpl> inputSource)
    771 {
    772     if (isStopped())
    773         return;
    774 
    775     // We should never reach this point if we're using a parser thread,
    776     // as appendBytes() will directly ship the data to the thread.
    777     ASSERT(!shouldUseThreading());
    778 
    779     // pumpTokenizer can cause this parser to be detached from the Document,
    780     // but we need to ensure it isn't deleted yet.
    781     RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
    782     TRACE_EVENT1("net", "HTMLDocumentParser::append", "size", inputSource->length());
    783     String source(inputSource);
    784 
    785     if (m_preloadScanner) {
    786         if (m_input.current().isEmpty() && !isWaitingForScripts()) {
    787             // We have parsed until the end of the current input and so are now moving ahead of the preload scanner.
    788             // Clear the scanner so we know to scan starting from the current input point if we block again.
    789             m_preloadScanner.clear();
    790         } else {
    791             m_preloadScanner->appendToEnd(source);
    792             if (isWaitingForScripts())
    793                 m_preloadScanner->scan(m_preloader.get(), document()->baseElementURL());
    794         }
    795     }
    796 
    797     m_input.appendToEnd(source);
    798 
    799     if (inPumpSession()) {
    800         // We've gotten data off the network in a nested write.
    801         // We don't want to consume any more of the input stream now.  Do
    802         // not worry.  We'll consume this data in a less-nested write().
    803         return;
    804     }
    805 
    806     // A couple pinToMainThread() callers require synchronous parsing, but can't
    807     // easily use the insert() method, so we hack append() for them to be synchronous.
    808     // javascript: url handling is one such caller.
    809     // FIXME: This is gross, and we should separate the concept of synchronous parsing
    810     // from insert() so that only document.write() uses insert.
    811     if (m_isPinnedToMainThread)
    812         pumpTokenizerIfPossible(ForceSynchronous);
    813     else
    814         pumpTokenizerIfPossible(AllowYield);
    815 
    816     endIfDelayed();
    817 }
    818 
    819 void HTMLDocumentParser::end()
    820 {
    821     ASSERT(!isDetached());
    822     ASSERT(!isScheduledForResume());
    823 
    824     if (m_haveBackgroundParser)
    825         stopBackgroundParser();
    826 
    827     // Informs the the rest of WebCore that parsing is really finished (and deletes this).
    828     m_treeBuilder->finished();
    829 }
    830 
    831 void HTMLDocumentParser::attemptToRunDeferredScriptsAndEnd()
    832 {
    833     ASSERT(isStopping());
    834     // FIXME: It may not be correct to disable this for the background parser.
    835     // That means hasInsertionPoint() may not be correct in some cases.
    836     ASSERT(!hasInsertionPoint() || m_haveBackgroundParser);
    837     if (m_scriptRunner && !m_scriptRunner->executeScriptsWaitingForParsing())
    838         return;
    839     end();
    840 }
    841 
    842 void HTMLDocumentParser::attemptToEnd()
    843 {
    844     // finish() indicates we will not receive any more data. If we are waiting on
    845     // an external script to load, we can't finish parsing quite yet.
    846 
    847     if (shouldDelayEnd()) {
    848         m_endWasDelayed = true;
    849         return;
    850     }
    851     prepareToStopParsing();
    852 }
    853 
    854 void HTMLDocumentParser::endIfDelayed()
    855 {
    856     // If we've already been detached, don't bother ending.
    857     if (isDetached())
    858         return;
    859 
    860     if (!m_endWasDelayed || shouldDelayEnd())
    861         return;
    862 
    863     m_endWasDelayed = false;
    864     prepareToStopParsing();
    865 }
    866 
    867 void HTMLDocumentParser::finish()
    868 {
    869     // FIXME: We should ASSERT(!m_parserStopped) here, since it does not
    870     // makes sense to call any methods on DocumentParser once it's been stopped.
    871     // However, FrameLoader::stop calls DocumentParser::finish unconditionally.
    872 
    873     // Empty documents never got an append() call, and thus have never started
    874     // a background parser. In those cases, we ignore shouldUseThreading()
    875     // and fall through to the non-threading case.
    876     if (m_haveBackgroundParser) {
    877         if (!m_input.haveSeenEndOfFile())
    878             m_input.closeWithoutMarkingEndOfFile();
    879         HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::finish, m_backgroundParser));
    880         return;
    881     }
    882 
    883     if (!m_tokenizer) {
    884         ASSERT(!m_token);
    885         // We're finishing before receiving any data. Rather than booting up
    886         // the background parser just to spin it down, we finish parsing
    887         // synchronously.
    888         m_token = adoptPtr(new HTMLToken);
    889         m_tokenizer = HTMLTokenizer::create(m_options);
    890     }
    891 
    892     // We're not going to get any more data off the network, so we tell the
    893     // input stream we've reached the end of file. finish() can be called more
    894     // than once, if the first time does not call end().
    895     if (!m_input.haveSeenEndOfFile())
    896         m_input.markEndOfFile();
    897 
    898     attemptToEnd();
    899 }
    900 
    901 bool HTMLDocumentParser::isExecutingScript() const
    902 {
    903     if (!m_scriptRunner)
    904         return false;
    905     return m_scriptRunner->isExecutingScript();
    906 }
    907 
    908 OrdinalNumber HTMLDocumentParser::lineNumber() const
    909 {
    910     if (m_haveBackgroundParser)
    911         return m_textPosition.m_line;
    912 
    913     return m_input.current().currentLine();
    914 }
    915 
    916 TextPosition HTMLDocumentParser::textPosition() const
    917 {
    918     if (m_haveBackgroundParser)
    919         return m_textPosition;
    920 
    921     const SegmentedString& currentString = m_input.current();
    922     OrdinalNumber line = currentString.currentLine();
    923     OrdinalNumber column = currentString.currentColumn();
    924 
    925     return TextPosition(line, column);
    926 }
    927 
    928 bool HTMLDocumentParser::isWaitingForScripts() const
    929 {
    930     // When the TreeBuilder encounters a </script> tag, it returns to the HTMLDocumentParser
    931     // where the script is transfered from the treebuilder to the script runner.
    932     // The script runner will hold the script until its loaded and run. During
    933     // any of this time, we want to count ourselves as "waiting for a script" and thus
    934     // run the preload scanner, as well as delay completion of parsing.
    935     bool treeBuilderHasBlockingScript = m_treeBuilder->hasParserBlockingScript();
    936     bool scriptRunnerHasBlockingScript = m_scriptRunner && m_scriptRunner->hasParserBlockingScript();
    937     // Since the parser is paused while a script runner has a blocking script, it should
    938     // never be possible to end up with both objects holding a blocking script.
    939     ASSERT(!(treeBuilderHasBlockingScript && scriptRunnerHasBlockingScript));
    940     // If either object has a blocking script, the parser should be paused.
    941     return treeBuilderHasBlockingScript || scriptRunnerHasBlockingScript;
    942 }
    943 
    944 void HTMLDocumentParser::resumeParsingAfterScriptExecution()
    945 {
    946     ASSERT(!isExecutingScript());
    947     ASSERT(!isWaitingForScripts());
    948 
    949     if (m_haveBackgroundParser) {
    950         validateSpeculations(m_lastChunkBeforeScript.release());
    951         ASSERT(!m_lastChunkBeforeScript);
    952         // processParsedChunkFromBackgroundParser can cause this parser to be detached from the Document,
    953         // but we need to ensure it isn't deleted yet.
    954         RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
    955         pumpPendingSpeculations();
    956         return;
    957     }
    958 
    959     m_insertionPreloadScanner.clear();
    960     pumpTokenizerIfPossible(AllowYield);
    961     endIfDelayed();
    962 }
    963 
    964 void HTMLDocumentParser::appendCurrentInputStreamToPreloadScannerAndScan()
    965 {
    966     ASSERT(m_preloadScanner);
    967     m_preloadScanner->appendToEnd(m_input.current());
    968     m_preloadScanner->scan(m_preloader.get(), document()->baseElementURL());
    969 }
    970 
    971 void HTMLDocumentParser::notifyScriptLoaded(Resource* cachedResource)
    972 {
    973     // pumpTokenizer can cause this parser to be detached from the Document,
    974     // but we need to ensure it isn't deleted yet.
    975     RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
    976 
    977     ASSERT(m_scriptRunner);
    978     ASSERT(!isExecutingScript());
    979     if (isStopping()) {
    980         attemptToRunDeferredScriptsAndEnd();
    981         return;
    982     }
    983 
    984     m_scriptRunner->executeScriptsWaitingForLoad(cachedResource);
    985     if (!isWaitingForScripts())
    986         resumeParsingAfterScriptExecution();
    987 }
    988 
    989 void HTMLDocumentParser::executeScriptsWaitingForResources()
    990 {
    991     // Document only calls this when the Document owns the DocumentParser
    992     // so this will not be called in the DocumentFragment case.
    993     ASSERT(m_scriptRunner);
    994     // Ignore calls unless we have a script blocking the parser waiting on a
    995     // stylesheet load.  Otherwise we are currently parsing and this
    996     // is a re-entrant call from encountering a </ style> tag.
    997     if (!m_scriptRunner->hasScriptsWaitingForResources())
    998         return;
    999 
   1000     // pumpTokenizer can cause this parser to be detached from the Document,
   1001     // but we need to ensure it isn't deleted yet.
   1002     RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
   1003     m_scriptRunner->executeScriptsWaitingForResources();
   1004     if (!isWaitingForScripts())
   1005         resumeParsingAfterScriptExecution();
   1006 }
   1007 
   1008 void HTMLDocumentParser::parseDocumentFragment(const String& source, DocumentFragment* fragment, Element* contextElement, ParserContentPolicy parserContentPolicy)
   1009 {
   1010     RefPtrWillBeRawPtr<HTMLDocumentParser> parser = HTMLDocumentParser::create(fragment, contextElement, parserContentPolicy);
   1011     parser->insert(source); // Use insert() so that the parser will not yield.
   1012     parser->finish();
   1013     ASSERT(!parser->processingData()); // Make sure we're done. <rdar://problem/3963151>
   1014     parser->detach(); // Allows ~DocumentParser to assert it was detached before destruction.
   1015 }
   1016 
   1017 void HTMLDocumentParser::suspendScheduledTasks()
   1018 {
   1019     if (m_parserScheduler)
   1020         m_parserScheduler->suspend();
   1021 }
   1022 
   1023 void HTMLDocumentParser::resumeScheduledTasks()
   1024 {
   1025     if (m_parserScheduler)
   1026         m_parserScheduler->resume();
   1027 }
   1028 
   1029 void HTMLDocumentParser::appendBytes(const char* data, size_t length)
   1030 {
   1031     if (!length || isStopped())
   1032         return;
   1033 
   1034     if (shouldUseThreading()) {
   1035         if (!m_haveBackgroundParser)
   1036             startBackgroundParser();
   1037 
   1038         OwnPtr<Vector<char> > buffer = adoptPtr(new Vector<char>(length));
   1039         memcpy(buffer->data(), data, length);
   1040         TRACE_EVENT1("net", "HTMLDocumentParser::appendBytes", "size", (unsigned)length);
   1041 
   1042         HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::appendRawBytesFromMainThread, m_backgroundParser, buffer.release()));
   1043         return;
   1044     }
   1045 
   1046     DecodedDataDocumentParser::appendBytes(data, length);
   1047 }
   1048 
   1049 void HTMLDocumentParser::flush()
   1050 {
   1051     // If we've got no decoder, we never received any data.
   1052     if (isDetached() || needsDecoder())
   1053         return;
   1054 
   1055     if (m_haveBackgroundParser)
   1056         HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::flush, m_backgroundParser));
   1057     else
   1058         DecodedDataDocumentParser::flush();
   1059 }
   1060 
   1061 void HTMLDocumentParser::setDecoder(PassOwnPtr<TextResourceDecoder> decoder)
   1062 {
   1063     ASSERT(decoder);
   1064     DecodedDataDocumentParser::setDecoder(decoder);
   1065 
   1066     if (m_haveBackgroundParser)
   1067         HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::setDecoder, m_backgroundParser, takeDecoder()));
   1068 }
   1069 
   1070 }
   1071