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