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.
4 
5 #include "content/browser/appcache/appcache_internals_ui.h"
6 
7 #include <stddef.h>
8 #include <vector>
9 
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_piece.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/task/post_task.h"
18 #include "base/threading/platform_thread.h"
19 #include "base/values.h"
20 #include "content/browser/appcache/appcache.h"
21 #include "content/browser/appcache/appcache_disk_cache_ops.h"
22 #include "content/browser/appcache/appcache_response_info.h"
23 #include "content/browser/storage_partition_impl.h"
24 #include "content/grit/dev_ui_content_resources.h"
25 #include "content/public/browser/browser_context.h"
26 #include "content/public/browser/browser_task_traits.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/web_ui.h"
29 #include "content/public/browser/web_ui_data_source.h"
30 #include "content/public/common/url_constants.h"
31 #include "net/base/escape.h"
32 #include "net/http/http_response_headers.h"
33 #include "net/url_request/view_cache_helper.h"
34 #include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
35 #include "third_party/blink/public/mojom/appcache/appcache_info.mojom.h"
36 
37 namespace content {
38 
39 namespace {
40 const char kRequestGetAllAppCacheInfo[] = "getAllAppCache";
41 const char kRequestDeleteAppCache[] = "deleteAppCache";
42 const char kRequestGetAppCacheDetails[] = "getAppCacheDetails";
43 const char kRequestGetFileDetails[] = "getFileDetails";
44 
45 const char kFunctionOnAllAppCacheInfoReady[] =
46     "appcache.onAllAppCacheInfoReady";
47 const char kFunctionOnAppCacheInfoDeleted[] = "appcache.onAppCacheInfoDeleted";
48 const char kFunctionOnAppCacheDetailsReady[] =
49     "appcache.onAppCacheDetailsReady";
50 const char kFunctionOnFileDetailsReady[] = "appcache.onFileDetailsReady";
51 const char kFunctionOnFileDetailsFailed[] = "appcache.onFileDetailsFailed";
52 
ToInt64(const std::string & str)53 int64_t ToInt64(const std::string& str) {
54   int64_t i = 0;
55   base::StringToInt64(str.c_str(), &i);
56   return i;
57 }
58 
SortByResourceUrl(const blink::mojom::AppCacheResourceInfo & lhs,const blink::mojom::AppCacheResourceInfo & rhs)59 bool SortByResourceUrl(const blink::mojom::AppCacheResourceInfo& lhs,
60                        const blink::mojom::AppCacheResourceInfo& rhs) {
61   return lhs.url.spec() < rhs.url.spec();
62 }
63 
GetDictionaryValueForResponseEnquiry(const content::AppCacheInternalsUI::ProxyResponseEnquiry & response_enquiry)64 std::unique_ptr<base::DictionaryValue> GetDictionaryValueForResponseEnquiry(
65     const content::AppCacheInternalsUI::ProxyResponseEnquiry&
66         response_enquiry) {
67   auto dict_value = std::make_unique<base::DictionaryValue>();
68   dict_value->SetString("manifestURL", response_enquiry.manifest_url);
69   dict_value->SetString("groupId",
70                         base::NumberToString(response_enquiry.group_id));
71   dict_value->SetString("responseId",
72                         base::NumberToString(response_enquiry.response_id));
73   return dict_value;
74 }
75 
GetDictionaryValueForAppCacheInfo(const blink::mojom::AppCacheInfo & appcache_info)76 std::unique_ptr<base::DictionaryValue> GetDictionaryValueForAppCacheInfo(
77     const blink::mojom::AppCacheInfo& appcache_info) {
78   auto dict_value = std::make_unique<base::DictionaryValue>();
79   dict_value->SetString("manifestURL", appcache_info.manifest_url.spec());
80   dict_value->SetDouble("creationTime", appcache_info.creation_time.ToJsTime());
81   dict_value->SetDouble("lastUpdateTime",
82                         appcache_info.last_update_time.ToJsTime());
83   dict_value->SetDouble("lastAccessTime",
84                         appcache_info.last_access_time.ToJsTime());
85   dict_value->SetDouble("tokenExpires", appcache_info.token_expires.ToJsTime());
86   dict_value->SetString("responseSizes",
87                         base::UTF16ToUTF8(base::FormatBytesUnlocalized(
88                             appcache_info.response_sizes)));
89   dict_value->SetString("paddingSizes",
90                         base::UTF16ToUTF8(base::FormatBytesUnlocalized(
91                             appcache_info.padding_sizes)));
92   dict_value->SetString(
93       "totalSize",
94       base::UTF16ToUTF8(base::FormatBytesUnlocalized(
95           appcache_info.response_sizes + appcache_info.padding_sizes)));
96   dict_value->SetString("groupId",
97                         base::NumberToString(appcache_info.group_id));
98 
99   dict_value->SetString(
100       "manifestParserVersion",
101       base::NumberToString(appcache_info.manifest_parser_version));
102   dict_value->SetString("manifestScope", appcache_info.manifest_scope);
103 
104   return dict_value;
105 }
106 
GetListValueForAppCacheInfoVector(const std::vector<blink::mojom::AppCacheInfo> appcache_info_vector)107 std::unique_ptr<base::ListValue> GetListValueForAppCacheInfoVector(
108     const std::vector<blink::mojom::AppCacheInfo> appcache_info_vector) {
109   auto list = std::make_unique<base::ListValue>();
110   for (const blink::mojom::AppCacheInfo& info : appcache_info_vector)
111     list->Append(GetDictionaryValueForAppCacheInfo(info));
112   return list;
113 }
114 
GetListValueFromAppCacheInfoCollection(AppCacheInfoCollection * appcache_collection)115 std::unique_ptr<base::ListValue> GetListValueFromAppCacheInfoCollection(
116     AppCacheInfoCollection* appcache_collection) {
117   auto list = std::make_unique<base::ListValue>();
118   for (const auto& key_value : appcache_collection->infos_by_origin) {
119     auto dict = std::make_unique<base::DictionaryValue>();
120     // Use GURL::spec() to keep consistency with previous version
121     dict->SetString("originURL", key_value.first.GetURL().spec());
122     dict->Set("manifests", GetListValueForAppCacheInfoVector(key_value.second));
123     list->Append(std::move(dict));
124   }
125   return list;
126 }
127 
128 std::unique_ptr<base::DictionaryValue>
GetDictionaryValueForAppCacheResourceInfo(const blink::mojom::AppCacheResourceInfo & resource_info)129 GetDictionaryValueForAppCacheResourceInfo(
130     const blink::mojom::AppCacheResourceInfo& resource_info) {
131   auto dict = std::make_unique<base::DictionaryValue>();
132   dict->SetString("url", resource_info.url.spec());
133   dict->SetString("responseSize",
134                   base::UTF16ToUTF8(base::FormatBytesUnlocalized(
135                       resource_info.response_size)));
136   dict->SetString("paddingSize", base::UTF16ToUTF8(base::FormatBytesUnlocalized(
137                                      resource_info.padding_size)));
138   dict->SetString("totalSize", base::UTF16ToUTF8(base::FormatBytesUnlocalized(
139                                    resource_info.response_size +
140                                    resource_info.padding_size)));
141   dict->SetString("responseId",
142                   base::NumberToString(resource_info.response_id));
143   dict->SetBoolean("isExplicit", resource_info.is_explicit);
144   dict->SetBoolean("isManifest", resource_info.is_manifest);
145   dict->SetBoolean("isMaster", resource_info.is_master);
146   dict->SetBoolean("isFallback", resource_info.is_fallback);
147   dict->SetBoolean("isIntercept", resource_info.is_intercept);
148   dict->SetBoolean("isForeign", resource_info.is_foreign);
149   dict->SetDouble("tokenExpires", resource_info.token_expires.ToJsTime());
150 
151   return dict;
152 }
153 
GetListValueForAppCacheResourceInfoVector(std::vector<blink::mojom::AppCacheResourceInfo> * resource_info_vector)154 std::unique_ptr<base::ListValue> GetListValueForAppCacheResourceInfoVector(
155     std::vector<blink::mojom::AppCacheResourceInfo>* resource_info_vector) {
156   auto list = std::make_unique<base::ListValue>();
157   for (const blink::mojom::AppCacheResourceInfo& res_info :
158        *resource_info_vector)
159     list->Append(GetDictionaryValueForAppCacheResourceInfo(res_info));
160   return list;
161 }
162 
163 }  // namespace
164 
Proxy(base::WeakPtr<AppCacheInternalsUI> appcache_internals_ui,const base::FilePath & partition_path)165 AppCacheInternalsUI::Proxy::Proxy(
166     base::WeakPtr<AppCacheInternalsUI> appcache_internals_ui,
167     const base::FilePath& partition_path)
168     : appcache_internals_ui_(appcache_internals_ui),
169       partition_path_(partition_path) {}
170 
Initialize(const scoped_refptr<ChromeAppCacheService> & chrome_appcache_service)171 void AppCacheInternalsUI::Proxy::Initialize(
172     const scoped_refptr<ChromeAppCacheService>& chrome_appcache_service) {
173   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
174     base::PostTask(
175         FROM_HERE, {BrowserThread::UI},
176         base::BindOnce(&Proxy::Initialize, this, chrome_appcache_service));
177     return;
178   }
179   appcache_service_ = chrome_appcache_service->AsWeakPtr();
180   shutdown_called_ = false;
181   preparing_response_ = false;
182 }
183 
~Proxy()184 AppCacheInternalsUI::Proxy::~Proxy() {
185   DCHECK(shutdown_called_);
186 }
187 
Shutdown()188 void AppCacheInternalsUI::Proxy::Shutdown() {
189   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
190     base::PostTask(FROM_HERE, {BrowserThread::UI},
191                    base::BindOnce(&Proxy::Shutdown, this));
192     return;
193   }
194   shutdown_called_ = true;
195   if (appcache_service_) {
196     appcache_service_->storage()->CancelDelegateCallbacks(this);
197     appcache_service_.reset();
198     response_enquiries_.clear();
199   }
200 }
201 
RequestAllAppCacheInfo()202 void AppCacheInternalsUI::Proxy::RequestAllAppCacheInfo() {
203   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
204     base::PostTask(FROM_HERE, {BrowserThread::UI},
205                    base::BindOnce(&Proxy::RequestAllAppCacheInfo, this));
206     return;
207   }
208   if (appcache_service_) {
209     auto collection = base::MakeRefCounted<AppCacheInfoCollection>();
210     AppCacheInfoCollection* collection_ptr = collection.get();
211     appcache_service_->GetAllAppCacheInfo(
212         collection_ptr, base::BindOnce(&Proxy::OnAllAppCacheInfoReady, this,
213                                        std::move(collection)));
214   }
215 }
216 
OnAllAppCacheInfoReady(scoped_refptr<AppCacheInfoCollection> collection,int net_result_code)217 void AppCacheInternalsUI::Proxy::OnAllAppCacheInfoReady(
218     scoped_refptr<AppCacheInfoCollection> collection,
219     int net_result_code) {
220   appcache_internals_ui_->OnAllAppCacheInfoReady(collection, partition_path_);
221 }
222 
DeleteAppCache(const std::string & manifest_url)223 void AppCacheInternalsUI::Proxy::DeleteAppCache(
224     const std::string& manifest_url) {
225   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
226     base::PostTask(FROM_HERE, {BrowserThread::UI},
227                    base::BindOnce(&Proxy::DeleteAppCache, this, manifest_url));
228     return;
229   }
230   if (appcache_service_) {
231     appcache_service_->DeleteAppCacheGroup(
232         GURL(manifest_url),
233         base::BindOnce(&Proxy::OnAppCacheInfoDeleted, this, manifest_url));
234   }
235 }
236 
OnAppCacheInfoDeleted(const std::string & manifest_url,int net_result_code)237 void AppCacheInternalsUI::Proxy::OnAppCacheInfoDeleted(
238     const std::string& manifest_url,
239     int net_result_code) {
240   appcache_internals_ui_->OnAppCacheInfoDeleted(partition_path_, manifest_url,
241                                                 net_result_code == net::OK);
242 }
243 
RequestAppCacheDetails(const std::string & manifest_url)244 void AppCacheInternalsUI::Proxy::RequestAppCacheDetails(
245     const std::string& manifest_url) {
246   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
247     base::PostTask(
248         FROM_HERE, {BrowserThread::UI},
249         base::BindOnce(&Proxy::RequestAppCacheDetails, this, manifest_url));
250     return;
251   }
252 
253   if (appcache_service_)
254     appcache_service_->storage()->LoadOrCreateGroup(GURL(manifest_url), this);
255 }
256 
OnGroupLoaded(AppCacheGroup * appcache_group,const GURL & manifest_gurl)257 void AppCacheInternalsUI::Proxy::OnGroupLoaded(AppCacheGroup* appcache_group,
258                                                const GURL& manifest_gurl) {
259   std::unique_ptr<std::vector<blink::mojom::AppCacheResourceInfo>>
260       resource_info_vector;
261   if (appcache_group && appcache_group->newest_complete_cache()) {
262     resource_info_vector =
263         std::make_unique<std::vector<blink::mojom::AppCacheResourceInfo>>();
264     appcache_group->newest_complete_cache()->ToResourceInfoVector(
265         resource_info_vector.get());
266     std::sort(resource_info_vector->begin(), resource_info_vector->end(),
267               SortByResourceUrl);
268   }
269   appcache_internals_ui_->OnAppCacheDetailsReady(
270       partition_path_, manifest_gurl.spec(), std::move(resource_info_vector));
271 }
272 
RequestFileDetails(const ProxyResponseEnquiry & response_enquiry)273 void AppCacheInternalsUI::Proxy::RequestFileDetails(
274     const ProxyResponseEnquiry& response_enquiry) {
275   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
276     base::PostTask(
277         FROM_HERE, {BrowserThread::UI},
278         base::BindOnce(&Proxy::RequestFileDetails, this, response_enquiry));
279     return;
280   }
281   DCHECK(!shutdown_called_);
282   response_enquiries_.push_back(response_enquiry);
283   HandleFileDetailsRequest();
284 }
285 
HandleFileDetailsRequest()286 void AppCacheInternalsUI::Proxy::HandleFileDetailsRequest() {
287   if (preparing_response_ || response_enquiries_.empty() || !appcache_service_)
288     return;
289   preparing_response_ = true;
290   appcache_service_->storage()->LoadResponseInfo(
291       GURL(response_enquiries_.front().manifest_url),
292       response_enquiries_.front().response_id, this);
293 }
294 
OnResponseInfoLoaded(AppCacheResponseInfo * response,int64_t response_id)295 void AppCacheInternalsUI::Proxy::OnResponseInfoLoaded(
296     AppCacheResponseInfo* response,
297     int64_t response_id) {
298   if (shutdown_called_)
299     return;
300   if (!appcache_service_)
301     return;
302   ProxyResponseEnquiry response_enquiry = response_enquiries_.front();
303   response_enquiries_.pop_front();
304   if (response) {
305     scoped_refptr<AppCacheResponseInfo> response_info = response;
306     const int64_t kLimit = 100 * 1000;
307     int64_t amount_to_read =
308         std::min(kLimit, response_info->response_data_size());
309     scoped_refptr<net::IOBuffer> response_data =
310         base::MakeRefCounted<net::IOBuffer>(
311             base::checked_cast<size_t>(amount_to_read));
312     std::unique_ptr<AppCacheResponseReader> reader =
313         appcache_service_->storage()->CreateResponseReader(
314             GURL(response_enquiry.manifest_url), response_enquiry.response_id);
315 
316     reader->ReadData(response_data.get(), amount_to_read,
317                      base::BindOnce(&Proxy::OnResponseDataReadComplete, this,
318                                     response_enquiry, response_info,
319                                     std::move(reader), response_data));
320   } else {
321     OnResponseDataReadComplete(response_enquiry, nullptr, nullptr, nullptr, -1);
322   }
323 }
324 
OnResponseDataReadComplete(const ProxyResponseEnquiry & response_enquiry,scoped_refptr<AppCacheResponseInfo> response_info,std::unique_ptr<AppCacheResponseReader> reader,scoped_refptr<net::IOBuffer> response_data,int net_result_code)325 void AppCacheInternalsUI::Proxy::OnResponseDataReadComplete(
326     const ProxyResponseEnquiry& response_enquiry,
327     scoped_refptr<AppCacheResponseInfo> response_info,
328     std::unique_ptr<AppCacheResponseReader> reader,
329     scoped_refptr<net::IOBuffer> response_data,
330     int net_result_code) {
331   if (shutdown_called_)
332     return;
333   if (!response_info || net_result_code < 0) {
334     appcache_internals_ui_->OnFileDetailsFailed(response_enquiry,
335                                                 net_result_code);
336   } else {
337     appcache_internals_ui_->OnFileDetailsReady(response_enquiry, response_info,
338                                                response_data, net_result_code);
339   }
340   preparing_response_ = false;
341   HandleFileDetailsRequest();
342 }
343 
AppCacheInternalsUI(WebUI * web_ui)344 AppCacheInternalsUI::AppCacheInternalsUI(WebUI* web_ui)
345     : WebUIController(web_ui) {
346   web_ui->RegisterMessageCallback(
347       kRequestGetAllAppCacheInfo,
348       base::BindRepeating(&AppCacheInternalsUI::GetAllAppCache, AsWeakPtr()));
349 
350   web_ui->RegisterMessageCallback(
351       kRequestDeleteAppCache,
352       base::BindRepeating(&AppCacheInternalsUI::DeleteAppCache, AsWeakPtr()));
353 
354   web_ui->RegisterMessageCallback(
355       kRequestGetAppCacheDetails,
356       base::BindRepeating(&AppCacheInternalsUI::GetAppCacheDetails,
357                           AsWeakPtr()));
358 
359   web_ui->RegisterMessageCallback(
360       kRequestGetFileDetails,
361       base::BindRepeating(&AppCacheInternalsUI::GetFileDetails, AsWeakPtr()));
362 
363   WebUIDataSource* source =
364       WebUIDataSource::Create(kChromeUIAppCacheInternalsHost);
365   source->OverrideContentSecurityPolicyScriptSrc(
366       "script-src chrome://resources 'self' 'unsafe-eval';");
367 
368   source->UseStringsJs();
369   source->AddResourcePath("appcache_internals.js", IDR_APPCACHE_INTERNALS_JS);
370   source->AddResourcePath("appcache_internals.css", IDR_APPCACHE_INTERNALS_CSS);
371   source->SetDefaultResource(IDR_APPCACHE_INTERNALS_HTML);
372 
373   WebUIDataSource::Add(GetBrowserContext(), source);
374 
375   BrowserContext::ForEachStoragePartition(
376       GetBrowserContext(),
377       base::BindRepeating(&AppCacheInternalsUI::CreateProxyForPartition,
378                           AsWeakPtr()));
379 }
380 
~AppCacheInternalsUI()381 AppCacheInternalsUI::~AppCacheInternalsUI() {
382   for (auto& proxy : appcache_proxies_)
383     proxy->Shutdown();
384 }
385 
CreateProxyForPartition(StoragePartition * storage_partition)386 void AppCacheInternalsUI::CreateProxyForPartition(
387     StoragePartition* storage_partition) {
388   auto proxy = base::MakeRefCounted<Proxy>(weak_ptr_factory_.GetWeakPtr(),
389                                            storage_partition->GetPath());
390   proxy->Initialize(static_cast<StoragePartitionImpl*>(storage_partition)
391                         ->GetAppCacheService());
392   appcache_proxies_.emplace_back(std::move(proxy));
393 }
394 
GetAllAppCache(const base::ListValue * args)395 void AppCacheInternalsUI::GetAllAppCache(const base::ListValue* args) {
396   DCHECK_CURRENTLY_ON(BrowserThread::UI);
397   for (scoped_refptr<Proxy>& proxy : appcache_proxies_)
398     proxy->RequestAllAppCacheInfo();
399 }
400 
DeleteAppCache(const base::ListValue * args)401 void AppCacheInternalsUI::DeleteAppCache(const base::ListValue* args) {
402   DCHECK_CURRENTLY_ON(BrowserThread::UI);
403   std::string manifest_url, partition_path;
404   args->GetString(0, &partition_path);
405   args->GetString(1, &manifest_url);
406   Proxy* proxy =
407       GetProxyForPartitionPath(base::FilePath::FromUTF8Unsafe(partition_path));
408   if (proxy)
409     proxy->DeleteAppCache(manifest_url);
410 }
411 
GetAppCacheDetails(const base::ListValue * args)412 void AppCacheInternalsUI::GetAppCacheDetails(const base::ListValue* args) {
413   std::string manifest_url, partition_path;
414   args->GetString(0, &partition_path);
415   args->GetString(1, &manifest_url);
416   Proxy* proxy =
417       GetProxyForPartitionPath(base::FilePath::FromUTF8Unsafe(partition_path));
418   if (proxy)
419     proxy->RequestAppCacheDetails(manifest_url);
420 }
421 
GetFileDetails(const base::ListValue * args)422 void AppCacheInternalsUI::GetFileDetails(const base::ListValue* args) {
423   std::string manifest_url, partition_path, group_id_str, response_id_str;
424   args->GetString(0, &partition_path);
425   args->GetString(1, &manifest_url);
426   args->GetString(2, &group_id_str);
427   args->GetString(3, &response_id_str);
428   Proxy* proxy =
429       GetProxyForPartitionPath(base::FilePath::FromUTF8Unsafe(partition_path));
430   if (proxy)
431     proxy->RequestFileDetails(
432         {manifest_url, ToInt64(group_id_str), ToInt64(response_id_str)});
433 }
434 
OnAllAppCacheInfoReady(scoped_refptr<AppCacheInfoCollection> collection,const base::FilePath & partition_path)435 void AppCacheInternalsUI::OnAllAppCacheInfoReady(
436     scoped_refptr<AppCacheInfoCollection> collection,
437     const base::FilePath& partition_path) {
438   std::string incognito_path_prefix;
439   if (GetBrowserContext()->IsOffTheRecord())
440     incognito_path_prefix = "Incognito ";
441   web_ui()->CallJavascriptFunctionUnsafe(
442       kFunctionOnAllAppCacheInfoReady,
443       base::Value(partition_path.AsUTF8Unsafe()),
444       base::Value(incognito_path_prefix + partition_path.AsUTF8Unsafe()),
445       *GetListValueFromAppCacheInfoCollection(collection.get()));
446 }
447 
OnAppCacheInfoDeleted(const base::FilePath & partition_path,const std::string & manifest_url,bool deleted)448 void AppCacheInternalsUI::OnAppCacheInfoDeleted(
449     const base::FilePath& partition_path,
450     const std::string& manifest_url,
451     bool deleted) {
452   web_ui()->CallJavascriptFunctionUnsafe(
453       kFunctionOnAppCacheInfoDeleted,
454       base::Value(partition_path.AsUTF8Unsafe()), base::Value(manifest_url),
455       base::Value(deleted));
456 }
457 
OnAppCacheDetailsReady(const base::FilePath & partition_path,const std::string & manifest_url,std::unique_ptr<std::vector<blink::mojom::AppCacheResourceInfo>> resource_info_vector)458 void AppCacheInternalsUI::OnAppCacheDetailsReady(
459     const base::FilePath& partition_path,
460     const std::string& manifest_url,
461     std::unique_ptr<std::vector<blink::mojom::AppCacheResourceInfo>>
462         resource_info_vector) {
463   if (resource_info_vector) {
464     web_ui()->CallJavascriptFunctionUnsafe(
465         kFunctionOnAppCacheDetailsReady, base::Value(manifest_url),
466         base::Value(partition_path.AsUTF8Unsafe()),
467         *GetListValueForAppCacheResourceInfoVector(resource_info_vector.get()));
468   } else {
469     web_ui()->CallJavascriptFunctionUnsafe(
470         kFunctionOnAppCacheDetailsReady, base::Value(manifest_url),
471         base::Value(partition_path.AsUTF8Unsafe()));
472   }
473 }
474 
OnFileDetailsReady(const ProxyResponseEnquiry & response_enquiry,scoped_refptr<AppCacheResponseInfo> response_info,scoped_refptr<net::IOBuffer> response_data,int data_length)475 void AppCacheInternalsUI::OnFileDetailsReady(
476     const ProxyResponseEnquiry& response_enquiry,
477     scoped_refptr<AppCacheResponseInfo> response_info,
478     scoped_refptr<net::IOBuffer> response_data,
479     int data_length) {
480   std::string headers;
481   headers.append("<hr><pre>");
482   headers.append(net::EscapeForHTML(
483       response_info->http_response_info().headers->GetStatusLine()));
484   headers.push_back('\n');
485 
486   size_t iter = 0;
487   std::string name, value;
488   while (response_info->http_response_info().headers->EnumerateHeaderLines(
489       &iter, &name, &value)) {
490     headers.append(net::EscapeForHTML(name));
491     headers.append(": ");
492     headers.append(net::EscapeForHTML(value));
493     headers.push_back('\n');
494   }
495   headers.append("</pre>");
496 
497   std::string hex_dump = base::StringPrintf(
498       "<hr><pre> Showing %d of %d bytes\n\n", static_cast<int>(data_length),
499       static_cast<int>(response_info->response_data_size()));
500   net::ViewCacheHelper::HexDump(response_data->data(), data_length, &hex_dump);
501   if (data_length < response_info->response_data_size())
502     hex_dump.append("\nNote: data is truncated...");
503   hex_dump.append("</pre>");
504   web_ui()->CallJavascriptFunctionUnsafe(
505       kFunctionOnFileDetailsReady,
506       *GetDictionaryValueForResponseEnquiry(response_enquiry),
507       base::Value(headers), base::Value(hex_dump));
508 }
509 
OnFileDetailsFailed(const ProxyResponseEnquiry & response_enquiry,int net_result_code)510 void AppCacheInternalsUI::OnFileDetailsFailed(
511     const ProxyResponseEnquiry& response_enquiry,
512     int net_result_code) {
513   web_ui()->CallJavascriptFunctionUnsafe(
514       kFunctionOnFileDetailsFailed,
515       *GetDictionaryValueForResponseEnquiry(response_enquiry),
516       base::Value(net_result_code));
517 }
518 
GetBrowserContext()519 BrowserContext* AppCacheInternalsUI::GetBrowserContext() {
520   return web_ui()->GetWebContents()->GetBrowserContext();
521 }
522 
GetProxyForPartitionPath(const base::FilePath & partition_path)523 AppCacheInternalsUI::Proxy* AppCacheInternalsUI::GetProxyForPartitionPath(
524     const base::FilePath& partition_path) {
525   for (const scoped_refptr<Proxy>& proxy : appcache_proxies_) {
526     if (proxy->partition_path_ == partition_path)
527       return proxy.get();
528   }
529   NOTREACHED();
530   return nullptr;
531 }
532 
533 }  // namespace content
534