Save running SW instance info, including its SiteInstance, into the ProcessManager.
[chromium-blink-merge.git] / content / browser / appcache / view_appcache_internals_job.cc
blobb6a3c3fdbbd1438579fe50fc3704b8f6cdffdd92
1 // Copyright 2014 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/view_appcache_internals_job.h"
7 #include <algorithm>
8 #include <string>
10 #include "base/base64.h"
11 #include "base/bind.h"
12 #include "base/format_macros.h"
13 #include "base/i18n/time_formatting.h"
14 #include "base/logging.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "net/base/escape.h"
21 #include "net/base/io_buffer.h"
22 #include "net/base/net_errors.h"
23 #include "net/http/http_response_headers.h"
24 #include "net/url_request/url_request.h"
25 #include "net/url_request/url_request_simple_job.h"
26 #include "net/url_request/view_cache_helper.h"
27 #include "webkit/browser/appcache/appcache.h"
28 #include "webkit/browser/appcache/appcache_group.h"
29 #include "webkit/browser/appcache/appcache_policy.h"
30 #include "webkit/browser/appcache/appcache_response.h"
31 #include "webkit/browser/appcache/appcache_service.h"
32 #include "webkit/browser/appcache/appcache_storage.h"
34 using appcache::AppCacheGroup;
35 using appcache::AppCacheInfo;
36 using appcache::AppCacheInfoCollection;
37 using appcache::AppCacheInfoVector;
38 using appcache::AppCacheService;
39 using appcache::AppCacheStorage;
40 using appcache::AppCacheStorageReference;
41 using appcache::AppCacheResourceInfo;
42 using appcache::AppCacheResourceInfoVector;
43 using appcache::AppCacheResponseInfo;
44 using appcache::AppCacheResponseReader;
46 namespace content {
47 namespace {
49 const char kErrorMessage[] = "Error in retrieving Application Caches.";
50 const char kEmptyAppCachesMessage[] = "No available Application Caches.";
51 const char kManifestNotFoundMessage[] = "Manifest not found.";
52 const char kManifest[] = "Manifest: ";
53 const char kSize[] = "Size: ";
54 const char kCreationTime[] = "Creation Time: ";
55 const char kLastAccessTime[] = "Last Access Time: ";
56 const char kLastUpdateTime[] = "Last Update Time: ";
57 const char kFormattedDisabledAppCacheMsg[] =
58 "<b><i><font color=\"FF0000\">"
59 "This Application Cache is disabled by policy.</font></i></b><br/>";
60 const char kRemoveCacheLabel[] = "Remove";
61 const char kViewCacheLabel[] = "View Entries";
62 const char kRemoveCacheCommand[] = "remove-cache";
63 const char kViewCacheCommand[] = "view-cache";
64 const char kViewEntryCommand[] = "view-entry";
66 void EmitPageStart(std::string* out) {
67 out->append(
68 "<!DOCTYPE HTML>\n"
69 "<html><title>AppCache Internals</title>\n"
70 "<meta http-equiv=\"Content-Security-Policy\""
71 " content=\"object-src 'none'; script-src 'none'\">\n"
72 "<style>\n"
73 "body { font-family: sans-serif; font-size: 0.8em; }\n"
74 "tt, code, pre { font-family: WebKitHack, monospace; }\n"
75 "form { display: inline; }\n"
76 ".subsection_body { margin: 10px 0 10px 2em; }\n"
77 ".subsection_title { font-weight: bold; }\n"
78 "</style>\n"
79 "</head><body>\n");
82 void EmitPageEnd(std::string* out) {
83 out->append("</body></html>\n");
86 void EmitListItem(const std::string& label,
87 const std::string& data,
88 std::string* out) {
89 out->append("<li>");
90 out->append(net::EscapeForHTML(label));
91 out->append(net::EscapeForHTML(data));
92 out->append("</li>\n");
95 void EmitAnchor(const std::string& url, const std::string& text,
96 std::string* out) {
97 out->append("<a href=\"");
98 out->append(net::EscapeForHTML(url));
99 out->append("\">");
100 out->append(net::EscapeForHTML(text));
101 out->append("</a>");
104 void EmitCommandAnchor(const char* label,
105 const GURL& base_url,
106 const char* command,
107 const char* param,
108 std::string* out) {
109 std::string query(command);
110 query.push_back('=');
111 query.append(param);
112 GURL::Replacements replacements;
113 replacements.SetQuery(query.data(), url::Component(0, query.length()));
114 GURL command_url = base_url.ReplaceComponents(replacements);
115 EmitAnchor(command_url.spec(), label, out);
118 void EmitAppCacheInfo(const GURL& base_url,
119 AppCacheService* service,
120 const AppCacheInfo* info,
121 std::string* out) {
122 std::string manifest_url_base64;
123 base::Base64Encode(info->manifest_url.spec(), &manifest_url_base64);
125 out->append("\n<p>");
126 out->append(kManifest);
127 EmitAnchor(info->manifest_url.spec(), info->manifest_url.spec(), out);
128 out->append("<br/>\n");
129 if (!service->appcache_policy()->CanLoadAppCache(
130 info->manifest_url, info->manifest_url)) {
131 out->append(kFormattedDisabledAppCacheMsg);
133 out->append("\n<br/>\n");
134 EmitCommandAnchor(kRemoveCacheLabel, base_url,
135 kRemoveCacheCommand, manifest_url_base64.c_str(), out);
136 out->append("&nbsp;&nbsp;");
137 EmitCommandAnchor(kViewCacheLabel, base_url,
138 kViewCacheCommand, manifest_url_base64.c_str(), out);
139 out->append("\n<br/>\n");
140 out->append("<ul>");
141 EmitListItem(
142 kSize,
143 base::UTF16ToUTF8(FormatBytesUnlocalized(info->size)),
144 out);
145 EmitListItem(
146 kCreationTime,
147 base::UTF16ToUTF8(TimeFormatFriendlyDateAndTime(info->creation_time)),
148 out);
149 EmitListItem(
150 kLastUpdateTime,
151 base::UTF16ToUTF8(TimeFormatFriendlyDateAndTime(info->last_update_time)),
152 out);
153 EmitListItem(
154 kLastAccessTime,
155 base::UTF16ToUTF8(TimeFormatFriendlyDateAndTime(info->last_access_time)),
156 out);
157 out->append("</ul></p></br>\n");
160 void EmitAppCacheInfoVector(
161 const GURL& base_url,
162 AppCacheService* service,
163 const AppCacheInfoVector& appcaches,
164 std::string* out) {
165 for (std::vector<AppCacheInfo>::const_iterator info =
166 appcaches.begin();
167 info != appcaches.end(); ++info) {
168 EmitAppCacheInfo(base_url, service, &(*info), out);
172 void EmitTableData(const std::string& data, bool align_right, bool bold,
173 std::string* out) {
174 if (align_right)
175 out->append("<td align='right'>");
176 else
177 out->append("<td>");
178 if (bold)
179 out->append("<b>");
180 out->append(data);
181 if (bold)
182 out->append("</b>");
183 out->append("</td>");
186 std::string FormFlagsString(const AppCacheResourceInfo& info) {
187 std::string str;
188 if (info.is_manifest)
189 str.append("Manifest, ");
190 if (info.is_master)
191 str.append("Master, ");
192 if (info.is_intercept)
193 str.append("Intercept, ");
194 if (info.is_fallback)
195 str.append("Fallback, ");
196 if (info.is_explicit)
197 str.append("Explicit, ");
198 if (info.is_foreign)
199 str.append("Foreign, ");
200 return str;
203 std::string FormViewEntryAnchor(const GURL& base_url,
204 const GURL& manifest_url, const GURL& entry_url,
205 int64 response_id,
206 int64 group_id) {
207 std::string manifest_url_base64;
208 std::string entry_url_base64;
209 std::string response_id_string;
210 std::string group_id_string;
211 base::Base64Encode(manifest_url.spec(), &manifest_url_base64);
212 base::Base64Encode(entry_url.spec(), &entry_url_base64);
213 response_id_string = base::Int64ToString(response_id);
214 group_id_string = base::Int64ToString(group_id);
216 std::string query(kViewEntryCommand);
217 query.push_back('=');
218 query.append(manifest_url_base64);
219 query.push_back('|');
220 query.append(entry_url_base64);
221 query.push_back('|');
222 query.append(response_id_string);
223 query.push_back('|');
224 query.append(group_id_string);
226 GURL::Replacements replacements;
227 replacements.SetQuery(query.data(), url::Component(0, query.length()));
228 GURL view_entry_url = base_url.ReplaceComponents(replacements);
230 std::string anchor;
231 EmitAnchor(view_entry_url.spec(), entry_url.spec(), &anchor);
232 return anchor;
235 void EmitAppCacheResourceInfoVector(
236 const GURL& base_url,
237 const GURL& manifest_url,
238 const AppCacheResourceInfoVector& resource_infos,
239 int64 group_id,
240 std::string* out) {
241 out->append("<table border='0'>\n");
242 out->append("<tr>");
243 EmitTableData("Flags", false, true, out);
244 EmitTableData("URL", false, true, out);
245 EmitTableData("Size (headers and data)", true, true, out);
246 out->append("</tr>\n");
247 for (AppCacheResourceInfoVector::const_iterator
248 iter = resource_infos.begin();
249 iter != resource_infos.end(); ++iter) {
250 out->append("<tr>");
251 EmitTableData(FormFlagsString(*iter), false, false, out);
252 EmitTableData(FormViewEntryAnchor(base_url, manifest_url,
253 iter->url, iter->response_id,
254 group_id),
255 false, false, out);
256 EmitTableData(base::UTF16ToUTF8(FormatBytesUnlocalized(iter->size)),
257 true, false, out);
258 out->append("</tr>\n");
260 out->append("</table>\n");
263 void EmitResponseHeaders(net::HttpResponseHeaders* headers, std::string* out) {
264 out->append("<hr><pre>");
265 out->append(net::EscapeForHTML(headers->GetStatusLine()));
266 out->push_back('\n');
268 void* iter = NULL;
269 std::string name, value;
270 while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
271 out->append(net::EscapeForHTML(name));
272 out->append(": ");
273 out->append(net::EscapeForHTML(value));
274 out->push_back('\n');
276 out->append("</pre>");
279 void EmitHexDump(const char *buf, size_t buf_len, size_t total_len,
280 std::string* out) {
281 out->append("<hr><pre>");
282 base::StringAppendF(out, "Showing %d of %d bytes\n\n",
283 static_cast<int>(buf_len), static_cast<int>(total_len));
284 net::ViewCacheHelper::HexDump(buf, buf_len, out);
285 if (buf_len < total_len)
286 out->append("\nNote: data is truncated...");
287 out->append("</pre>");
290 GURL DecodeBase64URL(const std::string& base64) {
291 std::string url;
292 base::Base64Decode(base64, &url);
293 return GURL(url);
296 bool ParseQuery(const std::string& query,
297 std::string* command, std::string* value) {
298 size_t position = query.find("=");
299 if (position == std::string::npos)
300 return false;
301 *command = query.substr(0, position);
302 *value = query.substr(position + 1);
303 return !command->empty() && !value->empty();
306 bool SortByManifestUrl(const AppCacheInfo& lhs,
307 const AppCacheInfo& rhs) {
308 return lhs.manifest_url.spec() < rhs.manifest_url.spec();
311 bool SortByResourceUrl(const AppCacheResourceInfo& lhs,
312 const AppCacheResourceInfo& rhs) {
313 return lhs.url.spec() < rhs.url.spec();
316 GURL ClearQuery(const GURL& url) {
317 GURL::Replacements replacements;
318 replacements.ClearQuery();
319 return url.ReplaceComponents(replacements);
322 // Simple base class for the job subclasses defined here.
323 class BaseInternalsJob : public net::URLRequestSimpleJob,
324 public AppCacheService::Observer {
325 protected:
326 BaseInternalsJob(net::URLRequest* request,
327 net::NetworkDelegate* network_delegate,
328 AppCacheService* service)
329 : URLRequestSimpleJob(request, network_delegate),
330 appcache_service_(service),
331 appcache_storage_(service->storage()) {
332 appcache_service_->AddObserver(this);
335 virtual ~BaseInternalsJob() {
336 appcache_service_->RemoveObserver(this);
339 virtual void OnServiceReinitialized(
340 AppCacheStorageReference* old_storage_ref) OVERRIDE {
341 if (old_storage_ref->storage() == appcache_storage_)
342 disabled_storage_reference_ = old_storage_ref;
345 AppCacheService* appcache_service_;
346 AppCacheStorage* appcache_storage_;
347 scoped_refptr<AppCacheStorageReference> disabled_storage_reference_;
350 // Job that lists all appcaches in the system.
351 class MainPageJob : public BaseInternalsJob {
352 public:
353 MainPageJob(net::URLRequest* request,
354 net::NetworkDelegate* network_delegate,
355 AppCacheService* service)
356 : BaseInternalsJob(request, network_delegate, service),
357 weak_factory_(this) {
360 virtual void Start() OVERRIDE {
361 DCHECK(request_);
362 info_collection_ = new AppCacheInfoCollection;
363 appcache_service_->GetAllAppCacheInfo(
364 info_collection_.get(),
365 base::Bind(&MainPageJob::OnGotInfoComplete,
366 weak_factory_.GetWeakPtr()));
369 // Produces a page containing the listing
370 virtual int GetData(std::string* mime_type,
371 std::string* charset,
372 std::string* out,
373 const net::CompletionCallback& callback) const OVERRIDE {
374 mime_type->assign("text/html");
375 charset->assign("UTF-8");
377 out->clear();
378 EmitPageStart(out);
379 if (!info_collection_.get()) {
380 out->append(kErrorMessage);
381 } else if (info_collection_->infos_by_origin.empty()) {
382 out->append(kEmptyAppCachesMessage);
383 } else {
384 typedef std::map<GURL, AppCacheInfoVector> InfoByOrigin;
385 AppCacheInfoVector appcaches;
386 for (InfoByOrigin::const_iterator origin =
387 info_collection_->infos_by_origin.begin();
388 origin != info_collection_->infos_by_origin.end(); ++origin) {
389 appcaches.insert(appcaches.end(),
390 origin->second.begin(), origin->second.end());
392 std::sort(appcaches.begin(), appcaches.end(), SortByManifestUrl);
394 GURL base_url = ClearQuery(request_->url());
395 EmitAppCacheInfoVector(base_url, appcache_service_, appcaches, out);
397 EmitPageEnd(out);
398 return net::OK;
401 private:
402 virtual ~MainPageJob() {}
404 void OnGotInfoComplete(int rv) {
405 if (rv != net::OK)
406 info_collection_ = NULL;
407 StartAsync();
410 scoped_refptr<AppCacheInfoCollection> info_collection_;
411 base::WeakPtrFactory<MainPageJob> weak_factory_;
412 DISALLOW_COPY_AND_ASSIGN(MainPageJob);
415 // Job that redirects back to the main appcache internals page.
416 class RedirectToMainPageJob : public BaseInternalsJob {
417 public:
418 RedirectToMainPageJob(net::URLRequest* request,
419 net::NetworkDelegate* network_delegate,
420 AppCacheService* service)
421 : BaseInternalsJob(request, network_delegate, service) {}
423 virtual int GetData(std::string* mime_type,
424 std::string* charset,
425 std::string* data,
426 const net::CompletionCallback& callback) const OVERRIDE {
427 return net::OK; // IsRedirectResponse induces a redirect.
430 virtual bool IsRedirectResponse(GURL* location,
431 int* http_status_code) OVERRIDE {
432 *location = ClearQuery(request_->url());
433 *http_status_code = 307;
434 return true;
437 protected:
438 virtual ~RedirectToMainPageJob() {}
441 // Job that removes an appcache and then redirects back to the main page.
442 class RemoveAppCacheJob : public RedirectToMainPageJob {
443 public:
444 RemoveAppCacheJob(
445 net::URLRequest* request,
446 net::NetworkDelegate* network_delegate,
447 AppCacheService* service,
448 const GURL& manifest_url)
449 : RedirectToMainPageJob(request, network_delegate, service),
450 manifest_url_(manifest_url),
451 weak_factory_(this) {
454 virtual void Start() OVERRIDE {
455 DCHECK(request_);
457 appcache_service_->DeleteAppCacheGroup(
458 manifest_url_,base::Bind(&RemoveAppCacheJob::OnDeleteAppCacheComplete,
459 weak_factory_.GetWeakPtr()));
462 private:
463 virtual ~RemoveAppCacheJob() {}
465 void OnDeleteAppCacheComplete(int rv) {
466 StartAsync(); // Causes the base class to redirect.
469 GURL manifest_url_;
470 base::WeakPtrFactory<RemoveAppCacheJob> weak_factory_;
474 // Job shows the details of a particular manifest url.
475 class ViewAppCacheJob : public BaseInternalsJob,
476 public AppCacheStorage::Delegate {
477 public:
478 ViewAppCacheJob(
479 net::URLRequest* request,
480 net::NetworkDelegate* network_delegate,
481 AppCacheService* service,
482 const GURL& manifest_url)
483 : BaseInternalsJob(request, network_delegate, service),
484 manifest_url_(manifest_url) {}
486 virtual void Start() OVERRIDE {
487 DCHECK(request_);
488 appcache_storage_->LoadOrCreateGroup(manifest_url_, this);
491 // Produces a page containing the entries listing.
492 virtual int GetData(std::string* mime_type,
493 std::string* charset,
494 std::string* out,
495 const net::CompletionCallback& callback) const OVERRIDE {
496 mime_type->assign("text/html");
497 charset->assign("UTF-8");
498 out->clear();
499 EmitPageStart(out);
500 if (appcache_info_.manifest_url.is_empty()) {
501 out->append(kManifestNotFoundMessage);
502 } else {
503 GURL base_url = ClearQuery(request_->url());
504 EmitAppCacheInfo(base_url, appcache_service_, &appcache_info_, out);
505 EmitAppCacheResourceInfoVector(base_url,
506 manifest_url_,
507 resource_infos_,
508 appcache_info_.group_id,
509 out);
511 EmitPageEnd(out);
512 return net::OK;
515 private:
516 virtual ~ViewAppCacheJob() {
517 appcache_storage_->CancelDelegateCallbacks(this);
520 // AppCacheStorage::Delegate override
521 virtual void OnGroupLoaded(
522 AppCacheGroup* group, const GURL& manifest_url) OVERRIDE {
523 DCHECK_EQ(manifest_url_, manifest_url);
524 if (group && group->newest_complete_cache()) {
525 appcache_info_.manifest_url = manifest_url;
526 appcache_info_.group_id = group->group_id();
527 appcache_info_.size = group->newest_complete_cache()->cache_size();
528 appcache_info_.creation_time = group->creation_time();
529 appcache_info_.last_update_time =
530 group->newest_complete_cache()->update_time();
531 appcache_info_.last_access_time = base::Time::Now();
532 group->newest_complete_cache()->ToResourceInfoVector(&resource_infos_);
533 std::sort(resource_infos_.begin(), resource_infos_.end(),
534 SortByResourceUrl);
536 StartAsync();
539 GURL manifest_url_;
540 AppCacheInfo appcache_info_;
541 AppCacheResourceInfoVector resource_infos_;
542 DISALLOW_COPY_AND_ASSIGN(ViewAppCacheJob);
545 // Job that shows the details of a particular cached resource.
546 class ViewEntryJob : public BaseInternalsJob,
547 public AppCacheStorage::Delegate {
548 public:
549 ViewEntryJob(
550 net::URLRequest* request,
551 net::NetworkDelegate* network_delegate,
552 AppCacheService* service,
553 const GURL& manifest_url,
554 const GURL& entry_url,
555 int64 response_id, int64 group_id)
556 : BaseInternalsJob(request, network_delegate, service),
557 manifest_url_(manifest_url), entry_url_(entry_url),
558 response_id_(response_id), group_id_(group_id), amount_read_(0) {
561 virtual void Start() OVERRIDE {
562 DCHECK(request_);
563 appcache_storage_->LoadResponseInfo(
564 manifest_url_, group_id_, response_id_, this);
567 // Produces a page containing the response headers and data.
568 virtual int GetData(std::string* mime_type,
569 std::string* charset,
570 std::string* out,
571 const net::CompletionCallback& callback) const OVERRIDE {
572 mime_type->assign("text/html");
573 charset->assign("UTF-8");
574 out->clear();
575 EmitPageStart(out);
576 EmitAnchor(entry_url_.spec(), entry_url_.spec(), out);
577 out->append("<br/>\n");
578 if (response_info_.get()) {
579 if (response_info_->http_response_info())
580 EmitResponseHeaders(response_info_->http_response_info()->headers.get(),
581 out);
582 else
583 out->append("Failed to read response headers.<br>");
585 if (response_data_.get()) {
586 EmitHexDump(response_data_->data(),
587 amount_read_,
588 response_info_->response_data_size(),
589 out);
590 } else {
591 out->append("Failed to read response data.<br>");
593 } else {
594 out->append("Failed to read response headers and data.<br>");
596 EmitPageEnd(out);
597 return net::OK;
600 private:
601 virtual ~ViewEntryJob() {
602 appcache_storage_->CancelDelegateCallbacks(this);
605 virtual void OnResponseInfoLoaded(
606 AppCacheResponseInfo* response_info, int64 response_id) OVERRIDE {
607 if (!response_info) {
608 StartAsync();
609 return;
611 response_info_ = response_info;
613 // Read the response data, truncating if its too large.
614 const int64 kLimit = 100 * 1000;
615 int64 amount_to_read =
616 std::min(kLimit, response_info->response_data_size());
617 response_data_ = new net::IOBuffer(amount_to_read);
619 reader_.reset(appcache_storage_->CreateResponseReader(
620 manifest_url_, group_id_, response_id_));
621 reader_->ReadData(
622 response_data_.get(),
623 amount_to_read,
624 base::Bind(&ViewEntryJob::OnReadComplete, base::Unretained(this)));
627 void OnReadComplete(int result) {
628 reader_.reset();
629 amount_read_ = result;
630 if (result < 0)
631 response_data_ = NULL;
632 StartAsync();
635 GURL manifest_url_;
636 GURL entry_url_;
637 int64 response_id_;
638 int64 group_id_;
639 scoped_refptr<AppCacheResponseInfo> response_info_;
640 scoped_refptr<net::IOBuffer> response_data_;
641 int amount_read_;
642 scoped_ptr<AppCacheResponseReader> reader_;
645 } // namespace
647 net::URLRequestJob* ViewAppCacheInternalsJobFactory::CreateJobForRequest(
648 net::URLRequest* request,
649 net::NetworkDelegate* network_delegate,
650 AppCacheService* service) {
651 if (!request->url().has_query())
652 return new MainPageJob(request, network_delegate, service);
654 std::string command;
655 std::string param;
656 ParseQuery(request->url().query(), &command, &param);
658 if (command == kRemoveCacheCommand)
659 return new RemoveAppCacheJob(request, network_delegate, service,
660 DecodeBase64URL(param));
662 if (command == kViewCacheCommand)
663 return new ViewAppCacheJob(request, network_delegate, service,
664 DecodeBase64URL(param));
666 std::vector<std::string> tokens;
667 int64 response_id;
668 int64 group_id;
669 if (command == kViewEntryCommand && Tokenize(param, "|", &tokens) == 4u &&
670 base::StringToInt64(tokens[2], &response_id) &&
671 base::StringToInt64(tokens[3], &group_id)) {
672 return new ViewEntryJob(request, network_delegate, service,
673 DecodeBase64URL(tokens[0]), // manifest url
674 DecodeBase64URL(tokens[1]), // entry url
675 response_id, group_id);
678 return new RedirectToMainPageJob(request, network_delegate, service);
681 } // namespace content