f295cf2a99fd72516da16b5e253bfef5a63fcacd
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / parser / HTMLDocumentParser.cpp
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     document()->loader()->attachThreadedDataReceiver(adoptPtr(new ParserDataReceiver(m_backgroundParser)));
744
745     OwnPtr<BackgroundHTMLParser::Configuration> config = adoptPtr(new BackgroundHTMLParser::Configuration);
746     config->options = m_options;
747     config->parser = m_weakFactory.createWeakPtr();
748     config->xssAuditor = adoptPtr(new XSSAuditor);
749     config->xssAuditor->init(document(), &m_xssAuditorDelegate);
750     config->preloadScanner = adoptPtr(new TokenPreloadScanner(document()->url().copy(), createMediaValues(document())));
751     config->decoder = takeDecoder();
752
753     ASSERT(config->xssAuditor->isSafeToSendToAnotherThread());
754     ASSERT(config->preloadScanner->isSafeToSendToAnotherThread());
755     HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::start, reference.release(), config.release()));
756 }
757
758 void HTMLDocumentParser::stopBackgroundParser()
759 {
760     ASSERT(shouldUseThreading());
761     ASSERT(m_haveBackgroundParser);
762     m_haveBackgroundParser = false;
763
764     HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::stop, m_backgroundParser));
765     m_weakFactory.revokeAll();
766 }
767
768 void HTMLDocumentParser::append(PassRefPtr<StringImpl> inputSource)
769 {
770     if (isStopped())
771         return;
772
773     // We should never reach this point if we're using a parser thread,
774     // as appendBytes() will directly ship the data to the thread.
775     ASSERT(!shouldUseThreading());
776
777     // pumpTokenizer can cause this parser to be detached from the Document,
778     // but we need to ensure it isn't deleted yet.
779     RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
780     TRACE_EVENT1("net", "HTMLDocumentParser::append", "size", inputSource->length());
781     String source(inputSource);
782
783     if (m_preloadScanner) {
784         if (m_input.current().isEmpty() && !isWaitingForScripts()) {
785             // We have parsed until the end of the current input and so are now moving ahead of the preload scanner.
786             // Clear the scanner so we know to scan starting from the current input point if we block again.
787             m_preloadScanner.clear();
788         } else {
789             m_preloadScanner->appendToEnd(source);
790             if (isWaitingForScripts())
791                 m_preloadScanner->scan(m_preloader.get(), document()->baseElementURL());
792         }
793     }
794
795     m_input.appendToEnd(source);
796
797     if (inPumpSession()) {
798         // We've gotten data off the network in a nested write.
799         // We don't want to consume any more of the input stream now.  Do
800         // not worry.  We'll consume this data in a less-nested write().
801         return;
802     }
803
804     // A couple pinToMainThread() callers require synchronous parsing, but can't
805     // easily use the insert() method, so we hack append() for them to be synchronous.
806     // javascript: url handling is one such caller.
807     // FIXME: This is gross, and we should separate the concept of synchronous parsing
808     // from insert() so that only document.write() uses insert.
809     if (m_isPinnedToMainThread)
810         pumpTokenizerIfPossible(ForceSynchronous);
811     else
812         pumpTokenizerIfPossible(AllowYield);
813
814     endIfDelayed();
815 }
816
817 void HTMLDocumentParser::end()
818 {
819     ASSERT(!isDetached());
820     ASSERT(!isScheduledForResume());
821
822     if (m_haveBackgroundParser)
823         stopBackgroundParser();
824
825     // Informs the the rest of WebCore that parsing is really finished (and deletes this).
826     m_treeBuilder->finished();
827 }
828
829 void HTMLDocumentParser::attemptToRunDeferredScriptsAndEnd()
830 {
831     ASSERT(isStopping());
832     // FIXME: It may not be correct to disable this for the background parser.
833     // That means hasInsertionPoint() may not be correct in some cases.
834     ASSERT(!hasInsertionPoint() || m_haveBackgroundParser);
835     if (m_scriptRunner && !m_scriptRunner->executeScriptsWaitingForParsing())
836         return;
837     end();
838 }
839
840 void HTMLDocumentParser::attemptToEnd()
841 {
842     // finish() indicates we will not receive any more data. If we are waiting on
843     // an external script to load, we can't finish parsing quite yet.
844
845     if (shouldDelayEnd()) {
846         m_endWasDelayed = true;
847         return;
848     }
849     prepareToStopParsing();
850 }
851
852 void HTMLDocumentParser::endIfDelayed()
853 {
854     // If we've already been detached, don't bother ending.
855     if (isDetached())
856         return;
857
858     if (!m_endWasDelayed || shouldDelayEnd())
859         return;
860
861     m_endWasDelayed = false;
862     prepareToStopParsing();
863 }
864
865 void HTMLDocumentParser::finish()
866 {
867     // FIXME: We should ASSERT(!m_parserStopped) here, since it does not
868     // makes sense to call any methods on DocumentParser once it's been stopped.
869     // However, FrameLoader::stop calls DocumentParser::finish unconditionally.
870
871     // Empty documents never got an append() call, and thus have never started
872     // a background parser. In those cases, we ignore shouldUseThreading()
873     // and fall through to the non-threading case.
874     if (m_haveBackgroundParser) {
875         if (!m_input.haveSeenEndOfFile())
876             m_input.closeWithoutMarkingEndOfFile();
877         HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::finish, m_backgroundParser));
878         return;
879     }
880
881     if (!m_tokenizer) {
882         ASSERT(!m_token);
883         // We're finishing before receiving any data. Rather than booting up
884         // the background parser just to spin it down, we finish parsing
885         // synchronously.
886         m_token = adoptPtr(new HTMLToken);
887         m_tokenizer = HTMLTokenizer::create(m_options);
888     }
889
890     // We're not going to get any more data off the network, so we tell the
891     // input stream we've reached the end of file. finish() can be called more
892     // than once, if the first time does not call end().
893     if (!m_input.haveSeenEndOfFile())
894         m_input.markEndOfFile();
895
896     attemptToEnd();
897 }
898
899 bool HTMLDocumentParser::isExecutingScript() const
900 {
901     if (!m_scriptRunner)
902         return false;
903     return m_scriptRunner->isExecutingScript();
904 }
905
906 OrdinalNumber HTMLDocumentParser::lineNumber() const
907 {
908     if (m_haveBackgroundParser)
909         return m_textPosition.m_line;
910
911     return m_input.current().currentLine();
912 }
913
914 TextPosition HTMLDocumentParser::textPosition() const
915 {
916     if (m_haveBackgroundParser)
917         return m_textPosition;
918
919     const SegmentedString& currentString = m_input.current();
920     OrdinalNumber line = currentString.currentLine();
921     OrdinalNumber column = currentString.currentColumn();
922
923     return TextPosition(line, column);
924 }
925
926 bool HTMLDocumentParser::isWaitingForScripts() const
927 {
928     // When the TreeBuilder encounters a </script> tag, it returns to the HTMLDocumentParser
929     // where the script is transfered from the treebuilder to the script runner.
930     // The script runner will hold the script until its loaded and run. During
931     // any of this time, we want to count ourselves as "waiting for a script" and thus
932     // run the preload scanner, as well as delay completion of parsing.
933     bool treeBuilderHasBlockingScript = m_treeBuilder->hasParserBlockingScript();
934     bool scriptRunnerHasBlockingScript = m_scriptRunner && m_scriptRunner->hasParserBlockingScript();
935     // Since the parser is paused while a script runner has a blocking script, it should
936     // never be possible to end up with both objects holding a blocking script.
937     ASSERT(!(treeBuilderHasBlockingScript && scriptRunnerHasBlockingScript));
938     // If either object has a blocking script, the parser should be paused.
939     return treeBuilderHasBlockingScript || scriptRunnerHasBlockingScript;
940 }
941
942 void HTMLDocumentParser::resumeParsingAfterScriptExecution()
943 {
944     ASSERT(!isExecutingScript());
945     ASSERT(!isWaitingForScripts());
946
947     if (m_haveBackgroundParser) {
948         validateSpeculations(m_lastChunkBeforeScript.release());
949         ASSERT(!m_lastChunkBeforeScript);
950         // processParsedChunkFromBackgroundParser can cause this parser to be detached from the Document,
951         // but we need to ensure it isn't deleted yet.
952         RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
953         pumpPendingSpeculations();
954         return;
955     }
956
957     m_insertionPreloadScanner.clear();
958     pumpTokenizerIfPossible(AllowYield);
959     endIfDelayed();
960 }
961
962 void HTMLDocumentParser::appendCurrentInputStreamToPreloadScannerAndScan()
963 {
964     ASSERT(m_preloadScanner);
965     m_preloadScanner->appendToEnd(m_input.current());
966     m_preloadScanner->scan(m_preloader.get(), document()->baseElementURL());
967 }
968
969 void HTMLDocumentParser::notifyScriptLoaded(Resource* cachedResource)
970 {
971     // pumpTokenizer can cause this parser to be detached from the Document,
972     // but we need to ensure it isn't deleted yet.
973     RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
974
975     ASSERT(m_scriptRunner);
976     ASSERT(!isExecutingScript());
977     if (isStopping()) {
978         attemptToRunDeferredScriptsAndEnd();
979         return;
980     }
981
982     m_scriptRunner->executeScriptsWaitingForLoad(cachedResource);
983     if (!isWaitingForScripts())
984         resumeParsingAfterScriptExecution();
985 }
986
987 void HTMLDocumentParser::executeScriptsWaitingForResources()
988 {
989     // Document only calls this when the Document owns the DocumentParser
990     // so this will not be called in the DocumentFragment case.
991     ASSERT(m_scriptRunner);
992     // Ignore calls unless we have a script blocking the parser waiting on a
993     // stylesheet load.  Otherwise we are currently parsing and this
994     // is a re-entrant call from encountering a </ style> tag.
995     if (!m_scriptRunner->hasScriptsWaitingForResources())
996         return;
997
998     // pumpTokenizer can cause this parser to be detached from the Document,
999     // but we need to ensure it isn't deleted yet.
1000     RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
1001     m_scriptRunner->executeScriptsWaitingForResources();
1002     if (!isWaitingForScripts())
1003         resumeParsingAfterScriptExecution();
1004 }
1005
1006 void HTMLDocumentParser::parseDocumentFragment(const String& source, DocumentFragment* fragment, Element* contextElement, ParserContentPolicy parserContentPolicy)
1007 {
1008     RefPtrWillBeRawPtr<HTMLDocumentParser> parser = HTMLDocumentParser::create(fragment, contextElement, parserContentPolicy);
1009     parser->insert(source); // Use insert() so that the parser will not yield.
1010     parser->finish();
1011     ASSERT(!parser->processingData()); // Make sure we're done. <rdar://problem/3963151>
1012     parser->detach(); // Allows ~DocumentParser to assert it was detached before destruction.
1013 }
1014
1015 void HTMLDocumentParser::suspendScheduledTasks()
1016 {
1017     if (m_parserScheduler)
1018         m_parserScheduler->suspend();
1019 }
1020
1021 void HTMLDocumentParser::resumeScheduledTasks()
1022 {
1023     if (m_parserScheduler)
1024         m_parserScheduler->resume();
1025 }
1026
1027 void HTMLDocumentParser::appendBytes(const char* data, size_t length)
1028 {
1029     if (!length || isStopped())
1030         return;
1031
1032     if (shouldUseThreading()) {
1033         if (!m_haveBackgroundParser)
1034             startBackgroundParser();
1035
1036         OwnPtr<Vector<char> > buffer = adoptPtr(new Vector<char>(length));
1037         memcpy(buffer->data(), data, length);
1038         TRACE_EVENT1("net", "HTMLDocumentParser::appendBytes", "size", (unsigned)length);
1039
1040         HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::appendRawBytesFromMainThread, m_backgroundParser, buffer.release()));
1041         return;
1042     }
1043
1044     DecodedDataDocumentParser::appendBytes(data, length);
1045 }
1046
1047 void HTMLDocumentParser::flush()
1048 {
1049     // If we've got no decoder, we never received any data.
1050     if (isDetached() || needsDecoder())
1051         return;
1052
1053     if (m_haveBackgroundParser)
1054         HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::flush, m_backgroundParser));
1055     else
1056         DecodedDataDocumentParser::flush();
1057 }
1058
1059 void HTMLDocumentParser::setDecoder(PassOwnPtr<TextResourceDecoder> decoder)
1060 {
1061     ASSERT(decoder);
1062     DecodedDataDocumentParser::setDecoder(decoder);
1063
1064     if (m_haveBackgroundParser)
1065         HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::setDecoder, m_backgroundParser, takeDecoder()));
1066 }
1067
1068 }