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/V8BindingForCore.h" | |
31 #include "core/dom/ClassicScript.h" | |
32 #include "core/dom/Document.h" | |
33 #include "core/dom/ScriptElementBase.h" | 28 #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" | |
38 #include "platform/wtf/CurrentTime.h" | 29 #include "platform/wtf/CurrentTime.h" |
39 | 30 |
40 namespace blink { | 31 namespace blink { |
41 | 32 |
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 | |
56 PendingScript::PendingScript(ScriptElementBase* element, | 33 PendingScript::PendingScript(ScriptElementBase* element, |
57 ScriptResource* resource, | 34 const TextPosition& starting_position) |
58 const TextPosition& starting_position, | |
59 bool is_for_testing) | |
60 : watching_for_load_(false), | 35 : watching_for_load_(false), |
61 element_(element), | 36 element_(element), |
62 starting_position_(starting_position), | 37 starting_position_(starting_position), |
63 integrity_failure_(false), | |
64 parser_blocking_load_start_time_(0), | 38 parser_blocking_load_start_time_(0), |
65 client_(nullptr), | 39 client_(nullptr) {} |
66 is_for_testing_(is_for_testing) { | |
67 CheckState(); | |
68 SetResource(resource); | |
69 MemoryCoordinator::Instance().RegisterClient(this); | |
70 } | |
71 | 40 |
72 PendingScript::~PendingScript() {} | 41 PendingScript::~PendingScript() {} |
73 | 42 |
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 | |
81 void PendingScript::Dispose() { | 43 void PendingScript::Dispose() { |
82 StopWatchingForLoad(); | 44 StopWatchingForLoad(); |
83 DCHECK(!client_); | 45 DCHECK(!Client()); |
84 DCHECK(!watching_for_load_); | 46 DCHECK(!IsWatchingForLoad()); |
85 | 47 |
86 MemoryCoordinator::Instance().UnregisterClient(this); | |
87 SetResource(nullptr); | |
88 starting_position_ = TextPosition::BelowRangePosition(); | 48 starting_position_ = TextPosition::BelowRangePosition(); |
89 integrity_failure_ = false; | |
90 parser_blocking_load_start_time_ = 0; | 49 parser_blocking_load_start_time_ = 0; |
91 if (streamer_) | 50 |
92 streamer_->Cancel(); | 51 DisposeInternal(); |
93 streamer_ = nullptr; | |
94 element_ = nullptr; | 52 element_ = nullptr; |
95 } | 53 } |
96 | 54 |
97 void PendingScript::WatchForLoad(PendingScriptClient* client) { | 55 void PendingScript::WatchForLoad(PendingScriptClient* client) { |
98 CheckState(); | 56 CheckState(); |
99 | 57 |
100 DCHECK(!watching_for_load_); | 58 DCHECK(!IsWatchingForLoad()); |
101 // addClient() will call streamingFinished() if the load is complete. Callers | 59 // addClient() will call streamingFinished() if the load is complete. Callers |
102 // who do not expect to be re-entered from this call should not call | 60 // who do not expect to be re-entered from this call should not call |
103 // watchForLoad for a PendingScript which isReady. We also need to set | 61 // watchForLoad for a PendingScript which isReady. We also need to set |
104 // m_watchingForLoad early, since addClient() can result in calling | 62 // m_watchingForLoad early, since addClient() can result in calling |
105 // notifyFinished and further stopWatchingForLoad(). | 63 // notifyFinished and further stopWatchingForLoad(). |
106 watching_for_load_ = true; | 64 watching_for_load_ = true; |
107 client_ = client; | 65 client_ = client; |
108 if (IsReady()) | 66 if (IsReady()) |
109 client_->PendingScriptFinished(this); | 67 client_->PendingScriptFinished(this); |
110 } | 68 } |
111 | 69 |
112 void PendingScript::StopWatchingForLoad() { | 70 void PendingScript::StopWatchingForLoad() { |
113 if (!watching_for_load_) | 71 if (!IsWatchingForLoad()) |
114 return; | 72 return; |
115 CheckState(); | 73 CheckState(); |
116 DCHECK(GetResource()); | 74 DCHECK(IsExternal()); |
117 client_ = nullptr; | 75 client_ = nullptr; |
118 watching_for_load_ = false; | 76 watching_for_load_ = false; |
119 } | 77 } |
120 | 78 |
121 ScriptElementBase* PendingScript::GetElement() const { | 79 ScriptElementBase* PendingScript::GetElement() const { |
122 // As mentioned in the comment at |m_element| declaration, | 80 // As mentioned in the comment at |m_element| declaration, |
123 // |m_element| must point to the corresponding ScriptLoader's | 81 // |m_element| must point to the corresponding ScriptLoader's |
124 // client. | 82 // client. |
125 CHECK(element_); | 83 CHECK(element_); |
126 return element_.Get(); | 84 return element_.Get(); |
127 } | 85 } |
128 | 86 |
129 void PendingScript::StreamingFinished() { | |
130 CheckState(); | |
131 DCHECK(GetResource()); | |
132 if (client_) | |
133 client_->PendingScriptFinished(this); | |
134 } | |
135 | |
136 void PendingScript::MarkParserBlockingLoadStartTime() { | 87 void PendingScript::MarkParserBlockingLoadStartTime() { |
137 DCHECK_EQ(parser_blocking_load_start_time_, 0.0); | 88 DCHECK_EQ(parser_blocking_load_start_time_, 0.0); |
138 parser_blocking_load_start_time_ = MonotonicallyIncreasingTime(); | 89 parser_blocking_load_start_time_ = MonotonicallyIncreasingTime(); |
139 } | 90 } |
140 | 91 |
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 | |
227 DEFINE_TRACE(PendingScript) { | 92 DEFINE_TRACE(PendingScript) { |
228 visitor->Trace(element_); | 93 visitor->Trace(element_); |
229 visitor->Trace(streamer_); | |
230 visitor->Trace(client_); | 94 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)); | |
296 } | 95 } |
297 | 96 |
298 } // namespace blink | 97 } // namespace blink |
OLD | NEW |