1 /*
2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "third_party/blink/renderer/core/loader/appcache/application_cache_host.h"
32
33 #include <utility>
34
35 #include "mojo/public/cpp/bindings/pending_remote.h"
36 #include "third_party/blink/public/common/features.h"
37 #include "third_party/blink/public/mojom/appcache/appcache.mojom-blink.h"
38 #include "third_party/blink/public/mojom/appcache/appcache_info.mojom-blink.h"
39 #include "third_party/blink/public/platform/platform.h"
40 #include "third_party/blink/renderer/core/frame/deprecation.h"
41 #include "third_party/blink/renderer/core/frame/local_frame.h"
42 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
43 #include "third_party/blink/renderer/core/inspector/inspector_application_cache_agent.h"
44 #include "third_party/blink/renderer/core/loader/appcache/application_cache.h"
45 #include "third_party/blink/renderer/core/loader/appcache/application_cache_host_for_frame.h"
46 #include "third_party/blink/renderer/core/loader/appcache/application_cache_host_for_worker.h"
47 #include "third_party/blink/renderer/core/loader/frame_load_request.h"
48 #include "third_party/blink/renderer/core/loader/frame_loader.h"
49 #include "third_party/blink/renderer/core/page/frame_tree.h"
50 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
51 #include "third_party/blink/renderer/platform/wtf/assertions.h"
52
53 namespace blink {
54
55 namespace {
56
57 // Note: the order of the elements in this array must match those
58 // of the EventID enum in appcache_interfaces.h.
59 const char* const kEventNames[] = {"Checking", "Error", "NoUpdate",
60 "Downloading", "Progress", "UpdateReady",
61 "Cached", "Obsolete"};
62
63 } // namespace
64
ApplicationCacheHost(const BrowserInterfaceBrokerProxy & interface_broker_proxy,scoped_refptr<base::SingleThreadTaskRunner> task_runner,ContextLifecycleNotifier * notifier)65 ApplicationCacheHost::ApplicationCacheHost(
66 const BrowserInterfaceBrokerProxy& interface_broker_proxy,
67 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
68 ContextLifecycleNotifier* notifier)
69 : backend_host_(notifier),
70 receiver_(this, notifier),
71 backend_remote_(notifier),
72 task_runner_(std::move(task_runner)),
73 interface_broker_proxy_(interface_broker_proxy) {}
74
75 ApplicationCacheHost::~ApplicationCacheHost() = default;
76
Detach()77 void ApplicationCacheHost::Detach() {
78 receiver_.reset();
79 backend_host_.reset();
80 }
81
ApplicationCacheInfo()82 ApplicationCacheHost::CacheInfo ApplicationCacheHost::ApplicationCacheInfo() {
83 if (!backend_host_.is_bound())
84 return CacheInfo();
85
86 ApplicationCacheHost::CacheInfo cache_info;
87 GetAssociatedCacheInfo(&cache_info);
88 return cache_info;
89 }
90
GetHostID() const91 const base::UnguessableToken& ApplicationCacheHost::GetHostID() const {
92 return host_id_;
93 }
94
SetHostID(const base::UnguessableToken & host_id)95 void ApplicationCacheHost::SetHostID(const base::UnguessableToken& host_id) {
96 DCHECK(!host_id.is_empty());
97 host_id_ = host_id;
98 }
99
SelectCacheForWorker(int64_t app_cache_id,base::OnceClosure completion_callback)100 void ApplicationCacheHost::SelectCacheForWorker(
101 int64_t app_cache_id,
102 base::OnceClosure completion_callback) {
103 if (!backend_host_.is_bound())
104 return;
105
106 select_cache_for_worker_completion_callback_ = std::move(completion_callback);
107 backend_host_->SelectCacheForWorker(app_cache_id);
108 }
109
FillResourceList(Vector<mojom::blink::AppCacheResourceInfo> * resources)110 void ApplicationCacheHost::FillResourceList(
111 Vector<mojom::blink::AppCacheResourceInfo>* resources) {
112 DCHECK(resources);
113 if (!backend_host_.is_bound())
114 return;
115
116 if (!cache_info_.is_complete)
117 return;
118 Vector<mojom::blink::AppCacheResourceInfoPtr> boxed_infos;
119 backend_host_->GetResourceList(&boxed_infos);
120 for (auto& b : boxed_infos)
121 resources->emplace_back(std::move(*b));
122 }
123
GetStatus() const124 mojom::AppCacheStatus ApplicationCacheHost::GetStatus() const {
125 if (!backend_host_.is_bound())
126 return mojom::AppCacheStatus::APPCACHE_STATUS_UNCACHED;
127 return status_;
128 }
129
Abort()130 void ApplicationCacheHost::Abort() {
131 // This is not implemented intentionally. See https://crbug.com/175063
132 }
133
CacheSelected(mojom::blink::AppCacheInfoPtr info)134 void ApplicationCacheHost::CacheSelected(mojom::blink::AppCacheInfoPtr info) {
135 if (!backend_host_.is_bound())
136 return;
137
138 cache_info_ = *info;
139 // FIXME: Prod the inspector to update its notion of what cache the page is
140 // using.
141 if (select_cache_for_worker_completion_callback_)
142 std::move(select_cache_for_worker_completion_callback_).Run();
143 }
144
EventRaised(mojom::blink::AppCacheEventID event_id)145 void ApplicationCacheHost::EventRaised(mojom::blink::AppCacheEventID event_id) {
146 if (!backend_host_.is_bound())
147 return;
148
149 DCHECK_NE(event_id,
150 mojom::blink::AppCacheEventID::
151 APPCACHE_PROGRESS_EVENT); // See OnProgressEventRaised.
152 DCHECK_NE(event_id,
153 mojom::blink::AppCacheEventID::
154 APPCACHE_ERROR_EVENT); // See OnErrorEventRaised.
155
156 // Emit logging output prior to calling out to script as we can get
157 // deleted within the script event handler.
158 const char kFormatString[] = "Application Cache %s event";
159 String message =
160 String::Format(kFormatString, kEventNames[static_cast<int>(event_id)]);
161 LogMessage(mojom::blink::ConsoleMessageLevel::kInfo, message);
162
163 switch (event_id) {
164 case mojom::blink::AppCacheEventID::APPCACHE_CHECKING_EVENT:
165 status_ = mojom::blink::AppCacheStatus::APPCACHE_STATUS_CHECKING;
166 break;
167 case mojom::blink::AppCacheEventID::APPCACHE_DOWNLOADING_EVENT:
168 status_ = mojom::blink::AppCacheStatus::APPCACHE_STATUS_DOWNLOADING;
169 break;
170 case mojom::blink::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT:
171 status_ = mojom::blink::AppCacheStatus::APPCACHE_STATUS_UPDATE_READY;
172 break;
173 case mojom::blink::AppCacheEventID::APPCACHE_CACHED_EVENT:
174 case mojom::blink::AppCacheEventID::APPCACHE_NO_UPDATE_EVENT:
175 status_ = mojom::blink::AppCacheStatus::APPCACHE_STATUS_IDLE;
176 break;
177 case mojom::blink::AppCacheEventID::APPCACHE_OBSOLETE_EVENT:
178 status_ = mojom::blink::AppCacheStatus::APPCACHE_STATUS_OBSOLETE;
179 break;
180 default:
181 NOTREACHED();
182 break;
183 }
184
185 NotifyApplicationCache(event_id, 0, 0,
186 mojom::AppCacheErrorReason::APPCACHE_UNKNOWN_ERROR,
187 String(), 0, String());
188 }
189
ProgressEventRaised(const KURL & url,int num_total,int num_complete)190 void ApplicationCacheHost::ProgressEventRaised(const KURL& url,
191 int num_total,
192 int num_complete) {
193 if (!backend_host_.is_bound())
194 return;
195
196 // Emit logging output prior to calling out to script as we can get
197 // deleted within the script event handler.
198 const char kFormatString[] = "Application Cache Progress event (%d of %d) %s";
199 String message = String::Format(kFormatString, num_complete, num_total,
200 url.GetString().Utf8().c_str());
201 LogMessage(mojom::blink::ConsoleMessageLevel::kInfo, message);
202 status_ = mojom::blink::AppCacheStatus::APPCACHE_STATUS_DOWNLOADING;
203 NotifyApplicationCache(mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT,
204 num_total, num_complete,
205 mojom::AppCacheErrorReason::APPCACHE_UNKNOWN_ERROR,
206 String(), 0, String());
207 }
208
ErrorEventRaised(mojom::blink::AppCacheErrorDetailsPtr details)209 void ApplicationCacheHost::ErrorEventRaised(
210 mojom::blink::AppCacheErrorDetailsPtr details) {
211 if (!backend_host_.is_bound())
212 return;
213
214 // Emit logging output prior to calling out to script as we can get
215 // deleted within the script event handler.
216 const char kFormatString[] = "Application Cache Error event: %s";
217 String full_message =
218 String::Format(kFormatString, details->message.Utf8().c_str());
219 LogMessage(mojom::blink::ConsoleMessageLevel::kError, full_message);
220
221 status_ = cache_info_.is_complete
222 ? mojom::blink::AppCacheStatus::APPCACHE_STATUS_IDLE
223 : mojom::blink::AppCacheStatus::APPCACHE_STATUS_UNCACHED;
224 if (details->is_cross_origin) {
225 // Don't leak detailed information to script for cross-origin resources.
226 DCHECK_EQ(mojom::blink::AppCacheErrorReason::APPCACHE_RESOURCE_ERROR,
227 details->reason);
228 NotifyApplicationCache(mojom::AppCacheEventID::APPCACHE_ERROR_EVENT, 0, 0,
229 details->reason, details->url.GetString(), 0,
230 String());
231 } else {
232 NotifyApplicationCache(mojom::AppCacheEventID::APPCACHE_ERROR_EVENT, 0, 0,
233 details->reason, details->url.GetString(),
234 details->status, details->message);
235 }
236 }
237
Trace(Visitor * visitor) const238 void ApplicationCacheHost::Trace(Visitor* visitor) const {
239 visitor->Trace(backend_host_);
240 visitor->Trace(receiver_);
241 visitor->Trace(backend_remote_);
242 }
243
GetAssociatedCacheInfo(ApplicationCacheHost::CacheInfo * info)244 void ApplicationCacheHost::GetAssociatedCacheInfo(
245 ApplicationCacheHost::CacheInfo* info) {
246 if (!backend_host_.is_bound())
247 return;
248
249 info->manifest_ = cache_info_.manifest_url;
250 if (!cache_info_.is_complete)
251 return;
252 info->creation_time_ = cache_info_.creation_time.ToDoubleT();
253 info->update_time_ = cache_info_.last_update_time.ToDoubleT();
254 info->response_sizes_ = cache_info_.response_sizes;
255 info->padding_sizes_ = cache_info_.padding_sizes;
256 }
257
BindBackend()258 bool ApplicationCacheHost::BindBackend() {
259 if (!task_runner_)
260 return false;
261
262 if (!base::FeatureList::IsEnabled(blink::features::kAppCache))
263 return false;
264
265 DCHECK(!host_id_.is_empty());
266
267 mojo::PendingRemote<mojom::blink::AppCacheFrontend> frontend_remote;
268 receiver_.Bind(frontend_remote.InitWithNewPipeAndPassReceiver(),
269 task_runner_);
270
271 mojo::PendingReceiver<mojom::blink::AppCacheBackend> receiver =
272 backend_remote_.BindNewPipeAndPassReceiver(task_runner_);
273 interface_broker_proxy_.GetInterface(std::move(receiver));
274
275 backend_remote_->RegisterHost(
276 backend_host_.BindNewPipeAndPassReceiver(std::move(task_runner_)),
277 std::move(frontend_remote), host_id_);
278 return true;
279 }
280
281 } // namespace blink
282