blob: 714c66ae3aa6f304a7973124934107ae5a6377b5 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_STREAMER_H_
#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_STREAMER_H_
#include <memory>
#include <tuple>
#include "base/macros.h"
#include "base/single_thread_task_runner.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/script/script_scheduling_type.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "v8/include/v8.h"
namespace mojo {
class SimpleWatcher;
}
namespace blink {
class ScriptResource;
class SourceStream;
class ResponseBodyLoaderClient;
// ScriptStreamer streams incomplete script data to V8 so that it can be parsed
// while it's loaded. ScriptResource holds a reference to ScriptStreamer. If the
// Document and the ClassicPendingScript are destroyed while the streaming is in
// progress, and ScriptStreamer handles it gracefully.
class CORE_EXPORT ScriptStreamer final
: public GarbageCollected<ScriptStreamer> {
USING_PRE_FINALIZER(ScriptStreamer, Prefinalize);
public:
// For tracking why some scripts are not streamed. Not streaming is part of
// normal operation (e.g., script already loaded, script too small) and
// doesn't necessarily indicate a failure.
enum class NotStreamingReason {
kAlreadyLoaded, // DEPRECATED
kNotHTTP,
kRevalidate,
kContextNotValid, // DEPRECATED
kEncodingNotSupported,
kThreadBusy, // DEPRECATED
kV8CannotStream,
kScriptTooSmall,
kNoResourceBuffer,
kHasCodeCache,
kStreamerNotReadyOnGetSource, // DEPRECATED
kInlineScript,
kDidntTryToStartStreaming, // DEPRECATED
kErrorOccurred,
kStreamingDisabled,
kSecondScriptResourceUse,
kWorkerTopLevelScript,
kModuleScript,
kNoDataPipe,
kLoadingCancelled,
kNonJavascriptModule,
kDisabledByFeatureList,
// Pseudo values that should never be seen in reported metrics
kMaxValue = kDisabledByFeatureList,
kInvalid = -1,
};
ScriptStreamer(
ScriptResource* resource,
mojo::ScopedDataPipeConsumerHandle data_pipe,
ResponseBodyLoaderClient* response_body_loader_client,
scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner);
~ScriptStreamer();
void Trace(Visitor*) const;
static std::tuple<ScriptStreamer*, NotStreamingReason> TakeFrom(
ScriptResource*);
static void RecordStreamingHistogram(ScriptSchedulingType type,
bool can_use_streamer,
ScriptStreamer::NotStreamingReason);
// Returns false if we cannot stream the given encoding.
static bool ConvertEncoding(const char* encoding_name,
v8::ScriptCompiler::StreamedSource::Encoding*);
bool IsStreamingStarted() const; // Have we actually started streaming?
bool CanStartStreaming() const; // Can we still start streaming later?
bool IsLoaded() const; // Has loading finished?
bool IsFinished() const; // Has loading & streaming finished?
bool IsStreamingSuppressed() const; // Has streaming been suppressed?
v8::ScriptCompiler::StreamedSource* Source() { return source_.get(); }
// Called when the script is not needed any more (e.g., loading was
// cancelled). After calling cancel, ClassicPendingScript can drop its
// reference to ScriptStreamer, and ScriptStreamer takes care of eventually
// deleting itself (after the V8 side has finished too).
void Cancel();
NotStreamingReason StreamingSuppressedReason() const {
CheckState();
return suppressed_reason_;
}
const String& ScriptURLString() const { return script_url_string_; }
uint64_t ScriptResourceIdentifier() const {
return script_resource_identifier_;
}
static void SetSmallScriptThresholdForTesting(size_t threshold) {
small_script_threshold_ = threshold;
}
private:
friend class SourceStream;
// Valid loading state transitions:
//
// kLoading
// .--------|---------.
// | | |
// v v v
// kLoaded kFailed kCancelled
enum class LoadingState { kLoading, kLoaded, kFailed, kCancelled };
static const char* str(LoadingState state) {
switch (state) {
case LoadingState::kLoading:
return "Loading";
case LoadingState::kLoaded:
return "Loaded";
case LoadingState::kFailed:
return "Failed";
case LoadingState::kCancelled:
return "Cancelled";
}
}
// Scripts whose first data chunk is smaller than this constant won't be
// streamed. Non-const for testing.
static size_t small_script_threshold_;
// Maximum size of the BOM marker.
static constexpr size_t kMaximumLengthOfBOM = 4;
static void RunScriptStreamingTask(
std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> task,
ScriptStreamer* streamer,
SourceStream* stream);
void OnDataPipeReadable(MojoResult result,
const mojo::HandleSignalsState& state);
// Given the data we have collected already, try to start an actual V8
// streaming task. Returns true if the task was started.
bool TryStartStreamingTask();
void Prefinalize();
// When the streaming is suppressed, the data is not given to V8, but
// ScriptStreamer still watches the resource load and notifies the upper
// layers when loading is finished. It is used in situations when we have
// started streaming but then we detect we don't want to stream (e.g., when
// we have the code cache for the script) and we still want to parse and
// execute it when it has finished loading.
void SuppressStreaming(NotStreamingReason reason);
// Called by ScriptStreamingTask when it has streamed all data to V8 and V8
// has processed it.
void StreamingCompleteOnBackgroundThread(LoadingState loading_state);
// The four methods below should not be called synchronously, as they can
// trigger script resource client callbacks.
// Streaming completed with loading in the given |state|.
void StreamingComplete(LoadingState loading_state);
// Loading completed in the given state, without ever starting streaming.
void LoadCompleteWithoutStreaming(LoadingState loading_state,
NotStreamingReason no_streaming_reason);
// Helper for the above methods to notify the client that loading has
// completed in the given state. Streaming is guaranteed to either have
// completed or be suppressed.
void SendClientLoadFinishedCallback();
bool HasEnoughDataForStreaming(size_t resource_buffer_size);
// Has the script streamer been detached from its client. If true, then we can
// safely abort loading and not output any more data.
bool IsClientDetached() const;
void AdvanceLoadingState(LoadingState new_state);
void CheckState() const;
LoadingState loading_state_ = LoadingState::kLoading;
Member<ScriptResource> script_resource_;
Member<ResponseBodyLoaderClient> response_body_loader_client_;
// Fields active during asynchronous (non-streaming) reads.
mojo::ScopedDataPipeConsumerHandle data_pipe_;
std::unique_ptr<mojo::SimpleWatcher> watcher_;
// Fields active during streaming.
SourceStream* stream_ = nullptr;
std::unique_ptr<v8::ScriptCompiler::StreamedSource> source_;
// The reason that streaming is disabled
NotStreamingReason suppressed_reason_ = NotStreamingReason::kInvalid;
// Keep the script URL string for event tracing.
const String script_url_string_;
// Keep the script resource dentifier for event tracing.
const uint64_t script_resource_identifier_;
// Encoding of the streamed script. Saved for sanity checking purposes.
v8::ScriptCompiler::StreamedSource::Encoding encoding_;
scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner_;
DISALLOW_COPY_AND_ASSIGN(ScriptStreamer);
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_STREAMER_H_