Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / appcache / appcache_internals_ui.cc
blobdfd06b167bd4dd3af6bdc3e38c21ca1c4f0ff354
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/appcache/appcache_internals_ui.h"
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_piece.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/threading/platform_thread.h"
15 #include "base/values.h"
16 #include "content/browser/appcache/appcache.h"
17 #include "content/browser/appcache/appcache_response.h"
18 #include "content/browser/storage_partition_impl.h"
19 #include "content/grit/content_resources.h"
20 #include "content/public/browser/browser_context.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/web_ui.h"
23 #include "content/public/browser/web_ui_data_source.h"
24 #include "content/public/common/url_constants.h"
25 #include "net/base/escape.h"
26 #include "net/http/http_response_headers.h"
27 #include "net/url_request/view_cache_helper.h"
29 namespace content {
31 namespace {
32 const char kRequestGetAllAppCacheInfo[] = "getAllAppCache";
33 const char kRequestDeleteAppCache[] = "deleteAppCache";
34 const char kRequestGetAppCacheDetails[] = "getAppCacheDetails";
35 const char kRequestGetFileDetails[] = "getFileDetails";
37 const char kFunctionOnAllAppCacheInfoReady[] =
38 "appcache.onAllAppCacheInfoReady";
39 const char kFunctionOnAppCacheInfoDeleted[] = "appcache.onAppCacheInfoDeleted";
40 const char kFunctionOnAppCacheDetailsReady[] =
41 "appcache.onAppCacheDetailsReady";
42 const char kFunctionOnFileDetailsReady[] = "appcache.onFileDetailsReady";
43 const char kFunctionOnFileDetailsFailed[] = "appcache.onFileDetailsFailed";
45 int64 ToInt64(const std::string& str) {
46 int64 i = 0;
47 base::StringToInt64(str.c_str(), &i);
48 return i;
51 bool SortByResourceUrl(const AppCacheResourceInfo& lhs,
52 const AppCacheResourceInfo& rhs) {
53 return lhs.url.spec() < rhs.url.spec();
56 scoped_ptr<base::DictionaryValue> GetDictionaryValueForResponseEnquiry(
57 const content::AppCacheInternalsUI::Proxy::ResponseEnquiry&
58 response_enquiry) {
59 scoped_ptr<base::DictionaryValue> dict_value(new base::DictionaryValue());
60 dict_value->SetString("manifestURL", response_enquiry.manifest_url);
61 dict_value->SetString("groupId",
62 base::Int64ToString(response_enquiry.group_id));
63 dict_value->SetString("responseId",
64 base::Int64ToString(response_enquiry.response_id));
65 return dict_value;
68 scoped_ptr<base::DictionaryValue> GetDictionaryValueForAppCacheInfo(
69 const content::AppCacheInfo& appcache_info) {
70 scoped_ptr<base::DictionaryValue> dict_value(new base::DictionaryValue());
71 dict_value->SetString("manifestURL", appcache_info.manifest_url.spec());
72 dict_value->SetDouble("creationTime", appcache_info.creation_time.ToJsTime());
73 dict_value->SetDouble("lastUpdateTime",
74 appcache_info.last_update_time.ToJsTime());
75 dict_value->SetDouble("lastAccessTime",
76 appcache_info.last_access_time.ToJsTime());
77 dict_value->SetString(
78 "size",
79 base::UTF16ToUTF8(base::FormatBytesUnlocalized(appcache_info.size)));
80 dict_value->SetString("groupId", base::Int64ToString(appcache_info.group_id));
82 return dict_value;
85 scoped_ptr<base::ListValue> GetListValueForAppCacheInfoVector(
86 const AppCacheInfoVector& appcache_info_vector) {
87 scoped_ptr<base::ListValue> list(new base::ListValue());
88 for (const AppCacheInfo& info : appcache_info_vector)
89 list->Append(GetDictionaryValueForAppCacheInfo(info));
90 return list;
93 scoped_ptr<base::ListValue> GetListValueFromAppCacheInfoCollection(
94 AppCacheInfoCollection* appcache_collection) {
95 scoped_ptr<base::ListValue> list(new base::ListValue());
96 for (const auto& key_value : appcache_collection->infos_by_origin) {
97 base::DictionaryValue* dict = new base::DictionaryValue;
98 dict->SetString("originURL", key_value.first.spec());
99 dict->Set("manifests", GetListValueForAppCacheInfoVector(key_value.second));
100 list->Append(scoped_ptr<base::Value>(dict));
102 return list;
105 scoped_ptr<base::DictionaryValue> GetDictionaryValueForAppCacheResourceInfo(
106 const AppCacheResourceInfo& resource_info) {
107 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
108 dict->SetString("url", resource_info.url.spec());
109 dict->SetString(
110 "size",
111 base::UTF16ToUTF8(base::FormatBytesUnlocalized(resource_info.size)));
112 dict->SetString("responseId", base::Int64ToString(resource_info.response_id));
113 dict->SetBoolean("isExplicit", resource_info.is_explicit);
114 dict->SetBoolean("isManifest", resource_info.is_manifest);
115 dict->SetBoolean("isMaster", resource_info.is_master);
116 dict->SetBoolean("isFallback", resource_info.is_fallback);
117 dict->SetBoolean("isIntercept", resource_info.is_intercept);
118 dict->SetBoolean("isForeign", resource_info.is_foreign);
120 return dict;
123 scoped_ptr<base::ListValue> GetListValueForAppCacheResourceInfoVector(
124 AppCacheResourceInfoVector* resource_info_vector) {
125 scoped_ptr<base::ListValue> list(new base::ListValue);
126 for (const AppCacheResourceInfo& res_info : *resource_info_vector)
127 list->Append(GetDictionaryValueForAppCacheResourceInfo(res_info));
128 return list;
131 } // namespace
133 AppCacheInternalsUI::Proxy::Proxy(
134 base::WeakPtr<AppCacheInternalsUI> appcache_internals_ui,
135 const base::FilePath& partition_path)
136 : appcache_internals_ui_(appcache_internals_ui),
137 partition_path_(partition_path) {}
139 void AppCacheInternalsUI::Proxy::Initialize(
140 const scoped_refptr<ChromeAppCacheService>& chrome_appcache_service) {
141 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
142 BrowserThread::PostTask(
143 BrowserThread::IO, FROM_HERE,
144 base::Bind(&Proxy::Initialize, this, chrome_appcache_service));
145 return;
147 appcache_service_ = chrome_appcache_service->AsWeakPtr();
148 shutdown_called_ = false;
149 preparing_response_ = false;
152 AppCacheInternalsUI::Proxy::~Proxy() {
153 DCHECK(shutdown_called_);
156 void AppCacheInternalsUI::Proxy::Shutdown() {
157 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
158 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
159 base::Bind(&Proxy::Shutdown, this));
160 return;
162 shutdown_called_ = true;
163 if (appcache_service_) {
164 appcache_service_->storage()->CancelDelegateCallbacks(this);
165 appcache_service_.reset();
166 response_enquiries_.clear();
170 void AppCacheInternalsUI::Proxy::RequestAllAppCacheInfo() {
171 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
172 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
173 base::Bind(&Proxy::RequestAllAppCacheInfo, this));
174 return;
176 if (appcache_service_) {
177 scoped_refptr<AppCacheInfoCollection> collection(
178 new AppCacheInfoCollection());
179 appcache_service_->GetAllAppCacheInfo(
180 collection.get(),
181 base::Bind(&Proxy::OnAllAppCacheInfoReady, this, collection));
185 void AppCacheInternalsUI::Proxy::OnAllAppCacheInfoReady(
186 scoped_refptr<AppCacheInfoCollection> collection,
187 int net_result_code) {
188 BrowserThread::PostTask(
189 BrowserThread::UI, FROM_HERE,
190 base::Bind(&AppCacheInternalsUI::OnAllAppCacheInfoReady,
191 appcache_internals_ui_, collection, partition_path_));
194 void AppCacheInternalsUI::Proxy::DeleteAppCache(
195 const std::string& manifest_url) {
196 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
197 BrowserThread::PostTask(
198 BrowserThread::IO, FROM_HERE,
199 base::Bind(&Proxy::DeleteAppCache, this, manifest_url));
200 return;
202 if (appcache_service_) {
203 appcache_service_->DeleteAppCacheGroup(
204 GURL(manifest_url),
205 base::Bind(&Proxy::OnAppCacheInfoDeleted, this, manifest_url));
209 void AppCacheInternalsUI::Proxy::OnAppCacheInfoDeleted(
210 const std::string& manifest_url,
211 int net_result_code) {
212 BrowserThread::PostTask(
213 BrowserThread::UI, FROM_HERE,
214 base::Bind(&AppCacheInternalsUI::OnAppCacheInfoDeleted,
215 appcache_internals_ui_, partition_path_, manifest_url,
216 net_result_code == net::OK));
219 void AppCacheInternalsUI::Proxy::RequestAppCacheDetails(
220 const std::string& manifest_url) {
221 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
222 BrowserThread::PostTask(
223 BrowserThread::IO, FROM_HERE,
224 base::Bind(&Proxy::RequestAppCacheDetails, this, manifest_url));
225 return;
228 if (appcache_service_)
229 appcache_service_->storage()->LoadOrCreateGroup(GURL(manifest_url), this);
232 void AppCacheInternalsUI::Proxy::OnGroupLoaded(AppCacheGroup* appcache_group,
233 const GURL& manifest_gurl) {
234 scoped_ptr<AppCacheResourceInfoVector> resource_info_vector;
235 if (appcache_group && appcache_group->newest_complete_cache()) {
236 resource_info_vector.reset(new AppCacheResourceInfoVector);
237 appcache_group->newest_complete_cache()->ToResourceInfoVector(
238 resource_info_vector.get());
239 std::sort(resource_info_vector->begin(), resource_info_vector->end(),
240 SortByResourceUrl);
242 BrowserThread::PostTask(
243 BrowserThread::UI, FROM_HERE,
244 base::Bind(&AppCacheInternalsUI::OnAppCacheDetailsReady,
245 appcache_internals_ui_, partition_path_, manifest_gurl.spec(),
246 base::Passed(&resource_info_vector)));
249 void AppCacheInternalsUI::Proxy::RequestFileDetails(
250 const ResponseEnquiry& response_enquiry) {
251 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
252 BrowserThread::PostTask(
253 BrowserThread::IO, FROM_HERE,
254 base::Bind(&Proxy::RequestFileDetails, this, response_enquiry));
255 return;
257 DCHECK(!shutdown_called_);
258 response_enquiries_.push_back(response_enquiry);
259 HandleFileDetailsRequest();
262 void AppCacheInternalsUI::Proxy::HandleFileDetailsRequest() {
263 if (preparing_response_ || !response_enquiries_.size() || !appcache_service_)
264 return;
265 preparing_response_ = true;
266 appcache_service_->storage()->LoadResponseInfo(
267 GURL(response_enquiries_.front().manifest_url),
268 response_enquiries_.front().group_id,
269 response_enquiries_.front().response_id, this);
272 void AppCacheInternalsUI::Proxy::OnResponseInfoLoaded(
273 AppCacheResponseInfo* response,
274 int64 response_id) {
275 if (shutdown_called_)
276 return;
277 if (!appcache_service_)
278 return;
279 ResponseEnquiry response_enquiry = response_enquiries_.front();
280 response_enquiries_.pop_front();
281 if (response) {
282 scoped_refptr<AppCacheResponseInfo> response_info = response;
283 const int64 kLimit = 100 * 1000;
284 int64 amount_to_read =
285 std::min(kLimit, response_info->response_data_size());
286 scoped_refptr<net::IOBuffer> response_data(new net::IOBuffer(
287 base::CheckedNumeric<size_t>(amount_to_read).ValueOrDie()));
288 scoped_ptr<AppCacheResponseReader> reader(
289 appcache_service_->storage()->CreateResponseReader(
290 GURL(response_enquiry.manifest_url), response_enquiry.group_id,
291 response_enquiry.response_id));
293 reader->ReadData(
294 response_data.get(), amount_to_read,
295 base::Bind(&Proxy::OnResponseDataReadComplete, this, response_enquiry,
296 response_info, base::Passed(&reader), response_data));
297 } else {
298 OnResponseDataReadComplete(response_enquiry, nullptr, nullptr, nullptr, -1);
302 void AppCacheInternalsUI::Proxy::OnResponseDataReadComplete(
303 const ResponseEnquiry& response_enquiry,
304 scoped_refptr<AppCacheResponseInfo> response_info,
305 scoped_ptr<AppCacheResponseReader> reader,
306 scoped_refptr<net::IOBuffer> response_data,
307 int net_result_code) {
308 if (shutdown_called_)
309 return;
310 if (!response_info || net_result_code < 0) {
311 BrowserThread::PostTask(
312 BrowserThread::UI, FROM_HERE,
313 base::Bind(&AppCacheInternalsUI::OnFileDetailsFailed,
314 appcache_internals_ui_, response_enquiry, net_result_code));
315 } else {
316 BrowserThread::PostTask(
317 BrowserThread::UI, FROM_HERE,
318 base::Bind(&AppCacheInternalsUI::OnFileDetailsReady,
319 appcache_internals_ui_, response_enquiry, response_info,
320 response_data, net_result_code));
322 preparing_response_ = false;
323 HandleFileDetailsRequest();
326 AppCacheInternalsUI::AppCacheInternalsUI(WebUI* web_ui)
327 : WebUIController(web_ui), weak_ptr_factory_(this) {
328 web_ui->RegisterMessageCallback(
329 kRequestGetAllAppCacheInfo,
330 base::Bind(&AppCacheInternalsUI::GetAllAppCache, AsWeakPtr()));
332 web_ui->RegisterMessageCallback(
333 kRequestDeleteAppCache,
334 base::Bind(&AppCacheInternalsUI::DeleteAppCache, AsWeakPtr()));
336 web_ui->RegisterMessageCallback(
337 kRequestGetAppCacheDetails,
338 base::Bind(&AppCacheInternalsUI::GetAppCacheDetails, AsWeakPtr()));
340 web_ui->RegisterMessageCallback(
341 kRequestGetFileDetails,
342 base::Bind(&AppCacheInternalsUI::GetFileDetails, AsWeakPtr()));
344 WebUIDataSource* source =
345 WebUIDataSource::Create(kChromeUIAppCacheInternalsHost);
347 source->SetJsonPath("strings.js");
348 source->AddResourcePath("appcache_internals.js", IDR_APPCACHE_INTERNALS_JS);
349 source->AddResourcePath("appcache_internals.css", IDR_APPCACHE_INTERNALS_CSS);
350 source->SetDefaultResource(IDR_APPCACHE_INTERNALS_HTML);
352 WebUIDataSource::Add(browser_context(), source);
354 BrowserContext::StoragePartitionCallback callback =
355 base::Bind(&AppCacheInternalsUI::CreateProxyForPartition, AsWeakPtr());
356 BrowserContext::ForEachStoragePartition(browser_context(), callback);
359 AppCacheInternalsUI::~AppCacheInternalsUI() {
360 for (auto& proxy : appcache_proxies_)
361 proxy->Shutdown();
364 void AppCacheInternalsUI::CreateProxyForPartition(
365 StoragePartition* storage_partition) {
366 scoped_refptr<Proxy> proxy =
367 new Proxy(weak_ptr_factory_.GetWeakPtr(), storage_partition->GetPath());
368 proxy->Initialize(static_cast<StoragePartitionImpl*>(storage_partition)
369 ->GetAppCacheService());
370 appcache_proxies_.push_back(proxy);
373 void AppCacheInternalsUI::GetAllAppCache(const base::ListValue* args) {
374 DCHECK_CURRENTLY_ON(BrowserThread::UI);
375 for (scoped_refptr<Proxy>& proxy : appcache_proxies_)
376 proxy->RequestAllAppCacheInfo();
379 void AppCacheInternalsUI::DeleteAppCache(const base::ListValue* args) {
380 DCHECK_CURRENTLY_ON(BrowserThread::UI);
381 std::string manifest_url, partition_path;
382 args->GetString(0, &partition_path);
383 args->GetString(1, &manifest_url);
384 Proxy* proxy =
385 GetProxyForPartitionPath(base::FilePath::FromUTF8Unsafe(partition_path));
386 if (proxy)
387 proxy->DeleteAppCache(manifest_url);
390 void AppCacheInternalsUI::GetAppCacheDetails(const base::ListValue* args) {
391 std::string manifest_url, partition_path;
392 args->GetString(0, &partition_path);
393 args->GetString(1, &manifest_url);
394 Proxy* proxy =
395 GetProxyForPartitionPath(base::FilePath::FromUTF8Unsafe(partition_path));
396 if (proxy)
397 proxy->RequestAppCacheDetails(manifest_url);
400 void AppCacheInternalsUI::GetFileDetails(const base::ListValue* args) {
401 std::string manifest_url, partition_path, group_id_str, response_id_str;
402 args->GetString(0, &partition_path);
403 args->GetString(1, &manifest_url);
404 args->GetString(2, &group_id_str);
405 args->GetString(3, &response_id_str);
406 Proxy* proxy =
407 GetProxyForPartitionPath(base::FilePath::FromUTF8Unsafe(partition_path));
408 if (proxy)
409 proxy->RequestFileDetails(
410 {manifest_url, ToInt64(group_id_str), ToInt64(response_id_str)});
413 void AppCacheInternalsUI::OnAllAppCacheInfoReady(
414 scoped_refptr<AppCacheInfoCollection> collection,
415 const base::FilePath& partition_path) {
416 std::string incognito_path_prefix;
417 if (browser_context()->IsOffTheRecord())
418 incognito_path_prefix = "Incognito ";
419 web_ui()->CallJavascriptFunction(
420 kFunctionOnAllAppCacheInfoReady,
421 base::StringValue(incognito_path_prefix + partition_path.AsUTF8Unsafe()),
422 *GetListValueFromAppCacheInfoCollection(collection.get()));
425 void AppCacheInternalsUI::OnAppCacheInfoDeleted(
426 const base::FilePath& partition_path,
427 const std::string& manifest_url,
428 bool deleted) {
429 web_ui()->CallJavascriptFunction(
430 kFunctionOnAppCacheInfoDeleted,
431 base::StringValue(partition_path.AsUTF8Unsafe()),
432 base::StringValue(manifest_url), base::FundamentalValue(deleted));
435 void AppCacheInternalsUI::OnAppCacheDetailsReady(
436 const base::FilePath& partition_path,
437 const std::string& manifest_url,
438 scoped_ptr<AppCacheResourceInfoVector> resource_info_vector) {
439 if (resource_info_vector) {
440 web_ui()->CallJavascriptFunction(
441 kFunctionOnAppCacheDetailsReady, base::StringValue(manifest_url),
442 base::StringValue(partition_path.AsUTF8Unsafe()),
443 *GetListValueForAppCacheResourceInfoVector(resource_info_vector.get()));
444 } else {
445 web_ui()->CallJavascriptFunction(
446 kFunctionOnAppCacheDetailsReady, base::StringValue(manifest_url),
447 base::StringValue(partition_path.AsUTF8Unsafe()));
451 void AppCacheInternalsUI::OnFileDetailsReady(
452 const Proxy::ResponseEnquiry& response_enquiry,
453 scoped_refptr<AppCacheResponseInfo> response_info,
454 scoped_refptr<net::IOBuffer> response_data,
455 int data_length) {
456 std::string headers;
457 if (response_info->http_response_info()) {
458 headers.append("<hr><pre>");
459 headers.append(net::EscapeForHTML(
460 response_info->http_response_info()->headers->GetStatusLine()));
461 headers.push_back('\n');
463 void* iter = nullptr;
464 std::string name, value;
465 while (response_info->http_response_info()->headers->EnumerateHeaderLines(
466 &iter, &name, &value)) {
467 headers.append(net::EscapeForHTML(name));
468 headers.append(": ");
469 headers.append(net::EscapeForHTML(value));
470 headers.push_back('\n');
472 headers.append("</pre>");
473 } else {
474 headers.append("Failed to read response headers. <br>");
476 std::string hex_dump = base::StringPrintf(
477 "<hr><pre> Showing %d of %d bytes\n\n", static_cast<int>(data_length),
478 static_cast<int>(response_info->response_data_size()));
479 net::ViewCacheHelper::HexDump(response_data->data(), data_length, &hex_dump);
480 if (data_length < response_info->response_data_size())
481 hex_dump.append("\nNote: data is truncated...");
482 hex_dump.append("</pre>");
483 web_ui()->CallJavascriptFunction(
484 kFunctionOnFileDetailsReady,
485 *GetDictionaryValueForResponseEnquiry(response_enquiry),
486 base::StringValue(headers), base::StringValue(hex_dump));
489 void AppCacheInternalsUI::OnFileDetailsFailed(
490 const Proxy::ResponseEnquiry& response_enquiry,
491 int net_result_code) {
492 web_ui()->CallJavascriptFunction(
493 kFunctionOnFileDetailsFailed,
494 *GetDictionaryValueForResponseEnquiry(response_enquiry),
495 base::FundamentalValue(net_result_code));
498 AppCacheInternalsUI::Proxy* AppCacheInternalsUI::GetProxyForPartitionPath(
499 const base::FilePath& partition_path) {
500 for (const scoped_refptr<Proxy>& proxy : appcache_proxies_) {
501 if (proxy->partition_path_ == partition_path)
502 return proxy.get();
504 NOTREACHED();
505 return nullptr;
508 } // namespace content