| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2010 Google, Inc. All Rights Reserved. | 2 * Copyright (C) 2010 Google, Inc. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
| 6 * are met: | 6 * are met: |
| 7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
| 8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
| 10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
| (...skipping 30 matching lines...) Expand all Loading... |
| 41 #include "core/inspector/InspectorTraceEvents.h" | 41 #include "core/inspector/InspectorTraceEvents.h" |
| 42 #include "platform/SharedBuffer.h" | 42 #include "platform/SharedBuffer.h" |
| 43 #include "platform/TraceEvent.h" | 43 #include "platform/TraceEvent.h" |
| 44 #include "wtf/Functional.h" | 44 #include "wtf/Functional.h" |
| 45 | 45 |
| 46 namespace blink { | 46 namespace blink { |
| 47 | 47 |
| 48 HTMLDocumentParser::HTMLDocumentParser(HTMLDocument& document, bool reportErrors
) | 48 HTMLDocumentParser::HTMLDocumentParser(HTMLDocument& document, bool reportErrors
) |
| 49 : DecodedDataDocumentParser(document) | 49 : DecodedDataDocumentParser(document) |
| 50 , m_options(&document) | 50 , m_options(&document) |
| 51 , m_token(m_options.useThreading ? nullptr : adoptPtr(new HTMLToken)) | |
| 52 , m_tokenizer(m_options.useThreading ? nullptr : HTMLTokenizer::create(m_opt
ions)) | |
| 53 , m_treeBuilder(HTMLTreeBuilder::create(this, &document, reportErrors, m_opt
ions)) | 51 , m_treeBuilder(HTMLTreeBuilder::create(this, &document, reportErrors, m_opt
ions)) |
| 54 , m_parserScheduler(HTMLParserScheduler::create(this)) | 52 , m_parserScheduler(HTMLParserScheduler::create(this)) |
| 55 , m_weakFactory(this) | 53 , m_weakFactory(this) |
| 56 , m_isFragment(false) | 54 , m_isFragment(false) |
| 57 , m_endWasDelayed(false) | 55 , m_endWasDelayed(false) |
| 58 , m_haveBackgroundParser(false) | 56 , m_haveBackgroundParser(false) |
| 59 , m_pumpSessionNestingLevel(0) | 57 , m_pumpSessionNestingLevel(0) |
| 60 { | 58 { |
| 61 ASSERT(shouldUseThreading() || (m_token && m_tokenizer)); | 59 ASSERT(shouldUseThreading()); |
| 62 } | 60 } |
| 63 | 61 |
| 64 HTMLDocumentParser::~HTMLDocumentParser() | 62 HTMLDocumentParser::~HTMLDocumentParser() |
| 65 { | 63 { |
| 66 #if ENABLE(OILPAN) | 64 #if ENABLE(OILPAN) |
| 67 if (m_haveBackgroundParser) | 65 if (m_haveBackgroundParser) |
| 68 stopBackgroundParser(); | 66 stopBackgroundParser(); |
| 69 // In Oilpan, HTMLDocumentParser can die together with Document, and | 67 // In Oilpan, HTMLDocumentParser can die together with Document, and |
| 70 // detach() is not called in this case. | 68 // detach() is not called in this case. |
| 71 #else | 69 #else |
| 72 ASSERT(!m_parserScheduler); | 70 ASSERT(!m_parserScheduler); |
| 73 ASSERT(!m_pumpSessionNestingLevel); | 71 ASSERT(!m_pumpSessionNestingLevel); |
| 74 ASSERT(!m_haveBackgroundParser); | 72 ASSERT(!m_haveBackgroundParser); |
| 75 // FIXME: We should be able to ASSERT(m_speculations.isEmpty()), | 73 // FIXME: We should be able to ASSERT(m_speculations.isEmpty()), |
| 76 // but there are cases where that's not true currently. For example, | 74 // but there are cases where that's not true currently. For example, |
| 77 // we we're told to stop parsing before we've consumed all the input. | 75 // we we're told to stop parsing before we've consumed all the input. |
| 78 #endif | 76 #endif |
| 79 } | 77 } |
| 80 | 78 |
| 81 void HTMLDocumentParser::trace(Visitor* visitor) | 79 void HTMLDocumentParser::trace(Visitor* visitor) |
| 82 { | 80 { |
| 83 visitor->trace(m_treeBuilder); | 81 visitor->trace(m_treeBuilder); |
| 84 DecodedDataDocumentParser::trace(visitor); | 82 DecodedDataDocumentParser::trace(visitor); |
| 85 } | 83 } |
| 86 | 84 |
| 85 void HTMLDocumentParser::parse(mojo::ScopedDataPipeConsumerHandle source) |
| 86 { |
| 87 ASSERT(!isStopped()); |
| 88 ASSERT(shouldUseThreading()); |
| 89 ASSERT(!m_haveBackgroundParser); |
| 90 m_haveBackgroundParser = true; |
| 91 |
| 92 OwnPtr<BackgroundHTMLParser::Configuration> config = adoptPtr(new Background
HTMLParser::Configuration); |
| 93 config->options = m_options; |
| 94 config->source = source.Pass(); |
| 95 config->parser = m_weakFactory.createWeakPtr(); |
| 96 |
| 97 m_backgroundParser = BackgroundHTMLParser::create(config.release()); |
| 98 HTMLParserThread::taskRunner()->PostTask(FROM_HERE, |
| 99 base::Bind(&BackgroundHTMLParser::start, m_backgroundParser)); |
| 100 } |
| 101 |
| 87 void HTMLDocumentParser::detach() | 102 void HTMLDocumentParser::detach() |
| 88 { | 103 { |
| 89 if (m_haveBackgroundParser) | 104 if (m_haveBackgroundParser) |
| 90 stopBackgroundParser(); | 105 stopBackgroundParser(); |
| 91 DocumentParser::detach(); | 106 DocumentParser::detach(); |
| 92 m_treeBuilder->detach(); | 107 m_treeBuilder->detach(); |
| 93 // FIXME: It seems wrong that we would have a preload scanner here. | 108 // FIXME: It seems wrong that we would have a preload scanner here. |
| 94 // Yet during fast/dom/HTMLScriptElement/script-load-events.html we do. | 109 // Yet during fast/dom/HTMLScriptElement/script-load-events.html we do. |
| 95 m_parserScheduler.clear(); // Deleting the scheduler will clear any timers. | 110 m_parserScheduler.clear(); // Deleting the scheduler will clear any timers. |
| 96 } | 111 } |
| (...skipping 11 matching lines...) Expand all Loading... |
| 108 void HTMLDocumentParser::prepareToStopParsing() | 123 void HTMLDocumentParser::prepareToStopParsing() |
| 109 { | 124 { |
| 110 // FIXME: It may not be correct to disable this for the background parser. | 125 // FIXME: It may not be correct to disable this for the background parser. |
| 111 // That means hasInsertionPoint() may not be correct in some cases. | 126 // That means hasInsertionPoint() may not be correct in some cases. |
| 112 ASSERT(!hasInsertionPoint() || m_haveBackgroundParser); | 127 ASSERT(!hasInsertionPoint() || m_haveBackgroundParser); |
| 113 | 128 |
| 114 // pumpTokenizer can cause this parser to be detached from the Document, | 129 // pumpTokenizer can cause this parser to be detached from the Document, |
| 115 // but we need to ensure it isn't deleted yet. | 130 // but we need to ensure it isn't deleted yet. |
| 116 RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this); | 131 RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this); |
| 117 | 132 |
| 118 // NOTE: This pump should only ever emit buffered character tokens, | |
| 119 // so ForceSynchronous vs. AllowYield should be meaningless. | |
| 120 if (m_tokenizer) { | |
| 121 ASSERT(!m_haveBackgroundParser); | |
| 122 pumpTokenizerIfPossible(ForceSynchronous); | |
| 123 } | |
| 124 | |
| 125 if (isStopped()) | 133 if (isStopped()) |
| 126 return; | 134 return; |
| 127 | 135 |
| 128 DocumentParser::prepareToStopParsing(); | 136 DocumentParser::prepareToStopParsing(); |
| 129 | 137 |
| 130 // We will not have a scriptRunner when parsing a DocumentFragment. | 138 // We will not have a scriptRunner when parsing a DocumentFragment. |
| 131 if (!m_isFragment) | 139 if (!m_isFragment) |
| 132 document()->setReadyState(Document::Interactive); | 140 document()->setReadyState(Document::Interactive); |
| 133 | 141 |
| 134 // Setting the ready state above can fire mutation event and detach us | 142 // Setting the ready state above can fire mutation event and detach us |
| 135 // from underneath. In that case, just bail out. | 143 // from underneath. In that case, just bail out. |
| 136 if (isDetached()) | 144 if (isDetached()) |
| 137 return; | 145 return; |
| 138 | 146 |
| 139 ASSERT(isStopping()); | 147 ASSERT(isStopping()); |
| 140 ASSERT(!hasInsertionPoint() || m_haveBackgroundParser); | 148 ASSERT(!hasInsertionPoint() || m_haveBackgroundParser); |
| 141 end(); | 149 end(); |
| 142 } | 150 } |
| 143 | 151 |
| 144 bool HTMLDocumentParser::isParsingFragment() const | 152 bool HTMLDocumentParser::isParsingFragment() const |
| 145 { | 153 { |
| 146 return m_treeBuilder->isParsingFragment(); | 154 return m_treeBuilder->isParsingFragment(); |
| 147 } | 155 } |
| 148 | 156 |
| 149 bool HTMLDocumentParser::processingData() const | 157 bool HTMLDocumentParser::processingData() const |
| 150 { | 158 { |
| 151 return isScheduledForResume() || inPumpSession() || m_haveBackgroundParser; | 159 return isScheduledForResume() || inPumpSession() || m_haveBackgroundParser; |
| 152 } | 160 } |
| 153 | 161 |
| 154 void HTMLDocumentParser::pumpTokenizerIfPossible(SynchronousMode mode) | |
| 155 { | |
| 156 if (isStopped()) | |
| 157 return; | |
| 158 if (isWaitingForScripts()) | |
| 159 return; | |
| 160 | |
| 161 // Once a resume is scheduled, HTMLParserScheduler controls when we next pum
p. | |
| 162 if (isScheduledForResume()) { | |
| 163 ASSERT(mode == AllowYield); | |
| 164 return; | |
| 165 } | |
| 166 | |
| 167 pumpTokenizer(mode); | |
| 168 } | |
| 169 | |
| 170 bool HTMLDocumentParser::isScheduledForResume() const | 162 bool HTMLDocumentParser::isScheduledForResume() const |
| 171 { | 163 { |
| 172 return m_parserScheduler && m_parserScheduler->isScheduledForResume(); | 164 return m_parserScheduler && m_parserScheduler->isScheduledForResume(); |
| 173 } | 165 } |
| 174 | 166 |
| 175 // Used by HTMLParserScheduler | 167 // Used by HTMLParserScheduler |
| 176 void HTMLDocumentParser::resumeParsingAfterYield() | 168 void HTMLDocumentParser::resumeParsingAfterYield() |
| 177 { | 169 { |
| 178 // pumpTokenizer can cause this parser to be detached from the Document, | 170 // pumpTokenizer can cause this parser to be detached from the Document, |
| 179 // but we need to ensure it isn't deleted yet. | 171 // but we need to ensure it isn't deleted yet. |
| 180 RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this); | 172 RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this); |
| 181 | 173 |
| 182 if (m_haveBackgroundParser) { | 174 ASSERT(m_haveBackgroundParser); |
| 183 pumpPendingSpeculations(); | 175 pumpPendingSpeculations(); |
| 184 return; | |
| 185 } | |
| 186 | |
| 187 // We should never be here unless we can pump immediately. Call pumpTokeniz
er() | |
| 188 // directly so that ASSERTS will fire if we're wrong. | |
| 189 pumpTokenizer(AllowYield); | |
| 190 endIfDelayed(); | |
| 191 } | 176 } |
| 192 | 177 |
| 193 void HTMLDocumentParser::runScriptsForPausedTreeBuilder() | 178 void HTMLDocumentParser::runScriptsForPausedTreeBuilder() |
| 194 { | 179 { |
| 195 if (m_isFragment) | 180 if (m_isFragment) |
| 196 return; | 181 return; |
| 197 TextPosition scriptStartPosition = TextPosition::belowRangePosition(); | 182 TextPosition scriptStartPosition = TextPosition::belowRangePosition(); |
| 198 RefPtrWillBeRawPtr<Element> scriptToProcess = m_treeBuilder->takeScriptToPro
cess(scriptStartPosition); | 183 RefPtrWillBeRawPtr<Element> scriptToProcess = m_treeBuilder->takeScriptToPro
cess(scriptStartPosition); |
| 199 m_scriptRunner.runScript(toHTMLScriptElement(scriptToProcess.get()), scriptS
tartPosition); | 184 m_scriptRunner.runScript(toHTMLScriptElement(scriptToProcess.get()), scriptS
tartPosition); |
| 200 } | 185 } |
| 201 | 186 |
| 202 bool HTMLDocumentParser::canTakeNextToken(SynchronousMode mode, PumpSession& ses
sion) | |
| 203 { | |
| 204 if (isStopped()) | |
| 205 return false; | |
| 206 | |
| 207 ASSERT(!m_haveBackgroundParser || mode == ForceSynchronous); | |
| 208 | |
| 209 if (isWaitingForScripts()) { | |
| 210 if (mode == AllowYield) | |
| 211 session.didSeeScript = true; | |
| 212 | |
| 213 // If we don't run the script, we cannot allow the next token to be take
n. | |
| 214 if (session.needsYield) | |
| 215 return false; | |
| 216 | |
| 217 // If we're paused waiting for a script, we try to execute scripts befor
e continuing. | |
| 218 runScriptsForPausedTreeBuilder(); | |
| 219 if (isStopped()) | |
| 220 return false; | |
| 221 if (isWaitingForScripts()) | |
| 222 return false; | |
| 223 } | |
| 224 | |
| 225 if (mode == AllowYield) | |
| 226 m_parserScheduler->checkForYieldBeforeToken(session); | |
| 227 | |
| 228 return true; | |
| 229 } | |
| 230 | |
| 231 void HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser(PassOwnPtr<Pa
rsedChunk> chunk) | 187 void HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser(PassOwnPtr<Pa
rsedChunk> chunk) |
| 232 { | 188 { |
| 233 TRACE_EVENT0("blink", "HTMLDocumentParser::didReceiveParsedChunkFromBackgrou
ndParser"); | 189 TRACE_EVENT0("blink", "HTMLDocumentParser::didReceiveParsedChunkFromBackgrou
ndParser"); |
| 234 | 190 |
| 235 // alert(), runModalDialog, and the JavaScript Debugger all run nested event
loops | 191 // alert(), runModalDialog, and the JavaScript Debugger all run nested event
loops |
| 236 // which can cause this method to be re-entered. We detect re-entry using | 192 // which can cause this method to be re-entered. We detect re-entry using |
| 237 // hasActiveParser(), save the chunk as a speculation, and return. | 193 // hasActiveParser(), save the chunk as a speculation, and return. |
| 238 if (isWaitingForScripts() || !m_speculations.isEmpty() || document()->active
ParserCount() > 0) { | 194 if (isWaitingForScripts() || !m_speculations.isEmpty() || document()->active
ParserCount() > 0) { |
| 239 m_speculations.append(chunk); | 195 m_speculations.append(chunk); |
| 240 return; | 196 return; |
| (...skipping 28 matching lines...) Expand all Loading... |
| 269 | 225 |
| 270 ASSERT_WITH_SECURITY_IMPLICATION(!document()->activeParserCount()); | 226 ASSERT_WITH_SECURITY_IMPLICATION(!document()->activeParserCount()); |
| 271 ASSERT(!isParsingFragment()); | 227 ASSERT(!isParsingFragment()); |
| 272 ASSERT(!isWaitingForScripts()); | 228 ASSERT(!isWaitingForScripts()); |
| 273 ASSERT(!isStopped()); | 229 ASSERT(!isStopped()); |
| 274 #if !ENABLE(OILPAN) | 230 #if !ENABLE(OILPAN) |
| 275 // ASSERT that this object is both attached to the Document and protected. | 231 // ASSERT that this object is both attached to the Document and protected. |
| 276 ASSERT(refCount() >= 2); | 232 ASSERT(refCount() >= 2); |
| 277 #endif | 233 #endif |
| 278 ASSERT(shouldUseThreading()); | 234 ASSERT(shouldUseThreading()); |
| 279 ASSERT(!m_tokenizer); | |
| 280 ASSERT(!m_token); | |
| 281 ASSERT(!m_lastChunkBeforeScript); | 235 ASSERT(!m_lastChunkBeforeScript); |
| 282 | 236 |
| 283 ActiveParserSession session(contextForParsingSession()); | 237 ActiveParserSession session(contextForParsingSession()); |
| 284 | 238 |
| 285 OwnPtr<ParsedChunk> chunk(popChunk); | 239 OwnPtr<ParsedChunk> chunk(popChunk); |
| 286 OwnPtr<CompactHTMLTokenStream> tokens = chunk->tokens.release(); | 240 OwnPtr<CompactHTMLTokenStream> tokens = chunk->tokens.release(); |
| 287 | 241 |
| 288 for (Vector<CompactHTMLToken>::const_iterator it = tokens->begin(); it != to
kens->end(); ++it) { | 242 for (Vector<CompactHTMLToken>::const_iterator it = tokens->begin(); it != to
kens->end(); ++it) { |
| 289 ASSERT(!isWaitingForScripts()); | 243 ASSERT(!isWaitingForScripts()); |
| 290 | 244 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 301 validateSpeculations(chunk.release()); | 255 validateSpeculations(chunk.release()); |
| 302 break; | 256 break; |
| 303 } | 257 } |
| 304 | 258 |
| 305 if (it->type() == HTMLToken::EndOfFile) { | 259 if (it->type() == HTMLToken::EndOfFile) { |
| 306 ASSERT(it + 1 == tokens->end()); // The EOF is assumed to be the las
t token of this bunch. | 260 ASSERT(it + 1 == tokens->end()); // The EOF is assumed to be the las
t token of this bunch. |
| 307 ASSERT(m_speculations.isEmpty()); // There should never be any chunk
s after the EOF. | 261 ASSERT(m_speculations.isEmpty()); // There should never be any chunk
s after the EOF. |
| 308 prepareToStopParsing(); | 262 prepareToStopParsing(); |
| 309 break; | 263 break; |
| 310 } | 264 } |
| 311 | |
| 312 ASSERT(!m_tokenizer); | |
| 313 ASSERT(!m_token); | |
| 314 } | 265 } |
| 315 | 266 |
| 316 // Make sure any pending text nodes are emitted before returning. | 267 // Make sure any pending text nodes are emitted before returning. |
| 317 if (!isStopped()) | 268 if (!isStopped()) |
| 318 m_treeBuilder->flush(); | 269 m_treeBuilder->flush(); |
| 319 } | 270 } |
| 320 | 271 |
| 321 void HTMLDocumentParser::pumpPendingSpeculations() | 272 void HTMLDocumentParser::pumpPendingSpeculations() |
| 322 { | 273 { |
| 323 // FIXME: Share this constant with the parser scheduler. | 274 // FIXME: Share this constant with the parser scheduler. |
| 324 const double parserTimeLimit = 0.500; | 275 const double parserTimeLimit = 0.500; |
| 325 | 276 |
| 326 #if !ENABLE(OILPAN) | 277 #if !ENABLE(OILPAN) |
| 327 // ASSERT that this object is both attached to the Document and protected. | 278 // ASSERT that this object is both attached to the Document and protected. |
| 328 ASSERT(refCount() >= 2); | 279 ASSERT(refCount() >= 2); |
| 329 #endif | 280 #endif |
| 330 // If this assert fails, you need to call validateSpeculations to make sure | |
| 331 // m_tokenizer and m_token don't have state that invalidates m_speculations. | |
| 332 ASSERT(!m_tokenizer); | |
| 333 ASSERT(!m_token); | |
| 334 ASSERT(!m_lastChunkBeforeScript); | 281 ASSERT(!m_lastChunkBeforeScript); |
| 335 ASSERT(!isWaitingForScripts()); | 282 ASSERT(!isWaitingForScripts()); |
| 336 ASSERT(!isStopped()); | 283 ASSERT(!isStopped()); |
| 337 | 284 |
| 338 // FIXME: Pass in current input length. | 285 // FIXME: Pass in current input length. |
| 339 TRACE_EVENT_BEGIN1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ParseHTM
L", "beginData", InspectorParseHtmlEvent::beginData(document(), lineNumber().zer
oBasedInt())); | 286 TRACE_EVENT_BEGIN1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ParseHTM
L", "beginData", InspectorParseHtmlEvent::beginData(document(), lineNumber().zer
oBasedInt())); |
| 340 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "
CallStack", "stack", InspectorCallStackEvent::currentCallStack()); | 287 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "
CallStack", "stack", InspectorCallStackEvent::currentCallStack()); |
| 341 | 288 |
| 342 double startTime = currentTime(); | 289 double startTime = currentTime(); |
| 343 | 290 |
| (...skipping 16 matching lines...) Expand all Loading... |
| 360 | 307 |
| 361 Document* HTMLDocumentParser::contextForParsingSession() | 308 Document* HTMLDocumentParser::contextForParsingSession() |
| 362 { | 309 { |
| 363 // The parsing session should interact with the document only when parsing | 310 // The parsing session should interact with the document only when parsing |
| 364 // non-fragments. Otherwise, we might delay the load event mistakenly. | 311 // non-fragments. Otherwise, we might delay the load event mistakenly. |
| 365 if (isParsingFragment()) | 312 if (isParsingFragment()) |
| 366 return 0; | 313 return 0; |
| 367 return document(); | 314 return document(); |
| 368 } | 315 } |
| 369 | 316 |
| 370 void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode) | |
| 371 { | |
| 372 ASSERT(!isStopped()); | |
| 373 ASSERT(!isScheduledForResume()); | |
| 374 #if !ENABLE(OILPAN) | |
| 375 // ASSERT that this object is both attached to the Document and protected. | |
| 376 ASSERT(refCount() >= 2); | |
| 377 #endif | |
| 378 ASSERT(m_tokenizer); | |
| 379 ASSERT(m_token); | |
| 380 ASSERT(!m_haveBackgroundParser || mode == ForceSynchronous); | |
| 381 | |
| 382 PumpSession session(m_pumpSessionNestingLevel, contextForParsingSession()); | |
| 383 | |
| 384 TRACE_EVENT_BEGIN1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ParseHTM
L", "beginData", InspectorParseHtmlEvent::beginData(document(), m_input.current(
).currentLine().zeroBasedInt())); | |
| 385 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "
CallStack", "stack", InspectorCallStackEvent::currentCallStack()); | |
| 386 | |
| 387 while (canTakeNextToken(mode, session) && !session.needsYield) { | |
| 388 if (!m_tokenizer->nextToken(m_input.current(), token())) | |
| 389 break; | |
| 390 | |
| 391 constructTreeFromHTMLToken(token()); | |
| 392 ASSERT(token().isUninitialized()); | |
| 393 } | |
| 394 | |
| 395 #if !ENABLE(OILPAN) | |
| 396 // Ensure we haven't been totally deref'ed after pumping. Any caller of this | |
| 397 // function should be holding a RefPtr to this to ensure we weren't deleted. | |
| 398 ASSERT(refCount() >= 1); | |
| 399 #endif | |
| 400 | |
| 401 if (isStopped()) | |
| 402 return; | |
| 403 | |
| 404 // There should only be PendingText left since the tree-builder always flush
es | |
| 405 // the task queue before returning. In case that ever changes, crash. | |
| 406 if (mode == ForceSynchronous) | |
| 407 m_treeBuilder->flush(); | |
| 408 RELEASE_ASSERT(!isStopped()); | |
| 409 | |
| 410 if (session.needsYield) | |
| 411 m_parserScheduler->scheduleForResume(); | |
| 412 | |
| 413 TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ParseHTML"
, "endLine", m_input.current().currentLine().zeroBasedInt()); | |
| 414 } | |
| 415 | |
| 416 void HTMLDocumentParser::constructTreeFromHTMLToken(HTMLToken& rawToken) | 317 void HTMLDocumentParser::constructTreeFromHTMLToken(HTMLToken& rawToken) |
| 417 { | 318 { |
| 418 AtomicHTMLToken token(rawToken); | 319 AtomicHTMLToken token(rawToken); |
| 419 | 320 |
| 420 // We clear the rawToken in case constructTreeFromAtomicToken | 321 // We clear the rawToken in case constructTreeFromAtomicToken |
| 421 // synchronously re-enters the parser. We don't clear the token immedately | 322 // synchronously re-enters the parser. We don't clear the token immedately |
| 422 // for Character tokens because the AtomicHTMLToken avoids copying the | 323 // for Character tokens because the AtomicHTMLToken avoids copying the |
| 423 // characters by keeping a pointer to the underlying buffer in the | 324 // characters by keeping a pointer to the underlying buffer in the |
| 424 // HTMLToken. Fortunately, Character tokens can't cause us to re-enter | 325 // HTMLToken. Fortunately, Character tokens can't cause us to re-enter |
| 425 // the parser. | 326 // the parser. |
| (...skipping 13 matching lines...) Expand all Loading... |
| 439 } | 340 } |
| 440 | 341 |
| 441 void HTMLDocumentParser::constructTreeFromCompactHTMLToken(const CompactHTMLToke
n& compactToken) | 342 void HTMLDocumentParser::constructTreeFromCompactHTMLToken(const CompactHTMLToke
n& compactToken) |
| 442 { | 343 { |
| 443 AtomicHTMLToken token(compactToken); | 344 AtomicHTMLToken token(compactToken); |
| 444 m_treeBuilder->constructTree(&token); | 345 m_treeBuilder->constructTree(&token); |
| 445 } | 346 } |
| 446 | 347 |
| 447 bool HTMLDocumentParser::hasInsertionPoint() | 348 bool HTMLDocumentParser::hasInsertionPoint() |
| 448 { | 349 { |
| 449 return m_input.hasInsertionPoint(); | 350 return false; |
| 450 } | |
| 451 | |
| 452 void HTMLDocumentParser::insert(const SegmentedString& source) | |
| 453 { | |
| 454 if (isStopped()) | |
| 455 return; | |
| 456 | |
| 457 TRACE_EVENT1("blink", "HTMLDocumentParser::insert", "source_length", source.
length()); | |
| 458 | |
| 459 // pumpTokenizer can cause this parser to be detached from the Document, | |
| 460 // but we need to ensure it isn't deleted yet. | |
| 461 RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this); | |
| 462 | |
| 463 if (!m_tokenizer) { | |
| 464 ASSERT(!inPumpSession()); | |
| 465 ASSERT(m_haveBackgroundParser); | |
| 466 m_token = adoptPtr(new HTMLToken); | |
| 467 m_tokenizer = HTMLTokenizer::create(m_options); | |
| 468 } | |
| 469 | |
| 470 SegmentedString excludedLineNumberSource(source); | |
| 471 excludedLineNumberSource.setExcludeLineNumbers(); | |
| 472 m_input.insertAtCurrentInsertionPoint(excludedLineNumberSource); | |
| 473 pumpTokenizerIfPossible(ForceSynchronous); | |
| 474 | |
| 475 endIfDelayed(); | |
| 476 } | 351 } |
| 477 | 352 |
| 478 void HTMLDocumentParser::startBackgroundParser() | 353 void HTMLDocumentParser::startBackgroundParser() |
| 479 { | 354 { |
| 480 ASSERT(!isStopped()); | |
| 481 ASSERT(shouldUseThreading()); | |
| 482 ASSERT(!m_haveBackgroundParser); | |
| 483 m_haveBackgroundParser = true; | |
| 484 | |
| 485 OwnPtr<BackgroundHTMLParser::Configuration> config = adoptPtr(new Background
HTMLParser::Configuration); | |
| 486 config->options = m_options; | |
| 487 config->parser = m_weakFactory.createWeakPtr(); | |
| 488 | |
| 489 m_backgroundParser = BackgroundHTMLParser::create(config.release()); | |
| 490 } | 355 } |
| 491 | 356 |
| 492 void HTMLDocumentParser::stopBackgroundParser() | 357 void HTMLDocumentParser::stopBackgroundParser() |
| 493 { | 358 { |
| 494 ASSERT(shouldUseThreading()); | 359 ASSERT(shouldUseThreading()); |
| 495 ASSERT(m_haveBackgroundParser); | 360 ASSERT(m_haveBackgroundParser); |
| 496 m_haveBackgroundParser = false; | 361 m_haveBackgroundParser = false; |
| 497 | 362 |
| 498 HTMLParserThread::taskRunner()->PostTask(FROM_HERE, | 363 HTMLParserThread::taskRunner()->PostTask(FROM_HERE, |
| 499 base::Bind(&BackgroundHTMLParser::stop, m_backgroundParser)); | 364 base::Bind(&BackgroundHTMLParser::stop, m_backgroundParser)); |
| 500 m_weakFactory.revokeAll(); | 365 m_weakFactory.revokeAll(); |
| 501 } | 366 } |
| 502 | 367 |
| 503 void HTMLDocumentParser::append(PassRefPtr<StringImpl> inputSource) | |
| 504 { | |
| 505 if (isStopped()) | |
| 506 return; | |
| 507 | |
| 508 // We should never reach this point if we're using a parser thread, | |
| 509 // as appendBytes() will directly ship the data to the thread. | |
| 510 ASSERT(!shouldUseThreading()); | |
| 511 | |
| 512 // pumpTokenizer can cause this parser to be detached from the Document, | |
| 513 // but we need to ensure it isn't deleted yet. | |
| 514 RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this); | |
| 515 TRACE_EVENT1("net", "HTMLDocumentParser::append", "size", inputSource->lengt
h()); | |
| 516 String source(inputSource); | |
| 517 | |
| 518 m_input.appendToEnd(source); | |
| 519 | |
| 520 if (inPumpSession()) { | |
| 521 // We've gotten data off the network in a nested write. | |
| 522 // We don't want to consume any more of the input stream now. Do | |
| 523 // not worry. We'll consume this data in a less-nested write(). | |
| 524 return; | |
| 525 } | |
| 526 | |
| 527 pumpTokenizerIfPossible(AllowYield); | |
| 528 | |
| 529 endIfDelayed(); | |
| 530 } | |
| 531 | |
| 532 void HTMLDocumentParser::end() | 368 void HTMLDocumentParser::end() |
| 533 { | 369 { |
| 534 ASSERT(!isDetached()); | 370 ASSERT(!isDetached()); |
| 535 ASSERT(!isScheduledForResume()); | 371 ASSERT(!isScheduledForResume()); |
| 536 | 372 |
| 537 if (m_haveBackgroundParser) | 373 if (m_haveBackgroundParser) |
| 538 stopBackgroundParser(); | 374 stopBackgroundParser(); |
| 539 | 375 |
| 540 // Informs the the rest of WebCore that parsing is really finished (and dele
tes this). | 376 // Informs the the rest of WebCore that parsing is really finished (and dele
tes this). |
| 541 m_treeBuilder->finished(); | 377 m_treeBuilder->finished(); |
| 542 } | 378 } |
| 543 | 379 |
| 544 void HTMLDocumentParser::attemptToEnd() | 380 void HTMLDocumentParser::attemptToEnd() |
| 545 { | 381 { |
| 546 // finish() indicates we will not receive any more data. If we are waiting o
n | |
| 547 // an external script to load, we can't finish parsing quite yet. | |
| 548 | |
| 549 if (shouldDelayEnd()) { | 382 if (shouldDelayEnd()) { |
| 550 m_endWasDelayed = true; | 383 m_endWasDelayed = true; |
| 551 return; | 384 return; |
| 552 } | 385 } |
| 553 prepareToStopParsing(); | 386 prepareToStopParsing(); |
| 554 } | 387 } |
| 555 | 388 |
| 556 void HTMLDocumentParser::endIfDelayed() | 389 void HTMLDocumentParser::endIfDelayed() |
| 557 { | 390 { |
| 558 // If we've already been detached, don't bother ending. | 391 // If we've already been detached, don't bother ending. |
| 559 if (isDetached()) | 392 if (isDetached()) |
| 560 return; | 393 return; |
| 561 | 394 |
| 562 if (!m_endWasDelayed || shouldDelayEnd()) | 395 if (!m_endWasDelayed || shouldDelayEnd()) |
| 563 return; | 396 return; |
| 564 | 397 |
| 565 m_endWasDelayed = false; | 398 m_endWasDelayed = false; |
| 566 prepareToStopParsing(); | 399 prepareToStopParsing(); |
| 567 } | 400 } |
| 568 | 401 |
| 569 void HTMLDocumentParser::finish() | |
| 570 { | |
| 571 // FIXME: We should ASSERT(!m_parserStopped) here, since it does not | |
| 572 // makes sense to call any methods on DocumentParser once it's been stopped. | |
| 573 // However, FrameLoader::stop calls DocumentParser::finish unconditionally. | |
| 574 | |
| 575 // flush may ending up executing arbitrary script, and possibly detach the p
arser. | |
| 576 RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this); | |
| 577 flush(); | |
| 578 if (isDetached()) | |
| 579 return; | |
| 580 | |
| 581 // Empty documents never got an append() call, and thus have never started | |
| 582 // a background parser. In those cases, we ignore shouldUseThreading() | |
| 583 // and fall through to the non-threading case. | |
| 584 if (m_haveBackgroundParser) { | |
| 585 if (!m_input.haveSeenEndOfFile()) | |
| 586 m_input.closeWithoutMarkingEndOfFile(); | |
| 587 HTMLParserThread::taskRunner()->PostTask(FROM_HERE, | |
| 588 base::Bind(&BackgroundHTMLParser::finish, m_backgroundParser)); | |
| 589 return; | |
| 590 } | |
| 591 | |
| 592 if (!m_tokenizer) { | |
| 593 ASSERT(!m_token); | |
| 594 // We're finishing before receiving any data. Rather than booting up | |
| 595 // the background parser just to spin it down, we finish parsing | |
| 596 // synchronously. | |
| 597 m_token = adoptPtr(new HTMLToken); | |
| 598 m_tokenizer = HTMLTokenizer::create(m_options); | |
| 599 } | |
| 600 | |
| 601 // We're not going to get any more data off the network, so we tell the | |
| 602 // input stream we've reached the end of file. finish() can be called more | |
| 603 // than once, if the first time does not call end(). | |
| 604 if (!m_input.haveSeenEndOfFile()) | |
| 605 m_input.markEndOfFile(); | |
| 606 | |
| 607 attemptToEnd(); | |
| 608 } | |
| 609 | |
| 610 bool HTMLDocumentParser::isExecutingScript() const | 402 bool HTMLDocumentParser::isExecutingScript() const |
| 611 { | 403 { |
| 612 return m_scriptRunner.isExecutingScript(); | 404 return m_scriptRunner.isExecutingScript(); |
| 613 } | 405 } |
| 614 | 406 |
| 615 OrdinalNumber HTMLDocumentParser::lineNumber() const | 407 OrdinalNumber HTMLDocumentParser::lineNumber() const |
| 616 { | 408 { |
| 617 if (m_haveBackgroundParser) | 409 ASSERT(m_haveBackgroundParser); |
| 618 return m_textPosition.m_line; | 410 return m_textPosition.m_line; |
| 619 | |
| 620 return m_input.current().currentLine(); | |
| 621 } | 411 } |
| 622 | 412 |
| 623 TextPosition HTMLDocumentParser::textPosition() const | 413 TextPosition HTMLDocumentParser::textPosition() const |
| 624 { | 414 { |
| 625 if (m_haveBackgroundParser) | 415 ASSERT(m_haveBackgroundParser); |
| 626 return m_textPosition; | 416 return m_textPosition; |
| 627 | |
| 628 const SegmentedString& currentString = m_input.current(); | |
| 629 OrdinalNumber line = currentString.currentLine(); | |
| 630 OrdinalNumber column = currentString.currentColumn(); | |
| 631 | |
| 632 return TextPosition(line, column); | |
| 633 } | 417 } |
| 634 | 418 |
| 635 bool HTMLDocumentParser::isWaitingForScripts() const | 419 bool HTMLDocumentParser::isWaitingForScripts() const |
| 636 { | 420 { |
| 637 return m_treeBuilder->hasParserBlockingScript() || m_scriptRunner.hasPending
Scripts(); | 421 return m_treeBuilder->hasParserBlockingScript() || m_scriptRunner.hasPending
Scripts(); |
| 638 } | 422 } |
| 639 | 423 |
| 640 void HTMLDocumentParser::resumeParsingAfterScriptExecution() | 424 void HTMLDocumentParser::resumeParsingAfterScriptExecution() |
| 641 { | 425 { |
| 642 ASSERT(!isExecutingScript()); | 426 ASSERT(!isExecutingScript()); |
| 643 ASSERT(!isWaitingForScripts()); | 427 ASSERT(!isWaitingForScripts()); |
| 428 ASSERT(m_haveBackgroundParser); |
| 644 | 429 |
| 645 if (m_haveBackgroundParser) { | 430 validateSpeculations(m_lastChunkBeforeScript.release()); |
| 646 validateSpeculations(m_lastChunkBeforeScript.release()); | 431 ASSERT(!m_lastChunkBeforeScript); |
| 647 ASSERT(!m_lastChunkBeforeScript); | 432 // processParsedChunkFromBackgroundParser can cause this parser to be detach
ed from the Document, |
| 648 // processParsedChunkFromBackgroundParser can cause this parser to be de
tached from the Document, | 433 // but we need to ensure it isn't deleted yet. |
| 649 // but we need to ensure it isn't deleted yet. | 434 RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this); |
| 650 RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this); | 435 pumpPendingSpeculations(); |
| 651 pumpPendingSpeculations(); | |
| 652 return; | |
| 653 } | |
| 654 | |
| 655 pumpTokenizerIfPossible(AllowYield); | |
| 656 endIfDelayed(); | |
| 657 } | 436 } |
| 658 | 437 |
| 659 void HTMLDocumentParser::executeScriptsWaitingForResources() | 438 void HTMLDocumentParser::executeScriptsWaitingForResources() |
| 660 { | 439 { |
| 661 if (!m_scriptRunner.hasPendingScripts()) | 440 if (!m_scriptRunner.hasPendingScripts()) |
| 662 return; | 441 return; |
| 663 RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this); | 442 RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this); |
| 664 m_scriptRunner.executePendingScripts(); | 443 m_scriptRunner.executePendingScripts(); |
| 665 if (!isWaitingForScripts()) | 444 if (!isWaitingForScripts()) |
| 666 resumeParsingAfterScriptExecution(); | 445 resumeParsingAfterScriptExecution(); |
| 667 } | 446 } |
| 668 | 447 |
| 669 void HTMLDocumentParser::appendBytes(const char* data, size_t length) | |
| 670 { | |
| 671 if (!length || isStopped()) | |
| 672 return; | |
| 673 | |
| 674 if (shouldUseThreading()) { | |
| 675 if (!m_haveBackgroundParser) | |
| 676 startBackgroundParser(); | |
| 677 | |
| 678 OwnPtr<Vector<char> > buffer = adoptPtr(new Vector<char>(length)); | |
| 679 memcpy(buffer->data(), data, length); | |
| 680 TRACE_EVENT1("net", "HTMLDocumentParser::appendBytes", "size", (unsigned
)length); | |
| 681 | |
| 682 HTMLParserThread::taskRunner()->PostTask(FROM_HERE, | |
| 683 base::Bind(&BackgroundHTMLParser::appendRawBytesFromMainThread, m_ba
ckgroundParser, buffer.release())); | |
| 684 return; | |
| 685 } | |
| 686 | |
| 687 DecodedDataDocumentParser::appendBytes(data, length); | |
| 688 } | 448 } |
| 689 | |
| 690 void HTMLDocumentParser::flush() | |
| 691 { | |
| 692 // If we've got no decoder, we never received any data. | |
| 693 if (isDetached()) | |
| 694 return; | |
| 695 | |
| 696 if (m_haveBackgroundParser) { | |
| 697 HTMLParserThread::taskRunner()->PostTask(FROM_HERE, | |
| 698 base::Bind(&BackgroundHTMLParser::flush, m_backgroundParser)); | |
| 699 } else { | |
| 700 DecodedDataDocumentParser::flush(); | |
| 701 } | |
| 702 } | |
| 703 | |
| 704 } | |
| OLD | NEW |