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