| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "bindings/core/v8/ScriptStreamer.h" | |
| 6 | |
| 7 #include <memory> | |
| 8 #include "bindings/core/v8/ScriptStreamerThread.h" | |
| 9 #include "bindings/core/v8/V8ScriptRunner.h" | |
| 10 #include "core/dom/Document.h" | |
| 11 #include "core/dom/Element.h" | |
| 12 #include "core/dom/PendingScript.h" | |
| 13 #include "core/frame/Settings.h" | |
| 14 #include "core/html/parser/TextResourceDecoder.h" | |
| 15 #include "core/loader/resource/ScriptResource.h" | |
| 16 #include "platform/CrossThreadFunctional.h" | |
| 17 #include "platform/Histogram.h" | |
| 18 #include "platform/SharedBuffer.h" | |
| 19 #include "platform/instrumentation/tracing/TraceEvent.h" | |
| 20 #include "platform/loader/fetch/CachedMetadata.h" | |
| 21 #include "platform/wtf/Deque.h" | |
| 22 #include "platform/wtf/PtrUtil.h" | |
| 23 #include "platform/wtf/text/TextEncodingRegistry.h" | |
| 24 #include "public/platform/WebScheduler.h" | |
| 25 | |
| 26 namespace blink { | |
| 27 | |
| 28 namespace { | |
| 29 | |
| 30 void RecordStartedStreamingHistogram(ScriptStreamer::Type script_type, | |
| 31 int reason) { | |
| 32 switch (script_type) { | |
| 33 case ScriptStreamer::kParsingBlocking: { | |
| 34 DEFINE_STATIC_LOCAL( | |
| 35 EnumerationHistogram, parse_blocking_histogram, | |
| 36 ("WebCore.Scripts.ParsingBlocking.StartedStreaming", 2)); | |
| 37 parse_blocking_histogram.Count(reason); | |
| 38 break; | |
| 39 } | |
| 40 case ScriptStreamer::kDeferred: { | |
| 41 DEFINE_STATIC_LOCAL(EnumerationHistogram, deferred_histogram, | |
| 42 ("WebCore.Scripts.Deferred.StartedStreaming", 2)); | |
| 43 deferred_histogram.Count(reason); | |
| 44 break; | |
| 45 } | |
| 46 case ScriptStreamer::kAsync: { | |
| 47 DEFINE_STATIC_LOCAL(EnumerationHistogram, async_histogram, | |
| 48 ("WebCore.Scripts.Async.StartedStreaming", 2)); | |
| 49 async_histogram.Count(reason); | |
| 50 break; | |
| 51 } | |
| 52 default: | |
| 53 NOTREACHED(); | |
| 54 break; | |
| 55 } | |
| 56 } | |
| 57 | |
| 58 // For tracking why some scripts are not streamed. Not streaming is part of | |
| 59 // normal operation (e.g., script already loaded, script too small) and doesn't | |
| 60 // necessarily indicate a failure. | |
| 61 enum NotStreamingReason { | |
| 62 kAlreadyLoaded, | |
| 63 kNotHTTP, | |
| 64 kReload, | |
| 65 kContextNotValid, | |
| 66 kEncodingNotSupported, | |
| 67 kThreadBusy, | |
| 68 kV8CannotStream, | |
| 69 kScriptTooSmall, | |
| 70 kNotStreamingReasonEnd | |
| 71 }; | |
| 72 | |
| 73 void RecordNotStreamingReasonHistogram(ScriptStreamer::Type script_type, | |
| 74 NotStreamingReason reason) { | |
| 75 switch (script_type) { | |
| 76 case ScriptStreamer::kParsingBlocking: { | |
| 77 DEFINE_STATIC_LOCAL(EnumerationHistogram, parse_blocking_histogram, | |
| 78 ("WebCore.Scripts.ParsingBlocking.NotStreamingReason", | |
| 79 kNotStreamingReasonEnd)); | |
| 80 parse_blocking_histogram.Count(reason); | |
| 81 break; | |
| 82 } | |
| 83 case ScriptStreamer::kDeferred: { | |
| 84 DEFINE_STATIC_LOCAL(EnumerationHistogram, deferred_histogram, | |
| 85 ("WebCore.Scripts.Deferred.NotStreamingReason", | |
| 86 kNotStreamingReasonEnd)); | |
| 87 deferred_histogram.Count(reason); | |
| 88 break; | |
| 89 } | |
| 90 case ScriptStreamer::kAsync: { | |
| 91 DEFINE_STATIC_LOCAL( | |
| 92 EnumerationHistogram, async_histogram, | |
| 93 ("WebCore.Scripts.Async.NotStreamingReason", kNotStreamingReasonEnd)); | |
| 94 async_histogram.Count(reason); | |
| 95 break; | |
| 96 } | |
| 97 default: | |
| 98 NOTREACHED(); | |
| 99 break; | |
| 100 } | |
| 101 } | |
| 102 | |
| 103 } // namespace | |
| 104 | |
| 105 // For passing data between the main thread (producer) and the streamer thread | |
| 106 // (consumer). The main thread prepares the data (copies it from Resource) and | |
| 107 // the streamer thread feeds it to V8. | |
| 108 class SourceStreamDataQueue { | |
| 109 WTF_MAKE_NONCOPYABLE(SourceStreamDataQueue); | |
| 110 | |
| 111 public: | |
| 112 SourceStreamDataQueue() : finished_(false) {} | |
| 113 | |
| 114 ~SourceStreamDataQueue() { DiscardQueuedData(); } | |
| 115 | |
| 116 void Clear() { | |
| 117 MutexLocker locker(mutex_); | |
| 118 finished_ = false; | |
| 119 DiscardQueuedData(); | |
| 120 } | |
| 121 | |
| 122 void Produce(const uint8_t* data, size_t length) { | |
| 123 MutexLocker locker(mutex_); | |
| 124 DCHECK(!finished_); | |
| 125 data_.push_back(std::make_pair(data, length)); | |
| 126 have_data_.Signal(); | |
| 127 } | |
| 128 | |
| 129 void Finish() { | |
| 130 MutexLocker locker(mutex_); | |
| 131 finished_ = true; | |
| 132 have_data_.Signal(); | |
| 133 } | |
| 134 | |
| 135 void Consume(const uint8_t** data, size_t* length) { | |
| 136 MutexLocker locker(mutex_); | |
| 137 while (!TryGetData(data, length)) | |
| 138 have_data_.Wait(mutex_); | |
| 139 } | |
| 140 | |
| 141 private: | |
| 142 bool TryGetData(const uint8_t** data, size_t* length) { | |
| 143 #if DCHECK_IS_ON() | |
| 144 DCHECK(mutex_.Locked()); | |
| 145 #endif | |
| 146 if (!data_.IsEmpty()) { | |
| 147 std::pair<const uint8_t*, size_t> next_data = data_.TakeFirst(); | |
| 148 *data = next_data.first; | |
| 149 *length = next_data.second; | |
| 150 return true; | |
| 151 } | |
| 152 if (finished_) { | |
| 153 *length = 0; | |
| 154 return true; | |
| 155 } | |
| 156 return false; | |
| 157 } | |
| 158 | |
| 159 void DiscardQueuedData() { | |
| 160 while (!data_.IsEmpty()) { | |
| 161 std::pair<const uint8_t*, size_t> next_data = data_.TakeFirst(); | |
| 162 delete[] next_data.first; | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 Deque<std::pair<const uint8_t*, size_t>> data_; | |
| 167 bool finished_; | |
| 168 Mutex mutex_; | |
| 169 ThreadCondition have_data_; | |
| 170 }; | |
| 171 | |
| 172 // SourceStream implements the streaming interface towards V8. The main | |
| 173 // functionality is preparing the data to give to V8 on main thread, and | |
| 174 // actually giving the data (via GetMoreData which is called on a background | |
| 175 // thread). | |
| 176 class SourceStream : public v8::ScriptCompiler::ExternalSourceStream { | |
| 177 WTF_MAKE_NONCOPYABLE(SourceStream); | |
| 178 | |
| 179 public: | |
| 180 explicit SourceStream(RefPtr<WebTaskRunner> loading_task_runner) | |
| 181 : v8::ScriptCompiler::ExternalSourceStream(), | |
| 182 cancelled_(false), | |
| 183 finished_(false), | |
| 184 queue_lead_position_(0), | |
| 185 queue_tail_position_(0), | |
| 186 loading_task_runner_(std::move(loading_task_runner)) {} | |
| 187 | |
| 188 virtual ~SourceStream() override {} | |
| 189 | |
| 190 // Called by V8 on a background thread. Should block until we can return | |
| 191 // some data. | |
| 192 size_t GetMoreData(const uint8_t** src) override { | |
| 193 DCHECK(!IsMainThread()); | |
| 194 { | |
| 195 MutexLocker locker(mutex_); | |
| 196 if (cancelled_) | |
| 197 return 0; | |
| 198 } | |
| 199 size_t length = 0; | |
| 200 // This will wait until there is data. | |
| 201 data_queue_.Consume(src, &length); | |
| 202 { | |
| 203 MutexLocker locker(mutex_); | |
| 204 if (cancelled_) | |
| 205 return 0; | |
| 206 } | |
| 207 queue_lead_position_ += length; | |
| 208 return length; | |
| 209 } | |
| 210 | |
| 211 void DidFinishLoading() { | |
| 212 DCHECK(IsMainThread()); | |
| 213 finished_ = true; | |
| 214 data_queue_.Finish(); | |
| 215 } | |
| 216 | |
| 217 void DidReceiveData(ScriptStreamer* streamer) { | |
| 218 DCHECK(IsMainThread()); | |
| 219 PrepareDataOnMainThread(streamer); | |
| 220 } | |
| 221 | |
| 222 void Cancel() { | |
| 223 DCHECK(IsMainThread()); | |
| 224 // The script is no longer needed by the upper layers. Stop streaming | |
| 225 // it. The next time GetMoreData is called (or woken up), it will return | |
| 226 // 0, which will be interpreted as EOS by V8 and the parsing will | |
| 227 // fail. ScriptStreamer::streamingComplete will be called, and at that | |
| 228 // point we will release the references to SourceStream. | |
| 229 { | |
| 230 MutexLocker locker(mutex_); | |
| 231 cancelled_ = true; | |
| 232 } | |
| 233 data_queue_.Finish(); | |
| 234 } | |
| 235 | |
| 236 private: | |
| 237 void PrepareDataOnMainThread(ScriptStreamer* streamer) { | |
| 238 DCHECK(IsMainThread()); | |
| 239 | |
| 240 if (cancelled_) { | |
| 241 data_queue_.Finish(); | |
| 242 return; | |
| 243 } | |
| 244 | |
| 245 // The Resource must still be alive; otherwise we should've cancelled | |
| 246 // the streaming (if we have cancelled, the background thread is not | |
| 247 // waiting). | |
| 248 DCHECK(streamer->GetResource()); | |
| 249 | |
| 250 if (!streamer->GetResource() | |
| 251 ->GetResponse() | |
| 252 .CacheStorageCacheName() | |
| 253 .IsNull()) { | |
| 254 streamer->SuppressStreaming(); | |
| 255 Cancel(); | |
| 256 return; | |
| 257 } | |
| 258 | |
| 259 CachedMetadataHandler* cache_handler = | |
| 260 streamer->GetResource()->CacheHandler(); | |
| 261 RefPtr<CachedMetadata> code_cache( | |
| 262 cache_handler ? cache_handler->GetCachedMetadata( | |
| 263 V8ScriptRunner::TagForCodeCache(cache_handler)) | |
| 264 : nullptr); | |
| 265 if (code_cache.Get()) { | |
| 266 // The resource has a code cache, so it's unnecessary to stream and | |
| 267 // parse the code. Cancel the streaming and resume the non-streaming | |
| 268 // code path. | |
| 269 streamer->SuppressStreaming(); | |
| 270 Cancel(); | |
| 271 return; | |
| 272 } | |
| 273 | |
| 274 if (!resource_buffer_) { | |
| 275 // We don't have a buffer yet. Try to get it from the resource. | |
| 276 resource_buffer_ = streamer->GetResource()->ResourceBuffer(); | |
| 277 } | |
| 278 | |
| 279 FetchDataFromResourceBuffer(); | |
| 280 } | |
| 281 | |
| 282 void FetchDataFromResourceBuffer() { | |
| 283 DCHECK(IsMainThread()); | |
| 284 MutexLocker locker(mutex_); | |
| 285 | |
| 286 DCHECK(!finished_); | |
| 287 if (cancelled_) { | |
| 288 data_queue_.Finish(); | |
| 289 return; | |
| 290 } | |
| 291 | |
| 292 // Get as much data from the ResourceBuffer as we can. | |
| 293 const char* data = nullptr; | |
| 294 while (size_t length = | |
| 295 resource_buffer_->GetSomeData(data, queue_tail_position_)) { | |
| 296 // Copy the data chunks into a new buffer, since we're going to | |
| 297 // give the data to a background thread. | |
| 298 uint8_t* copied_data = new uint8_t[length]; | |
| 299 memcpy(copied_data, data, length); | |
| 300 data_queue_.Produce(copied_data, length); | |
| 301 | |
| 302 queue_tail_position_ += length; | |
| 303 } | |
| 304 } | |
| 305 | |
| 306 // For coordinating between the main thread and background thread tasks. | |
| 307 // Guards m_cancelled and m_queueTailPosition. | |
| 308 Mutex mutex_; | |
| 309 | |
| 310 // The shared buffer containing the resource data + state variables. | |
| 311 // Used by both threads, guarded by m_mutex. | |
| 312 bool cancelled_; | |
| 313 bool finished_; | |
| 314 | |
| 315 RefPtr<const SharedBuffer> resource_buffer_; // Only used by the main thread. | |
| 316 | |
| 317 // The queue contains the data to be passed to the V8 thread. | |
| 318 // queueLeadPosition: data we have handed off to the V8 thread. | |
| 319 // queueTailPosition: end of data we have enqued in the queue. | |
| 320 // bookmarkPosition: position of the bookmark. | |
| 321 SourceStreamDataQueue data_queue_; // Thread safe. | |
| 322 size_t queue_lead_position_; // Only used by v8 thread. | |
| 323 size_t queue_tail_position_; // Used by both threads; guarded by m_mutex. | |
| 324 | |
| 325 RefPtr<WebTaskRunner> loading_task_runner_; | |
| 326 }; | |
| 327 | |
| 328 size_t ScriptStreamer::small_script_threshold_ = 30 * 1024; | |
| 329 | |
| 330 void ScriptStreamer::StartStreaming(PendingScript* script, | |
| 331 Type script_type, | |
| 332 Settings* settings, | |
| 333 ScriptState* script_state, | |
| 334 RefPtr<WebTaskRunner> loading_task_runner) { | |
| 335 // We don't yet know whether the script will really be streamed. E.g., | |
| 336 // suppressing streaming for short scripts is done later. Record only the | |
| 337 // sure negative cases here. | |
| 338 bool started_streaming = | |
| 339 StartStreamingInternal(script, script_type, settings, script_state, | |
| 340 std::move(loading_task_runner)); | |
| 341 if (!started_streaming) | |
| 342 RecordStartedStreamingHistogram(script_type, 0); | |
| 343 } | |
| 344 | |
| 345 bool ScriptStreamer::ConvertEncoding( | |
| 346 const char* encoding_name, | |
| 347 v8::ScriptCompiler::StreamedSource::Encoding* encoding) { | |
| 348 // Here's a list of encodings we can use for streaming. These are | |
| 349 // the canonical names. | |
| 350 if (strcmp(encoding_name, "windows-1252") == 0 || | |
| 351 strcmp(encoding_name, "ISO-8859-1") == 0 || | |
| 352 strcmp(encoding_name, "US-ASCII") == 0) { | |
| 353 *encoding = v8::ScriptCompiler::StreamedSource::ONE_BYTE; | |
| 354 return true; | |
| 355 } | |
| 356 if (strcmp(encoding_name, "UTF-8") == 0) { | |
| 357 *encoding = v8::ScriptCompiler::StreamedSource::UTF8; | |
| 358 return true; | |
| 359 } | |
| 360 // We don't stream other encodings; especially we don't stream two | |
| 361 // byte scripts to avoid the handling of endianness. Most scripts | |
| 362 // are Latin1 or UTF-8 anyway, so this should be enough for most | |
| 363 // real world purposes. | |
| 364 return false; | |
| 365 } | |
| 366 | |
| 367 bool ScriptStreamer::IsFinished() const { | |
| 368 DCHECK(IsMainThread()); | |
| 369 return loading_finished_ && (parsing_finished_ || streaming_suppressed_); | |
| 370 } | |
| 371 | |
| 372 void ScriptStreamer::StreamingCompleteOnBackgroundThread() { | |
| 373 DCHECK(!IsMainThread()); | |
| 374 | |
| 375 // notifyFinished might already be called, or it might be called in the | |
| 376 // future (if the parsing finishes earlier because of a parse error). | |
| 377 loading_task_runner_->PostTask( | |
| 378 BLINK_FROM_HERE, CrossThreadBind(&ScriptStreamer::StreamingComplete, | |
| 379 WrapCrossThreadPersistent(this))); | |
| 380 | |
| 381 // The task might delete ScriptStreamer, so it's not safe to do anything | |
| 382 // after posting it. Note that there's no way to guarantee that this | |
| 383 // function has returned before the task is ran - however, we should not | |
| 384 // access the "this" object after posting the task. | |
| 385 } | |
| 386 | |
| 387 void ScriptStreamer::Cancel() { | |
| 388 DCHECK(IsMainThread()); | |
| 389 // The upper layer doesn't need the script any more, but streaming might | |
| 390 // still be ongoing. Tell SourceStream to try to cancel it whenever it gets | |
| 391 // the control the next time. It can also be that V8 has already completed | |
| 392 // its operations and streamingComplete will be called soon. | |
| 393 detached_ = true; | |
| 394 resource_ = 0; | |
| 395 if (stream_) | |
| 396 stream_->Cancel(); | |
| 397 } | |
| 398 | |
| 399 void ScriptStreamer::SuppressStreaming() { | |
| 400 DCHECK(IsMainThread()); | |
| 401 DCHECK(!loading_finished_); | |
| 402 // It can be that the parsing task has already finished (e.g., if there was | |
| 403 // a parse error). | |
| 404 streaming_suppressed_ = true; | |
| 405 } | |
| 406 | |
| 407 void ScriptStreamer::NotifyAppendData(ScriptResource* resource) { | |
| 408 DCHECK(IsMainThread()); | |
| 409 CHECK_EQ(resource_, resource); | |
| 410 if (streaming_suppressed_) | |
| 411 return; | |
| 412 if (!have_enough_data_for_streaming_) { | |
| 413 // Even if the first data chunk is small, the script can still be big | |
| 414 // enough - wait until the next data chunk comes before deciding whether | |
| 415 // to start the streaming. | |
| 416 DCHECK(resource->ResourceBuffer()); | |
| 417 if (resource->ResourceBuffer()->size() < small_script_threshold_) | |
| 418 return; | |
| 419 have_enough_data_for_streaming_ = true; | |
| 420 | |
| 421 { | |
| 422 // Check for BOM (byte order marks), because that might change our | |
| 423 // understanding of the data encoding. | |
| 424 constexpr size_t kMaximumLengthOfBOM = 4; | |
| 425 char maybe_bom[kMaximumLengthOfBOM] = {}; | |
| 426 if (!resource->ResourceBuffer()->GetPartAsBytes( | |
| 427 maybe_bom, static_cast<size_t>(0), kMaximumLengthOfBOM)) { | |
| 428 NOTREACHED(); | |
| 429 return; | |
| 430 } | |
| 431 | |
| 432 std::unique_ptr<TextResourceDecoder> decoder(TextResourceDecoder::Create( | |
| 433 "application/javascript", resource->Encoding())); | |
| 434 decoder->CheckForBOM(maybe_bom, kMaximumLengthOfBOM); | |
| 435 | |
| 436 // The encoding may change when we see the BOM. Check for BOM now | |
| 437 // and update the encoding from the decoder when necessary. Supress | |
| 438 // streaming if the encoding is unsupported. | |
| 439 // | |
| 440 // Also note that have at least s_smallScriptThreshold worth of | |
| 441 // data, which is more than enough for detecting a BOM. | |
| 442 if (!ConvertEncoding(decoder->Encoding().GetName(), &encoding_)) { | |
| 443 SuppressStreaming(); | |
| 444 RecordNotStreamingReasonHistogram(script_type_, kEncodingNotSupported); | |
| 445 RecordStartedStreamingHistogram(script_type_, 0); | |
| 446 return; | |
| 447 } | |
| 448 } | |
| 449 | |
| 450 if (ScriptStreamerThread::Shared()->IsRunningTask()) { | |
| 451 // At the moment we only have one thread for running the tasks. A | |
| 452 // new task shouldn't be queued before the running task completes, | |
| 453 // because the running task can block and wait for data from the | |
| 454 // network. | |
| 455 SuppressStreaming(); | |
| 456 RecordNotStreamingReasonHistogram(script_type_, kThreadBusy); | |
| 457 RecordStartedStreamingHistogram(script_type_, 0); | |
| 458 return; | |
| 459 } | |
| 460 | |
| 461 if (!script_state_->ContextIsValid()) { | |
| 462 SuppressStreaming(); | |
| 463 RecordNotStreamingReasonHistogram(script_type_, kContextNotValid); | |
| 464 RecordStartedStreamingHistogram(script_type_, 0); | |
| 465 return; | |
| 466 } | |
| 467 | |
| 468 DCHECK(!stream_); | |
| 469 DCHECK(!source_); | |
| 470 stream_ = new SourceStream(loading_task_runner_.Get()); | |
| 471 // m_source takes ownership of m_stream. | |
| 472 source_ = WTF::WrapUnique( | |
| 473 new v8::ScriptCompiler::StreamedSource(stream_, encoding_)); | |
| 474 | |
| 475 ScriptState::Scope scope(script_state_.Get()); | |
| 476 std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> | |
| 477 script_streaming_task( | |
| 478 WTF::WrapUnique(v8::ScriptCompiler::StartStreamingScript( | |
| 479 script_state_->GetIsolate(), source_.get(), compile_options_))); | |
| 480 if (!script_streaming_task) { | |
| 481 // V8 cannot stream the script. | |
| 482 SuppressStreaming(); | |
| 483 stream_ = 0; | |
| 484 source_.reset(); | |
| 485 RecordNotStreamingReasonHistogram(script_type_, kV8CannotStream); | |
| 486 RecordStartedStreamingHistogram(script_type_, 0); | |
| 487 return; | |
| 488 } | |
| 489 | |
| 490 ScriptStreamerThread::Shared()->PostTask( | |
| 491 CrossThreadBind(&ScriptStreamerThread::RunScriptStreamingTask, | |
| 492 WTF::Passed(std::move(script_streaming_task)), | |
| 493 WrapCrossThreadPersistent(this))); | |
| 494 RecordStartedStreamingHistogram(script_type_, 1); | |
| 495 } | |
| 496 if (stream_) | |
| 497 stream_->DidReceiveData(this); | |
| 498 } | |
| 499 | |
| 500 void ScriptStreamer::NotifyFinished(Resource* resource) { | |
| 501 DCHECK(IsMainThread()); | |
| 502 CHECK_EQ(resource_, resource); | |
| 503 // A special case: empty and small scripts. We didn't receive enough data to | |
| 504 // start the streaming before this notification. In that case, there won't | |
| 505 // be a "parsing complete" notification either, and we should not wait for | |
| 506 // it. | |
| 507 if (!have_enough_data_for_streaming_) { | |
| 508 RecordNotStreamingReasonHistogram(script_type_, kScriptTooSmall); | |
| 509 RecordStartedStreamingHistogram(script_type_, 0); | |
| 510 SuppressStreaming(); | |
| 511 } | |
| 512 if (stream_) | |
| 513 stream_->DidFinishLoading(); | |
| 514 loading_finished_ = true; | |
| 515 | |
| 516 NotifyFinishedToClient(); | |
| 517 } | |
| 518 | |
| 519 ScriptStreamer::ScriptStreamer( | |
| 520 PendingScript* script, | |
| 521 Type script_type, | |
| 522 ScriptState* script_state, | |
| 523 v8::ScriptCompiler::CompileOptions compile_options, | |
| 524 RefPtr<WebTaskRunner> loading_task_runner) | |
| 525 : pending_script_(script), | |
| 526 resource_(script->GetResource()), | |
| 527 detached_(false), | |
| 528 stream_(0), | |
| 529 loading_finished_(false), | |
| 530 parsing_finished_(false), | |
| 531 have_enough_data_for_streaming_(false), | |
| 532 streaming_suppressed_(false), | |
| 533 compile_options_(compile_options), | |
| 534 script_state_(script_state), | |
| 535 script_type_(script_type), | |
| 536 script_url_string_(resource_->Url().Copy().GetString()), | |
| 537 script_resource_identifier_(resource_->Identifier()), | |
| 538 // Unfortunately there's no dummy encoding value in the enum; let's use | |
| 539 // one we don't stream. | |
| 540 encoding_(v8::ScriptCompiler::StreamedSource::TWO_BYTE), | |
| 541 loading_task_runner_(std::move(loading_task_runner)) {} | |
| 542 | |
| 543 ScriptStreamer::~ScriptStreamer() {} | |
| 544 | |
| 545 DEFINE_TRACE(ScriptStreamer) { | |
| 546 visitor->Trace(pending_script_); | |
| 547 visitor->Trace(resource_); | |
| 548 } | |
| 549 | |
| 550 void ScriptStreamer::StreamingComplete() { | |
| 551 // The background task is completed; do the necessary ramp-down in the main | |
| 552 // thread. | |
| 553 DCHECK(IsMainThread()); | |
| 554 parsing_finished_ = true; | |
| 555 | |
| 556 // It's possible that the corresponding Resource was deleted before V8 | |
| 557 // finished streaming. In that case, the data or the notification is not | |
| 558 // needed. In addition, if the streaming is suppressed, the non-streaming | |
| 559 // code path will resume after the resource has loaded, before the | |
| 560 // background task finishes. | |
| 561 if (detached_ || streaming_suppressed_) | |
| 562 return; | |
| 563 | |
| 564 // We have now streamed the whole script to V8 and it has parsed the | |
| 565 // script. We're ready for the next step: compiling and executing the | |
| 566 // script. | |
| 567 NotifyFinishedToClient(); | |
| 568 } | |
| 569 | |
| 570 void ScriptStreamer::NotifyFinishedToClient() { | |
| 571 DCHECK(IsMainThread()); | |
| 572 // Usually, the loading will be finished first, and V8 will still need some | |
| 573 // time to catch up. But the other way is possible too: if V8 detects a | |
| 574 // parse error, the V8 side can complete before loading has finished. Send | |
| 575 // the notification after both loading and V8 side operations have | |
| 576 // completed. | |
| 577 if (!IsFinished()) | |
| 578 return; | |
| 579 | |
| 580 pending_script_->StreamingFinished(); | |
| 581 } | |
| 582 | |
| 583 bool ScriptStreamer::StartStreamingInternal( | |
| 584 PendingScript* script, | |
| 585 Type script_type, | |
| 586 Settings* settings, | |
| 587 ScriptState* script_state, | |
| 588 RefPtr<WebTaskRunner> loading_task_runner) { | |
| 589 DCHECK(IsMainThread()); | |
| 590 DCHECK(script_state->ContextIsValid()); | |
| 591 ScriptResource* resource = script->GetResource(); | |
| 592 if (resource->IsLoaded()) { | |
| 593 RecordNotStreamingReasonHistogram(script_type, kAlreadyLoaded); | |
| 594 return false; | |
| 595 } | |
| 596 if (!resource->Url().ProtocolIsInHTTPFamily()) { | |
| 597 RecordNotStreamingReasonHistogram(script_type, kNotHTTP); | |
| 598 return false; | |
| 599 } | |
| 600 if (resource->IsCacheValidator()) { | |
| 601 RecordNotStreamingReasonHistogram(script_type, kReload); | |
| 602 // This happens e.g., during reloads. We're actually not going to load | |
| 603 // the current Resource of the PendingScript but switch to another | |
| 604 // Resource -> don't stream. | |
| 605 return false; | |
| 606 } | |
| 607 // We cannot filter out short scripts, even if we wait for the HTTP headers | |
| 608 // to arrive: the Content-Length HTTP header is not sent for chunked | |
| 609 // downloads. | |
| 610 | |
| 611 // Decide what kind of cached data we should produce while streaming. Only | |
| 612 // produce parser cache if the non-streaming compile takes advantage of it. | |
| 613 v8::ScriptCompiler::CompileOptions compile_option = | |
| 614 v8::ScriptCompiler::kNoCompileOptions; | |
| 615 if (settings->GetV8CacheOptions() == kV8CacheOptionsParse) | |
| 616 compile_option = v8::ScriptCompiler::kProduceParserCache; | |
| 617 | |
| 618 // The Resource might go out of scope if the script is no longer | |
| 619 // needed. This makes PendingScript notify the ScriptStreamer when it is | |
| 620 // destroyed. | |
| 621 script->SetStreamer(ScriptStreamer::Create(script, script_type, script_state, | |
| 622 compile_option, | |
| 623 std::move(loading_task_runner))); | |
| 624 | |
| 625 return true; | |
| 626 } | |
| 627 | |
| 628 } // namespace blink | |
| OLD | NEW |