| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "config.h" | 5 #include "config.h" |
| 6 #include "bindings/core/v8/ScriptStreamer.h" | 6 #include "bindings/core/v8/ScriptStreamer.h" |
| 7 | 7 |
| 8 #include "bindings/core/v8/ScriptStreamerThread.h" | 8 #include "bindings/core/v8/ScriptStreamerThread.h" |
| 9 #include "bindings/core/v8/V8ScriptRunner.h" | 9 #include "bindings/core/v8/V8ScriptRunner.h" |
| 10 #include "core/dom/Document.h" | 10 #include "core/dom/Document.h" |
| 11 #include "core/dom/Element.h" | 11 #include "core/dom/Element.h" |
| 12 #include "core/dom/PendingScript.h" | 12 #include "core/dom/PendingScript.h" |
| 13 #include "core/fetch/ScriptResource.h" | 13 #include "core/fetch/ScriptResource.h" |
| 14 #include "core/frame/Settings.h" | 14 #include "core/frame/Settings.h" |
| 15 #include "core/html/parser/TextResourceDecoder.h" |
| 15 #include "platform/SharedBuffer.h" | 16 #include "platform/SharedBuffer.h" |
| 16 #include "platform/TraceEvent.h" | 17 #include "platform/TraceEvent.h" |
| 17 #include "public/platform/Platform.h" | 18 #include "public/platform/Platform.h" |
| 18 #include "wtf/MainThread.h" | 19 #include "wtf/MainThread.h" |
| 19 #include "wtf/text/TextEncodingRegistry.h" | 20 #include "wtf/text/TextEncodingRegistry.h" |
| 20 | 21 |
| 21 namespace blink { | 22 namespace blink { |
| 22 | 23 |
| 23 // For passing data between the main thread (producer) and the streamer thread | 24 // For passing data between the main thread (producer) and the streamer thread |
| 24 // (consumer). The main thread prepares the data (copies it from Resource) and | 25 // (consumer). The main thread prepares the data (copies it from Resource) and |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 116 } | 117 } |
| 117 return length; | 118 return length; |
| 118 } | 119 } |
| 119 | 120 |
| 120 void didFinishLoading() | 121 void didFinishLoading() |
| 121 { | 122 { |
| 122 ASSERT(isMainThread()); | 123 ASSERT(isMainThread()); |
| 123 m_dataQueue.finish(); | 124 m_dataQueue.finish(); |
| 124 } | 125 } |
| 125 | 126 |
| 126 void didReceiveData() | 127 void didReceiveData(size_t lengthOfBOM) |
| 127 { | 128 { |
| 128 ASSERT(isMainThread()); | 129 ASSERT(isMainThread()); |
| 129 prepareDataOnMainThread(); | 130 prepareDataOnMainThread(lengthOfBOM); |
| 130 } | 131 } |
| 131 | 132 |
| 132 void cancel() | 133 void cancel() |
| 133 { | 134 { |
| 134 ASSERT(isMainThread()); | 135 ASSERT(isMainThread()); |
| 135 // The script is no longer needed by the upper layers. Stop streaming | 136 // The script is no longer needed by the upper layers. Stop streaming |
| 136 // it. The next time GetMoreData is called (or woken up), it will return | 137 // it. The next time GetMoreData is called (or woken up), it will return |
| 137 // 0, which will be interpreted as EOS by V8 and the parsing will | 138 // 0, which will be interpreted as EOS by V8 and the parsing will |
| 138 // fail. ScriptStreamer::streamingComplete will be called, and at that | 139 // fail. ScriptStreamer::streamingComplete will be called, and at that |
| 139 // point we will release the references to SourceStream. | 140 // point we will release the references to SourceStream. |
| 140 { | 141 { |
| 141 MutexLocker locker(m_mutex); | 142 MutexLocker locker(m_mutex); |
| 142 m_cancelled = true; | 143 m_cancelled = true; |
| 143 } | 144 } |
| 144 m_dataQueue.finish(); | 145 m_dataQueue.finish(); |
| 145 } | 146 } |
| 146 | 147 |
| 147 private: | 148 private: |
| 148 void prepareDataOnMainThread() | 149 void prepareDataOnMainThread(size_t lengthOfBOM) |
| 149 { | 150 { |
| 150 ASSERT(isMainThread()); | 151 ASSERT(isMainThread()); |
| 151 // The Resource must still be alive; otherwise we should've cancelled | 152 // The Resource must still be alive; otherwise we should've cancelled |
| 152 // the streaming (if we have cancelled, the background thread is not | 153 // the streaming (if we have cancelled, the background thread is not |
| 153 // waiting). | 154 // waiting). |
| 154 ASSERT(m_streamer->resource()); | 155 ASSERT(m_streamer->resource()); |
| 155 | 156 |
| 157 // BOM can only occur at the beginning of the data. |
| 158 ASSERT(lengthOfBOM == 0 || m_dataPosition == 0); |
| 159 |
| 156 if (m_streamer->resource()->cachedMetadata(V8ScriptRunner::tagForCodeCac
he())) { | 160 if (m_streamer->resource()->cachedMetadata(V8ScriptRunner::tagForCodeCac
he())) { |
| 157 // The resource has a code cache, so it's unnecessary to stream and | 161 // The resource has a code cache, so it's unnecessary to stream and |
| 158 // parse the code. Cancel the streaming and resume the non-streaming | 162 // parse the code. Cancel the streaming and resume the non-streaming |
| 159 // code path. | 163 // code path. |
| 160 m_streamer->suppressStreaming(); | 164 m_streamer->suppressStreaming(); |
| 161 { | 165 { |
| 162 MutexLocker locker(m_mutex); | 166 MutexLocker locker(m_mutex); |
| 163 m_cancelled = true; | 167 m_cancelled = true; |
| 164 } | 168 } |
| 165 m_dataQueue.finish(); | 169 m_dataQueue.finish(); |
| 166 return; | 170 return; |
| 167 } | 171 } |
| 168 | 172 |
| 169 if (!m_resourceBuffer) { | 173 if (!m_resourceBuffer) { |
| 170 // We don't have a buffer yet. Try to get it from the resource. | 174 // We don't have a buffer yet. Try to get it from the resource. |
| 171 SharedBuffer* buffer = m_streamer->resource()->resourceBuffer(); | 175 SharedBuffer* buffer = m_streamer->resource()->resourceBuffer(); |
| 172 if (!buffer) | |
| 173 return; | |
| 174 m_resourceBuffer = RefPtr<SharedBuffer>(buffer); | 176 m_resourceBuffer = RefPtr<SharedBuffer>(buffer); |
| 175 } | 177 } |
| 176 | 178 |
| 177 // Get as much data from the ResourceBuffer as we can. | 179 // Get as much data from the ResourceBuffer as we can. |
| 178 const char* data = 0; | 180 const char* data = 0; |
| 179 Vector<const char*> chunks; | 181 Vector<const char*> chunks; |
| 180 Vector<unsigned> chunkLengths; | 182 Vector<unsigned> chunkLengths; |
| 181 size_t dataLength = 0; | 183 size_t dataLength = 0; |
| 182 while (unsigned length = m_resourceBuffer->getSomeData(data, m_dataPosit
ion)) { | 184 while (unsigned length = m_resourceBuffer->getSomeData(data, m_dataPosit
ion)) { |
| 183 // FIXME: Here we can limit based on the total length, if it turns | 185 // FIXME: Here we can limit based on the total length, if it turns |
| 184 // out that we don't want to give all the data we have (memory | 186 // out that we don't want to give all the data we have (memory |
| 185 // vs. speed). | 187 // vs. speed). |
| 186 chunks.append(data); | 188 chunks.append(data); |
| 187 chunkLengths.append(length); | 189 chunkLengths.append(length); |
| 188 dataLength += length; | 190 dataLength += length; |
| 189 m_dataPosition += length; | 191 m_dataPosition += length; |
| 190 } | 192 } |
| 191 // Copy the data chunks into a new buffer, since we're going to give the | 193 // Copy the data chunks into a new buffer, since we're going to give the |
| 192 // data to a background thread. | 194 // data to a background thread. |
| 193 if (dataLength > 0) { | 195 if (dataLength > lengthOfBOM) { |
| 196 dataLength -= lengthOfBOM; |
| 194 uint8_t* copiedData = new uint8_t[dataLength]; | 197 uint8_t* copiedData = new uint8_t[dataLength]; |
| 195 unsigned offset = 0; | 198 unsigned offset = 0; |
| 196 for (size_t i = 0; i < chunks.size(); ++i) { | 199 for (size_t i = 0; i < chunks.size(); ++i) { |
| 197 memcpy(copiedData + offset, chunks[i], chunkLengths[i]); | 200 memcpy(copiedData + offset, chunks[i] + lengthOfBOM, chunkLength
s[i] - lengthOfBOM); |
| 198 offset += chunkLengths[i]; | 201 offset += chunkLengths[i] - lengthOfBOM; |
| 202 // BOM is only in the first chunk |
| 203 lengthOfBOM = 0; |
| 199 } | 204 } |
| 200 m_dataQueue.produce(copiedData, dataLength); | 205 m_dataQueue.produce(copiedData, dataLength); |
| 201 } | 206 } |
| 202 } | 207 } |
| 203 | 208 |
| 204 ScriptStreamer* m_streamer; | 209 ScriptStreamer* m_streamer; |
| 205 | 210 |
| 206 // For coordinating between the main thread and background thread tasks. | 211 // For coordinating between the main thread and background thread tasks. |
| 207 // Guarded by m_mutex. | 212 // Guarded by m_mutex. |
| 208 bool m_cancelled; | 213 bool m_cancelled; |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 279 | 284 |
| 280 void ScriptStreamer::notifyAppendData(ScriptResource* resource) | 285 void ScriptStreamer::notifyAppendData(ScriptResource* resource) |
| 281 { | 286 { |
| 282 ASSERT(isMainThread()); | 287 ASSERT(isMainThread()); |
| 283 ASSERT(m_resource == resource); | 288 ASSERT(m_resource == resource); |
| 284 { | 289 { |
| 285 MutexLocker locker(m_mutex); | 290 MutexLocker locker(m_mutex); |
| 286 if (m_streamingSuppressed) | 291 if (m_streamingSuppressed) |
| 287 return; | 292 return; |
| 288 } | 293 } |
| 294 size_t lengthOfBOM = 0; |
| 289 if (!m_haveEnoughDataForStreaming) { | 295 if (!m_haveEnoughDataForStreaming) { |
| 290 // Even if the first data chunk is small, the script can still be big | 296 // Even if the first data chunk is small, the script can still be big |
| 291 // enough - wait until the next data chunk comes before deciding whether | 297 // enough - wait until the next data chunk comes before deciding whether |
| 292 // to start the streaming. | 298 // to start the streaming. |
| 293 if (resource->resourceBuffer()->size() < kSmallScriptThreshold) { | 299 ASSERT(resource->resourceBuffer()); |
| 300 if (resource->resourceBuffer()->size() < kSmallScriptThreshold) |
| 294 return; | 301 return; |
| 295 } | |
| 296 m_haveEnoughDataForStreaming = true; | 302 m_haveEnoughDataForStreaming = true; |
| 297 const char* histogramName = startedStreamingHistogramName(m_scriptType); | 303 const char* histogramName = startedStreamingHistogramName(m_scriptType); |
| 298 | 304 |
| 299 // Encoding should be detected only when we have some data. It's | 305 // Encoding should be detected only when we have some data. It's |
| 300 // possible that resource->encoding() returns a different encoding | 306 // possible that resource->encoding() returns a different encoding |
| 301 // before the loading has started and after we got some data. | 307 // before the loading has started and after we got some data. In |
| 302 WTF::TextEncoding textEncoding(resource->encoding()); | 308 // addition, check for byte order marks. Note that checking the byte |
| 303 const char* encodingName = textEncoding.name(); | 309 // order mark might change the encoding. We cannot decode the full text |
| 310 // here, because it might contain incomplete UTF-8 characters. Also note |
| 311 // that have at least kSmallScriptThreshold worth of data, which is more |
| 312 // than enough for detecting a BOM. |
| 313 const char* data = 0; |
| 314 unsigned length = resource->resourceBuffer()->getSomeData(data, 0); |
| 315 |
| 316 OwnPtr<TextResourceDecoder> decoder(TextResourceDecoder::create("applica
tion/javascript", resource->encoding())); |
| 317 lengthOfBOM = decoder->checkForBOM(data, length); |
| 318 |
| 319 // Maybe the encoding changed because we saw the BOM; get the encoding |
| 320 // from the decoder. |
| 321 const char* encodingName = decoder->encoding().name(); |
| 304 | 322 |
| 305 // Here's a list of encodings we can use for streaming. These are | 323 // Here's a list of encodings we can use for streaming. These are |
| 306 // the canonical names. | 324 // the canonical names. |
| 307 v8::ScriptCompiler::StreamedSource::Encoding encoding; | 325 v8::ScriptCompiler::StreamedSource::Encoding encoding; |
| 308 if (strcmp(encodingName, "windows-1252") == 0 | 326 if (strcmp(encodingName, "windows-1252") == 0 |
| 309 || strcmp(encodingName, "ISO-8859-1") == 0 | 327 || strcmp(encodingName, "ISO-8859-1") == 0 |
| 310 || strcmp(encodingName, "US-ASCII") == 0) { | 328 || strcmp(encodingName, "US-ASCII") == 0) { |
| 311 encoding = v8::ScriptCompiler::StreamedSource::ONE_BYTE; | 329 encoding = v8::ScriptCompiler::StreamedSource::ONE_BYTE; |
| 312 } else if (strcmp(encodingName, "UTF-8") == 0) { | 330 } else if (strcmp(encodingName, "UTF-8") == 0) { |
| 313 encoding = v8::ScriptCompiler::StreamedSource::UTF8; | 331 encoding = v8::ScriptCompiler::StreamedSource::UTF8; |
| 314 } else { | 332 } else { |
| 315 // We don't stream other encodings; especially we don't stream two | 333 // We don't stream other encodings; especially we don't stream two |
| 316 // byte scripts to avoid the handling of byte order marks. Most | 334 // byte scripts to avoid the handling of endianness. Most scripts |
| 317 // scripts are Latin1 or UTF-8 anyway, so this should be enough for | 335 // are Latin1 or UTF-8 anyway, so this should be enough for most |
| 318 // most real world purposes. | 336 // real world purposes. |
| 319 suppressStreaming(); | 337 suppressStreaming(); |
| 320 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2
); | 338 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2
); |
| 321 return; | 339 return; |
| 322 } | 340 } |
| 323 if (ScriptStreamerThread::shared()->isRunningTask()) { | 341 if (ScriptStreamerThread::shared()->isRunningTask()) { |
| 324 // At the moment we only have one thread for running the tasks. A | 342 // At the moment we only have one thread for running the tasks. A |
| 325 // new task shouldn't be queued before the running task completes, | 343 // new task shouldn't be queued before the running task completes, |
| 326 // because the running task can block and wait for data from the | 344 // because the running task can block and wait for data from the |
| 327 // network. | 345 // network. |
| 328 suppressStreaming(); | 346 suppressStreaming(); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 356 // ScriptStreamer needs to stay alive as long as the background task is | 374 // ScriptStreamer needs to stay alive as long as the background task is |
| 357 // running. This is taken care of with a manual ref() & deref() pair; | 375 // running. This is taken care of with a manual ref() & deref() pair; |
| 358 // the corresponding deref() is in streamingComplete or in | 376 // the corresponding deref() is in streamingComplete or in |
| 359 // notifyFinished. | 377 // notifyFinished. |
| 360 ref(); | 378 ref(); |
| 361 ScriptStreamingTask* task = new ScriptStreamingTask(scriptStreamingTask.
release(), this); | 379 ScriptStreamingTask* task = new ScriptStreamingTask(scriptStreamingTask.
release(), this); |
| 362 ScriptStreamerThread::shared()->postTask(task); | 380 ScriptStreamerThread::shared()->postTask(task); |
| 363 blink::Platform::current()->histogramEnumeration(histogramName, 1, 2); | 381 blink::Platform::current()->histogramEnumeration(histogramName, 1, 2); |
| 364 } | 382 } |
| 365 if (m_stream) | 383 if (m_stream) |
| 366 m_stream->didReceiveData(); | 384 m_stream->didReceiveData(lengthOfBOM); |
| 367 } | 385 } |
| 368 | 386 |
| 369 void ScriptStreamer::notifyFinished(Resource* resource) | 387 void ScriptStreamer::notifyFinished(Resource* resource) |
| 370 { | 388 { |
| 371 ASSERT(isMainThread()); | 389 ASSERT(isMainThread()); |
| 372 ASSERT(m_resource == resource); | 390 ASSERT(m_resource == resource); |
| 373 // A special case: empty and small scripts. We didn't receive enough data to | 391 // A special case: empty and small scripts. We didn't receive enough data to |
| 374 // start the streaming before this notification. In that case, there won't | 392 // start the streaming before this notification. In that case, there won't |
| 375 // be a "parsing complete" notification either, and we should not wait for | 393 // be a "parsing complete" notification either, and we should not wait for |
| 376 // it. | 394 // it. |
| (...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 532 | 550 |
| 533 // The Resource might go out of scope if the script is no longer | 551 // The Resource might go out of scope if the script is no longer |
| 534 // needed. This makes PendingScript notify the ScriptStreamer when it is | 552 // needed. This makes PendingScript notify the ScriptStreamer when it is |
| 535 // destroyed. | 553 // destroyed. |
| 536 script.setStreamer(adoptRef(new ScriptStreamer(resource, scriptType, setting
s->v8ScriptStreamingMode(), scriptState, compileOption))); | 554 script.setStreamer(adoptRef(new ScriptStreamer(resource, scriptType, setting
s->v8ScriptStreamingMode(), scriptState, compileOption))); |
| 537 | 555 |
| 538 return true; | 556 return true; |
| 539 } | 557 } |
| 540 | 558 |
| 541 } // namespace blink | 559 } // namespace blink |
| OLD | NEW |