Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <algorithm> | 5 #include <algorithm> |
| 6 #include <string> | |
| 6 | 7 |
| 7 #include "webkit/appcache/view_appcache_internals_job.h" | 8 #include "webkit/appcache/view_appcache_internals_job.h" |
| 8 | 9 |
| 9 #include "base/base64.h" | 10 #include "base/base64.h" |
| 11 #include "base/format_macros.h" | |
| 12 #include "base/i18n/time_formatting.h" | |
| 10 #include "base/logging.h" | 13 #include "base/logging.h" |
| 11 #include "base/format_macros.h" | 14 #include "base/string_number_conversions.h" |
| 15 #include "base/string_util.h" | |
| 12 #include "base/stringprintf.h" | 16 #include "base/stringprintf.h" |
| 13 #include "base/utf_string_conversions.h" | 17 #include "base/utf_string_conversions.h" |
| 14 #include "base/i18n/time_formatting.h" | |
| 15 #include "base/string_util.h" | |
| 16 #include "net/base/escape.h" | 18 #include "net/base/escape.h" |
| 19 #include "net/base/io_buffer.h" | |
| 20 #include "net/http/http_response_headers.h" | |
| 17 #include "net/url_request/url_request.h" | 21 #include "net/url_request/url_request.h" |
| 22 #include "net/url_request/url_request_simple_job.h" | |
| 23 #include "net/url_request/view_cache_helper.h" | |
| 24 #include "webkit/appcache/appcache.h" | |
| 25 #include "webkit/appcache/appcache_group.h" | |
| 18 #include "webkit/appcache/appcache_policy.h" | 26 #include "webkit/appcache/appcache_policy.h" |
| 27 #include "webkit/appcache/appcache_response.h" | |
| 19 #include "webkit/appcache/appcache_service.h" | 28 #include "webkit/appcache/appcache_service.h" |
| 20 | 29 |
| 30 namespace appcache { | |
| 21 namespace { | 31 namespace { |
| 22 | 32 |
| 23 const char kErrorMessage[] = "Error in retrieving Application Caches."; | 33 const char kErrorMessage[] = "Error in retrieving Application Caches."; |
| 24 const char kEmptyAppCachesMessage[] = "No available Application Caches."; | 34 const char kEmptyAppCachesMessage[] = "No available Application Caches."; |
| 25 const char kRemoveAppCache[] = "Remove this AppCache"; | 35 const char kManifestNotFoundMessage[] = "Manifest not found."; |
| 26 const char kManifest[] = "Manifest: "; | 36 const char kManifest[] = "Manifest: "; |
| 27 const char kSize[] = "Size: "; | 37 const char kSize[] = "Size: "; |
| 28 const char kCreationTime[] = "Creation Time: "; | 38 const char kCreationTime[] = "Creation Time: "; |
| 29 const char kLastAccessTime[] = "Last Access Time: "; | 39 const char kLastAccessTime[] = "Last Access Time: "; |
| 30 const char kLastUpdateTime[] = "Last Update Time: "; | 40 const char kLastUpdateTime[] = "Last Update Time: "; |
| 31 const char kFormattedDisabledAppCacheMsg[] = | 41 const char kFormattedDisabledAppCacheMsg[] = |
| 32 "<b><i><font color=\"FF0000\">" | 42 "<b><i><font color=\"FF0000\">" |
| 33 "This Application Cache is disabled by policy.</font></i></b><br/>"; | 43 "This Application Cache is disabled by policy.</font></i></b><br/>"; |
| 34 | 44 const char kRemoveCacheLabel[] = "Remove"; |
| 35 void StartHTML(std::string* out) { | 45 const char kViewCacheLabel[] = "View Entries"; |
| 46 const char kRemoveCacheCommand[] = "remove-cache"; | |
| 47 const char kViewCacheCommand[] = "view-cache"; | |
| 48 const char kViewEntryCommand[] = "view-entry"; | |
| 49 | |
| 50 void EmitPageStart(std::string* out) { | |
| 36 DCHECK(out); | 51 DCHECK(out); |
| 37 out->append( | 52 out->append( |
| 38 "<!DOCTYPE HTML>" | 53 "<!DOCTYPE HTML>\n" |
| 39 "<html><title>AppCache Internals</title>" | 54 "<html><title>AppCache Internals</title>\n" |
| 40 "<style>" | 55 "<style>\n" |
| 41 "body { font-family: sans-serif; font-size: 0.8em; }\n" | 56 "body { font-family: sans-serif; font-size: 0.8em; }\n" |
| 42 "tt, code, pre { font-family: WebKitHack, monospace; }\n" | 57 "tt, code, pre { font-family: WebKitHack, monospace; }\n" |
| 43 ".subsection_body { margin: 10px 0 10px 2em; }\n" | 58 ".subsection_body { margin: 10px 0 10px 2em; }\n" |
| 44 ".subsection_title { font-weight: bold; }\n" | 59 ".subsection_title { font-weight: bold; }\n" |
| 45 "</style>" | 60 "</style>\n" |
| 46 "<script>\n" | 61 "<script>\n" |
| 47 // Unfortunately we can't do XHR from chrome://appcache-internals | 62 "function PerformCommand(command, param) {\n" |
| 48 // because the chrome:// protocol restricts access. | 63 " location = location.pathname + '?' + command + '=' + param;\n" |
| 49 // | |
| 50 // So instead, we will send commands by doing a form | |
| 51 // submission (which as a side effect will reload the page). | |
| 52 "function RemoveCommand(command) {\n" | |
| 53 " document.getElementById('cmd').value = command;\n" | |
| 54 " document.getElementById('cmdsender').submit();\n" | |
| 55 "}\n" | 64 "}\n" |
| 56 "</script>\n" | 65 "</script>\n" |
| 57 "</head><body>" | 66 "</head><body>\n"); |
| 58 "<form action='' method=GET id=cmdsender>" | 67 } |
| 59 "<input type='hidden' id=cmd name='remove'>" | 68 |
| 60 "</form>"); | 69 void EmitPageEnd(std::string* out) { |
| 61 } | |
| 62 | |
| 63 void EndHTML(std::string* out) { | |
| 64 DCHECK(out); | 70 DCHECK(out); |
| 65 out->append("</body></html>"); | 71 out->append("</body></html>\n"); |
| 66 } | 72 } |
| 67 | 73 |
| 68 // Appends an input button to |data| with text |title| that sends the command | 74 // Appends an input button to |out| with text |label| that sends |
| 69 // string |command| back to the browser, and then refreshes the page. | 75 // |command| and |param| back to the browser and navigates the frame |
| 70 void DrawCommandButton(const std::string& title, | 76 // to the resulting page. |
| 77 void EmitCommandButton(const std::string& label, | |
| 71 const std::string& command, | 78 const std::string& command, |
| 72 std::string* data) { | 79 const std::string& param, |
| 73 base::StringAppendF(data, "<input type=\"button\" value=\"%s\" " | 80 std::string* out) { |
| 74 "onclick=\"RemoveCommand('%s')\" />", | 81 base::StringAppendF(out, "<input type=\"button\" value=\"%s\" " |
| 75 title.c_str(), | 82 "onclick=\"PerformCommand('%s', '%s')\" />\n", |
| 76 command.c_str()); | 83 label.c_str(), command.c_str(), param.c_str()); |
| 77 } | 84 } |
| 78 | 85 |
| 79 void AddLiTag(const std::string& element_title, | 86 void EmitListItem(const std::string& label, const std::string& data, |
| 80 const std::string& element_data, std::string* out) { | 87 std::string* out) { |
| 81 DCHECK(out); | 88 DCHECK(out); |
| 82 out->append("<li>"); | 89 out->append("<li>"); |
| 83 out->append(element_title); | 90 out->append(label); |
| 84 out->append(element_data); | 91 out->append(data); |
| 85 out->append("</li>"); | 92 out->append("</li>\n"); |
| 86 } | 93 } |
| 87 | 94 |
| 88 void WrapInHREF(const std::string& in, std::string* out) { | 95 void EmitAnchor(const std::string& url, const std::string& text, |
| 96 std::string* out) { | |
| 89 out->append("<a href="); | 97 out->append("<a href="); |
| 90 out->append(in); | 98 out->append(url); |
| 91 out->append(">"); | 99 out->append(">"); |
| 92 out->append(in); | 100 out->append(text); |
| 93 out->append("</a><br/>"); | 101 out->append("</a><br/>"); |
| 94 } | 102 } |
| 95 | 103 |
| 96 void AddHTMLFromAppCacheToOutput( | 104 void EmitAppCacheInfo(AppCacheService* service, |
| 97 const appcache::AppCacheService& appcache_service, | 105 const AppCacheInfo* info, |
| 98 const appcache::AppCacheInfoVector& appcaches, std::string* out) { | 106 std::string* out) { |
| 99 for (std::vector<appcache::AppCacheInfo>::const_iterator info = | 107 std::string manifest_url_base64; |
| 108 base::Base64Encode(info->manifest_url.spec(), &manifest_url_base64); | |
| 109 | |
| 110 out->append("\n<p>"); | |
| 111 out->append(kManifest); | |
| 112 EmitAnchor(info->manifest_url.spec(), info->manifest_url.spec(), out); | |
| 113 if (!service->appcache_policy()->CanLoadAppCache( | |
| 114 info->manifest_url)) { | |
| 115 out->append(kFormattedDisabledAppCacheMsg); | |
| 116 } | |
| 117 out->append("\n<br/>\n"); | |
| 118 EmitCommandButton(kRemoveCacheLabel, kRemoveCacheCommand, | |
| 119 manifest_url_base64, out); | |
| 120 EmitCommandButton(kViewCacheLabel, kViewCacheCommand, | |
| 121 manifest_url_base64, out); | |
| 122 out->append("<ul>"); | |
| 123 EmitListItem( | |
| 124 kSize, | |
| 125 UTF16ToUTF8(FormatBytesUnlocalized(info->size)), | |
| 126 out); | |
| 127 EmitListItem( | |
| 128 kCreationTime, | |
| 129 UTF16ToUTF8(TimeFormatFriendlyDateAndTime(info->creation_time)), | |
| 130 out); | |
| 131 EmitListItem( | |
| 132 kLastUpdateTime, | |
| 133 UTF16ToUTF8(TimeFormatFriendlyDateAndTime(info->last_update_time)), | |
| 134 out); | |
| 135 EmitListItem( | |
| 136 kLastAccessTime, | |
| 137 UTF16ToUTF8(TimeFormatFriendlyDateAndTime(info->last_access_time)), | |
| 138 out); | |
| 139 out->append("</ul></p></br>\n"); | |
| 140 } | |
| 141 | |
| 142 void EmitAppCacheInfoVector( | |
| 143 AppCacheService* service, | |
| 144 const AppCacheInfoVector& appcaches, | |
| 145 std::string* out) { | |
| 146 for (std::vector<AppCacheInfo>::const_iterator info = | |
| 100 appcaches.begin(); | 147 appcaches.begin(); |
| 101 info != appcaches.end(); ++info) { | 148 info != appcaches.end(); ++info) { |
| 102 std::string manifest_url_base64; | 149 EmitAppCacheInfo(service, &(*info), out); |
| 103 base::Base64Encode(info->manifest_url.spec(), &manifest_url_base64); | 150 } |
| 104 | 151 } |
| 105 out->append("<p>"); | 152 |
| 106 std::string anchored_manifest; | 153 void EmitTableData(const std::string& data, bool align_right, bool bold, |
| 107 WrapInHREF(info->manifest_url.spec(), &anchored_manifest); | 154 std::string* out) { |
| 108 out->append(kManifest); | 155 if (align_right) |
| 109 out->append(anchored_manifest); | 156 out->append("<td align='right'>"); |
| 110 if (!appcache_service.appcache_policy()->CanLoadAppCache( | 157 else |
| 111 info->manifest_url)) { | 158 out->append("<td>"); |
| 112 out->append(kFormattedDisabledAppCacheMsg); | 159 if (bold) |
| 160 out->append("<b>"); | |
| 161 out->append(data); | |
| 162 if (bold) | |
| 163 out->append("</b>"); | |
| 164 out->append("</td>"); | |
| 165 } | |
| 166 | |
| 167 std::string FormFlagsString(const AppCacheResourceInfo& info) { | |
| 168 std::string str; | |
| 169 if (info.is_manifest) | |
| 170 str.append("Manifest, "); | |
| 171 if (info.is_master) | |
| 172 str.append("Master, "); | |
| 173 if (info.is_fallback) | |
| 174 str.append("Fallback. "); | |
|
rvargas (doing something else)
2011/07/11 22:34:07
Is it intentional that this one ends with a period
michaeln
2011/07/12 23:35:26
Done, changed to a comma like the others. The URL
| |
| 175 if (info.is_explicit) | |
| 176 str.append("Explicit, "); | |
| 177 if (info.is_foreign) | |
| 178 str.append("Foreign, "); | |
| 179 return str; | |
| 180 } | |
| 181 | |
| 182 std::string FormViewEntryAnchor(const GURL& base_url, | |
| 183 const GURL& manifest_url, const GURL& entry_url, | |
| 184 int64 response_id) { | |
| 185 std::string manifest_url_base64; | |
| 186 std::string entry_url_base64; | |
| 187 std::string response_id_string; | |
| 188 base::Base64Encode(manifest_url.spec(), &manifest_url_base64); | |
| 189 base::Base64Encode(entry_url.spec(), &entry_url_base64); | |
| 190 response_id_string = base::Int64ToString(response_id); | |
| 191 | |
| 192 std::string query(kViewEntryCommand); | |
| 193 query.push_back('='); | |
| 194 query.append(manifest_url_base64); | |
| 195 query.push_back('|'); | |
| 196 query.append(entry_url_base64); | |
| 197 query.push_back('|'); | |
| 198 query.append(response_id_string); | |
| 199 | |
| 200 GURL::Replacements replacements; | |
| 201 replacements.SetQuery(query.data(), | |
| 202 url_parse::Component(0, query.length())); | |
| 203 GURL view_entry_url = base_url.ReplaceComponents(replacements); | |
| 204 | |
| 205 std::string anchor; | |
| 206 EmitAnchor(view_entry_url.spec(), entry_url.spec(), &anchor); | |
| 207 return anchor; | |
| 208 } | |
| 209 | |
| 210 void EmitAppCacheResourceInfoVector( | |
| 211 const GURL& base_url, | |
| 212 const GURL& manifest_url, | |
| 213 const AppCacheResourceInfoVector& resource_infos, | |
| 214 std::string* out) { | |
| 215 out->append("<table border='0'>\n"); | |
| 216 out->append("<tr>"); | |
| 217 EmitTableData("Flags", false, true, out); | |
| 218 EmitTableData("URL", false, true, out); | |
| 219 EmitTableData("Size (headers and data)", true, true, out); | |
| 220 out->append("</tr>\n"); | |
| 221 for (AppCacheResourceInfoVector::const_iterator | |
| 222 iter = resource_infos.begin(); | |
| 223 iter != resource_infos.end(); ++iter) { | |
| 224 out->append("<tr>"); | |
| 225 EmitTableData(FormFlagsString(*iter), false, false, out); | |
| 226 EmitTableData(FormViewEntryAnchor(base_url, manifest_url, | |
| 227 iter->url, iter->response_id), | |
| 228 false, false, out); | |
| 229 EmitTableData(UTF16ToUTF8(FormatBytesUnlocalized(iter->size)), | |
| 230 true, false, out); | |
| 231 out->append("</tr>\n"); | |
| 232 } | |
| 233 out->append("</table>\n"); | |
| 234 } | |
| 235 | |
| 236 void EmitResponseHeaders(net::HttpResponseHeaders* headers, std::string* out) { | |
| 237 out->append("<hr><pre>"); | |
| 238 out->append(EscapeForHTML(headers->GetStatusLine())); | |
| 239 out->push_back('\n'); | |
| 240 | |
| 241 void* iter = NULL; | |
| 242 std::string name, value; | |
| 243 while (headers->EnumerateHeaderLines(&iter, &name, &value)) { | |
| 244 out->append(EscapeForHTML(name)); | |
| 245 out->append(": "); | |
| 246 out->append(EscapeForHTML(value)); | |
| 247 out->push_back('\n'); | |
| 248 } | |
| 249 out->append("</pre>"); | |
| 250 } | |
| 251 | |
| 252 void EmitHexDump(const char *buf, size_t buf_len, size_t total_len, | |
| 253 std::string* out) { | |
| 254 out->append("<hr><pre>"); | |
| 255 base::StringAppendF(out, "Showing %d of %d bytes\n\n", | |
| 256 static_cast<int>(buf_len), static_cast<int>(total_len)); | |
| 257 net::ViewCacheHelper::HexDump(buf, buf_len, out); | |
| 258 if (buf_len < total_len) | |
| 259 out->append("\nNote: data is truncated..."); | |
| 260 out->append("</pre>"); | |
| 261 } | |
| 262 | |
| 263 GURL DecodeBase64URL(const std::string base64) { | |
| 264 std::string url; | |
| 265 base::Base64Decode(base64, &url); | |
| 266 return GURL(url); | |
| 267 } | |
| 268 | |
| 269 bool ParseQuery(const std::string& query, | |
| 270 std::string* command, std::string* value) { | |
| 271 size_t position = query.find("="); | |
| 272 if (position == std::string::npos) | |
| 273 return false; | |
| 274 *command = query.substr(0, position); | |
| 275 *value = query.substr(position + 1); | |
| 276 return !command->empty() && !value->empty(); | |
| 277 } | |
| 278 | |
| 279 bool SortByManifestUrl(const AppCacheInfo& lhs, | |
| 280 const AppCacheInfo& rhs) { | |
| 281 return lhs.manifest_url.spec() < rhs.manifest_url.spec(); | |
| 282 } | |
| 283 | |
| 284 bool SortByResourceUrl(const AppCacheResourceInfo& lhs, | |
| 285 const AppCacheResourceInfo& rhs) { | |
| 286 return lhs.url.spec() < rhs.url.spec(); | |
| 287 } | |
| 288 | |
| 289 GURL ClearQuery(const GURL& url) { | |
| 290 GURL::Replacements replacements; | |
| 291 replacements.ClearQuery(); | |
| 292 return url.ReplaceComponents(replacements); | |
| 293 } | |
| 294 | |
| 295 // Simple base class for the job subclasses defined here. | |
| 296 class BaseInternalsJob : public net::URLRequestSimpleJob { | |
| 297 protected: | |
| 298 BaseInternalsJob(net::URLRequest* request, AppCacheService* service) | |
| 299 : URLRequestSimpleJob(request), appcache_service_(service) {} | |
| 300 | |
| 301 AppCacheService* appcache_service_; | |
| 302 }; | |
| 303 | |
| 304 // Job that lists all appcaches in the system. | |
| 305 class MainPageJob : public BaseInternalsJob { | |
| 306 public: | |
| 307 MainPageJob(net::URLRequest* request, AppCacheService* service) | |
| 308 : BaseInternalsJob(request, service) {} | |
| 309 | |
| 310 virtual void Start() { | |
| 311 DCHECK(request_); | |
| 312 info_collection_ = new AppCacheInfoCollection; | |
| 313 gotinfo_complete_callback_ = | |
| 314 new net::CancelableCompletionCallback<MainPageJob>( | |
| 315 this, &MainPageJob::OnGotInfoComplete); | |
| 316 appcache_service_->GetAllAppCacheInfo( | |
| 317 info_collection_, gotinfo_complete_callback_); | |
| 318 } | |
| 319 | |
| 320 // Produces a page containing the listing | |
| 321 virtual bool GetData(std::string* mime_type, | |
| 322 std::string* charset, | |
| 323 std::string* out) const { | |
| 324 mime_type->assign("text/html"); | |
| 325 charset->assign("UTF-8"); | |
| 326 | |
| 327 out->clear(); | |
| 328 EmitPageStart(out); | |
| 329 if (!info_collection_.get()) { | |
| 330 out->append(kErrorMessage); | |
| 331 } else if (info_collection_->infos_by_origin.empty()) { | |
| 332 out->append(kEmptyAppCachesMessage); | |
| 333 } else { | |
| 334 typedef std::map<GURL, AppCacheInfoVector> InfoByOrigin; | |
| 335 AppCacheInfoVector appcaches; | |
| 336 for (InfoByOrigin::const_iterator origin = | |
| 337 info_collection_->infos_by_origin.begin(); | |
| 338 origin != info_collection_->infos_by_origin.end(); ++origin) { | |
| 339 appcaches.insert(appcaches.end(), | |
| 340 origin->second.begin(), origin->second.end()); | |
| 341 } | |
| 342 std::sort(appcaches.begin(), appcaches.end(), SortByManifestUrl); | |
| 343 EmitAppCacheInfoVector(appcache_service_, appcaches, out); | |
| 113 } | 344 } |
| 114 out->append("<br/>"); | 345 EmitPageEnd(out); |
| 115 DrawCommandButton(kRemoveAppCache, manifest_url_base64, out); | 346 return true; |
| 116 out->append("<ul>"); | 347 } |
| 117 | 348 |
| 118 AddLiTag(kSize, | 349 private: |
| 119 UTF16ToUTF8(FormatBytesUnlocalized(info->size)), | 350 virtual ~MainPageJob() { |
| 120 out); | 351 if (gotinfo_complete_callback_) |
| 121 AddLiTag(kCreationTime, | 352 gotinfo_complete_callback_.release()->Cancel(); |
| 122 UTF16ToUTF8(TimeFormatFriendlyDateAndTime(info->creation_time)), | 353 } |
| 123 out); | 354 |
| 124 AddLiTag(kLastAccessTime, | 355 void OnGotInfoComplete(int rv) { |
| 125 UTF16ToUTF8(TimeFormatFriendlyDateAndTime(info->last_access_time)), | 356 gotinfo_complete_callback_ = NULL; |
| 126 out); | 357 if (rv != net::OK) |
| 127 AddLiTag(kLastUpdateTime, | 358 info_collection_ = NULL; |
| 128 UTF16ToUTF8(TimeFormatFriendlyDateAndTime(info->last_update_time)), | 359 StartAsync(); |
| 129 out); | 360 } |
| 130 | 361 |
| 131 out->append("</ul></p></br>"); | 362 scoped_refptr<net::CancelableCompletionCallback<MainPageJob> > |
| 132 } | 363 gotinfo_complete_callback_; |
| 133 } | 364 scoped_refptr<AppCacheInfoCollection> info_collection_; |
| 134 | 365 DISALLOW_COPY_AND_ASSIGN(MainPageJob); |
| 135 std::string GetAppCacheManifestToRemove(const std::string& query) { | 366 }; |
| 136 if (!StartsWithASCII(query, "remove=", true)) { | 367 |
| 137 // Not a recognized format. | 368 // Job that redirects back to the main appcache internals page. |
| 138 return std::string(); | 369 class RedirectToMainPageJob : public BaseInternalsJob { |
| 139 } | |
| 140 std::string manifest_url_base64 = UnescapeURLComponent( | |
| 141 query.substr(strlen("remove=")), | |
| 142 UnescapeRule::NORMAL | UnescapeRule::URL_SPECIAL_CHARS); | |
| 143 std::string manifest_url; | |
| 144 base::Base64Decode(manifest_url_base64, &manifest_url); | |
| 145 return manifest_url; | |
| 146 } | |
| 147 | |
| 148 struct ManifestURLComparator { | |
| 149 public: | 370 public: |
| 150 bool operator() ( | 371 RedirectToMainPageJob(net::URLRequest* request, AppCacheService* service) |
| 151 const appcache::AppCacheInfo& lhs, | 372 : BaseInternalsJob(request, service) {} |
| 152 const appcache::AppCacheInfo& rhs) const { | 373 |
| 153 return (lhs.manifest_url.spec() < rhs.manifest_url.spec()); | 374 virtual bool GetData(std::string* mime_type, |
| 154 } | 375 std::string* charset, |
| 155 } manifest_url_comparator; | 376 std::string* data) const { |
| 156 | 377 return true; // IsRedirectResponse induces a redirect. |
| 157 } // namespace | 378 } |
| 158 | 379 |
| 159 namespace appcache { | 380 virtual bool IsRedirectResponse(GURL* location, int* http_status_code) { |
| 160 | 381 *location = ClearQuery(request_->url()); |
| 161 ViewAppCacheInternalsJob::ViewAppCacheInternalsJob( | |
| 162 net::URLRequest* request, | |
| 163 AppCacheService* service) | |
| 164 : net::URLRequestSimpleJob(request), | |
| 165 appcache_service_(service) { | |
| 166 } | |
| 167 | |
| 168 ViewAppCacheInternalsJob::~ViewAppCacheInternalsJob() { | |
| 169 // Cancel callback if job is destroyed before callback is called. | |
| 170 if (appcache_done_callback_) | |
| 171 appcache_done_callback_.release()->Cancel(); | |
| 172 } | |
| 173 | |
| 174 void ViewAppCacheInternalsJob::GetAppCacheInfoAsync() { | |
| 175 info_collection_ = new AppCacheInfoCollection; | |
| 176 appcache_done_callback_ = | |
| 177 new net::CancelableCompletionCallback<ViewAppCacheInternalsJob>( | |
| 178 this, &ViewAppCacheInternalsJob::AppCacheDone); | |
| 179 appcache_service_->GetAllAppCacheInfo( | |
| 180 info_collection_, appcache_done_callback_); | |
| 181 } | |
| 182 | |
| 183 void ViewAppCacheInternalsJob::RemoveAppCacheInfoAsync( | |
| 184 const std::string& manifest_url_spec) { | |
| 185 appcache_done_callback_ = | |
| 186 new net::CancelableCompletionCallback<ViewAppCacheInternalsJob>( | |
| 187 this, &ViewAppCacheInternalsJob::AppCacheDone); | |
| 188 | |
| 189 GURL manifest(manifest_url_spec); | |
| 190 appcache_service_->DeleteAppCacheGroup( | |
| 191 manifest, appcache_done_callback_); | |
| 192 } | |
| 193 | |
| 194 void ViewAppCacheInternalsJob::Start() { | |
| 195 if (!request_) | |
| 196 return; | |
| 197 | |
| 198 // Handle any remove appcache request, then redirect back to | |
| 199 // the same URL stripped of query parameters. The redirect happens as part | |
| 200 // of IsRedirectResponse(). | |
| 201 if (request_->url().has_query()) { | |
| 202 std::string remove_appcache_manifest( | |
| 203 GetAppCacheManifestToRemove(request_->url().query())); | |
| 204 | |
| 205 // Empty manifests are dealt with by the deleter. | |
| 206 RemoveAppCacheInfoAsync(remove_appcache_manifest); | |
| 207 } else { | |
| 208 GetAppCacheInfoAsync(); | |
| 209 } | |
| 210 } | |
| 211 | |
| 212 bool ViewAppCacheInternalsJob::IsRedirectResponse( | |
| 213 GURL* location, int* http_status_code) { | |
| 214 if (request_->url().has_query()) { | |
| 215 // Strip the query parameters. | |
| 216 GURL::Replacements replacements; | |
| 217 replacements.ClearQuery(); | |
| 218 *location = request_->url().ReplaceComponents(replacements); | |
| 219 *http_status_code = 307; | 382 *http_status_code = 307; |
| 220 return true; | 383 return true; |
| 221 } | 384 } |
| 222 return false; | 385 }; |
| 223 } | 386 |
| 224 | 387 // Job that removes an appcache and then redirects back to the main page. |
| 225 void ViewAppCacheInternalsJob::GenerateHTMLAppCacheInfo( | 388 class RemoveAppCacheJob : public RedirectToMainPageJob { |
| 226 std::string* out) const { | 389 public: |
| 227 typedef std::map<GURL, AppCacheInfoVector> InfoByOrigin; | 390 RemoveAppCacheJob( |
| 228 AppCacheInfoVector appcaches; | 391 net::URLRequest* request, AppCacheService* service, |
|
rvargas (doing something else)
2011/07/11 22:34:07
nit: put the arguments on the previous line.
| |
| 229 for (InfoByOrigin::const_iterator origin = | 392 const GURL& manifest_url) |
| 230 info_collection_->infos_by_origin.begin(); | 393 : RedirectToMainPageJob(request, service), |
| 231 origin != info_collection_->infos_by_origin.end(); ++origin) { | 394 manifest_url_(manifest_url) {} |
| 232 for (AppCacheInfoVector::const_iterator info = | 395 |
| 233 origin->second.begin(); info != origin->second.end(); ++info) | 396 virtual void Start() { |
| 234 appcaches.push_back(*info); | 397 DCHECK(request_); |
| 235 } | 398 delete_appcache_callback_ = |
| 236 | 399 new net::CancelableCompletionCallback<RemoveAppCacheJob>( |
| 237 std::sort(appcaches.begin(), appcaches.end(), manifest_url_comparator); | 400 this, &RemoveAppCacheJob::OnDeleteAppCacheComplete); |
| 238 | 401 appcache_service_->DeleteAppCacheGroup( |
| 239 AddHTMLFromAppCacheToOutput(*appcache_service_, appcaches, out); | 402 manifest_url_, delete_appcache_callback_); |
| 240 } | 403 } |
| 241 | 404 |
| 242 void ViewAppCacheInternalsJob::AppCacheDone(int rv) { | 405 private: |
| 243 appcache_done_callback_ = NULL; | 406 virtual ~RemoveAppCacheJob() { |
| 244 if (rv != net::OK) | 407 if (delete_appcache_callback_) |
| 245 info_collection_ = NULL; | 408 delete_appcache_callback_.release()->Cancel(); |
| 246 StartAsync(); | 409 } |
| 247 } | 410 |
| 248 | 411 void OnDeleteAppCacheComplete(int rv) { |
| 249 bool ViewAppCacheInternalsJob::GetData(std::string* mime_type, | 412 delete_appcache_callback_ = NULL; |
| 250 std::string* charset, | 413 StartAsync(); // Causes the base class to redirect. |
| 251 std::string* data) const { | 414 } |
| 252 mime_type->assign("text/html"); | 415 |
| 253 charset->assign("UTF-8"); | 416 GURL manifest_url_; |
| 254 | 417 scoped_refptr<net::CancelableCompletionCallback<RemoveAppCacheJob> > |
| 255 data->clear(); | 418 delete_appcache_callback_; |
| 256 StartHTML(data); | 419 }; |
| 257 if (!info_collection_.get()) | 420 |
| 258 data->append(kErrorMessage); | 421 |
| 259 else if (info_collection_->infos_by_origin.empty()) | 422 // Job shows the details of a particular manifest url. |
| 260 data->append(kEmptyAppCachesMessage); | 423 class ViewAppCacheJob : public BaseInternalsJob, |
| 261 else | 424 public AppCacheStorage::Delegate { |
| 262 GenerateHTMLAppCacheInfo(data); | 425 public: |
| 263 EndHTML(data); | 426 ViewAppCacheJob( |
| 264 return true; | 427 net::URLRequest* request, AppCacheService* service, |
|
rvargas (doing something else)
2011/07/11 22:34:07
ditto
| |
| 428 const GURL manifest_url) | |
| 429 : BaseInternalsJob(request, service), | |
| 430 manifest_url_(manifest_url) {} | |
| 431 | |
| 432 virtual void Start() { | |
| 433 DCHECK(request_); | |
| 434 appcache_service_->storage()->LoadOrCreateGroup(manifest_url_, this); | |
| 435 } | |
| 436 | |
| 437 // Produces a page containing the entries listing. | |
| 438 virtual bool GetData(std::string* mime_type, | |
| 439 std::string* charset, | |
| 440 std::string* out) const { | |
| 441 mime_type->assign("text/html"); | |
| 442 charset->assign("UTF-8"); | |
| 443 out->clear(); | |
| 444 EmitPageStart(out); | |
| 445 if (appcache_info_.manifest_url.is_empty()) { | |
| 446 out->append(kManifestNotFoundMessage); | |
| 447 } else { | |
| 448 EmitAppCacheInfo(appcache_service_, &appcache_info_, out); | |
| 449 EmitAppCacheResourceInfoVector(ClearQuery(request_->url()), | |
| 450 manifest_url_, | |
| 451 resource_infos_, out); | |
| 452 } | |
| 453 EmitPageEnd(out); | |
| 454 return true; | |
| 455 } | |
| 456 | |
| 457 private: | |
| 458 virtual ~ViewAppCacheJob() { | |
| 459 appcache_service_->storage()->CancelDelegateCallbacks(this); | |
| 460 } | |
| 461 | |
| 462 // AppCacheStorage::Delegate override | |
| 463 virtual void OnGroupLoaded(AppCacheGroup* group, const GURL& manifest_url) { | |
|
rvargas (doing something else)
2011/07/11 22:34:07
nit: Add OVERRIDE
michaeln
2011/07/12 23:35:26
Done.
| |
| 464 DCHECK_EQ(manifest_url_, manifest_url); | |
| 465 if (group && group->newest_complete_cache()) { | |
| 466 appcache_info_.manifest_url = manifest_url; | |
| 467 appcache_info_.size = group->newest_complete_cache()->cache_size(); | |
| 468 appcache_info_.creation_time = group->creation_time(); | |
| 469 appcache_info_.last_update_time = | |
| 470 group->newest_complete_cache()->update_time(); | |
| 471 appcache_info_.last_access_time = base::Time::Now(); | |
| 472 group->newest_complete_cache()->ToResourceInfoVector(&resource_infos_); | |
| 473 std::sort(resource_infos_.begin(), resource_infos_.end(), | |
| 474 SortByResourceUrl); | |
| 475 } | |
| 476 StartAsync(); | |
| 477 } | |
| 478 | |
| 479 GURL manifest_url_; | |
| 480 AppCacheInfo appcache_info_; | |
| 481 AppCacheResourceInfoVector resource_infos_; | |
| 482 DISALLOW_COPY_AND_ASSIGN(ViewAppCacheJob); | |
| 483 }; | |
| 484 | |
| 485 // Job that shows the details of a particular cached resource. | |
| 486 class ViewEntryJob : public BaseInternalsJob, | |
| 487 public AppCacheStorage::Delegate { | |
| 488 public: | |
| 489 ViewEntryJob( | |
| 490 net::URLRequest* request, AppCacheService* service, | |
|
rvargas (doing something else)
2011/07/11 22:34:07
ditto
| |
| 491 const GURL& manifest_url, const GURL& entry_url, | |
| 492 int64 response_id) | |
| 493 : BaseInternalsJob(request, service), | |
| 494 manifest_url_(manifest_url), entry_url_(entry_url), | |
| 495 response_id_(response_id), amount_read_(0), | |
| 496 ALLOW_THIS_IN_INITIALIZER_LIST(read_callback_( | |
| 497 this, &ViewEntryJob::OnReadComplete)) {} | |
| 498 | |
| 499 virtual void Start() { | |
| 500 DCHECK(request_); | |
| 501 appcache_service_->storage()->LoadResponseInfo( | |
| 502 manifest_url_, response_id_, this); | |
| 503 } | |
| 504 | |
| 505 // Produces a page containing the response headers and data. | |
| 506 virtual bool GetData(std::string* mime_type, | |
| 507 std::string* charset, | |
| 508 std::string* out) const { | |
| 509 mime_type->assign("text/html"); | |
| 510 charset->assign("UTF-8"); | |
| 511 out->clear(); | |
| 512 EmitPageStart(out); | |
| 513 EmitAnchor(entry_url_.spec(), entry_url_.spec(), out); | |
| 514 if (response_info_) { | |
| 515 if (response_info_->http_response_info()) | |
| 516 EmitResponseHeaders(response_info_->http_response_info()->headers, out); | |
| 517 else | |
| 518 out->append("Failed to read response headers.<br>"); | |
| 519 | |
| 520 if (response_data_) { | |
| 521 EmitHexDump(response_data_->data(), amount_read_, | |
| 522 response_info_->response_data_size(), out); | |
| 523 } else { | |
| 524 out->append("Failed to read response data.<br>"); | |
| 525 } | |
| 526 } else { | |
| 527 out->append("Failed to read response headers and data.<br>"); | |
| 528 } | |
| 529 EmitPageEnd(out); | |
| 530 return true; | |
| 531 } | |
| 532 | |
| 533 private: | |
| 534 virtual ~ViewEntryJob() { | |
| 535 appcache_service_->storage()->CancelDelegateCallbacks(this); | |
| 536 } | |
| 537 | |
| 538 virtual void OnResponseInfoLoaded( | |
| 539 AppCacheResponseInfo* response_info, int64 response_id) { | |
| 540 if (!response_info) { | |
| 541 StartAsync(); | |
| 542 return; | |
| 543 } | |
| 544 response_info_ = response_info; | |
| 545 | |
| 546 // Read the response data, truncating if its too large. | |
| 547 const int64 kLimit = 100 * 1000; | |
| 548 int64 amount_to_read = | |
| 549 std::min(kLimit, response_info->response_data_size()); | |
| 550 response_data_ = new net::IOBuffer(amount_to_read); | |
| 551 | |
| 552 reader_.reset(appcache_service_->storage()->CreateResponseReader( | |
| 553 manifest_url_, response_id_)); | |
| 554 reader_->ReadData( | |
| 555 response_data_, amount_to_read, &read_callback_); | |
| 556 } | |
| 557 | |
| 558 void OnReadComplete(int result) { | |
| 559 reader_.reset(); | |
| 560 amount_read_ = result; | |
| 561 if (result < 0) | |
| 562 response_data_ = NULL; | |
| 563 StartAsync(); | |
| 564 } | |
| 565 | |
| 566 GURL manifest_url_; | |
| 567 GURL entry_url_; | |
| 568 int64 response_id_; | |
| 569 scoped_refptr<AppCacheResponseInfo> response_info_; | |
| 570 scoped_refptr<net::IOBuffer> response_data_; | |
| 571 int amount_read_; | |
| 572 scoped_ptr<AppCacheResponseReader> reader_; | |
| 573 net::CompletionCallbackImpl<ViewEntryJob> read_callback_; | |
| 574 }; | |
| 575 | |
| 576 } // anon namespace | |
|
rvargas (doing something else)
2011/07/11 22:34:07
s/anon namespace/namespace
michaeln
2011/07/12 23:35:26
Done.
| |
| 577 | |
| 578 net::URLRequestJob* ViewAppCacheInternalsJobFactory::CreateJobForRequest( | |
| 579 net::URLRequest* request, AppCacheService* service) { | |
| 580 if (!request->url().has_query()) | |
| 581 return new MainPageJob(request, service); | |
| 582 | |
| 583 std::string command; | |
| 584 std::string param; | |
| 585 ParseQuery(request->url().query(), &command, ¶m); | |
| 586 | |
| 587 if (command == kRemoveCacheCommand) | |
| 588 return new RemoveAppCacheJob(request, service, | |
| 589 DecodeBase64URL(param)); | |
| 590 | |
| 591 if (command == kViewCacheCommand) | |
| 592 return new ViewAppCacheJob(request, service, | |
| 593 DecodeBase64URL(param)); | |
| 594 | |
| 595 std::vector<std::string> tokens; | |
| 596 int64 response_id; | |
| 597 if (command == kViewEntryCommand && | |
| 598 Tokenize(param, "|", &tokens) == 3u && | |
| 599 base::StringToInt64(tokens[2], &response_id)) { | |
| 600 return new ViewEntryJob(request, service, | |
| 601 DecodeBase64URL(tokens[0]), // manifest url | |
| 602 DecodeBase64URL(tokens[1]), // entry url | |
| 603 response_id); | |
| 604 } | |
| 605 | |
| 606 return new RedirectToMainPageJob(request, service); | |
| 265 } | 607 } |
| 266 | 608 |
| 267 } // namespace appcache | 609 } // namespace appcache |
| OLD | NEW |