OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 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 "webkit/browser/blob/blob_storage_context.h" | 5 #include "storage/browser/blob/blob_storage_context.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/location.h" | 8 #include "base/location.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/message_loop/message_loop_proxy.h" | 10 #include "base/message_loop/message_loop_proxy.h" |
11 #include "url/gurl.h" | 11 #include "url/gurl.h" |
12 #include "webkit/browser/blob/blob_data_handle.h" | 12 #include "storage/browser/blob/blob_data_handle.h" |
13 #include "webkit/common/blob/blob_data.h" | 13 #include "storage/common/blob/blob_data.h" |
14 | 14 |
15 namespace webkit_blob { | 15 namespace storage { |
16 | 16 |
17 namespace { | 17 namespace { |
18 | 18 |
19 // We can't use GURL directly for these hash fragment manipulations | 19 // We can't use GURL directly for these hash fragment manipulations |
20 // since it doesn't have specific knowlege of the BlobURL format. GURL | 20 // since it doesn't have specific knowlege of the BlobURL format. GURL |
21 // treats BlobURLs as if they were PathURLs which don't support hash | 21 // treats BlobURLs as if they were PathURLs which don't support hash |
22 // fragments. | 22 // fragments. |
23 | 23 |
24 bool BlobUrlHasRef(const GURL& url) { | 24 bool BlobUrlHasRef(const GURL& url) { |
25 return url.spec().find('#') != std::string::npos; | 25 return url.spec().find('#') != std::string::npos; |
26 } | 26 } |
27 | 27 |
28 GURL ClearBlobUrlRef(const GURL& url) { | 28 GURL ClearBlobUrlRef(const GURL& url) { |
29 size_t hash_pos = url.spec().find('#'); | 29 size_t hash_pos = url.spec().find('#'); |
30 if (hash_pos == std::string::npos) | 30 if (hash_pos == std::string::npos) |
31 return url; | 31 return url; |
32 return GURL(url.spec().substr(0, hash_pos)); | 32 return GURL(url.spec().substr(0, hash_pos)); |
33 } | 33 } |
34 | 34 |
35 // TODO(michaeln): use base::SysInfo::AmountOfPhysicalMemoryMB() in some | 35 // TODO(michaeln): use base::SysInfo::AmountOfPhysicalMemoryMB() in some |
36 // way to come up with a better limit. | 36 // way to come up with a better limit. |
37 static const int64 kMaxMemoryUsage = 500 * 1024 * 1024; // Half a gig. | 37 static const int64 kMaxMemoryUsage = 500 * 1024 * 1024; // Half a gig. |
38 | 38 |
39 } // namespace | 39 } // namespace |
40 | 40 |
41 BlobStorageContext::BlobMapEntry::BlobMapEntry() | 41 BlobStorageContext::BlobMapEntry::BlobMapEntry() : refcount(0), flags(0) { |
42 : refcount(0), flags(0) { | |
43 } | 42 } |
44 | 43 |
45 BlobStorageContext::BlobMapEntry::BlobMapEntry( | 44 BlobStorageContext::BlobMapEntry::BlobMapEntry(int refcount, |
46 int refcount, int flags, BlobData* data) | 45 int flags, |
| 46 BlobData* data) |
47 : refcount(refcount), flags(flags), data(data) { | 47 : refcount(refcount), flags(flags), data(data) { |
48 } | 48 } |
49 | 49 |
50 BlobStorageContext::BlobMapEntry::~BlobMapEntry() { | 50 BlobStorageContext::BlobMapEntry::~BlobMapEntry() { |
51 } | 51 } |
52 | 52 |
53 BlobStorageContext::BlobStorageContext() | 53 BlobStorageContext::BlobStorageContext() : memory_usage_(0) { |
54 : memory_usage_(0) { | |
55 } | 54 } |
56 | 55 |
57 BlobStorageContext::~BlobStorageContext() { | 56 BlobStorageContext::~BlobStorageContext() { |
58 } | 57 } |
59 | 58 |
60 scoped_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromUUID( | 59 scoped_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromUUID( |
61 const std::string& uuid) { | 60 const std::string& uuid) { |
62 scoped_ptr<BlobDataHandle> result; | 61 scoped_ptr<BlobDataHandle> result; |
63 BlobMap::iterator found = blob_map_.find(uuid); | 62 BlobMap::iterator found = blob_map_.find(uuid); |
64 if (found == blob_map_.end()) | 63 if (found == blob_map_.end()) |
65 return result.Pass(); | 64 return result.Pass(); |
66 if (found->second.flags & EXCEEDED_MEMORY) | 65 if (found->second.flags & EXCEEDED_MEMORY) |
67 return result.Pass(); | 66 return result.Pass(); |
68 DCHECK(!(found->second.flags & BEING_BUILT)); | 67 DCHECK(!(found->second.flags & BEING_BUILT)); |
69 result.reset(new BlobDataHandle( | 68 result.reset(new BlobDataHandle( |
70 found->second.data.get(), this, base::MessageLoopProxy::current().get())); | 69 found->second.data.get(), this, base::MessageLoopProxy::current().get())); |
71 return result.Pass(); | 70 return result.Pass(); |
72 } | 71 } |
73 | 72 |
74 scoped_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromPublicURL( | 73 scoped_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromPublicURL( |
75 const GURL& url) { | 74 const GURL& url) { |
76 BlobURLMap::iterator found = public_blob_urls_.find( | 75 BlobURLMap::iterator found = |
77 BlobUrlHasRef(url) ? ClearBlobUrlRef(url) : url); | 76 public_blob_urls_.find(BlobUrlHasRef(url) ? ClearBlobUrlRef(url) : url); |
78 if (found == public_blob_urls_.end()) | 77 if (found == public_blob_urls_.end()) |
79 return scoped_ptr<BlobDataHandle>(); | 78 return scoped_ptr<BlobDataHandle>(); |
80 return GetBlobDataFromUUID(found->second); | 79 return GetBlobDataFromUUID(found->second); |
81 } | 80 } |
82 | 81 |
83 scoped_ptr<BlobDataHandle> BlobStorageContext::AddFinishedBlob( | 82 scoped_ptr<BlobDataHandle> BlobStorageContext::AddFinishedBlob( |
84 const BlobData* data) { | 83 const BlobData* data) { |
85 StartBuildingBlob(data->uuid()); | 84 StartBuildingBlob(data->uuid()); |
86 for (std::vector<BlobData::Item>::const_iterator iter = | 85 for (std::vector<BlobData::Item>::const_iterator iter = data->items().begin(); |
87 data->items().begin(); | 86 iter != data->items().end(); |
88 iter != data->items().end(); ++iter) { | 87 ++iter) { |
89 AppendBlobDataItem(data->uuid(), *iter); | 88 AppendBlobDataItem(data->uuid(), *iter); |
90 } | 89 } |
91 FinishBuildingBlob(data->uuid(), data->content_type()); | 90 FinishBuildingBlob(data->uuid(), data->content_type()); |
92 scoped_ptr<BlobDataHandle> handle = GetBlobDataFromUUID(data->uuid()); | 91 scoped_ptr<BlobDataHandle> handle = GetBlobDataFromUUID(data->uuid()); |
93 DecrementBlobRefCount(data->uuid()); | 92 DecrementBlobRefCount(data->uuid()); |
94 return handle.Pass(); | 93 return handle.Pass(); |
95 } | 94 } |
96 | 95 |
97 bool BlobStorageContext::RegisterPublicBlobURL( | 96 bool BlobStorageContext::RegisterPublicBlobURL(const GURL& blob_url, |
98 const GURL& blob_url, const std::string& uuid) { | 97 const std::string& uuid) { |
99 DCHECK(!BlobUrlHasRef(blob_url)); | 98 DCHECK(!BlobUrlHasRef(blob_url)); |
100 DCHECK(IsInUse(uuid)); | 99 DCHECK(IsInUse(uuid)); |
101 DCHECK(!IsUrlRegistered(blob_url)); | 100 DCHECK(!IsUrlRegistered(blob_url)); |
102 if (!IsInUse(uuid) || IsUrlRegistered(blob_url)) | 101 if (!IsInUse(uuid) || IsUrlRegistered(blob_url)) |
103 return false; | 102 return false; |
104 IncrementBlobRefCount(uuid); | 103 IncrementBlobRefCount(uuid); |
105 public_blob_urls_[blob_url] = uuid; | 104 public_blob_urls_[blob_url] = uuid; |
106 return true; | 105 return true; |
107 } | 106 } |
108 | 107 |
109 void BlobStorageContext::RevokePublicBlobURL(const GURL& blob_url) { | 108 void BlobStorageContext::RevokePublicBlobURL(const GURL& blob_url) { |
110 DCHECK(!BlobUrlHasRef(blob_url)); | 109 DCHECK(!BlobUrlHasRef(blob_url)); |
111 if (!IsUrlRegistered(blob_url)) | 110 if (!IsUrlRegistered(blob_url)) |
112 return; | 111 return; |
113 DecrementBlobRefCount(public_blob_urls_[blob_url]); | 112 DecrementBlobRefCount(public_blob_urls_[blob_url]); |
114 public_blob_urls_.erase(blob_url); | 113 public_blob_urls_.erase(blob_url); |
115 } | 114 } |
116 | 115 |
117 void BlobStorageContext::StartBuildingBlob(const std::string& uuid) { | 116 void BlobStorageContext::StartBuildingBlob(const std::string& uuid) { |
118 DCHECK(!IsInUse(uuid) && !uuid.empty()); | 117 DCHECK(!IsInUse(uuid) && !uuid.empty()); |
119 blob_map_[uuid] = BlobMapEntry(1, BEING_BUILT, new BlobData(uuid)); | 118 blob_map_[uuid] = BlobMapEntry(1, BEING_BUILT, new BlobData(uuid)); |
120 } | 119 } |
121 | 120 |
122 void BlobStorageContext::AppendBlobDataItem( | 121 void BlobStorageContext::AppendBlobDataItem(const std::string& uuid, |
123 const std::string& uuid, const BlobData::Item& item) { | 122 const BlobData::Item& item) { |
124 DCHECK(IsBeingBuilt(uuid)); | 123 DCHECK(IsBeingBuilt(uuid)); |
125 BlobMap::iterator found = blob_map_.find(uuid); | 124 BlobMap::iterator found = blob_map_.find(uuid); |
126 if (found == blob_map_.end()) | 125 if (found == blob_map_.end()) |
127 return; | 126 return; |
128 if (found->second.flags & EXCEEDED_MEMORY) | 127 if (found->second.flags & EXCEEDED_MEMORY) |
129 return; | 128 return; |
130 BlobData* target_blob_data = found->second.data.get(); | 129 BlobData* target_blob_data = found->second.data.get(); |
131 DCHECK(target_blob_data); | 130 DCHECK(target_blob_data); |
132 | 131 |
133 bool exceeded_memory = false; | 132 bool exceeded_memory = false; |
(...skipping 27 matching lines...) Expand all Loading... |
161 case BlobData::Item::TYPE_FILE_FILESYSTEM: | 160 case BlobData::Item::TYPE_FILE_FILESYSTEM: |
162 AppendFileSystemFileItem(target_blob_data, | 161 AppendFileSystemFileItem(target_blob_data, |
163 item.filesystem_url(), | 162 item.filesystem_url(), |
164 item.offset(), | 163 item.offset(), |
165 item.length(), | 164 item.length(), |
166 item.expected_modification_time()); | 165 item.expected_modification_time()); |
167 break; | 166 break; |
168 case BlobData::Item::TYPE_BLOB: { | 167 case BlobData::Item::TYPE_BLOB: { |
169 scoped_ptr<BlobDataHandle> src = GetBlobDataFromUUID(item.blob_uuid()); | 168 scoped_ptr<BlobDataHandle> src = GetBlobDataFromUUID(item.blob_uuid()); |
170 if (src) | 169 if (src) |
171 exceeded_memory = !ExpandStorageItems(target_blob_data, | 170 exceeded_memory = |
172 src->data(), | 171 !ExpandStorageItems( |
173 item.offset(), | 172 target_blob_data, src->data(), item.offset(), item.length()); |
174 item.length()); | |
175 break; | 173 break; |
176 } | 174 } |
177 default: | 175 default: |
178 NOTREACHED(); | 176 NOTREACHED(); |
179 break; | 177 break; |
180 } | 178 } |
181 | 179 |
182 // If we're using too much memory, drop this blob's data. | 180 // If we're using too much memory, drop this blob's data. |
183 // TODO(michaeln): Blob memory storage does not yet spill over to disk, | 181 // TODO(michaeln): Blob memory storage does not yet spill over to disk, |
184 // as a stop gap, we'll prevent memory usage over a max amount. | 182 // as a stop gap, we'll prevent memory usage over a max amount. |
185 if (exceeded_memory) { | 183 if (exceeded_memory) { |
186 memory_usage_ -= target_blob_data->GetMemoryUsage(); | 184 memory_usage_ -= target_blob_data->GetMemoryUsage(); |
187 found->second.flags |= EXCEEDED_MEMORY; | 185 found->second.flags |= EXCEEDED_MEMORY; |
188 found->second.data = new BlobData(uuid); | 186 found->second.data = new BlobData(uuid); |
189 return; | 187 return; |
190 } | 188 } |
191 } | 189 } |
192 | 190 |
193 void BlobStorageContext::FinishBuildingBlob( | 191 void BlobStorageContext::FinishBuildingBlob(const std::string& uuid, |
194 const std::string& uuid, const std::string& content_type) { | 192 const std::string& content_type) { |
195 DCHECK(IsBeingBuilt(uuid)); | 193 DCHECK(IsBeingBuilt(uuid)); |
196 BlobMap::iterator found = blob_map_.find(uuid); | 194 BlobMap::iterator found = blob_map_.find(uuid); |
197 if (found == blob_map_.end()) | 195 if (found == blob_map_.end()) |
198 return; | 196 return; |
199 found->second.data->set_content_type(content_type); | 197 found->second.data->set_content_type(content_type); |
200 found->second.flags &= ~BEING_BUILT; | 198 found->second.flags &= ~BEING_BUILT; |
201 } | 199 } |
202 | 200 |
203 void BlobStorageContext::CancelBuildingBlob(const std::string& uuid) { | 201 void BlobStorageContext::CancelBuildingBlob(const std::string& uuid) { |
204 DCHECK(IsBeingBuilt(uuid)); | 202 DCHECK(IsBeingBuilt(uuid)); |
(...skipping 13 matching lines...) Expand all Loading... |
218 BlobMap::iterator found = blob_map_.find(uuid); | 216 BlobMap::iterator found = blob_map_.find(uuid); |
219 if (found == blob_map_.end()) | 217 if (found == blob_map_.end()) |
220 return; | 218 return; |
221 DCHECK_EQ(found->second.data->uuid(), uuid); | 219 DCHECK_EQ(found->second.data->uuid(), uuid); |
222 if (--(found->second.refcount) == 0) { | 220 if (--(found->second.refcount) == 0) { |
223 memory_usage_ -= found->second.data->GetMemoryUsage(); | 221 memory_usage_ -= found->second.data->GetMemoryUsage(); |
224 blob_map_.erase(found); | 222 blob_map_.erase(found); |
225 } | 223 } |
226 } | 224 } |
227 | 225 |
228 bool BlobStorageContext::ExpandStorageItems( | 226 bool BlobStorageContext::ExpandStorageItems(BlobData* target_blob_data, |
229 BlobData* target_blob_data, BlobData* src_blob_data, | 227 BlobData* src_blob_data, |
230 uint64 offset, uint64 length) { | 228 uint64 offset, |
| 229 uint64 length) { |
231 DCHECK(target_blob_data && src_blob_data && | 230 DCHECK(target_blob_data && src_blob_data && |
232 length != static_cast<uint64>(-1)); | 231 length != static_cast<uint64>(-1)); |
233 | 232 |
234 std::vector<BlobData::Item>::const_iterator iter = | 233 std::vector<BlobData::Item>::const_iterator iter = |
235 src_blob_data->items().begin(); | 234 src_blob_data->items().begin(); |
236 if (offset) { | 235 if (offset) { |
237 for (; iter != src_blob_data->items().end(); ++iter) { | 236 for (; iter != src_blob_data->items().end(); ++iter) { |
238 if (offset >= iter->length()) | 237 if (offset >= iter->length()) |
239 offset -= iter->length(); | 238 offset -= iter->length(); |
240 else | 239 else |
(...skipping 24 matching lines...) Expand all Loading... |
265 iter->offset() + offset, | 264 iter->offset() + offset, |
266 new_length, | 265 new_length, |
267 iter->expected_modification_time()); | 266 iter->expected_modification_time()); |
268 } | 267 } |
269 length -= new_length; | 268 length -= new_length; |
270 offset = 0; | 269 offset = 0; |
271 } | 270 } |
272 return true; | 271 return true; |
273 } | 272 } |
274 | 273 |
275 bool BlobStorageContext::AppendBytesItem( | 274 bool BlobStorageContext::AppendBytesItem(BlobData* target_blob_data, |
276 BlobData* target_blob_data, const char* bytes, int64 length) { | 275 const char* bytes, |
| 276 int64 length) { |
277 if (length < 0) { | 277 if (length < 0) { |
278 DCHECK(false); | 278 DCHECK(false); |
279 return false; | 279 return false; |
280 } | 280 } |
281 if (memory_usage_ + length > kMaxMemoryUsage) | 281 if (memory_usage_ + length > kMaxMemoryUsage) |
282 return false; | 282 return false; |
283 target_blob_data->AppendData(bytes, static_cast<size_t>(length)); | 283 target_blob_data->AppendData(bytes, static_cast<size_t>(length)); |
284 memory_usage_ += length; | 284 memory_usage_ += length; |
285 return true; | 285 return true; |
286 } | 286 } |
287 | 287 |
288 void BlobStorageContext::AppendFileItem( | 288 void BlobStorageContext::AppendFileItem( |
289 BlobData* target_blob_data, | 289 BlobData* target_blob_data, |
290 const base::FilePath& file_path, uint64 offset, uint64 length, | 290 const base::FilePath& file_path, |
| 291 uint64 offset, |
| 292 uint64 length, |
291 const base::Time& expected_modification_time) { | 293 const base::Time& expected_modification_time) { |
292 target_blob_data->AppendFile(file_path, offset, length, | 294 target_blob_data->AppendFile( |
293 expected_modification_time); | 295 file_path, offset, length, expected_modification_time); |
294 | 296 |
295 // It may be a temporary file that should be deleted when no longer needed. | 297 // It may be a temporary file that should be deleted when no longer needed. |
296 scoped_refptr<ShareableFileReference> shareable_file = | 298 scoped_refptr<ShareableFileReference> shareable_file = |
297 ShareableFileReference::Get(file_path); | 299 ShareableFileReference::Get(file_path); |
298 if (shareable_file.get()) | 300 if (shareable_file.get()) |
299 target_blob_data->AttachShareableFileReference(shareable_file.get()); | 301 target_blob_data->AttachShareableFileReference(shareable_file.get()); |
300 } | 302 } |
301 | 303 |
302 void BlobStorageContext::AppendFileSystemFileItem( | 304 void BlobStorageContext::AppendFileSystemFileItem( |
303 BlobData* target_blob_data, | 305 BlobData* target_blob_data, |
304 const GURL& filesystem_url, uint64 offset, uint64 length, | 306 const GURL& filesystem_url, |
| 307 uint64 offset, |
| 308 uint64 length, |
305 const base::Time& expected_modification_time) { | 309 const base::Time& expected_modification_time) { |
306 target_blob_data->AppendFileSystemFile(filesystem_url, offset, length, | 310 target_blob_data->AppendFileSystemFile( |
307 expected_modification_time); | 311 filesystem_url, offset, length, expected_modification_time); |
308 } | 312 } |
309 | 313 |
310 bool BlobStorageContext::IsInUse(const std::string& uuid) { | 314 bool BlobStorageContext::IsInUse(const std::string& uuid) { |
311 return blob_map_.find(uuid) != blob_map_.end(); | 315 return blob_map_.find(uuid) != blob_map_.end(); |
312 } | 316 } |
313 | 317 |
314 bool BlobStorageContext::IsBeingBuilt(const std::string& uuid) { | 318 bool BlobStorageContext::IsBeingBuilt(const std::string& uuid) { |
315 BlobMap::iterator found = blob_map_.find(uuid); | 319 BlobMap::iterator found = blob_map_.find(uuid); |
316 if (found == blob_map_.end()) | 320 if (found == blob_map_.end()) |
317 return false; | 321 return false; |
318 return found->second.flags & BEING_BUILT; | 322 return found->second.flags & BEING_BUILT; |
319 } | 323 } |
320 | 324 |
321 bool BlobStorageContext::IsUrlRegistered(const GURL& blob_url) { | 325 bool BlobStorageContext::IsUrlRegistered(const GURL& blob_url) { |
322 return public_blob_urls_.find(blob_url) != public_blob_urls_.end(); | 326 return public_blob_urls_.find(blob_url) != public_blob_urls_.end(); |
323 } | 327 } |
324 | 328 |
325 } // namespace webkit_blob | 329 } // namespace storage |
OLD | NEW |