| 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" |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 82 }; | 82 }; |
| 83 | 83 |
| 84 | 84 |
| 85 // SourceStream implements the streaming interface towards V8. The main | 85 // SourceStream implements the streaming interface towards V8. The main |
| 86 // functionality is preparing the data to give to V8 on main thread, and | 86 // functionality is preparing the data to give to V8 on main thread, and |
| 87 // actually giving the data (via GetMoreData which is called on a background | 87 // actually giving the data (via GetMoreData which is called on a background |
| 88 // thread). | 88 // thread). |
| 89 class SourceStream : public v8::ScriptCompiler::ExternalSourceStream { | 89 class SourceStream : public v8::ScriptCompiler::ExternalSourceStream { |
| 90 WTF_MAKE_NONCOPYABLE(SourceStream); | 90 WTF_MAKE_NONCOPYABLE(SourceStream); |
| 91 public: | 91 public: |
| 92 SourceStream(ScriptStreamer* streamer) | 92 SourceStream() |
| 93 : v8::ScriptCompiler::ExternalSourceStream() | 93 : v8::ScriptCompiler::ExternalSourceStream() |
| 94 , m_streamer(streamer) | |
| 95 , m_cancelled(false) | 94 , m_cancelled(false) |
| 96 , m_dataPosition(0) { } | 95 , m_dataPosition(0) { } |
| 97 | 96 |
| 98 virtual ~SourceStream() { } | 97 virtual ~SourceStream() { } |
| 99 | 98 |
| 100 // Called by V8 on a background thread. Should block until we can return | 99 // Called by V8 on a background thread. Should block until we can return |
| 101 // some data. | 100 // some data. |
| 102 virtual size_t GetMoreData(const uint8_t** src) override | 101 virtual size_t GetMoreData(const uint8_t** src) override |
| 103 { | 102 { |
| 104 ASSERT(!isMainThread()); | 103 ASSERT(!isMainThread()); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 117 } | 116 } |
| 118 return length; | 117 return length; |
| 119 } | 118 } |
| 120 | 119 |
| 121 void didFinishLoading() | 120 void didFinishLoading() |
| 122 { | 121 { |
| 123 ASSERT(isMainThread()); | 122 ASSERT(isMainThread()); |
| 124 m_dataQueue.finish(); | 123 m_dataQueue.finish(); |
| 125 } | 124 } |
| 126 | 125 |
| 127 void didReceiveData(size_t lengthOfBOM) | 126 void didReceiveData(ScriptStreamer* streamer, size_t lengthOfBOM) |
| 128 { | 127 { |
| 129 ASSERT(isMainThread()); | 128 ASSERT(isMainThread()); |
| 130 prepareDataOnMainThread(lengthOfBOM); | 129 prepareDataOnMainThread(streamer, lengthOfBOM); |
| 131 } | 130 } |
| 132 | 131 |
| 133 void cancel() | 132 void cancel() |
| 134 { | 133 { |
| 135 ASSERT(isMainThread()); | 134 ASSERT(isMainThread()); |
| 136 // The script is no longer needed by the upper layers. Stop streaming | 135 // The script is no longer needed by the upper layers. Stop streaming |
| 137 // it. The next time GetMoreData is called (or woken up), it will return | 136 // it. The next time GetMoreData is called (or woken up), it will return |
| 138 // 0, which will be interpreted as EOS by V8 and the parsing will | 137 // 0, which will be interpreted as EOS by V8 and the parsing will |
| 139 // fail. ScriptStreamer::streamingComplete will be called, and at that | 138 // fail. ScriptStreamer::streamingComplete will be called, and at that |
| 140 // point we will release the references to SourceStream. | 139 // point we will release the references to SourceStream. |
| 141 { | 140 { |
| 142 MutexLocker locker(m_mutex); | 141 MutexLocker locker(m_mutex); |
| 143 m_cancelled = true; | 142 m_cancelled = true; |
| 144 } | 143 } |
| 145 m_dataQueue.finish(); | 144 m_dataQueue.finish(); |
| 146 } | 145 } |
| 147 | 146 |
| 148 private: | 147 private: |
| 149 void prepareDataOnMainThread(size_t lengthOfBOM) | 148 void prepareDataOnMainThread(ScriptStreamer* streamer, size_t lengthOfBOM) |
| 150 { | 149 { |
| 151 ASSERT(isMainThread()); | 150 ASSERT(isMainThread()); |
| 152 // The Resource must still be alive; otherwise we should've cancelled | 151 // The Resource must still be alive; otherwise we should've cancelled |
| 153 // the streaming (if we have cancelled, the background thread is not | 152 // the streaming (if we have cancelled, the background thread is not |
| 154 // waiting). | 153 // waiting). |
| 155 ASSERT(m_streamer->resource()); | 154 ASSERT(streamer->resource()); |
| 156 | 155 |
| 157 // BOM can only occur at the beginning of the data. | 156 // BOM can only occur at the beginning of the data. |
| 158 ASSERT(lengthOfBOM == 0 || m_dataPosition == 0); | 157 ASSERT(lengthOfBOM == 0 || m_dataPosition == 0); |
| 159 | 158 |
| 160 if (m_streamer->resource()->cachedMetadata(V8ScriptRunner::tagForCodeCac
he(m_streamer->resource()))) { | 159 if (streamer->resource()->cachedMetadata(V8ScriptRunner::tagForCodeCache
(streamer->resource()))) { |
| 161 // The resource has a code cache, so it's unnecessary to stream and | 160 // The resource has a code cache, so it's unnecessary to stream and |
| 162 // parse the code. Cancel the streaming and resume the non-streaming | 161 // parse the code. Cancel the streaming and resume the non-streaming |
| 163 // code path. | 162 // code path. |
| 164 m_streamer->suppressStreaming(); | 163 streamer->suppressStreaming(); |
| 165 { | 164 { |
| 166 MutexLocker locker(m_mutex); | 165 MutexLocker locker(m_mutex); |
| 167 m_cancelled = true; | 166 m_cancelled = true; |
| 168 } | 167 } |
| 169 m_dataQueue.finish(); | 168 m_dataQueue.finish(); |
| 170 return; | 169 return; |
| 171 } | 170 } |
| 172 | 171 |
| 173 if (!m_resourceBuffer) { | 172 if (!m_resourceBuffer) { |
| 174 // We don't have a buffer yet. Try to get it from the resource. | 173 // We don't have a buffer yet. Try to get it from the resource. |
| 175 SharedBuffer* buffer = m_streamer->resource()->resourceBuffer(); | 174 SharedBuffer* buffer = streamer->resource()->resourceBuffer(); |
| 176 m_resourceBuffer = RefPtr<SharedBuffer>(buffer); | 175 m_resourceBuffer = RefPtr<SharedBuffer>(buffer); |
| 177 } | 176 } |
| 178 | 177 |
| 179 // Get as much data from the ResourceBuffer as we can. | 178 // Get as much data from the ResourceBuffer as we can. |
| 180 const char* data = 0; | 179 const char* data = 0; |
| 181 Vector<const char*> chunks; | 180 Vector<const char*> chunks; |
| 182 Vector<unsigned> chunkLengths; | 181 Vector<unsigned> chunkLengths; |
| 183 size_t dataLength = 0; | 182 size_t dataLength = 0; |
| 184 while (unsigned length = m_resourceBuffer->getSomeData(data, m_dataPosit
ion)) { | 183 while (unsigned length = m_resourceBuffer->getSomeData(data, m_dataPosit
ion)) { |
| 185 // FIXME: Here we can limit based on the total length, if it turns | 184 // FIXME: Here we can limit based on the total length, if it turns |
| (...skipping 13 matching lines...) Expand all Loading... |
| 199 for (size_t i = 0; i < chunks.size(); ++i) { | 198 for (size_t i = 0; i < chunks.size(); ++i) { |
| 200 memcpy(copiedData + offset, chunks[i] + lengthOfBOM, chunkLength
s[i] - lengthOfBOM); | 199 memcpy(copiedData + offset, chunks[i] + lengthOfBOM, chunkLength
s[i] - lengthOfBOM); |
| 201 offset += chunkLengths[i] - lengthOfBOM; | 200 offset += chunkLengths[i] - lengthOfBOM; |
| 202 // BOM is only in the first chunk | 201 // BOM is only in the first chunk |
| 203 lengthOfBOM = 0; | 202 lengthOfBOM = 0; |
| 204 } | 203 } |
| 205 m_dataQueue.produce(copiedData, dataLength); | 204 m_dataQueue.produce(copiedData, dataLength); |
| 206 } | 205 } |
| 207 } | 206 } |
| 208 | 207 |
| 209 ScriptStreamer* m_streamer; | |
| 210 | |
| 211 // For coordinating between the main thread and background thread tasks. | 208 // For coordinating between the main thread and background thread tasks. |
| 212 // Guarded by m_mutex. | 209 // Guarded by m_mutex. |
| 213 bool m_cancelled; | 210 bool m_cancelled; |
| 214 Mutex m_mutex; | 211 Mutex m_mutex; |
| 215 | 212 |
| 216 unsigned m_dataPosition; // Only used by the main thread. | 213 unsigned m_dataPosition; // Only used by the main thread. |
| 217 RefPtr<SharedBuffer> m_resourceBuffer; // Only used by the main thread. | 214 RefPtr<SharedBuffer> m_resourceBuffer; // Only used by the main thread. |
| 218 SourceStreamDataQueue m_dataQueue; // Thread safe. | 215 SourceStreamDataQueue m_dataQueue; // Thread safe. |
| 219 }; | 216 }; |
| 220 | 217 |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 344 } | 341 } |
| 345 | 342 |
| 346 if (!m_scriptState->contextIsValid()) { | 343 if (!m_scriptState->contextIsValid()) { |
| 347 suppressStreaming(); | 344 suppressStreaming(); |
| 348 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2
); | 345 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2
); |
| 349 return; | 346 return; |
| 350 } | 347 } |
| 351 | 348 |
| 352 ASSERT(!m_stream); | 349 ASSERT(!m_stream); |
| 353 ASSERT(!m_source); | 350 ASSERT(!m_source); |
| 354 m_stream = new SourceStream(this); | 351 m_stream = new SourceStream(); |
| 355 // m_source takes ownership of m_stream. | 352 // m_source takes ownership of m_stream. |
| 356 m_source = adoptPtr(new v8::ScriptCompiler::StreamedSource(m_stream, m_e
ncoding)); | 353 m_source = adoptPtr(new v8::ScriptCompiler::StreamedSource(m_stream, m_e
ncoding)); |
| 357 | 354 |
| 358 ScriptState::Scope scope(m_scriptState.get()); | 355 ScriptState::Scope scope(m_scriptState.get()); |
| 359 WTF::OwnPtr<v8::ScriptCompiler::ScriptStreamingTask> scriptStreamingTask
(adoptPtr(v8::ScriptCompiler::StartStreamingScript(m_scriptState->isolate(), m_s
ource.get(), m_compileOptions))); | 356 WTF::OwnPtr<v8::ScriptCompiler::ScriptStreamingTask> scriptStreamingTask
(adoptPtr(v8::ScriptCompiler::StartStreamingScript(m_scriptState->isolate(), m_s
ource.get(), m_compileOptions))); |
| 360 if (!scriptStreamingTask) { | 357 if (!scriptStreamingTask) { |
| 361 // V8 cannot stream the script. | 358 // V8 cannot stream the script. |
| 362 suppressStreaming(); | 359 suppressStreaming(); |
| 363 m_stream = 0; | 360 m_stream = 0; |
| 364 m_source.clear(); | 361 m_source.clear(); |
| 365 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2
); | 362 blink::Platform::current()->histogramEnumeration(histogramName, 0, 2
); |
| 366 return; | 363 return; |
| 367 } | 364 } |
| 368 | 365 |
| 369 // ScriptStreamer needs to stay alive as long as the background task is | 366 // ScriptStreamer needs to stay alive as long as the background task is |
| 370 // running. This is taken care of with a manual ref() & deref() pair; | 367 // running. This is taken care of with a manual ref() & deref() pair; |
| 371 // the corresponding deref() is in streamingComplete or in | 368 // the corresponding deref() is in streamingComplete or in |
| 372 // notifyFinished. | 369 // notifyFinished. |
| 373 ref(); | 370 ref(); |
| 374 ScriptStreamingTask* task = new ScriptStreamingTask(scriptStreamingTask.
release(), this); | 371 ScriptStreamingTask* task = new ScriptStreamingTask(scriptStreamingTask.
release(), this); |
| 375 ScriptStreamerThread::shared()->postTask(task); | 372 ScriptStreamerThread::shared()->postTask(task); |
| 376 blink::Platform::current()->histogramEnumeration(histogramName, 1, 2); | 373 blink::Platform::current()->histogramEnumeration(histogramName, 1, 2); |
| 377 } | 374 } |
| 378 if (m_stream) | 375 if (m_stream) |
| 379 m_stream->didReceiveData(lengthOfBOM); | 376 m_stream->didReceiveData(this, lengthOfBOM); |
| 380 } | 377 } |
| 381 | 378 |
| 382 void ScriptStreamer::notifyFinished(Resource* resource) | 379 void ScriptStreamer::notifyFinished(Resource* resource) |
| 383 { | 380 { |
| 384 ASSERT(isMainThread()); | 381 ASSERT(isMainThread()); |
| 385 ASSERT(m_resource == resource); | 382 ASSERT(m_resource == resource); |
| 386 // A special case: empty and small scripts. We didn't receive enough data to | 383 // A special case: empty and small scripts. We didn't receive enough data to |
| 387 // start the streaming before this notification. In that case, there won't | 384 // start the streaming before this notification. In that case, there won't |
| 388 // be a "parsing complete" notification either, and we should not wait for | 385 // be a "parsing complete" notification either, and we should not wait for |
| 389 // it. | 386 // it. |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 546 | 543 |
| 547 // The Resource might go out of scope if the script is no longer | 544 // The Resource might go out of scope if the script is no longer |
| 548 // needed. This makes PendingScript notify the ScriptStreamer when it is | 545 // needed. This makes PendingScript notify the ScriptStreamer when it is |
| 549 // destroyed. | 546 // destroyed. |
| 550 script.setStreamer(adoptRef(new ScriptStreamer(resource, scriptType, setting
s->v8ScriptStreamingMode(), scriptState, compileOption))); | 547 script.setStreamer(adoptRef(new ScriptStreamer(resource, scriptType, setting
s->v8ScriptStreamingMode(), scriptState, compileOption))); |
| 551 | 548 |
| 552 return true; | 549 return true; |
| 553 } | 550 } |
| 554 | 551 |
| 555 } // namespace blink | 552 } // namespace blink |
| OLD | NEW |