Home | History | Annotate | Download | only in blob
      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/blob/view_blob_internals_job.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/compiler_specific.h"
      9 #include "base/format_macros.h"
     10 #include "base/i18n/number_formatting.h"
     11 #include "base/i18n/time_formatting.h"
     12 #include "base/logging.h"
     13 #include "base/message_loop/message_loop.h"
     14 #include "base/strings/string_util.h"
     15 #include "base/strings/stringprintf.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "net/base/escape.h"
     18 #include "net/base/net_errors.h"
     19 #include "net/url_request/url_request.h"
     20 #include "webkit/browser/blob/blob_storage_controller.h"
     21 #include "webkit/common/blob/blob_data.h"
     22 
     23 namespace {
     24 
     25 const char kEmptyBlobStorageMessage[] = "No available blob data.";
     26 const char kRemove[] = "Remove";
     27 const char kContentType[] = "Content Type: ";
     28 const char kContentDisposition[] = "Content Disposition: ";
     29 const char kCount[] = "Count: ";
     30 const char kIndex[] = "Index: ";
     31 const char kType[] = "Type: ";
     32 const char kPath[] = "Path: ";
     33 const char kURL[] = "URL: ";
     34 const char kModificationTime[] = "Modification Time: ";
     35 const char kOffset[] = "Offset: ";
     36 const char kLength[] = "Length: ";
     37 
     38 void StartHTML(std::string* out) {
     39   out->append(
     40       "<!DOCTYPE HTML>"
     41       "<html><title>Blob Storage Internals</title>"
     42       "<meta http-equiv=\"Content-Security-Policy\""
     43       "  content=\"object-src 'none'; script-src 'none'\">\n"
     44       "<style>\n"
     45       "body { font-family: sans-serif; font-size: 0.8em; }\n"
     46       "tt, code, pre { font-family: WebKitHack, monospace; }\n"
     47       "form { display: inline }\n"
     48       ".subsection_body { margin: 10px 0 10px 2em; }\n"
     49       ".subsection_title { font-weight: bold; }\n"
     50       "</style>\n"
     51       "</head><body>\n");
     52 }
     53 
     54 void EndHTML(std::string* out) {
     55   out->append("</body></html>");
     56 }
     57 
     58 void AddHTMLBoldText(const std::string& text, std::string* out) {
     59   out->append("<b>");
     60   out->append(net::EscapeForHTML(text));
     61   out->append("</b>");
     62 }
     63 
     64 void StartHTMLList(std::string* out) {
     65   out->append("<ul>");
     66 }
     67 
     68 void EndHTMLList(std::string* out) {
     69   out->append("</ul>");
     70 }
     71 
     72 void AddHTMLListItem(const std::string& element_title,
     73                      const std::string& element_data,
     74                      std::string* out) {
     75   out->append("<li>");
     76   // No need to escape element_title since constant string is passed.
     77   out->append(element_title);
     78   out->append(net::EscapeForHTML(element_data));
     79   out->append("</li>");
     80 }
     81 
     82 void AddHTMLButton(const std::string& title,
     83                    const std::string& command,
     84                    std::string* out) {
     85   // No need to escape title since constant string is passed.
     86   std::string escaped_command = net::EscapeForHTML(command.c_str());
     87   base::StringAppendF(out,
     88                       "<form action=\"\" method=\"GET\">\n"
     89                       "<input type=\"hidden\" name=\"remove\" value=\"%s\">\n"
     90                       "<input type=\"submit\" value=\"%s\">\n"
     91                       "</form><br/>\n",
     92                       escaped_command.c_str(),
     93                       title.c_str());
     94 }
     95 
     96 }  // namespace
     97 
     98 namespace webkit_blob {
     99 
    100 ViewBlobInternalsJob::ViewBlobInternalsJob(
    101     net::URLRequest* request,
    102     net::NetworkDelegate* network_delegate,
    103     BlobStorageController* blob_storage_controller)
    104     : net::URLRequestSimpleJob(request, network_delegate),
    105       blob_storage_controller_(blob_storage_controller),
    106       weak_factory_(this) {
    107 }
    108 
    109 ViewBlobInternalsJob::~ViewBlobInternalsJob() {
    110 }
    111 
    112 void ViewBlobInternalsJob::Start() {
    113   base::MessageLoop::current()->PostTask(
    114       FROM_HERE,
    115       base::Bind(&ViewBlobInternalsJob::DoWorkAsync,
    116                  weak_factory_.GetWeakPtr()));
    117 }
    118 
    119 bool ViewBlobInternalsJob::IsRedirectResponse(GURL* location,
    120                                               int* http_status_code) {
    121   if (request_->url().has_query()) {
    122     // Strip the query parameters.
    123     GURL::Replacements replacements;
    124     replacements.ClearQuery();
    125     *location = request_->url().ReplaceComponents(replacements);
    126     *http_status_code = 307;
    127     return true;
    128   }
    129   return false;
    130 }
    131 
    132 void ViewBlobInternalsJob::Kill() {
    133   net::URLRequestSimpleJob::Kill();
    134   weak_factory_.InvalidateWeakPtrs();
    135 }
    136 
    137 void ViewBlobInternalsJob::DoWorkAsync() {
    138   if (request_->url().has_query() &&
    139       StartsWithASCII(request_->url().query(), "remove=", true)) {
    140     std::string blob_url = request_->url().query().substr(strlen("remove="));
    141     blob_url = net::UnescapeURLComponent(blob_url,
    142         net::UnescapeRule::NORMAL | net::UnescapeRule::URL_SPECIAL_CHARS);
    143     blob_storage_controller_->RemoveBlob(GURL(blob_url));
    144   }
    145 
    146   StartAsync();
    147 }
    148 
    149 int ViewBlobInternalsJob::GetData(
    150     std::string* mime_type,
    151     std::string* charset,
    152     std::string* data,
    153     const net::CompletionCallback& callback) const {
    154   mime_type->assign("text/html");
    155   charset->assign("UTF-8");
    156 
    157   data->clear();
    158   StartHTML(data);
    159   if (blob_storage_controller_->blob_map_.empty())
    160     data->append(kEmptyBlobStorageMessage);
    161   else
    162     GenerateHTML(data);
    163   EndHTML(data);
    164   return net::OK;
    165 }
    166 
    167 void ViewBlobInternalsJob::GenerateHTML(std::string* out) const {
    168   for (BlobStorageController::BlobMap::const_iterator iter =
    169            blob_storage_controller_->blob_map_.begin();
    170        iter != blob_storage_controller_->blob_map_.end();
    171        ++iter) {
    172     AddHTMLBoldText(iter->first, out);
    173     AddHTMLButton(kRemove, iter->first, out);
    174     GenerateHTMLForBlobData(*iter->second.get(), out);
    175   }
    176 }
    177 
    178 void ViewBlobInternalsJob::GenerateHTMLForBlobData(const BlobData& blob_data,
    179                                                    std::string* out) {
    180   StartHTMLList(out);
    181 
    182   if (!blob_data.content_type().empty())
    183     AddHTMLListItem(kContentType, blob_data.content_type(), out);
    184   if (!blob_data.content_disposition().empty())
    185     AddHTMLListItem(kContentDisposition, blob_data.content_disposition(), out);
    186 
    187   bool has_multi_items = blob_data.items().size() > 1;
    188   if (has_multi_items) {
    189     AddHTMLListItem(kCount,
    190         UTF16ToUTF8(base::FormatNumber(blob_data.items().size())), out);
    191   }
    192 
    193   for (size_t i = 0; i < blob_data.items().size(); ++i) {
    194     if (has_multi_items) {
    195       AddHTMLListItem(kIndex, UTF16ToUTF8(base::FormatNumber(i)), out);
    196       StartHTMLList(out);
    197     }
    198     const BlobData::Item& item = blob_data.items().at(i);
    199 
    200     switch (item.type()) {
    201       case BlobData::Item::TYPE_BYTES:
    202         AddHTMLListItem(kType, "data", out);
    203         break;
    204       case BlobData::Item::TYPE_FILE:
    205         AddHTMLListItem(kType, "file", out);
    206         AddHTMLListItem(kPath,
    207                  net::EscapeForHTML(item.path().AsUTF8Unsafe()),
    208                  out);
    209         if (!item.expected_modification_time().is_null()) {
    210           AddHTMLListItem(kModificationTime, UTF16ToUTF8(
    211               TimeFormatFriendlyDateAndTime(item.expected_modification_time())),
    212               out);
    213         }
    214         break;
    215       case BlobData::Item::TYPE_BLOB:
    216         AddHTMLListItem(kType, "blob", out);
    217         AddHTMLListItem(kURL, item.url().spec(), out);
    218         break;
    219       case BlobData::Item::TYPE_FILE_FILESYSTEM:
    220         AddHTMLListItem(kType, "filesystem", out);
    221         AddHTMLListItem(kURL, item.url().spec(), out);
    222         if (!item.expected_modification_time().is_null()) {
    223           AddHTMLListItem(kModificationTime, UTF16ToUTF8(
    224               TimeFormatFriendlyDateAndTime(item.expected_modification_time())),
    225               out);
    226         }
    227         break;
    228       case BlobData::Item::TYPE_UNKNOWN:
    229         NOTREACHED();
    230         break;
    231     }
    232     if (item.offset()) {
    233       AddHTMLListItem(kOffset, UTF16ToUTF8(base::FormatNumber(
    234           static_cast<int64>(item.offset()))), out);
    235     }
    236     if (static_cast<int64>(item.length()) != -1) {
    237       AddHTMLListItem(kLength, UTF16ToUTF8(base::FormatNumber(
    238           static_cast<int64>(item.length()))), out);
    239     }
    240 
    241     if (has_multi_items)
    242       EndHTMLList(out);
    243   }
    244 
    245   EndHTMLList(out);
    246 }
    247 
    248 }  // namespace webkit_blob
    249