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 |