Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(545)

Side by Side Diff: third_party/WebKit/Source/bindings/core/v8/ScriptStreamer.cpp

Issue 2351943003: Adapt ScriptStreamer to recent changes in v8 streaming. (Closed)
Patch Set: WTF::Mutex::locked is #ifdef-ed on ASSERT, so keep that one ASSERT around. Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 "bindings/core/v8/ScriptStreamer.h" 5 #include "bindings/core/v8/ScriptStreamer.h"
6 6
7 #include "bindings/core/v8/ScriptStreamerThread.h" 7 #include "bindings/core/v8/ScriptStreamerThread.h"
8 #include "bindings/core/v8/V8ScriptRunner.h" 8 #include "bindings/core/v8/V8ScriptRunner.h"
9 #include "core/dom/Document.h" 9 #include "core/dom/Document.h"
10 #include "core/dom/Element.h" 10 #include "core/dom/Element.h"
(...skipping 28 matching lines...) Expand all
39 DEFINE_STATIC_LOCAL(EnumerationHistogram, deferredHistogram, ("WebCore.S cripts.Deferred.StartedStreaming", 2)); 39 DEFINE_STATIC_LOCAL(EnumerationHistogram, deferredHistogram, ("WebCore.S cripts.Deferred.StartedStreaming", 2));
40 deferredHistogram.count(reason); 40 deferredHistogram.count(reason);
41 break; 41 break;
42 } 42 }
43 case ScriptStreamer::Async: { 43 case ScriptStreamer::Async: {
44 DEFINE_STATIC_LOCAL(EnumerationHistogram, asyncHistogram, ("WebCore.Scri pts.Async.StartedStreaming", 2)); 44 DEFINE_STATIC_LOCAL(EnumerationHistogram, asyncHistogram, ("WebCore.Scri pts.Async.StartedStreaming", 2));
45 asyncHistogram.count(reason); 45 asyncHistogram.count(reason);
46 break; 46 break;
47 } 47 }
48 default: 48 default:
49 ASSERT_NOT_REACHED(); 49 NOTREACHED();
50 break; 50 break;
51 } 51 }
52 } 52 }
53 53
54 // For tracking why some scripts are not streamed. Not streaming is part of 54 // For tracking why some scripts are not streamed. Not streaming is part of
55 // normal operation (e.g., script already loaded, script too small) and doesn't 55 // normal operation (e.g., script already loaded, script too small) and doesn't
56 // necessarily indicate a failure. 56 // necessarily indicate a failure.
57 enum NotStreamingReason { 57 enum NotStreamingReason {
58 AlreadyLoaded, 58 AlreadyLoaded,
59 NotHTTP, 59 NotHTTP,
(...skipping 18 matching lines...) Expand all
78 DEFINE_STATIC_LOCAL(EnumerationHistogram, deferredHistogram, ("WebCore.S cripts.Deferred.NotStreamingReason", NotStreamingReasonEnd)); 78 DEFINE_STATIC_LOCAL(EnumerationHistogram, deferredHistogram, ("WebCore.S cripts.Deferred.NotStreamingReason", NotStreamingReasonEnd));
79 deferredHistogram.count(reason); 79 deferredHistogram.count(reason);
80 break; 80 break;
81 } 81 }
82 case ScriptStreamer::Async: { 82 case ScriptStreamer::Async: {
83 DEFINE_STATIC_LOCAL(EnumerationHistogram, asyncHistogram, ("WebCore.Scri pts.Async.NotStreamingReason", NotStreamingReasonEnd)); 83 DEFINE_STATIC_LOCAL(EnumerationHistogram, asyncHistogram, ("WebCore.Scri pts.Async.NotStreamingReason", NotStreamingReasonEnd));
84 asyncHistogram.count(reason); 84 asyncHistogram.count(reason);
85 break; 85 break;
86 } 86 }
87 default: 87 default:
88 ASSERT_NOT_REACHED(); 88 NOTREACHED();
89 break; 89 break;
90 } 90 }
91 } 91 }
92 92
93 } // namespace 93 } // namespace
94 94
95 // For passing data between the main thread (producer) and the streamer thread 95 // For passing data between the main thread (producer) and the streamer thread
96 // (consumer). The main thread prepares the data (copies it from Resource) and 96 // (consumer). The main thread prepares the data (copies it from Resource) and
97 // the streamer thread feeds it to V8. 97 // the streamer thread feeds it to V8.
98 class SourceStreamDataQueue { 98 class SourceStreamDataQueue {
(...skipping 10 matching lines...) Expand all
109 void clear() 109 void clear()
110 { 110 {
111 MutexLocker locker(m_mutex); 111 MutexLocker locker(m_mutex);
112 m_finished = false; 112 m_finished = false;
113 discardQueuedData(); 113 discardQueuedData();
114 } 114 }
115 115
116 void produce(const uint8_t* data, size_t length) 116 void produce(const uint8_t* data, size_t length)
117 { 117 {
118 MutexLocker locker(m_mutex); 118 MutexLocker locker(m_mutex);
119 ASSERT(!m_finished); 119 DCHECK(!m_finished);
120 m_data.append(std::make_pair(data, length)); 120 m_data.append(std::make_pair(data, length));
121 m_haveData.signal(); 121 m_haveData.signal();
122 } 122 }
123 123
124 void finish() 124 void finish()
125 { 125 {
126 MutexLocker locker(m_mutex); 126 MutexLocker locker(m_mutex);
127 m_finished = true; 127 m_finished = true;
128 m_haveData.signal(); 128 m_haveData.signal();
129 } 129 }
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
173 // thread). 173 // thread).
174 class SourceStream : public v8::ScriptCompiler::ExternalSourceStream { 174 class SourceStream : public v8::ScriptCompiler::ExternalSourceStream {
175 WTF_MAKE_NONCOPYABLE(SourceStream); 175 WTF_MAKE_NONCOPYABLE(SourceStream);
176 public: 176 public:
177 explicit SourceStream(WebTaskRunner* loadingTaskRunner) 177 explicit SourceStream(WebTaskRunner* loadingTaskRunner)
178 : v8::ScriptCompiler::ExternalSourceStream() 178 : v8::ScriptCompiler::ExternalSourceStream()
179 , m_cancelled(false) 179 , m_cancelled(false)
180 , m_finished(false) 180 , m_finished(false)
181 , m_queueLeadPosition(0) 181 , m_queueLeadPosition(0)
182 , m_queueTailPosition(0) 182 , m_queueTailPosition(0)
183 , m_bookmarkPosition(0)
184 , m_lengthOfBOM(0)
185 , m_loadingTaskRunner(loadingTaskRunner->clone()) 183 , m_loadingTaskRunner(loadingTaskRunner->clone())
186 { 184 {
187 } 185 }
188 186
189 virtual ~SourceStream() override { } 187 virtual ~SourceStream() override { }
190 188
191 // Called by V8 on a background thread. Should block until we can return 189 // Called by V8 on a background thread. Should block until we can return
192 // some data. 190 // some data.
193 size_t GetMoreData(const uint8_t** src) override 191 size_t GetMoreData(const uint8_t** src) override
194 { 192 {
195 ASSERT(!isMainThread()); 193 DCHECK(!isMainThread());
196 { 194 {
197 MutexLocker locker(m_mutex); 195 MutexLocker locker(m_mutex);
198 if (m_cancelled) 196 if (m_cancelled)
199 return 0; 197 return 0;
200 } 198 }
201 size_t length = 0; 199 size_t length = 0;
202 // This will wait until there is data. 200 // This will wait until there is data.
203 m_dataQueue.consume(src, &length); 201 m_dataQueue.consume(src, &length);
204 { 202 {
205 MutexLocker locker(m_mutex); 203 MutexLocker locker(m_mutex);
206 if (m_cancelled) 204 if (m_cancelled)
207 return 0; 205 return 0;
208 } 206 }
209 m_queueLeadPosition += length; 207 m_queueLeadPosition += length;
210 return length; 208 return length;
211 } 209 }
212 210
213 // Called by V8 on background thread.
214 bool SetBookmark() override
215 {
216 ASSERT(!isMainThread());
217 m_bookmarkPosition = m_queueLeadPosition;
218 return true;
219 }
220
221 // Called by V8 on background thread.
222 void ResetToBookmark() override
223 {
224 ASSERT(!isMainThread());
225 {
226 MutexLocker locker(m_mutex);
227 m_queueLeadPosition = m_bookmarkPosition;
228 // See comments at m_lengthOfBOM declaration below for why
229 // we need this here.
230 m_queueTailPosition = m_bookmarkPosition + m_lengthOfBOM;
231 m_dataQueue.clear();
232 }
233
234 // Inform main thread to re-queue the data.
235 m_loadingTaskRunner->postTask(
236 BLINK_FROM_HERE, crossThreadBind(&SourceStream::fetchDataFromResourc eBuffer, crossThreadUnretained(this), 0));
237 }
238
239 void didFinishLoading() 211 void didFinishLoading()
240 { 212 {
241 ASSERT(isMainThread()); 213 DCHECK(isMainThread());
242
243 // ResetToBookmark may reset the data queue's 'finished' status,
244 // so we may need to re-finish after a ResetToBookmark happened.
245 // We do this by remembering m_finished, and always checking for it
246 // at the end ot fetchDataFromResourceBuffer.
247 m_finished = true; 214 m_finished = true;
248 fetchDataFromResourceBuffer(0); 215 m_dataQueue.finish();
249 } 216 }
250 217
251 void didReceiveData(ScriptStreamer* streamer, size_t lengthOfBOM) 218 void didReceiveData(ScriptStreamer* streamer)
252 { 219 {
253 ASSERT(isMainThread()); 220 DCHECK(isMainThread());
254 prepareDataOnMainThread(streamer, lengthOfBOM); 221 prepareDataOnMainThread(streamer);
255 } 222 }
256 223
257 void cancel() 224 void cancel()
258 { 225 {
259 ASSERT(isMainThread()); 226 DCHECK(isMainThread());
260 // The script is no longer needed by the upper layers. Stop streaming 227 // The script is no longer needed by the upper layers. Stop streaming
261 // it. The next time GetMoreData is called (or woken up), it will return 228 // it. The next time GetMoreData is called (or woken up), it will return
262 // 0, which will be interpreted as EOS by V8 and the parsing will 229 // 0, which will be interpreted as EOS by V8 and the parsing will
263 // fail. ScriptStreamer::streamingComplete will be called, and at that 230 // fail. ScriptStreamer::streamingComplete will be called, and at that
264 // point we will release the references to SourceStream. 231 // point we will release the references to SourceStream.
265 { 232 {
266 MutexLocker locker(m_mutex); 233 MutexLocker locker(m_mutex);
267 m_cancelled = true; 234 m_cancelled = true;
268 } 235 }
269 m_dataQueue.finish(); 236 m_dataQueue.finish();
270 } 237 }
271 238
272 private: 239 private:
273 void prepareDataOnMainThread(ScriptStreamer* streamer, size_t lengthOfBOM) 240 void prepareDataOnMainThread(ScriptStreamer* streamer)
274 { 241 {
275 ASSERT(isMainThread()); 242 DCHECK(isMainThread());
276 243
277 // The Resource must still be alive; otherwise we should've cancelled 244 // The Resource must still be alive; otherwise we should've cancelled
278 // the streaming (if we have cancelled, the background thread is not 245 // the streaming (if we have cancelled, the background thread is not
279 // waiting). 246 // waiting).
280 ASSERT(streamer->resource()); 247 DCHECK(streamer->resource());
281
282 // BOM can only occur at the beginning of the data.
283 ASSERT(lengthOfBOM == 0 || m_queueTailPosition == 0);
284 248
285 if (!streamer->resource()->response().cacheStorageCacheName().isNull()) { 249 if (!streamer->resource()->response().cacheStorageCacheName().isNull()) {
286 streamer->suppressStreaming(); 250 streamer->suppressStreaming();
287 cancel(); 251 cancel();
288 return; 252 return;
289 } 253 }
290 254
291 CachedMetadataHandler* cacheHandler = streamer->resource()->cacheHandler (); 255 CachedMetadataHandler* cacheHandler = streamer->resource()->cacheHandler ();
292 RefPtr<CachedMetadata> codeCache(cacheHandler ? cacheHandler->cachedMeta data(V8ScriptRunner::tagForCodeCache(cacheHandler)) : nullptr); 256 RefPtr<CachedMetadata> codeCache(cacheHandler ? cacheHandler->cachedMeta data(V8ScriptRunner::tagForCodeCache(cacheHandler)) : nullptr);
293 if (codeCache.get()) { 257 if (codeCache.get()) {
294 // The resource has a code cache, so it's unnecessary to stream and 258 // The resource has a code cache, so it's unnecessary to stream and
295 // parse the code. Cancel the streaming and resume the non-streaming 259 // parse the code. Cancel the streaming and resume the non-streaming
296 // code path. 260 // code path.
297 streamer->suppressStreaming(); 261 streamer->suppressStreaming();
298 cancel(); 262 cancel();
299 return; 263 return;
300 } 264 }
301 265
302 if (!m_resourceBuffer) { 266 if (!m_resourceBuffer) {
303 // We don't have a buffer yet. Try to get it from the resource. 267 // We don't have a buffer yet. Try to get it from the resource.
304 m_resourceBuffer = streamer->resource()->resourceBuffer(); 268 m_resourceBuffer = streamer->resource()->resourceBuffer();
305 } 269 }
306 270
307 fetchDataFromResourceBuffer(lengthOfBOM); 271 fetchDataFromResourceBuffer();
308 } 272 }
309 273
310 void fetchDataFromResourceBuffer(size_t lengthOfBOM) 274 void fetchDataFromResourceBuffer()
311 { 275 {
312 ASSERT(isMainThread()); 276 DCHECK(isMainThread());
313 MutexLocker locker(m_mutex); // For m_cancelled + m_queueTailPosition. 277 MutexLocker locker(m_mutex);
314 278
315 if (lengthOfBOM > 0) { 279 DCHECK(!m_finished);
316 ASSERT(!m_lengthOfBOM); // There should be only one BOM.
317 m_lengthOfBOM = lengthOfBOM;
318 }
319
320 if (m_cancelled) { 280 if (m_cancelled) {
321 m_dataQueue.finish(); 281 m_dataQueue.finish();
322 return; 282 return;
323 } 283 }
324 284
325 // Get as much data from the ResourceBuffer as we can. 285 // Get as much data from the ResourceBuffer as we can.
326 const char* data = nullptr; 286 const char* data = nullptr;
327 Vector<const char*> chunks;
328 Vector<size_t> chunkLengths;
329 size_t bufferLength = 0;
330 while (size_t length = m_resourceBuffer->getSomeData(data, m_queueTailPo sition)) { 287 while (size_t length = m_resourceBuffer->getSomeData(data, m_queueTailPo sition)) {
331 // FIXME: Here we can limit based on the total length, if it turns 288 // Copy the data chunks into a new buffer, since we're going to
332 // out that we don't want to give all the data we have (memory 289 // give the data to a background thread.
333 // vs. speed). 290 uint8_t* copiedData = new uint8_t[length];
334 chunks.append(data); 291 memcpy(copiedData, data, length);
335 chunkLengths.append(length); 292 m_dataQueue.produce(copiedData, length);
336 bufferLength += length; 293
337 m_queueTailPosition += length; 294 m_queueTailPosition += length;
338 } 295 }
339
340 // Copy the data chunks into a new buffer, since we're going to give the
341 // data to a background thread.
342 if (bufferLength > lengthOfBOM) {
343 size_t totalLength = bufferLength - lengthOfBOM;
344 uint8_t* copiedData = new uint8_t[totalLength];
345 size_t offset = 0;
346 size_t offsetInChunk = lengthOfBOM;
347 for (size_t i = 0; i < chunks.size(); ++i) {
348 if (offsetInChunk >= chunkLengths[i]) {
349 offsetInChunk -= chunkLengths[i];
350 continue;
351 }
352
353 size_t dataLength = chunkLengths[i] - offsetInChunk;
354 memcpy(copiedData + offset, chunks[i] + offsetInChunk, dataLengt h);
355 offset += dataLength;
356 // BOM is in the beginning of the buffer.
357 offsetInChunk = 0;
358 }
359 m_dataQueue.produce(copiedData, totalLength);
360 }
361
362 if (m_finished)
363 m_dataQueue.finish();
364 } 296 }
365 297
366 // For coordinating between the main thread and background thread tasks. 298 // For coordinating between the main thread and background thread tasks.
367 // Guards m_cancelled and m_queueTailPosition. 299 // Guards m_cancelled and m_queueTailPosition.
368 Mutex m_mutex; 300 Mutex m_mutex;
369 301
370 // The shared buffer containing the resource data + state variables. 302 // The shared buffer containing the resource data + state variables.
371 // Used by both threads, guarded by m_mutex. 303 // Used by both threads, guarded by m_mutex.
372 bool m_cancelled; 304 bool m_cancelled;
373 bool m_finished; 305 bool m_finished;
374 306
375 RefPtr<const SharedBuffer> m_resourceBuffer; // Only used by the main thread . 307 RefPtr<const SharedBuffer> m_resourceBuffer; // Only used by the main thread .
376 308
377 // The queue contains the data to be passed to the V8 thread. 309 // The queue contains the data to be passed to the V8 thread.
378 // queueLeadPosition: data we have handed off to the V8 thread. 310 // queueLeadPosition: data we have handed off to the V8 thread.
379 // queueTailPosition: end of data we have enqued in the queue. 311 // queueTailPosition: end of data we have enqued in the queue.
380 // bookmarkPosition: position of the bookmark. 312 // bookmarkPosition: position of the bookmark.
381 SourceStreamDataQueue m_dataQueue; // Thread safe. 313 SourceStreamDataQueue m_dataQueue; // Thread safe.
382 size_t m_queueLeadPosition; // Only used by v8 thread. 314 size_t m_queueLeadPosition; // Only used by v8 thread.
383 size_t m_queueTailPosition; // Used by both threads; guarded by m_mutex. 315 size_t m_queueTailPosition; // Used by both threads; guarded by m_mutex.
384 size_t m_bookmarkPosition; // Only used by v8 thread.
385
386 // BOM (Unicode Byte Order Mark) handling:
387 // This class is responsible for stripping out the BOM, since Chrome
388 // delivers the input stream potentially with BOM, but V8 doesn't want
389 // to see the BOM. This is mostly easy to do, except for a funky edge
390 // condition with bookmarking:
391 // - m_queueLeadPosition counts the bytes that V8 has received
392 // (i.e., without BOM)
393 // - m_queueTailPosition counts the bytes that Chrome has sent
394 // (i.e., with BOM)
395 // So when resetting the bookmark, we have to adjust the lead position
396 // to account for the BOM (which happens implicitly in the regular
397 // streaming case).
398 // We store this separately, to avoid having to guard all
399 // m_queueLeadPosition references with a mutex.
400 size_t m_lengthOfBOM; // Used by both threads; guarded by m_mutex.
401 316
402 std::unique_ptr<WebTaskRunner> m_loadingTaskRunner; 317 std::unique_ptr<WebTaskRunner> m_loadingTaskRunner;
403 }; 318 };
404 319
405 size_t ScriptStreamer::s_smallScriptThreshold = 30 * 1024; 320 size_t ScriptStreamer::s_smallScriptThreshold = 30 * 1024;
406 321
407 void ScriptStreamer::startStreaming(PendingScript* script, Type scriptType, Sett ings* settings, ScriptState* scriptState, WebTaskRunner* loadingTaskRunner) 322 void ScriptStreamer::startStreaming(PendingScript* script, Type scriptType, Sett ings* settings, ScriptState* scriptState, WebTaskRunner* loadingTaskRunner)
408 { 323 {
409 // We don't yet know whether the script will really be streamed. E.g., 324 // We don't yet know whether the script will really be streamed. E.g.,
410 // suppressing streaming for short scripts is done later. Record only the 325 // suppressing streaming for short scripts is done later. Record only the
(...skipping 25 matching lines...) Expand all
436 } 351 }
437 352
438 bool ScriptStreamer::isFinished() const 353 bool ScriptStreamer::isFinished() const
439 { 354 {
440 MutexLocker locker(m_mutex); 355 MutexLocker locker(m_mutex);
441 return m_loadingFinished && (m_parsingFinished || m_streamingSuppressed); 356 return m_loadingFinished && (m_parsingFinished || m_streamingSuppressed);
442 } 357 }
443 358
444 void ScriptStreamer::streamingCompleteOnBackgroundThread() 359 void ScriptStreamer::streamingCompleteOnBackgroundThread()
445 { 360 {
446 ASSERT(!isMainThread()); 361 DCHECK(!isMainThread());
447 { 362 {
448 MutexLocker locker(m_mutex); 363 MutexLocker locker(m_mutex);
449 m_parsingFinished = true; 364 m_parsingFinished = true;
450 } 365 }
451 366
452 // notifyFinished might already be called, or it might be called in the 367 // notifyFinished might already be called, or it might be called in the
453 // future (if the parsing finishes earlier because of a parse error). 368 // future (if the parsing finishes earlier because of a parse error).
454 m_loadingTaskRunner->postTask(BLINK_FROM_HERE, crossThreadBind(&ScriptStream er::streamingComplete, wrapCrossThreadPersistent(this))); 369 m_loadingTaskRunner->postTask(BLINK_FROM_HERE, crossThreadBind(&ScriptStream er::streamingComplete, wrapCrossThreadPersistent(this)));
455 370
456 // The task might delete ScriptStreamer, so it's not safe to do anything 371 // The task might delete ScriptStreamer, so it's not safe to do anything
457 // after posting it. Note that there's no way to guarantee that this 372 // after posting it. Note that there's no way to guarantee that this
458 // function has returned before the task is ran - however, we should not 373 // function has returned before the task is ran - however, we should not
459 // access the "this" object after posting the task. (Especially, we should 374 // access the "this" object after posting the task. (Especially, we should
460 // not be holding the mutex at this point.) 375 // not be holding the mutex at this point.)
461 } 376 }
462 377
463 void ScriptStreamer::cancel() 378 void ScriptStreamer::cancel()
464 { 379 {
465 ASSERT(isMainThread()); 380 DCHECK(isMainThread());
466 // The upper layer doesn't need the script any more, but streaming might 381 // The upper layer doesn't need the script any more, but streaming might
467 // still be ongoing. Tell SourceStream to try to cancel it whenever it gets 382 // still be ongoing. Tell SourceStream to try to cancel it whenever it gets
468 // the control the next time. It can also be that V8 has already completed 383 // the control the next time. It can also be that V8 has already completed
469 // its operations and streamingComplete will be called soon. 384 // its operations and streamingComplete will be called soon.
470 m_detached = true; 385 m_detached = true;
471 m_resource = 0; 386 m_resource = 0;
472 if (m_stream) 387 if (m_stream)
473 m_stream->cancel(); 388 m_stream->cancel();
474 } 389 }
475 390
476 void ScriptStreamer::suppressStreaming() 391 void ScriptStreamer::suppressStreaming()
477 { 392 {
478 MutexLocker locker(m_mutex); 393 MutexLocker locker(m_mutex);
479 ASSERT(!m_loadingFinished); 394 DCHECK(!m_loadingFinished);
480 // It can be that the parsing task has already finished (e.g., if there was 395 // It can be that the parsing task has already finished (e.g., if there was
481 // a parse error). 396 // a parse error).
482 m_streamingSuppressed = true; 397 m_streamingSuppressed = true;
483 } 398 }
484 399
485 void ScriptStreamer::notifyAppendData(ScriptResource* resource) 400 void ScriptStreamer::notifyAppendData(ScriptResource* resource)
486 { 401 {
487 ASSERT(isMainThread()); 402 DCHECK(isMainThread());
488 ASSERT(m_resource == resource); 403 DCHECK_EQ(m_resource, resource);
489 { 404 {
490 MutexLocker locker(m_mutex); 405 MutexLocker locker(m_mutex);
491 if (m_streamingSuppressed) 406 if (m_streamingSuppressed)
492 return; 407 return;
493 } 408 }
494 size_t lengthOfBOM = 0;
495 if (!m_haveEnoughDataForStreaming) { 409 if (!m_haveEnoughDataForStreaming) {
496 // Even if the first data chunk is small, the script can still be big 410 // Even if the first data chunk is small, the script can still be big
497 // enough - wait until the next data chunk comes before deciding whether 411 // enough - wait until the next data chunk comes before deciding whether
498 // to start the streaming. 412 // to start the streaming.
499 ASSERT(resource->resourceBuffer()); 413 DCHECK(resource->resourceBuffer());
500 if (resource->resourceBuffer()->size() < s_smallScriptThreshold) 414 if (resource->resourceBuffer()->size() < s_smallScriptThreshold)
501 return; 415 return;
502 m_haveEnoughDataForStreaming = true; 416 m_haveEnoughDataForStreaming = true;
503 417
504 // Encoding should be detected only when we have some data. It's 418 {
505 // possible that resource->encoding() returns a different encoding 419 // Check for BOM (byte order marks), because that might change our
506 // before the loading has started and after we got some data. In 420 // understanding of the data encoding.
507 // addition, check for byte order marks. Note that checking the byte 421 constexpr size_t maximumLengthOfBOM = 4;
508 // order mark might change the encoding. We cannot decode the full text 422 char maybeBOM[maximumLengthOfBOM] = {};
509 // here, because it might contain incomplete UTF-8 characters. Also note 423 if (!resource->resourceBuffer()->getPartAsBytes(maybeBOM, static_cas t<size_t>(0), maximumLengthOfBOM)) {
510 // that have at least s_smallScriptThreshold worth of data, which is mor e 424 NOTREACHED();
511 // than enough for detecting a BOM. 425 return;
512 constexpr size_t maximumLengthOfBOM = 4; 426 }
513 char maybeBOM[maximumLengthOfBOM] = {}; 427
514 if (!resource->resourceBuffer()->getPartAsBytes(maybeBOM, static_cast<si ze_t>(0), maximumLengthOfBOM)) { 428 std::unique_ptr<TextResourceDecoder> decoder(TextResourceDecoder::cr eate("application/javascript", resource->encoding()));
515 NOTREACHED(); 429 decoder->checkForBOM(maybeBOM, maximumLengthOfBOM);
516 return; 430
431 // The encoding may change when we see the BOM. Check for BOM now
432 // and update the encoding from the decoder when necessary. Abort
433 // streaming if the encoding is different from the one we started
marja 2016/09/20 19:03:55 This comment is not correct: we don't abort stream
vogelheim 2016/09/22 08:15:14 Within Chromium: Yes, I think so. This is a bit s
vogelheim 2016/09/22 08:15:14 Done.
434 // with.
435 //
436 // Also note that have at least s_smallScriptThreshold worth of
437 // data, which is more than enough for detecting a BOM.
438 if (!convertEncoding(decoder->encoding().name(), &m_encoding)) {
439 suppressStreaming();
440 recordNotStreamingReasonHistogram(m_scriptType, EncodingNotSuppo rted);
441 recordStartedStreamingHistogram(m_scriptType, 0);
442 return;
443 }
517 } 444 }
518 445
519 std::unique_ptr<TextResourceDecoder> decoder(TextResourceDecoder::create ("application/javascript", resource->encoding()));
520 lengthOfBOM = decoder->checkForBOM(maybeBOM, maximumLengthOfBOM);
521
522 // Maybe the encoding changed because we saw the BOM; get the encoding
523 // from the decoder.
524 if (!convertEncoding(decoder->encoding().name(), &m_encoding)) {
525 suppressStreaming();
526 recordNotStreamingReasonHistogram(m_scriptType, EncodingNotSupported );
527 recordStartedStreamingHistogram(m_scriptType, 0);
528 return;
529 }
530 if (ScriptStreamerThread::shared()->isRunningTask()) { 446 if (ScriptStreamerThread::shared()->isRunningTask()) {
531 // At the moment we only have one thread for running the tasks. A 447 // At the moment we only have one thread for running the tasks. A
532 // new task shouldn't be queued before the running task completes, 448 // new task shouldn't be queued before the running task completes,
533 // because the running task can block and wait for data from the 449 // because the running task can block and wait for data from the
534 // network. 450 // network.
535 suppressStreaming(); 451 suppressStreaming();
536 recordNotStreamingReasonHistogram(m_scriptType, ThreadBusy); 452 recordNotStreamingReasonHistogram(m_scriptType, ThreadBusy);
537 recordStartedStreamingHistogram(m_scriptType, 0); 453 recordStartedStreamingHistogram(m_scriptType, 0);
538 return; 454 return;
539 } 455 }
540 456
541 if (!m_scriptState->contextIsValid()) { 457 if (!m_scriptState->contextIsValid()) {
542 suppressStreaming(); 458 suppressStreaming();
543 recordNotStreamingReasonHistogram(m_scriptType, ContextNotValid); 459 recordNotStreamingReasonHistogram(m_scriptType, ContextNotValid);
544 recordStartedStreamingHistogram(m_scriptType, 0); 460 recordStartedStreamingHistogram(m_scriptType, 0);
545 return; 461 return;
546 } 462 }
547 463
548 ASSERT(!m_stream); 464 DCHECK(!m_stream);
549 ASSERT(!m_source); 465 DCHECK(!m_source);
550 m_stream = new SourceStream(m_loadingTaskRunner.get()); 466 m_stream = new SourceStream(m_loadingTaskRunner.get());
551 // m_source takes ownership of m_stream. 467 // m_source takes ownership of m_stream.
552 m_source = wrapUnique(new v8::ScriptCompiler::StreamedSource(m_stream, m _encoding)); 468 m_source = wrapUnique(new v8::ScriptCompiler::StreamedSource(m_stream, m _encoding));
553 469
554 ScriptState::Scope scope(m_scriptState.get()); 470 ScriptState::Scope scope(m_scriptState.get());
555 std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> scriptStreaming Task(wrapUnique(v8::ScriptCompiler::StartStreamingScript(m_scriptState->isolate( ), m_source.get(), m_compileOptions))); 471 std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> scriptStreaming Task(wrapUnique(v8::ScriptCompiler::StartStreamingScript(m_scriptState->isolate( ), m_source.get(), m_compileOptions)));
556 if (!scriptStreamingTask) { 472 if (!scriptStreamingTask) {
557 // V8 cannot stream the script. 473 // V8 cannot stream the script.
558 suppressStreaming(); 474 suppressStreaming();
559 m_stream = 0; 475 m_stream = 0;
560 m_source.reset(); 476 m_source.reset();
561 recordNotStreamingReasonHistogram(m_scriptType, V8CannotStream); 477 recordNotStreamingReasonHistogram(m_scriptType, V8CannotStream);
562 recordStartedStreamingHistogram(m_scriptType, 0); 478 recordStartedStreamingHistogram(m_scriptType, 0);
563 return; 479 return;
564 } 480 }
565 481
566 ScriptStreamerThread::shared()->postTask(crossThreadBind(&ScriptStreamer Thread::runScriptStreamingTask, passed(std::move(scriptStreamingTask)), wrapCros sThreadPersistent(this))); 482 ScriptStreamerThread::shared()->postTask(crossThreadBind(&ScriptStreamer Thread::runScriptStreamingTask, passed(std::move(scriptStreamingTask)), wrapCros sThreadPersistent(this)));
567 recordStartedStreamingHistogram(m_scriptType, 1); 483 recordStartedStreamingHistogram(m_scriptType, 1);
568 } 484 }
569 if (m_stream) 485 if (m_stream)
570 m_stream->didReceiveData(this, lengthOfBOM); 486 m_stream->didReceiveData(this);
571 } 487 }
572 488
573 void ScriptStreamer::notifyFinished(Resource* resource) 489 void ScriptStreamer::notifyFinished(Resource* resource)
574 { 490 {
575 ASSERT(isMainThread()); 491 DCHECK(isMainThread());
576 ASSERT(m_resource == resource); 492 DCHECK_EQ(m_resource, resource);
577 // A special case: empty and small scripts. We didn't receive enough data to 493 // A special case: empty and small scripts. We didn't receive enough data to
578 // start the streaming before this notification. In that case, there won't 494 // start the streaming before this notification. In that case, there won't
579 // be a "parsing complete" notification either, and we should not wait for 495 // be a "parsing complete" notification either, and we should not wait for
580 // it. 496 // it.
581 if (!m_haveEnoughDataForStreaming) { 497 if (!m_haveEnoughDataForStreaming) {
582 recordNotStreamingReasonHistogram(m_scriptType, ScriptTooSmall); 498 recordNotStreamingReasonHistogram(m_scriptType, ScriptTooSmall);
583 recordStartedStreamingHistogram(m_scriptType, 0); 499 recordStartedStreamingHistogram(m_scriptType, 0);
584 suppressStreaming(); 500 suppressStreaming();
585 } 501 }
586 if (m_stream) 502 if (m_stream)
(...skipping 29 matching lines...) Expand all
616 DEFINE_TRACE(ScriptStreamer) 532 DEFINE_TRACE(ScriptStreamer)
617 { 533 {
618 visitor->trace(m_pendingScript); 534 visitor->trace(m_pendingScript);
619 visitor->trace(m_resource); 535 visitor->trace(m_resource);
620 } 536 }
621 537
622 void ScriptStreamer::streamingComplete() 538 void ScriptStreamer::streamingComplete()
623 { 539 {
624 // The background task is completed; do the necessary ramp-down in the main 540 // The background task is completed; do the necessary ramp-down in the main
625 // thread. 541 // thread.
626 ASSERT(isMainThread()); 542 DCHECK(isMainThread());
627 543
628 // It's possible that the corresponding Resource was deleted before V8 544 // It's possible that the corresponding Resource was deleted before V8
629 // finished streaming. In that case, the data or the notification is not 545 // finished streaming. In that case, the data or the notification is not
630 // needed. In addition, if the streaming is suppressed, the non-streaming 546 // needed. In addition, if the streaming is suppressed, the non-streaming
631 // code path will resume after the resource has loaded, before the 547 // code path will resume after the resource has loaded, before the
632 // background task finishes. 548 // background task finishes.
633 if (m_detached || m_streamingSuppressed) 549 if (m_detached || m_streamingSuppressed)
634 return; 550 return;
635 551
636 // We have now streamed the whole script to V8 and it has parsed the 552 // We have now streamed the whole script to V8 and it has parsed the
637 // script. We're ready for the next step: compiling and executing the 553 // script. We're ready for the next step: compiling and executing the
638 // script. 554 // script.
639 notifyFinishedToClient(); 555 notifyFinishedToClient();
640 } 556 }
641 557
642 void ScriptStreamer::notifyFinishedToClient() 558 void ScriptStreamer::notifyFinishedToClient()
643 { 559 {
644 ASSERT(isMainThread()); 560 DCHECK(isMainThread());
645 // Usually, the loading will be finished first, and V8 will still need some 561 // Usually, the loading will be finished first, and V8 will still need some
646 // time to catch up. But the other way is possible too: if V8 detects a 562 // time to catch up. But the other way is possible too: if V8 detects a
647 // parse error, the V8 side can complete before loading has finished. Send 563 // parse error, the V8 side can complete before loading has finished. Send
648 // the notification after both loading and V8 side operations have 564 // the notification after both loading and V8 side operations have
649 // completed. Here we also check that we have a client: it can happen that a 565 // completed. Here we also check that we have a client: it can happen that a
650 // function calling notifyFinishedToClient was already scheduled in the task 566 // function calling notifyFinishedToClient was already scheduled in the task
651 // queue and the upper layer decided that it's not interested in the script 567 // queue and the upper layer decided that it's not interested in the script
652 // and called removeClient. 568 // and called removeClient.
653 if (!isFinished()) 569 if (!isFinished())
654 return; 570 return;
655 571
656 m_pendingScript->streamingFinished(); 572 m_pendingScript->streamingFinished();
657 } 573 }
658 574
659 bool ScriptStreamer::startStreamingInternal(PendingScript* script, Type scriptTy pe, Settings* settings, ScriptState* scriptState, WebTaskRunner* loadingTaskRunn er) 575 bool ScriptStreamer::startStreamingInternal(PendingScript* script, Type scriptTy pe, Settings* settings, ScriptState* scriptState, WebTaskRunner* loadingTaskRunn er)
660 { 576 {
661 ASSERT(isMainThread()); 577 DCHECK(isMainThread());
662 ASSERT(scriptState->contextIsValid()); 578 DCHECK(scriptState->contextIsValid());
663 ScriptResource* resource = script->resource(); 579 ScriptResource* resource = script->resource();
664 if (resource->isLoaded()) { 580 if (resource->isLoaded()) {
665 recordNotStreamingReasonHistogram(scriptType, AlreadyLoaded); 581 recordNotStreamingReasonHistogram(scriptType, AlreadyLoaded);
666 return false; 582 return false;
667 } 583 }
668 if (!resource->url().protocolIsInHTTPFamily()) { 584 if (!resource->url().protocolIsInHTTPFamily()) {
669 recordNotStreamingReasonHistogram(scriptType, NotHTTP); 585 recordNotStreamingReasonHistogram(scriptType, NotHTTP);
670 return false; 586 return false;
671 } 587 }
672 if (resource->isCacheValidator()) { 588 if (resource->isCacheValidator()) {
(...skipping 15 matching lines...) Expand all
688 604
689 // The Resource might go out of scope if the script is no longer 605 // The Resource might go out of scope if the script is no longer
690 // needed. This makes PendingScript notify the ScriptStreamer when it is 606 // needed. This makes PendingScript notify the ScriptStreamer when it is
691 // destroyed. 607 // destroyed.
692 script->setStreamer(ScriptStreamer::create(script, scriptType, scriptState, compileOption, loadingTaskRunner)); 608 script->setStreamer(ScriptStreamer::create(script, scriptType, scriptState, compileOption, loadingTaskRunner));
693 609
694 return true; 610 return true;
695 } 611 }
696 612
697 } // namespace blink 613 } // namespace blink
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698