| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2013 Google, Inc. All Rights Reserved. | 2 * Copyright (C) 2013 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 24 matching lines...) Expand all Loading... |
| 35 #include "public/platform/Platform.h" | 35 #include "public/platform/Platform.h" |
| 36 #include "public/platform/WebTaskRunner.h" | 36 #include "public/platform/WebTaskRunner.h" |
| 37 #include "wtf/CurrentTime.h" | 37 #include "wtf/CurrentTime.h" |
| 38 #include "wtf/Functional.h" | 38 #include "wtf/Functional.h" |
| 39 #include "wtf/PtrUtil.h" | 39 #include "wtf/PtrUtil.h" |
| 40 #include "wtf/text/TextPosition.h" | 40 #include "wtf/text/TextPosition.h" |
| 41 #include <memory> | 41 #include <memory> |
| 42 | 42 |
| 43 namespace blink { | 43 namespace blink { |
| 44 | 44 |
| 45 // On a network with high latency and high bandwidth, using a device | 45 // On a network with high latency and high bandwidth, using a device with a fast |
| 46 // with a fast CPU, we could end up speculatively tokenizing | 46 // CPU, we could end up speculatively tokenizing the whole document, well ahead |
| 47 // the whole document, well ahead of when the main-thread actually needs it. | 47 // of when the main-thread actually needs it. This is a waste of memory (and |
| 48 // This is a waste of memory (and potentially time if the speculation fails). | 48 // potentially time if the speculation fails). So we limit our outstanding |
| 49 // So we limit our outstanding tokens arbitrarily to 10,000. | 49 // tokens arbitrarily to 10,000. Our maximal memory spent speculating will be |
| 50 // Our maximal memory spent speculating will be approximately: | 50 // approximately: |
| 51 // (defaultOutstandingTokenLimit + defaultPendingTokenLimit) * | 51 // (defaultOutstandingTokenLimit + defaultPendingTokenLimit) * |
| 52 // sizeof(CompactToken) | 52 // sizeof(CompactToken) |
| 53 // We use a separate low and high water mark to avoid constantly topping | 53 // |
| 54 // off the main thread's token buffer. | 54 // We use a separate low and high water mark to avoid |
| 55 // At time of writing, this is (10000 + 1000) * 28 bytes = ~308kb of memory. | 55 // constantly topping off the main thread's token buffer. At time of writing, |
| 56 // These numbers have not been tuned. | 56 // this is (10000 + 1000) * 28 bytes = ~308kb of memory. These numbers have not |
| 57 // been tuned. |
| 57 static const size_t defaultOutstandingTokenLimit = 10000; | 58 static const size_t defaultOutstandingTokenLimit = 10000; |
| 58 | 59 |
| 59 // We limit our chucks to 1000 tokens, to make sure the main | 60 // We limit our chucks to 1000 tokens, to make sure the main thread is never |
| 60 // thread is never waiting on the parser thread for tokens. | 61 // waiting on the parser thread for tokens. This was tuned in |
| 61 // This was tuned in https://bugs.webkit.org/show_bug.cgi?id=110408. | 62 // https://bugs.webkit.org/show_bug.cgi?id=110408. |
| 62 static const size_t defaultPendingTokenLimit = 1000; | 63 static const size_t defaultPendingTokenLimit = 1000; |
| 63 | 64 |
| 64 using namespace HTMLNames; | 65 using namespace HTMLNames; |
| 65 | 66 |
| 66 #if ENABLE(ASSERT) | 67 #if ENABLE(ASSERT) |
| 67 | 68 |
| 68 static void checkThatTokensAreSafeToSendToAnotherThread( | 69 static void checkThatTokensAreSafeToSendToAnotherThread( |
| 69 const CompactHTMLTokenStream* tokens) { | 70 const CompactHTMLTokenStream* tokens) { |
| 70 for (size_t i = 0; i < tokens->size(); ++i) | 71 for (size_t i = 0; i < tokens->size(); ++i) |
| 71 ASSERT(tokens->at(i).isSafeToSendToAnotherThread()); | 72 ASSERT(tokens->at(i).isSafeToSendToAnotherThread()); |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 188 m_input.rewindTo(checkpoint->inputCheckpoint, checkpoint->unparsedInput); | 189 m_input.rewindTo(checkpoint->inputCheckpoint, checkpoint->unparsedInput); |
| 189 m_preloadScanner->rewindTo(checkpoint->preloadScannerCheckpoint); | 190 m_preloadScanner->rewindTo(checkpoint->preloadScannerCheckpoint); |
| 190 m_startingScript = false; | 191 m_startingScript = false; |
| 191 m_tokenizedChunkQueue->clear(); | 192 m_tokenizedChunkQueue->clear(); |
| 192 m_lastBytesReceivedTime = monotonicallyIncreasingTimeMS(); | 193 m_lastBytesReceivedTime = monotonicallyIncreasingTimeMS(); |
| 193 pumpTokenizer(); | 194 pumpTokenizer(); |
| 194 } | 195 } |
| 195 | 196 |
| 196 void BackgroundHTMLParser::startedChunkWithCheckpoint( | 197 void BackgroundHTMLParser::startedChunkWithCheckpoint( |
| 197 HTMLInputCheckpoint inputCheckpoint) { | 198 HTMLInputCheckpoint inputCheckpoint) { |
| 198 // Note, we should not have to worry about the index being invalid | 199 // Note, we should not have to worry about the index being invalid as messages |
| 199 // as messages from the main thread will be processed in FIFO order. | 200 // from the main thread will be processed in FIFO order. |
| 200 m_input.invalidateCheckpointsBefore(inputCheckpoint); | 201 m_input.invalidateCheckpointsBefore(inputCheckpoint); |
| 201 pumpTokenizer(); | 202 pumpTokenizer(); |
| 202 } | 203 } |
| 203 | 204 |
| 204 void BackgroundHTMLParser::finish() { | 205 void BackgroundHTMLParser::finish() { |
| 205 markEndOfFile(); | 206 markEndOfFile(); |
| 206 pumpTokenizer(); | 207 pumpTokenizer(); |
| 207 } | 208 } |
| 208 | 209 |
| 209 void BackgroundHTMLParser::stop() { | 210 void BackgroundHTMLParser::stop() { |
| 210 delete this; | 211 delete this; |
| 211 } | 212 } |
| 212 | 213 |
| 213 void BackgroundHTMLParser::forcePlaintextForTextDocument() { | 214 void BackgroundHTMLParser::forcePlaintextForTextDocument() { |
| 214 // This is only used by the TextDocumentParser (a subclass of HTMLDocumentPars
er) | 215 // This is only used by the TextDocumentParser (a subclass of |
| 215 // to force us into the PLAINTEXT state w/o using a <plaintext> tag. | 216 // HTMLDocumentParser) to force us into the PLAINTEXT state w/o using a |
| 216 // The TextDocumentParser uses a <pre> tag for historical/compatibility reason
s. | 217 // <plaintext> tag. The TextDocumentParser uses a <pre> tag for historical / |
| 218 // compatibility reasons. |
| 217 m_tokenizer->setState(HTMLTokenizer::PLAINTEXTState); | 219 m_tokenizer->setState(HTMLTokenizer::PLAINTEXTState); |
| 218 } | 220 } |
| 219 | 221 |
| 220 void BackgroundHTMLParser::markEndOfFile() { | 222 void BackgroundHTMLParser::markEndOfFile() { |
| 221 ASSERT(!m_input.current().isClosed()); | 223 ASSERT(!m_input.current().isClosed()); |
| 222 m_input.append(String(&kEndOfFileMarker, 1)); | 224 m_input.append(String(&kEndOfFileMarker, 1)); |
| 223 m_input.close(); | 225 m_input.close(); |
| 224 } | 226 } |
| 225 | 227 |
| 226 void BackgroundHTMLParser::pumpTokenizer() { | 228 void BackgroundHTMLParser::pumpTokenizer() { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 261 | 263 |
| 262 bool shouldEvaluateForDocumentWrite = false; | 264 bool shouldEvaluateForDocumentWrite = false; |
| 263 bool isCSPMetaTag = false; | 265 bool isCSPMetaTag = false; |
| 264 m_preloadScanner->scan(token, m_input.current(), m_pendingPreloads, | 266 m_preloadScanner->scan(token, m_input.current(), m_pendingPreloads, |
| 265 &m_viewportDescription, &isCSPMetaTag, | 267 &m_viewportDescription, &isCSPMetaTag, |
| 266 &shouldEvaluateForDocumentWrite); | 268 &shouldEvaluateForDocumentWrite); |
| 267 | 269 |
| 268 simulatedToken = | 270 simulatedToken = |
| 269 m_treeBuilderSimulator.simulate(token, m_tokenizer.get()); | 271 m_treeBuilderSimulator.simulate(token, m_tokenizer.get()); |
| 270 | 272 |
| 271 // Break chunks before a script tag is inserted and flag the chunk as star
ting a script | 273 // Break chunks before a script tag is inserted and flag the chunk as |
| 272 // so the main parser can decide if it should yield before processing the
chunk. | 274 // starting a script so the main parser can decide if it should yield |
| 275 // before processing the chunk. |
| 273 if (simulatedToken == HTMLTreeBuilderSimulator::ScriptStart) { | 276 if (simulatedToken == HTMLTreeBuilderSimulator::ScriptStart) { |
| 274 shouldNotifyMainThread |= queueChunkForMainThread(); | 277 shouldNotifyMainThread |= queueChunkForMainThread(); |
| 275 m_startingScript = true; | 278 m_startingScript = true; |
| 276 } | 279 } |
| 277 | 280 |
| 278 m_pendingTokens->append(token); | 281 m_pendingTokens->append(token); |
| 279 if (isCSPMetaTag) { | 282 if (isCSPMetaTag) { |
| 280 m_pendingCSPMetaTokenIndex = m_pendingTokens->size() - 1; | 283 m_pendingCSPMetaTokenIndex = m_pendingTokens->size() - 1; |
| 281 } | 284 } |
| 282 if (shouldEvaluateForDocumentWrite) { | 285 if (shouldEvaluateForDocumentWrite) { |
| 283 m_likelyDocumentWriteScriptIndices.append(m_pendingTokens->size() - 1); | 286 m_likelyDocumentWriteScriptIndices.append(m_pendingTokens->size() - 1); |
| 284 } | 287 } |
| 285 } | 288 } |
| 286 | 289 |
| 287 m_token->clear(); | 290 m_token->clear(); |
| 288 | 291 |
| 289 if (simulatedToken == HTMLTreeBuilderSimulator::ScriptEnd || | 292 if (simulatedToken == HTMLTreeBuilderSimulator::ScriptEnd || |
| 290 m_pendingTokens->size() >= m_pendingTokenLimit) { | 293 m_pendingTokens->size() >= m_pendingTokenLimit) { |
| 291 shouldNotifyMainThread |= queueChunkForMainThread(); | 294 shouldNotifyMainThread |= queueChunkForMainThread(); |
| 292 // If we're far ahead of the main thread, yield for a bit to avoid consumi
ng too much memory. | 295 // If we're far ahead of the main thread, yield for a bit to avoid |
| 296 // consuming too much memory. |
| 293 if (m_input.totalCheckpointTokenCount() > m_outstandingTokenLimit) | 297 if (m_input.totalCheckpointTokenCount() > m_outstandingTokenLimit) |
| 294 break; | 298 break; |
| 295 } | 299 } |
| 296 | 300 |
| 297 if (!m_shouldCoalesceChunks && shouldNotifyMainThread) { | 301 if (!m_shouldCoalesceChunks && shouldNotifyMainThread) { |
| 298 runOnMainThread(&HTMLDocumentParser::notifyPendingTokenizedChunks, | 302 runOnMainThread(&HTMLDocumentParser::notifyPendingTokenizedChunks, |
| 299 m_parser); | 303 m_parser); |
| 300 shouldNotifyMainThread = false; | 304 shouldNotifyMainThread = false; |
| 301 } | 305 } |
| 302 } | 306 } |
| 303 // Wait to notify the main thread about the chunks until we're at the | 307 // Wait to notify the main thread about the chunks until we're at the limit. |
| 304 // limit. This lets the background parser generate lots of valuable | 308 // This lets the background parser generate lots of valuable preloads before |
| 305 // preloads before anything expensive (extensions, scripts) take up time | 309 // anything expensive (extensions, scripts) take up time on the main thread. A |
| 306 // on the main thread. A busy main thread can cause preload delays. | 310 // busy main thread can cause preload delays. |
| 307 if (shouldNotifyMainThread) { | 311 if (shouldNotifyMainThread) { |
| 308 runOnMainThread(&HTMLDocumentParser::notifyPendingTokenizedChunks, | 312 runOnMainThread(&HTMLDocumentParser::notifyPendingTokenizedChunks, |
| 309 m_parser); | 313 m_parser); |
| 310 } | 314 } |
| 311 } | 315 } |
| 312 | 316 |
| 313 bool BackgroundHTMLParser::queueChunkForMainThread() { | 317 bool BackgroundHTMLParser::queueChunkForMainThread() { |
| 314 if (m_pendingTokens->isEmpty()) | 318 if (m_pendingTokens->isEmpty()) |
| 315 return false; | 319 return false; |
| 316 | 320 |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 372 if (isMainThread()) { | 376 if (isMainThread()) { |
| 373 (*WTF::bind(function, std::forward<Ps>(parameters)...))(); | 377 (*WTF::bind(function, std::forward<Ps>(parameters)...))(); |
| 374 } else { | 378 } else { |
| 375 m_loadingTaskRunner->postTask( | 379 m_loadingTaskRunner->postTask( |
| 376 BLINK_FROM_HERE, | 380 BLINK_FROM_HERE, |
| 377 crossThreadBind(function, std::forward<Ps>(parameters)...)); | 381 crossThreadBind(function, std::forward<Ps>(parameters)...)); |
| 378 } | 382 } |
| 379 } | 383 } |
| 380 | 384 |
| 381 } // namespace blink | 385 } // namespace blink |
| OLD | NEW |