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