OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2010 Google, Inc. All Rights Reserved. | 2 * Copyright (C) 2010 Google, Inc. All Rights Reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
11 * documentation and/or other materials provided with the distribution. | 11 * documentation and/or other materials provided with the distribution. |
12 * | 12 * |
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 */ | 24 */ |
25 | 25 |
26 #include "core/dom/PendingScript.h" | 26 #include "core/dom/PendingScript.h" |
27 | 27 |
| 28 #include "bindings/core/v8/ScriptSourceCode.h" |
| 29 #include "bindings/core/v8/ScriptState.h" |
| 30 #include "bindings/core/v8/V8Binding.h" |
| 31 #include "core/dom/ClassicScript.h" |
| 32 #include "core/dom/Document.h" |
28 #include "core/dom/ScriptElementBase.h" | 33 #include "core/dom/ScriptElementBase.h" |
| 34 #include "core/dom/TaskRunnerHelper.h" |
| 35 #include "core/frame/LocalFrame.h" |
| 36 #include "core/frame/SubresourceIntegrity.h" |
| 37 #include "platform/SharedBuffer.h" |
29 #include "platform/wtf/CurrentTime.h" | 38 #include "platform/wtf/CurrentTime.h" |
30 | 39 |
31 namespace blink { | 40 namespace blink { |
32 | 41 |
| 42 PendingScript* PendingScript::Create(ScriptElementBase* element, |
| 43 ScriptResource* resource) { |
| 44 return new PendingScript(element, resource, TextPosition()); |
| 45 } |
| 46 |
| 47 PendingScript* PendingScript::Create(ScriptElementBase* element, |
| 48 const TextPosition& starting_position) { |
| 49 return new PendingScript(element, nullptr, starting_position); |
| 50 } |
| 51 |
| 52 PendingScript* PendingScript::CreateForTesting(ScriptResource* resource) { |
| 53 return new PendingScript(nullptr, resource, TextPosition(), true); |
| 54 } |
| 55 |
33 PendingScript::PendingScript(ScriptElementBase* element, | 56 PendingScript::PendingScript(ScriptElementBase* element, |
34 const TextPosition& starting_position) | 57 ScriptResource* resource, |
| 58 const TextPosition& starting_position, |
| 59 bool is_for_testing) |
35 : watching_for_load_(false), | 60 : watching_for_load_(false), |
36 element_(element), | 61 element_(element), |
37 starting_position_(starting_position), | 62 starting_position_(starting_position), |
| 63 integrity_failure_(false), |
38 parser_blocking_load_start_time_(0), | 64 parser_blocking_load_start_time_(0), |
39 client_(nullptr) {} | 65 client_(nullptr), |
| 66 is_for_testing_(is_for_testing) { |
| 67 CheckState(); |
| 68 SetResource(resource); |
| 69 MemoryCoordinator::Instance().RegisterClient(this); |
| 70 } |
40 | 71 |
41 PendingScript::~PendingScript() {} | 72 PendingScript::~PendingScript() {} |
42 | 73 |
| 74 NOINLINE void PendingScript::CheckState() const { |
| 75 // TODO(hiroshige): Turn these CHECK()s into DCHECK() before going to beta. |
| 76 CHECK(is_for_testing_ || element_); |
| 77 CHECK(GetResource() || !streamer_); |
| 78 CHECK(!streamer_ || streamer_->GetResource() == GetResource()); |
| 79 } |
| 80 |
43 void PendingScript::Dispose() { | 81 void PendingScript::Dispose() { |
44 StopWatchingForLoad(); | 82 StopWatchingForLoad(); |
45 DCHECK(!Client()); | 83 DCHECK(!client_); |
46 DCHECK(!IsWatchingForLoad()); | 84 DCHECK(!watching_for_load_); |
47 | 85 |
| 86 MemoryCoordinator::Instance().UnregisterClient(this); |
| 87 SetResource(nullptr); |
48 starting_position_ = TextPosition::BelowRangePosition(); | 88 starting_position_ = TextPosition::BelowRangePosition(); |
| 89 integrity_failure_ = false; |
49 parser_blocking_load_start_time_ = 0; | 90 parser_blocking_load_start_time_ = 0; |
50 | 91 if (streamer_) |
51 DisposeInternal(); | 92 streamer_->Cancel(); |
| 93 streamer_ = nullptr; |
52 element_ = nullptr; | 94 element_ = nullptr; |
53 } | 95 } |
54 | 96 |
55 void PendingScript::WatchForLoad(PendingScriptClient* client) { | 97 void PendingScript::WatchForLoad(PendingScriptClient* client) { |
56 CheckState(); | 98 CheckState(); |
57 | 99 |
58 DCHECK(!IsWatchingForLoad()); | 100 DCHECK(!watching_for_load_); |
59 // addClient() will call streamingFinished() if the load is complete. Callers | 101 // addClient() will call streamingFinished() if the load is complete. Callers |
60 // who do not expect to be re-entered from this call should not call | 102 // who do not expect to be re-entered from this call should not call |
61 // watchForLoad for a PendingScript which isReady. We also need to set | 103 // watchForLoad for a PendingScript which isReady. We also need to set |
62 // m_watchingForLoad early, since addClient() can result in calling | 104 // m_watchingForLoad early, since addClient() can result in calling |
63 // notifyFinished and further stopWatchingForLoad(). | 105 // notifyFinished and further stopWatchingForLoad(). |
64 watching_for_load_ = true; | 106 watching_for_load_ = true; |
65 client_ = client; | 107 client_ = client; |
66 if (IsReady()) | 108 if (IsReady()) |
67 client_->PendingScriptFinished(this); | 109 client_->PendingScriptFinished(this); |
68 } | 110 } |
69 | 111 |
70 void PendingScript::StopWatchingForLoad() { | 112 void PendingScript::StopWatchingForLoad() { |
71 if (!IsWatchingForLoad()) | 113 if (!watching_for_load_) |
72 return; | 114 return; |
73 CheckState(); | 115 CheckState(); |
74 DCHECK(IsExternal()); | 116 DCHECK(GetResource()); |
75 client_ = nullptr; | 117 client_ = nullptr; |
76 watching_for_load_ = false; | 118 watching_for_load_ = false; |
77 } | 119 } |
78 | 120 |
79 ScriptElementBase* PendingScript::GetElement() const { | 121 ScriptElementBase* PendingScript::GetElement() const { |
80 // As mentioned in the comment at |m_element| declaration, | 122 // As mentioned in the comment at |m_element| declaration, |
81 // |m_element| must point to the corresponding ScriptLoader's | 123 // |m_element| must point to the corresponding ScriptLoader's |
82 // client. | 124 // client. |
83 CHECK(element_); | 125 CHECK(element_); |
84 return element_.Get(); | 126 return element_.Get(); |
85 } | 127 } |
86 | 128 |
| 129 void PendingScript::StreamingFinished() { |
| 130 CheckState(); |
| 131 DCHECK(GetResource()); |
| 132 if (client_) |
| 133 client_->PendingScriptFinished(this); |
| 134 } |
| 135 |
87 void PendingScript::MarkParserBlockingLoadStartTime() { | 136 void PendingScript::MarkParserBlockingLoadStartTime() { |
88 DCHECK_EQ(parser_blocking_load_start_time_, 0.0); | 137 DCHECK_EQ(parser_blocking_load_start_time_, 0.0); |
89 parser_blocking_load_start_time_ = MonotonicallyIncreasingTime(); | 138 parser_blocking_load_start_time_ = MonotonicallyIncreasingTime(); |
90 } | 139 } |
91 | 140 |
| 141 // Returns true if SRI check passed. |
| 142 static bool CheckScriptResourceIntegrity(Resource* resource, |
| 143 ScriptElementBase* element) { |
| 144 DCHECK_EQ(resource->GetType(), Resource::kScript); |
| 145 ScriptResource* script_resource = ToScriptResource(resource); |
| 146 String integrity_attr = element->IntegrityAttributeValue(); |
| 147 |
| 148 // It is possible to get back a script resource with integrity metadata |
| 149 // for a request with an empty integrity attribute. In that case, the |
| 150 // integrity check should be skipped, so this check ensures that the |
| 151 // integrity attribute isn't empty in addition to checking if the |
| 152 // resource has empty integrity metadata. |
| 153 if (integrity_attr.IsEmpty() || |
| 154 script_resource->IntegrityMetadata().IsEmpty()) |
| 155 return true; |
| 156 |
| 157 switch (script_resource->IntegrityDisposition()) { |
| 158 case ResourceIntegrityDisposition::kPassed: |
| 159 return true; |
| 160 |
| 161 case ResourceIntegrityDisposition::kFailed: |
| 162 // TODO(jww): This should probably also generate a console |
| 163 // message identical to the one produced by |
| 164 // CheckSubresourceIntegrity below. See https://crbug.com/585267. |
| 165 return false; |
| 166 |
| 167 case ResourceIntegrityDisposition::kNotChecked: { |
| 168 if (!resource->ResourceBuffer()) |
| 169 return true; |
| 170 |
| 171 bool passed = SubresourceIntegrity::CheckSubresourceIntegrity( |
| 172 script_resource->IntegrityMetadata(), element->GetDocument(), |
| 173 resource->ResourceBuffer()->Data(), |
| 174 resource->ResourceBuffer()->size(), resource->Url(), *resource); |
| 175 script_resource->SetIntegrityDisposition( |
| 176 passed ? ResourceIntegrityDisposition::kPassed |
| 177 : ResourceIntegrityDisposition::kFailed); |
| 178 return passed; |
| 179 } |
| 180 } |
| 181 |
| 182 NOTREACHED(); |
| 183 return true; |
| 184 } |
| 185 |
| 186 void PendingScript::NotifyFinished(Resource* resource) { |
| 187 // The following SRI checks need to be here because, unfortunately, fetches |
| 188 // are not done purely according to the Fetch spec. In particular, |
| 189 // different requests for the same resource do not have different |
| 190 // responses; the memory cache can (and will) return the exact same |
| 191 // Resource object. |
| 192 // |
| 193 // For different requests, the same Resource object will be returned and |
| 194 // will not be associated with the particular request. Therefore, when the |
| 195 // body of the response comes in, there's no way to validate the integrity |
| 196 // of the Resource object against a particular request (since there may be |
| 197 // several pending requests all tied to the identical object, and the |
| 198 // actual requests are not stored). |
| 199 // |
| 200 // In order to simulate the correct behavior, Blink explicitly does the SRI |
| 201 // checks here, when a PendingScript tied to a particular request is |
| 202 // finished (and in the case of a StyleSheet, at the point of execution), |
| 203 // while having proper Fetch checks in the fetch module for use in the |
| 204 // fetch JavaScript API. In a future world where the ResourceFetcher uses |
| 205 // the Fetch algorithm, this should be fixed by having separate Response |
| 206 // objects (perhaps attached to identical Resource objects) per request. |
| 207 // |
| 208 // See https://crbug.com/500701 for more information. |
| 209 CheckState(); |
| 210 if (element_) { |
| 211 integrity_failure_ = !CheckScriptResourceIntegrity(resource, element_); |
| 212 } |
| 213 |
| 214 // If script streaming is in use, the client will be notified in |
| 215 // streamingFinished. |
| 216 if (streamer_) |
| 217 streamer_->NotifyFinished(resource); |
| 218 else if (client_) |
| 219 client_->PendingScriptFinished(this); |
| 220 } |
| 221 |
| 222 void PendingScript::NotifyAppendData(ScriptResource* resource) { |
| 223 if (streamer_) |
| 224 streamer_->NotifyAppendData(resource); |
| 225 } |
| 226 |
92 DEFINE_TRACE(PendingScript) { | 227 DEFINE_TRACE(PendingScript) { |
93 visitor->Trace(element_); | 228 visitor->Trace(element_); |
| 229 visitor->Trace(streamer_); |
94 visitor->Trace(client_); | 230 visitor->Trace(client_); |
| 231 ResourceOwner<ScriptResource>::Trace(visitor); |
| 232 MemoryCoordinatorClient::Trace(visitor); |
| 233 } |
| 234 |
| 235 ClassicScript* PendingScript::GetSource(const KURL& document_url, |
| 236 bool& error_occurred) const { |
| 237 CheckState(); |
| 238 |
| 239 error_occurred = this->ErrorOccurred(); |
| 240 if (GetResource()) { |
| 241 DCHECK(GetResource()->IsLoaded()); |
| 242 if (streamer_ && !streamer_->StreamingSuppressed()) |
| 243 return ClassicScript::Create(ScriptSourceCode(streamer_, GetResource())); |
| 244 return ClassicScript::Create(ScriptSourceCode(GetResource())); |
| 245 } |
| 246 |
| 247 return ClassicScript::Create(ScriptSourceCode( |
| 248 element_->TextContent(), document_url, StartingPosition())); |
| 249 } |
| 250 |
| 251 void PendingScript::SetStreamer(ScriptStreamer* streamer) { |
| 252 DCHECK(!streamer_); |
| 253 DCHECK(!watching_for_load_); |
| 254 streamer_ = streamer; |
| 255 CheckState(); |
| 256 } |
| 257 |
| 258 bool PendingScript::IsReady() const { |
| 259 CheckState(); |
| 260 if (GetResource()) { |
| 261 return GetResource()->IsLoaded() && (!streamer_ || streamer_->IsFinished()); |
| 262 } |
| 263 |
| 264 return true; |
| 265 } |
| 266 |
| 267 bool PendingScript::ErrorOccurred() const { |
| 268 CheckState(); |
| 269 if (GetResource()) |
| 270 return GetResource()->ErrorOccurred() || integrity_failure_; |
| 271 |
| 272 return false; |
| 273 } |
| 274 |
| 275 void PendingScript::OnPurgeMemory() { |
| 276 CheckState(); |
| 277 if (!streamer_) |
| 278 return; |
| 279 streamer_->Cancel(); |
| 280 streamer_ = nullptr; |
| 281 } |
| 282 |
| 283 void PendingScript::StartStreamingIfPossible( |
| 284 Document* document, |
| 285 ScriptStreamer::Type streamer_type) { |
| 286 if (!document->GetFrame()) |
| 287 return; |
| 288 |
| 289 ScriptState* script_state = ToScriptStateForMainWorld(document->GetFrame()); |
| 290 if (!script_state) |
| 291 return; |
| 292 |
| 293 ScriptStreamer::StartStreaming( |
| 294 this, streamer_type, document->GetFrame()->GetSettings(), script_state, |
| 295 TaskRunnerHelper::Get(TaskType::kNetworking, document)); |
95 } | 296 } |
96 | 297 |
97 } // namespace blink | 298 } // namespace blink |
OLD | NEW |