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