Index: third_party/WebKit/Source/core/dom/PendingScript.cpp |
diff --git a/third_party/WebKit/Source/core/dom/PendingScript.cpp b/third_party/WebKit/Source/core/dom/PendingScript.cpp |
index cb907e201d15a4e3f91027ce8752cb158718b415..c3471192149d5137d2587196893412edf0d0d8b1 100644 |
--- a/third_party/WebKit/Source/core/dom/PendingScript.cpp |
+++ b/third_party/WebKit/Source/core/dom/PendingScript.cpp |
@@ -25,37 +25,79 @@ |
#include "core/dom/PendingScript.h" |
+#include "bindings/core/v8/ScriptSourceCode.h" |
+#include "bindings/core/v8/ScriptState.h" |
+#include "bindings/core/v8/V8Binding.h" |
+#include "core/dom/ClassicScript.h" |
+#include "core/dom/Document.h" |
#include "core/dom/ScriptElementBase.h" |
+#include "core/dom/TaskRunnerHelper.h" |
+#include "core/frame/LocalFrame.h" |
+#include "core/frame/SubresourceIntegrity.h" |
+#include "platform/SharedBuffer.h" |
#include "platform/wtf/CurrentTime.h" |
namespace blink { |
+PendingScript* PendingScript::Create(ScriptElementBase* element, |
+ ScriptResource* resource) { |
+ return new PendingScript(element, resource, TextPosition()); |
+} |
+ |
+PendingScript* PendingScript::Create(ScriptElementBase* element, |
+ const TextPosition& starting_position) { |
+ return new PendingScript(element, nullptr, starting_position); |
+} |
+ |
+PendingScript* PendingScript::CreateForTesting(ScriptResource* resource) { |
+ return new PendingScript(nullptr, resource, TextPosition(), true); |
+} |
+ |
PendingScript::PendingScript(ScriptElementBase* element, |
- const TextPosition& starting_position) |
+ ScriptResource* resource, |
+ const TextPosition& starting_position, |
+ bool is_for_testing) |
: watching_for_load_(false), |
element_(element), |
starting_position_(starting_position), |
+ integrity_failure_(false), |
parser_blocking_load_start_time_(0), |
- client_(nullptr) {} |
+ client_(nullptr), |
+ is_for_testing_(is_for_testing) { |
+ CheckState(); |
+ SetResource(resource); |
+ MemoryCoordinator::Instance().RegisterClient(this); |
+} |
PendingScript::~PendingScript() {} |
+ |
+NOINLINE void PendingScript::CheckState() const { |
+ // TODO(hiroshige): Turn these CHECK()s into DCHECK() before going to beta. |
+ CHECK(is_for_testing_ || element_); |
+ CHECK(GetResource() || !streamer_); |
+ CHECK(!streamer_ || streamer_->GetResource() == GetResource()); |
+} |
void PendingScript::Dispose() { |
StopWatchingForLoad(); |
- DCHECK(!Client()); |
- DCHECK(!IsWatchingForLoad()); |
- |
+ DCHECK(!client_); |
+ DCHECK(!watching_for_load_); |
+ |
+ MemoryCoordinator::Instance().UnregisterClient(this); |
+ SetResource(nullptr); |
starting_position_ = TextPosition::BelowRangePosition(); |
+ integrity_failure_ = false; |
parser_blocking_load_start_time_ = 0; |
- |
- DisposeInternal(); |
+ if (streamer_) |
+ streamer_->Cancel(); |
+ streamer_ = nullptr; |
element_ = nullptr; |
} |
void PendingScript::WatchForLoad(PendingScriptClient* client) { |
CheckState(); |
- DCHECK(!IsWatchingForLoad()); |
+ DCHECK(!watching_for_load_); |
// addClient() will call streamingFinished() if the load is complete. Callers |
// who do not expect to be re-entered from this call should not call |
// watchForLoad for a PendingScript which isReady. We also need to set |
@@ -68,10 +110,10 @@ |
} |
void PendingScript::StopWatchingForLoad() { |
- if (!IsWatchingForLoad()) |
- return; |
- CheckState(); |
- DCHECK(IsExternal()); |
+ if (!watching_for_load_) |
+ return; |
+ CheckState(); |
+ DCHECK(GetResource()); |
client_ = nullptr; |
watching_for_load_ = false; |
} |
@@ -84,14 +126,173 @@ |
return element_.Get(); |
} |
+void PendingScript::StreamingFinished() { |
+ CheckState(); |
+ DCHECK(GetResource()); |
+ if (client_) |
+ client_->PendingScriptFinished(this); |
+} |
+ |
void PendingScript::MarkParserBlockingLoadStartTime() { |
DCHECK_EQ(parser_blocking_load_start_time_, 0.0); |
parser_blocking_load_start_time_ = MonotonicallyIncreasingTime(); |
} |
+// Returns true if SRI check passed. |
+static bool CheckScriptResourceIntegrity(Resource* resource, |
+ ScriptElementBase* element) { |
+ DCHECK_EQ(resource->GetType(), Resource::kScript); |
+ ScriptResource* script_resource = ToScriptResource(resource); |
+ String integrity_attr = element->IntegrityAttributeValue(); |
+ |
+ // It is possible to get back a script resource with integrity metadata |
+ // for a request with an empty integrity attribute. In that case, the |
+ // integrity check should be skipped, so this check ensures that the |
+ // integrity attribute isn't empty in addition to checking if the |
+ // resource has empty integrity metadata. |
+ if (integrity_attr.IsEmpty() || |
+ script_resource->IntegrityMetadata().IsEmpty()) |
+ return true; |
+ |
+ switch (script_resource->IntegrityDisposition()) { |
+ case ResourceIntegrityDisposition::kPassed: |
+ return true; |
+ |
+ case ResourceIntegrityDisposition::kFailed: |
+ // TODO(jww): This should probably also generate a console |
+ // message identical to the one produced by |
+ // CheckSubresourceIntegrity below. See https://crbug.com/585267. |
+ return false; |
+ |
+ case ResourceIntegrityDisposition::kNotChecked: { |
+ if (!resource->ResourceBuffer()) |
+ return true; |
+ |
+ bool passed = SubresourceIntegrity::CheckSubresourceIntegrity( |
+ script_resource->IntegrityMetadata(), element->GetDocument(), |
+ resource->ResourceBuffer()->Data(), |
+ resource->ResourceBuffer()->size(), resource->Url(), *resource); |
+ script_resource->SetIntegrityDisposition( |
+ passed ? ResourceIntegrityDisposition::kPassed |
+ : ResourceIntegrityDisposition::kFailed); |
+ return passed; |
+ } |
+ } |
+ |
+ NOTREACHED(); |
+ return true; |
+} |
+ |
+void PendingScript::NotifyFinished(Resource* resource) { |
+ // The following SRI checks need to be here because, unfortunately, fetches |
+ // are not done purely according to the Fetch spec. In particular, |
+ // different requests for the same resource do not have different |
+ // responses; the memory cache can (and will) return the exact same |
+ // Resource object. |
+ // |
+ // For different requests, the same Resource object will be returned and |
+ // will not be associated with the particular request. Therefore, when the |
+ // body of the response comes in, there's no way to validate the integrity |
+ // of the Resource object against a particular request (since there may be |
+ // several pending requests all tied to the identical object, and the |
+ // actual requests are not stored). |
+ // |
+ // In order to simulate the correct behavior, Blink explicitly does the SRI |
+ // checks here, when a PendingScript tied to a particular request is |
+ // finished (and in the case of a StyleSheet, at the point of execution), |
+ // while having proper Fetch checks in the fetch module for use in the |
+ // fetch JavaScript API. In a future world where the ResourceFetcher uses |
+ // the Fetch algorithm, this should be fixed by having separate Response |
+ // objects (perhaps attached to identical Resource objects) per request. |
+ // |
+ // See https://crbug.com/500701 for more information. |
+ CheckState(); |
+ if (element_) { |
+ integrity_failure_ = !CheckScriptResourceIntegrity(resource, element_); |
+ } |
+ |
+ // If script streaming is in use, the client will be notified in |
+ // streamingFinished. |
+ if (streamer_) |
+ streamer_->NotifyFinished(resource); |
+ else if (client_) |
+ client_->PendingScriptFinished(this); |
+} |
+ |
+void PendingScript::NotifyAppendData(ScriptResource* resource) { |
+ if (streamer_) |
+ streamer_->NotifyAppendData(resource); |
+} |
+ |
DEFINE_TRACE(PendingScript) { |
visitor->Trace(element_); |
+ visitor->Trace(streamer_); |
visitor->Trace(client_); |
+ ResourceOwner<ScriptResource>::Trace(visitor); |
+ MemoryCoordinatorClient::Trace(visitor); |
+} |
+ |
+ClassicScript* PendingScript::GetSource(const KURL& document_url, |
+ bool& error_occurred) const { |
+ CheckState(); |
+ |
+ error_occurred = this->ErrorOccurred(); |
+ if (GetResource()) { |
+ DCHECK(GetResource()->IsLoaded()); |
+ if (streamer_ && !streamer_->StreamingSuppressed()) |
+ return ClassicScript::Create(ScriptSourceCode(streamer_, GetResource())); |
+ return ClassicScript::Create(ScriptSourceCode(GetResource())); |
+ } |
+ |
+ return ClassicScript::Create(ScriptSourceCode( |
+ element_->TextContent(), document_url, StartingPosition())); |
+} |
+ |
+void PendingScript::SetStreamer(ScriptStreamer* streamer) { |
+ DCHECK(!streamer_); |
+ DCHECK(!watching_for_load_); |
+ streamer_ = streamer; |
+ CheckState(); |
+} |
+ |
+bool PendingScript::IsReady() const { |
+ CheckState(); |
+ if (GetResource()) { |
+ return GetResource()->IsLoaded() && (!streamer_ || streamer_->IsFinished()); |
+ } |
+ |
+ return true; |
+} |
+ |
+bool PendingScript::ErrorOccurred() const { |
+ CheckState(); |
+ if (GetResource()) |
+ return GetResource()->ErrorOccurred() || integrity_failure_; |
+ |
+ return false; |
+} |
+ |
+void PendingScript::OnPurgeMemory() { |
+ CheckState(); |
+ if (!streamer_) |
+ return; |
+ streamer_->Cancel(); |
+ streamer_ = nullptr; |
+} |
+ |
+void PendingScript::StartStreamingIfPossible( |
+ Document* document, |
+ ScriptStreamer::Type streamer_type) { |
+ if (!document->GetFrame()) |
+ return; |
+ |
+ ScriptState* script_state = ToScriptStateForMainWorld(document->GetFrame()); |
+ if (!script_state) |
+ return; |
+ |
+ ScriptStreamer::StartStreaming( |
+ this, streamer_type, document->GetFrame()->GetSettings(), script_state, |
+ TaskRunnerHelper::Get(TaskType::kNetworking, document)); |
} |
} // namespace blink |