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/blob_storage_controller.h"
      6 
      7 #include "base/logging.h"
      8 #include "url/gurl.h"
      9 #include "webkit/common/blob/blob_data.h"
     10 
     11 namespace webkit_blob {
     12 
     13 namespace {
     14 
     15 // We can't use GURL directly for these hash fragment manipulations
     16 // since it doesn't have specific knowlege of the BlobURL format. GURL
     17 // treats BlobURLs as if they were PathURLs which don't support hash
     18 // fragments.
     19 
     20 bool BlobUrlHasRef(const GURL& url) {
     21   return url.spec().find('#') != std::string::npos;
     22 }
     23 
     24 GURL ClearBlobUrlRef(const GURL& url) {
     25   size_t hash_pos = url.spec().find('#');
     26   if (hash_pos == std::string::npos)
     27     return url;
     28   return GURL(url.spec().substr(0, hash_pos));
     29 }
     30 
     31 static const int64 kMaxMemoryUsage = 1024 * 1024 * 1024;  // 1G
     32 
     33 }  // namespace
     34 
     35 BlobStorageController::BlobStorageController()
     36     : memory_usage_(0) {
     37 }
     38 
     39 BlobStorageController::~BlobStorageController() {
     40 }
     41 
     42 void BlobStorageController::StartBuildingBlob(const GURL& url) {
     43   DCHECK(url.SchemeIs("blob"));
     44   DCHECK(!BlobUrlHasRef(url));
     45   BlobData* blob_data = new BlobData;
     46   unfinalized_blob_map_[url.spec()] = blob_data;
     47   IncrementBlobDataUsage(blob_data);
     48 }
     49 
     50 void BlobStorageController::AppendBlobDataItem(
     51     const GURL& url, const BlobData::Item& item) {
     52   DCHECK(url.SchemeIs("blob"));
     53   DCHECK(!BlobUrlHasRef(url));
     54   BlobMap::iterator found = unfinalized_blob_map_.find(url.spec());
     55   if (found == unfinalized_blob_map_.end())
     56     return;
     57   BlobData* target_blob_data = found->second.get();
     58   DCHECK(target_blob_data);
     59 
     60   memory_usage_ -= target_blob_data->GetMemoryUsage();
     61 
     62   // The blob data is stored in the "canonical" way. That is, it only contains a
     63   // list of Data and File items.
     64   // 1) The Data item is denoted by the raw data and the range.
     65   // 2) The File item is denoted by the file path, the range and the expected
     66   //    modification time.
     67   // 3) The FileSystem File item is denoted by the FileSystem URL, the range
     68   //    and the expected modification time.
     69   // All the Blob items in the passing blob data are resolved and expanded into
     70   // a set of Data and File items.
     71 
     72   DCHECK(item.length() > 0);
     73   switch (item.type()) {
     74     case BlobData::Item::TYPE_BYTES:
     75       DCHECK(!item.offset());
     76       target_blob_data->AppendData(item.bytes(), item.length());
     77       break;
     78     case BlobData::Item::TYPE_FILE:
     79       AppendFileItem(target_blob_data,
     80                      item.path(),
     81                      item.offset(),
     82                      item.length(),
     83                      item.expected_modification_time());
     84       break;
     85     case BlobData::Item::TYPE_FILE_FILESYSTEM:
     86       AppendFileSystemFileItem(target_blob_data,
     87                                item.url(),
     88                                item.offset(),
     89                                item.length(),
     90                                item.expected_modification_time());
     91       break;
     92     case BlobData::Item::TYPE_BLOB: {
     93       BlobData* src_blob_data = GetBlobDataFromUrl(item.url());
     94       DCHECK(src_blob_data);
     95       if (src_blob_data)
     96         AppendStorageItems(target_blob_data,
     97                            src_blob_data,
     98                            item.offset(),
     99                            item.length());
    100       break;
    101     }
    102     default:
    103       NOTREACHED();
    104       break;
    105   }
    106 
    107   memory_usage_ += target_blob_data->GetMemoryUsage();
    108 
    109   // If we're using too much memory, drop this blob.
    110   // TODO(michaeln): Blob memory storage does not yet spill over to disk,
    111   // until it does, we'll prevent memory usage over a max amount.
    112   if (memory_usage_ > kMaxMemoryUsage)
    113     RemoveBlob(url);
    114 }
    115 
    116 void BlobStorageController::FinishBuildingBlob(
    117     const GURL& url, const std::string& content_type) {
    118   DCHECK(url.SchemeIs("blob"));
    119   DCHECK(!BlobUrlHasRef(url));
    120   BlobMap::iterator found = unfinalized_blob_map_.find(url.spec());
    121   if (found == unfinalized_blob_map_.end())
    122     return;
    123   found->second->set_content_type(content_type);
    124   blob_map_[url.spec()] = found->second;
    125   unfinalized_blob_map_.erase(found);
    126 }
    127 
    128 void BlobStorageController::AddFinishedBlob(const GURL& url,
    129                                             const BlobData* data) {
    130   StartBuildingBlob(url);
    131   for (std::vector<BlobData::Item>::const_iterator iter =
    132            data->items().begin();
    133        iter != data->items().end(); ++iter) {
    134     AppendBlobDataItem(url, *iter);
    135   }
    136   FinishBuildingBlob(url, data->content_type());
    137 }
    138 
    139 void BlobStorageController::CloneBlob(
    140     const GURL& url, const GURL& src_url) {
    141   DCHECK(url.SchemeIs("blob"));
    142   DCHECK(!BlobUrlHasRef(url));
    143 
    144   BlobData* blob_data = GetBlobDataFromUrl(src_url);
    145   DCHECK(blob_data);
    146   if (!blob_data)
    147     return;
    148 
    149   blob_map_[url.spec()] = blob_data;
    150   IncrementBlobDataUsage(blob_data);
    151 }
    152 
    153 void BlobStorageController::RemoveBlob(const GURL& url) {
    154   DCHECK(url.SchemeIs("blob"));
    155   DCHECK(!BlobUrlHasRef(url));
    156 
    157   if (!RemoveFromMapHelper(&unfinalized_blob_map_, url))
    158     RemoveFromMapHelper(&blob_map_, url);
    159 }
    160 
    161 bool BlobStorageController::RemoveFromMapHelper(
    162     BlobMap* map, const GURL& url) {
    163   BlobMap::iterator found = map->find(url.spec());
    164   if (found == map->end())
    165     return false;
    166   if (DecrementBlobDataUsage(found->second.get()))
    167     memory_usage_ -= found->second->GetMemoryUsage();
    168   map->erase(found);
    169   return true;
    170 }
    171 
    172 
    173 BlobData* BlobStorageController::GetBlobDataFromUrl(const GURL& url) {
    174   BlobMap::iterator found = blob_map_.find(
    175       BlobUrlHasRef(url) ? ClearBlobUrlRef(url).spec() : url.spec());
    176   return (found != blob_map_.end()) ? found->second.get() : NULL;
    177 }
    178 
    179 void BlobStorageController::AppendStorageItems(
    180     BlobData* target_blob_data, BlobData* src_blob_data,
    181     uint64 offset, uint64 length) {
    182   DCHECK(target_blob_data && src_blob_data &&
    183          length != static_cast<uint64>(-1));
    184 
    185   std::vector<BlobData::Item>::const_iterator iter =
    186       src_blob_data->items().begin();
    187   if (offset) {
    188     for (; iter != src_blob_data->items().end(); ++iter) {
    189       if (offset >= iter->length())
    190         offset -= iter->length();
    191       else
    192         break;
    193     }
    194   }
    195 
    196   for (; iter != src_blob_data->items().end() && length > 0; ++iter) {
    197     uint64 current_length = iter->length() - offset;
    198     uint64 new_length = current_length > length ? length : current_length;
    199     if (iter->type() == BlobData::Item::TYPE_BYTES) {
    200       target_blob_data->AppendData(
    201           iter->bytes() + static_cast<size_t>(iter->offset() + offset),
    202           static_cast<uint32>(new_length));
    203     } else if (iter->type() == BlobData::Item::TYPE_FILE) {
    204       AppendFileItem(target_blob_data,
    205                      iter->path(),
    206                      iter->offset() + offset,
    207                      new_length,
    208                      iter->expected_modification_time());
    209     } else {
    210       DCHECK(iter->type() == BlobData::Item::TYPE_FILE_FILESYSTEM);
    211       AppendFileSystemFileItem(target_blob_data,
    212                                iter->url(),
    213                                iter->offset() + offset,
    214                                new_length,
    215                                iter->expected_modification_time());
    216     }
    217     length -= new_length;
    218     offset = 0;
    219   }
    220 }
    221 
    222 void BlobStorageController::AppendFileItem(
    223     BlobData* target_blob_data,
    224     const base::FilePath& file_path, uint64 offset, uint64 length,
    225     const base::Time& expected_modification_time) {
    226   target_blob_data->AppendFile(file_path, offset, length,
    227                                expected_modification_time);
    228 
    229   // It may be a temporary file that should be deleted when no longer needed.
    230   scoped_refptr<ShareableFileReference> shareable_file =
    231       ShareableFileReference::Get(file_path);
    232   if (shareable_file.get())
    233     target_blob_data->AttachShareableFileReference(shareable_file.get());
    234 }
    235 
    236 void BlobStorageController::AppendFileSystemFileItem(
    237     BlobData* target_blob_data,
    238     const GURL& url, uint64 offset, uint64 length,
    239     const base::Time& expected_modification_time) {
    240   target_blob_data->AppendFileSystemFile(url, offset, length,
    241                                          expected_modification_time);
    242 }
    243 
    244 void BlobStorageController::IncrementBlobDataUsage(BlobData* blob_data) {
    245   blob_data_usage_count_[blob_data] += 1;
    246 }
    247 
    248 bool BlobStorageController::DecrementBlobDataUsage(BlobData* blob_data) {
    249   BlobDataUsageMap::iterator found = blob_data_usage_count_.find(blob_data);
    250   DCHECK(found != blob_data_usage_count_.end());
    251   if (--(found->second))
    252     return false;  // Still in use
    253   blob_data_usage_count_.erase(found);
    254   return true;
    255 }
    256 
    257 }  // namespace webkit_blob
    258