// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include "webkit/appcache/view_appcache_internals_job.h" #include "base/base64.h" #include "base/logging.h" #include "base/format_macros.h" #include "base/stringprintf.h" #include "base/utf_string_conversions.h" #include "base/i18n/time_formatting.h" #include "base/string_util.h" #include "net/base/escape.h" #include "net/url_request/url_request.h" #include "webkit/appcache/appcache_policy.h" #include "webkit/appcache/appcache_service.h" namespace { const char kErrorMessage[] = "Error in retrieving Application Caches."; const char kEmptyAppCachesMessage[] = "No available Application Caches."; const char kRemoveAppCache[] = "Remove this AppCache"; const char kManifest[] = "Manifest: "; const char kSize[] = "Size: "; const char kCreationTime[] = "Creation Time: "; const char kLastAccessTime[] = "Last Access Time: "; const char kLastUpdateTime[] = "Last Update Time: "; const char kFormattedDisabledAppCacheMsg[] = "" "This Application Cache is disabled by policy.
"; void StartHTML(std::string* out) { DCHECK(out); out->append( "" "AppCache Internals" "" "\n" "" "
" "" "
"); } void EndHTML(std::string* out) { DCHECK(out); out->append(""); } // Appends an input button to |data| with text |title| that sends the command // string |command| back to the browser, and then refreshes the page. void DrawCommandButton(const std::string& title, const std::string& command, std::string* data) { base::StringAppendF(data, "", title.c_str(), command.c_str()); } void AddLiTag(const std::string& element_title, const std::string& element_data, std::string* out) { DCHECK(out); out->append("
  • "); out->append(element_title); out->append(element_data); out->append("
  • "); } void WrapInHREF(const std::string& in, std::string* out) { out->append(""); out->append(in); out->append("
    "); } void AddHTMLFromAppCacheToOutput( const appcache::AppCacheService& appcache_service, const appcache::AppCacheInfoVector& appcaches, std::string* out) { for (std::vector::const_iterator info = appcaches.begin(); info != appcaches.end(); ++info) { std::string manifest_url_base64; base::Base64Encode(info->manifest_url.spec(), &manifest_url_base64); out->append("

    "); std::string anchored_manifest; WrapInHREF(info->manifest_url.spec(), &anchored_manifest); out->append(kManifest); out->append(anchored_manifest); if (!appcache_service.appcache_policy()->CanLoadAppCache( info->manifest_url)) { out->append(kFormattedDisabledAppCacheMsg); } out->append("
    "); DrawCommandButton(kRemoveAppCache, manifest_url_base64, out); out->append("


    "); } } std::string GetAppCacheManifestToRemove(const std::string& query) { if (!StartsWithASCII(query, "remove=", true)) { // Not a recognized format. return std::string(); } std::string manifest_url_base64 = UnescapeURLComponent( query.substr(strlen("remove=")), UnescapeRule::NORMAL | UnescapeRule::URL_SPECIAL_CHARS); std::string manifest_url; base::Base64Decode(manifest_url_base64, &manifest_url); return manifest_url; } struct ManifestURLComparator { public: bool operator() ( const appcache::AppCacheInfo& lhs, const appcache::AppCacheInfo& rhs) const { return (lhs.manifest_url.spec() < rhs.manifest_url.spec()); } } manifest_url_comparator; } // namespace namespace appcache { ViewAppCacheInternalsJob::ViewAppCacheInternalsJob( net::URLRequest* request, AppCacheService* service) : net::URLRequestSimpleJob(request), appcache_service_(service) { } ViewAppCacheInternalsJob::~ViewAppCacheInternalsJob() { // Cancel callback if job is destroyed before callback is called. if (appcache_done_callback_) appcache_done_callback_.release()->Cancel(); } void ViewAppCacheInternalsJob::GetAppCacheInfoAsync() { info_collection_ = new AppCacheInfoCollection; appcache_done_callback_ = new net::CancelableCompletionCallback( this, &ViewAppCacheInternalsJob::AppCacheDone); appcache_service_->GetAllAppCacheInfo( info_collection_, appcache_done_callback_); } void ViewAppCacheInternalsJob::RemoveAppCacheInfoAsync( const std::string& manifest_url_spec) { appcache_done_callback_ = new net::CancelableCompletionCallback( this, &ViewAppCacheInternalsJob::AppCacheDone); GURL manifest(manifest_url_spec); appcache_service_->DeleteAppCacheGroup( manifest, appcache_done_callback_); } void ViewAppCacheInternalsJob::Start() { if (!request_) return; // Handle any remove appcache request, then redirect back to // the same URL stripped of query parameters. The redirect happens as part // of IsRedirectResponse(). if (request_->url().has_query()) { std::string remove_appcache_manifest( GetAppCacheManifestToRemove(request_->url().query())); // Empty manifests are dealt with by the deleter. RemoveAppCacheInfoAsync(remove_appcache_manifest); } else { GetAppCacheInfoAsync(); } } bool ViewAppCacheInternalsJob::IsRedirectResponse( GURL* location, int* http_status_code) { if (request_->url().has_query()) { // Strip the query parameters. GURL::Replacements replacements; replacements.ClearQuery(); *location = request_->url().ReplaceComponents(replacements); *http_status_code = 307; return true; } return false; } void ViewAppCacheInternalsJob::GenerateHTMLAppCacheInfo( std::string* out) const { typedef std::map InfoByOrigin; AppCacheInfoVector appcaches; for (InfoByOrigin::const_iterator origin = info_collection_->infos_by_origin.begin(); origin != info_collection_->infos_by_origin.end(); ++origin) { for (AppCacheInfoVector::const_iterator info = origin->second.begin(); info != origin->second.end(); ++info) appcaches.push_back(*info); } std::sort(appcaches.begin(), appcaches.end(), manifest_url_comparator); AddHTMLFromAppCacheToOutput(*appcache_service_, appcaches, out); } void ViewAppCacheInternalsJob::AppCacheDone(int rv) { appcache_done_callback_ = NULL; if (rv != net::OK) info_collection_ = NULL; StartAsync(); } bool ViewAppCacheInternalsJob::GetData(std::string* mime_type, std::string* charset, std::string* data) const { mime_type->assign("text/html"); charset->assign("UTF-8"); data->clear(); StartHTML(data); if (!info_collection_.get()) data->append(kErrorMessage); else if (info_collection_->infos_by_origin.empty()) data->append(kEmptyAppCachesMessage); else GenerateHTMLAppCacheInfo(data); EndHTML(data); return true; } } // namespace appcache