1 // Copyright 2016 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 "components/services/storage/dom_storage/storage_area_impl.h"
6 
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/containers/span.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/threading/thread_task_runner_handle.h"
12 #include "base/trace_event/memory_dump_manager.h"
13 #include "base/trace_event/process_memory_dump.h"
14 #include "components/services/storage/dom_storage/async_dom_storage_database.h"
15 #include "third_party/leveldatabase/env_chromium.h"
16 
17 namespace storage {
18 
19 StorageAreaImpl::Delegate::~Delegate() = default;
20 
PrepareToCommit(std::vector<DomStorageDatabase::KeyValuePair> * extra_entries_to_add,std::vector<DomStorageDatabase::Key> * extra_keys_to_delete)21 void StorageAreaImpl::Delegate::PrepareToCommit(
22     std::vector<DomStorageDatabase::KeyValuePair>* extra_entries_to_add,
23     std::vector<DomStorageDatabase::Key>* extra_keys_to_delete) {}
24 
MigrateData(base::OnceCallback<void (std::unique_ptr<ValueMap>)> callback)25 void StorageAreaImpl::Delegate::MigrateData(
26     base::OnceCallback<void(std::unique_ptr<ValueMap>)> callback) {
27   std::move(callback).Run(nullptr);
28 }
29 
FixUpData(const ValueMap & data)30 std::vector<StorageAreaImpl::Change> StorageAreaImpl::Delegate::FixUpData(
31     const ValueMap& data) {
32   return std::vector<Change>();
33 }
34 
OnMapLoaded(leveldb::Status)35 void StorageAreaImpl::Delegate::OnMapLoaded(leveldb::Status) {}
36 
37 bool StorageAreaImpl::s_aggressive_flushing_enabled_ = false;
38 
RateLimiter(size_t desired_rate,base::TimeDelta time_quantum)39 StorageAreaImpl::RateLimiter::RateLimiter(size_t desired_rate,
40                                           base::TimeDelta time_quantum)
41     : rate_(desired_rate), samples_(0), time_quantum_(time_quantum) {
42   DCHECK_GT(desired_rate, 0ul);
43 }
44 
ComputeTimeNeeded() const45 base::TimeDelta StorageAreaImpl::RateLimiter::ComputeTimeNeeded() const {
46   return time_quantum_ * (samples_ / rate_);
47 }
48 
ComputeDelayNeeded(const base::TimeDelta elapsed_time) const49 base::TimeDelta StorageAreaImpl::RateLimiter::ComputeDelayNeeded(
50     const base::TimeDelta elapsed_time) const {
51   base::TimeDelta time_needed = ComputeTimeNeeded();
52   if (time_needed > elapsed_time)
53     return time_needed - elapsed_time;
54   return base::TimeDelta();
55 }
56 
57 StorageAreaImpl::CommitBatch::CommitBatch() = default;
58 
59 StorageAreaImpl::CommitBatch::~CommitBatch() = default;
60 
StorageAreaImpl(AsyncDomStorageDatabase * database,const std::string & prefix,Delegate * delegate,const Options & options)61 StorageAreaImpl::StorageAreaImpl(AsyncDomStorageDatabase* database,
62                                  const std::string& prefix,
63                                  Delegate* delegate,
64                                  const Options& options)
65     : StorageAreaImpl(database,
66                       std::vector<uint8_t>(prefix.begin(), prefix.end()),
67                       delegate,
68                       options) {}
69 
StorageAreaImpl(AsyncDomStorageDatabase * database,std::vector<uint8_t> prefix,Delegate * delegate,const Options & options)70 StorageAreaImpl::StorageAreaImpl(AsyncDomStorageDatabase* database,
71                                  std::vector<uint8_t> prefix,
72                                  Delegate* delegate,
73                                  const Options& options)
74     : prefix_(std::move(prefix)),
75       delegate_(delegate),
76       database_(database),
77       cache_mode_(database ? options.cache_mode : CacheMode::KEYS_AND_VALUES),
78       storage_used_(0),
79       max_size_(options.max_size),
80       memory_used_(0),
81       start_time_(base::TimeTicks::Now()),
82       default_commit_delay_(options.default_commit_delay),
83       data_rate_limiter_(options.max_bytes_per_hour,
84                          base::TimeDelta::FromHours(1)),
85       commit_rate_limiter_(options.max_commits_per_hour,
86                            base::TimeDelta::FromHours(1)) {
87   receivers_.set_disconnect_handler(base::BindRepeating(
88       &StorageAreaImpl::OnConnectionError, weak_ptr_factory_.GetWeakPtr()));
89 }
90 
~StorageAreaImpl()91 StorageAreaImpl::~StorageAreaImpl() {
92   DCHECK(!has_pending_load_tasks());
93   if (commit_batch_)
94     CommitChanges();
95 }
96 
InitializeAsEmpty()97 void StorageAreaImpl::InitializeAsEmpty() {
98   DCHECK_EQ(map_state_, MapState::UNLOADED);
99   map_state_ = MapState::LOADING_FROM_DATABASE;
100   OnMapLoaded(leveldb::Status::OK(), {});
101 }
102 
Bind(mojo::PendingReceiver<blink::mojom::StorageArea> receiver)103 void StorageAreaImpl::Bind(
104     mojo::PendingReceiver<blink::mojom::StorageArea> receiver) {
105   receivers_.Add(this, std::move(receiver));
106   // If the number of bindings is more than 1, then the |client_old_value| sent
107   // by the clients need not be valid due to races on updates from multiple
108   // clients. So, cache the values in the service. Setting cache mode back to
109   // only keys when the number of bindings goes back to 1 could cause
110   // inconsistency due to the async notifications of mutations to the client
111   // reaching late.
112   if (cache_mode_ == CacheMode::KEYS_ONLY_WHEN_POSSIBLE &&
113       receivers_.size() > 1) {
114     SetCacheMode(CacheMode::KEYS_AND_VALUES);
115   }
116 }
117 
ForkToNewPrefix(const std::string & new_prefix,Delegate * delegate,const Options & options)118 std::unique_ptr<StorageAreaImpl> StorageAreaImpl::ForkToNewPrefix(
119     const std::string& new_prefix,
120     Delegate* delegate,
121     const Options& options) {
122   return ForkToNewPrefix(
123       std::vector<uint8_t>(new_prefix.begin(), new_prefix.end()), delegate,
124       options);
125 }
126 
ForkToNewPrefix(std::vector<uint8_t> new_prefix,Delegate * delegate,const Options & options)127 std::unique_ptr<StorageAreaImpl> StorageAreaImpl::ForkToNewPrefix(
128     std::vector<uint8_t> new_prefix,
129     Delegate* delegate,
130     const Options& options) {
131   auto forked_area = std::make_unique<StorageAreaImpl>(
132       database_, std::move(new_prefix), delegate, options);
133   // If the source map is empty, don't bother hitting disk.
134   if (IsMapLoadedAndEmpty()) {
135     forked_area->InitializeAsEmpty();
136     return forked_area;
137   }
138   forked_area->map_state_ = MapState::LOADING_FROM_FORK;
139 
140   if (IsMapLoaded()) {
141     DoForkOperation(forked_area->weak_ptr_factory_.GetWeakPtr());
142   } else {
143     LoadMap(base::BindOnce(&StorageAreaImpl::DoForkOperation,
144                            weak_ptr_factory_.GetWeakPtr(),
145                            forked_area->weak_ptr_factory_.GetWeakPtr()));
146   }
147   return forked_area;
148 }
149 
CancelAllPendingRequests()150 void StorageAreaImpl::CancelAllPendingRequests() {
151   on_load_complete_tasks_.clear();
152 }
153 
EnableAggressiveCommitDelay()154 void StorageAreaImpl::EnableAggressiveCommitDelay() {
155   s_aggressive_flushing_enabled_ = true;
156 }
157 
ScheduleImmediateCommit(base::OnceClosure callback)158 void StorageAreaImpl::ScheduleImmediateCommit(base::OnceClosure callback) {
159   if (!on_load_complete_tasks_.empty()) {
160     LoadMap(base::BindOnce(&StorageAreaImpl::ScheduleImmediateCommit,
161                            weak_ptr_factory_.GetWeakPtr(),
162                            std::move(callback)));
163     return;
164   }
165 
166   if (!database_ || !commit_batch_) {
167     if (callback)
168       std::move(callback).Run();
169     return;
170   }
171   CommitChanges(std::move(callback));
172 }
173 
OnMemoryDump(const std::string & name,base::trace_event::ProcessMemoryDump * pmd)174 void StorageAreaImpl::OnMemoryDump(const std::string& name,
175                                    base::trace_event::ProcessMemoryDump* pmd) {
176   if (!IsMapLoaded())
177     return;
178 
179   const char* system_allocator_name =
180       base::trace_event::MemoryDumpManager::GetInstance()
181           ->system_allocator_pool_name();
182   if (commit_batch_) {
183     size_t data_size = 0;
184     for (const auto& iter : commit_batch_->changed_values)
185       data_size += iter.first.size() + iter.second.size();
186     for (const auto& key : commit_batch_->changed_keys)
187       data_size += key.size();
188 
189     auto* commit_batch_mad = pmd->CreateAllocatorDump(name + "/commit_batch");
190     commit_batch_mad->AddScalar(
191         base::trace_event::MemoryAllocatorDump::kNameSize,
192         base::trace_event::MemoryAllocatorDump::kUnitsBytes, data_size);
193     if (system_allocator_name)
194       pmd->AddSuballocation(commit_batch_mad->guid(), system_allocator_name);
195   }
196 
197   // Do not add storage map usage if less than 1KB.
198   if (memory_used_ < 1024)
199     return;
200 
201   auto* map_mad = pmd->CreateAllocatorDump(name + "/storage_map");
202   map_mad->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
203                      base::trace_event::MemoryAllocatorDump::kUnitsBytes,
204                      memory_used_);
205   map_mad->AddString("load_state", "",
206                      map_state_ == MapState::LOADED_KEYS_ONLY
207                          ? "keys_only"
208                          : "keys_and_values");
209   if (system_allocator_name)
210     pmd->AddSuballocation(map_mad->guid(), system_allocator_name);
211 }
212 
PurgeMemory()213 void StorageAreaImpl::PurgeMemory() {
214   if (!IsMapLoaded() ||  // We're not using any memory.
215       commit_batch_ ||   // We leave things alone with changes pending.
216       !database_) {  // Don't purge anything if we're not backed by a database.
217     return;
218   }
219 
220   map_state_ = MapState::UNLOADED;
221   memory_used_ = 0;
222   keys_only_map_.clear();
223   keys_values_map_.clear();
224 }
225 
SetCacheModeForTesting(CacheMode cache_mode)226 void StorageAreaImpl::SetCacheModeForTesting(CacheMode cache_mode) {
227   SetCacheMode(cache_mode);
228 }
229 
AddObserver(mojo::PendingRemote<blink::mojom::StorageAreaObserver> observer)230 void StorageAreaImpl::AddObserver(
231     mojo::PendingRemote<blink::mojom::StorageAreaObserver> observer) {
232   mojo::Remote<blink::mojom::StorageAreaObserver> observer_remote(
233       std::move(observer));
234   if (cache_mode_ == CacheMode::KEYS_AND_VALUES)
235     observer_remote->ShouldSendOldValueOnMutations(false);
236   observers_.Add(std::move(observer_remote));
237 }
238 
Put(const std::vector<uint8_t> & key,const std::vector<uint8_t> & value,const base::Optional<std::vector<uint8_t>> & client_old_value,const std::string & source,PutCallback callback)239 void StorageAreaImpl::Put(
240     const std::vector<uint8_t>& key,
241     const std::vector<uint8_t>& value,
242     const base::Optional<std::vector<uint8_t>>& client_old_value,
243     const std::string& source,
244     PutCallback callback) {
245   if (!IsMapLoaded() || IsMapUpgradeNeeded()) {
246     LoadMap(base::BindOnce(&StorageAreaImpl::Put,
247                            weak_ptr_factory_.GetWeakPtr(), key, value,
248                            client_old_value, source, std::move(callback)));
249     return;
250   }
251 
252   size_t old_item_size = 0;
253   size_t old_item_memory = 0;
254   size_t new_item_memory = 0;
255   base::Optional<std::vector<uint8_t>> old_value;
256   if (map_state_ == MapState::LOADED_KEYS_ONLY) {
257     KeysOnlyMap::const_iterator found = keys_only_map_.find(key);
258     if (found != keys_only_map_.end()) {
259       if (client_old_value &&
260           client_old_value.value().size() == found->second) {
261         if (client_old_value == value) {
262           // NOTE: Even though the key is not changing, we have to acknowledge
263           // the change request, as clients may rely on this acknowledgement for
264           // caching behavior.
265           for (const auto& observer : observers_)
266             observer->KeyChanged(key, value, value, source);
267           std::move(callback).Run(true);  // Key already has this value.
268           return;
269         }
270         old_value = client_old_value.value();
271       } else {
272 #if DCHECK_IS_ON()
273         // If |client_old_value| was not provided or if it's size does not
274         // match, then we still let the change go through. But the notification
275         // sent to clients will not contain old value. This is okay since
276         // currently the only observer to these notification is the client
277         // itself.
278         DVLOG(1) << "Storage area with prefix "
279                  << std::string(prefix_.begin(), prefix_.end())
280                  << ": past value has length of " << found->second << ", but:";
281         if (client_old_value) {
282           DVLOG(1) << "Given past value has incorrect length of "
283                    << client_old_value.value().size();
284         } else {
285           DVLOG(1) << "No given past value was provided.";
286         }
287 #endif
288       }
289       old_item_size = key.size() + found->second;
290       old_item_memory = key.size() + sizeof(size_t);
291     }
292     new_item_memory = key.size() + sizeof(size_t);
293   } else {
294     DCHECK_EQ(map_state_, MapState::LOADED_KEYS_AND_VALUES);
295     auto found = keys_values_map_.find(key);
296     if (found != keys_values_map_.end()) {
297       if (found->second == value) {
298         // NOTE: Even though the key is not changing, we have to acknowledge
299         // the change request, as clients may rely on this acknowledgement for
300         // caching behavior.
301         for (const auto& observer : observers_)
302           observer->KeyChanged(key, value, value, source);
303         std::move(callback).Run(true);  // Key already has this value.
304         return;
305       }
306       old_value = std::move(found->second);
307       old_item_size = key.size() + old_value.value().size();
308       old_item_memory = old_item_size;
309     }
310     new_item_memory = key.size() + value.size();
311   }
312 
313   size_t new_item_size = key.size() + value.size();
314   size_t new_storage_used = storage_used_ - old_item_size + new_item_size;
315 
316   // Only check quota if the size is increasing, this allows
317   // shrinking changes to pre-existing maps that are over budget.
318   if (new_item_size > old_item_size && new_storage_used > max_size_) {
319     if (map_state_ == MapState::LOADED_KEYS_ONLY) {
320       receivers_.ReportBadMessage(
321           "The quota in browser cannot exceed when there is only one "
322           "renderer.");
323     } else {
324       for (const auto& observer : observers_)
325         observer->KeyChangeFailed(key, source);
326       std::move(callback).Run(false);
327     }
328     return;
329   }
330 
331   if (database_) {
332     CreateCommitBatchIfNeeded();
333     // No need to store values in |commit_batch_| if values are already
334     // available in |keys_values_map_|, since CommitChanges() will take values
335     // from there.
336     if (map_state_ == MapState::LOADED_KEYS_ONLY)
337       commit_batch_->changed_values[key] = value;
338     else
339       commit_batch_->changed_keys.insert(key);
340   }
341 
342   if (map_state_ == MapState::LOADED_KEYS_ONLY)
343     keys_only_map_[key] = value.size();
344   else
345     keys_values_map_[key] = value;
346 
347   storage_used_ = new_storage_used;
348   memory_used_ += new_item_memory - old_item_memory;
349   for (const auto& observer : observers_)
350     observer->KeyChanged(key, value, old_value, source);
351   std::move(callback).Run(true);
352 }
353 
Delete(const std::vector<uint8_t> & key,const base::Optional<std::vector<uint8_t>> & client_old_value,const std::string & source,DeleteCallback callback)354 void StorageAreaImpl::Delete(
355     const std::vector<uint8_t>& key,
356     const base::Optional<std::vector<uint8_t>>& client_old_value,
357     const std::string& source,
358     DeleteCallback callback) {
359   // Map upgrade check is required because the cache state could be changed
360   // due to multiple bindings, and when multiple bindings are involved the
361   // |client_old_value| can race. Thus any changes require checking for an
362   // upgrade.
363   if (!IsMapLoaded() || IsMapUpgradeNeeded()) {
364     LoadMap(base::BindOnce(&StorageAreaImpl::Delete,
365                            weak_ptr_factory_.GetWeakPtr(), key,
366                            client_old_value, source, std::move(callback)));
367     return;
368   }
369 
370   if (database_)
371     CreateCommitBatchIfNeeded();
372 
373   std::vector<uint8_t> old_value;
374   if (map_state_ == MapState::LOADED_KEYS_ONLY) {
375     KeysOnlyMap::const_iterator found = keys_only_map_.find(key);
376     if (found == keys_only_map_.end()) {
377       // NOTE: Even though the key is not changing, we have to acknowledge
378       // the change request, as clients may rely on this acknowledgement for
379       // caching behavior.
380       for (const auto& observer : observers_)
381         observer->KeyDeleted(key, base::nullopt, source);
382       std::move(callback).Run(true);
383       return;
384     }
385     if (client_old_value && client_old_value.value().size() == found->second) {
386       old_value = client_old_value.value();
387     } else {
388 #if DCHECK_IS_ON()
389       // If |client_old_value| was not provided or if it's size does not match,
390       // then we still let the change go through. But the notification sent to
391       // clients will not contain old value. This is okay since currently the
392       // only observer to these notification is the client itself.
393       DVLOG(1) << "Storage area with prefix "
394                << std::string(prefix_.begin(), prefix_.end())
395                << ": past value has length of " << found->second << ", but:";
396       if (client_old_value) {
397         DVLOG(1) << "Given past value has incorrect length of "
398                  << client_old_value.value().size();
399       } else {
400         DVLOG(1) << "No given past value was provided.";
401       }
402 #endif
403     }
404     storage_used_ -= key.size() + found->second;
405     keys_only_map_.erase(found);
406     memory_used_ -= key.size() + sizeof(size_t);
407     if (commit_batch_)
408       commit_batch_->changed_values[key] = std::vector<uint8_t>();
409   } else {
410     DCHECK_EQ(map_state_, MapState::LOADED_KEYS_AND_VALUES);
411     auto found = keys_values_map_.find(key);
412     if (found == keys_values_map_.end()) {
413       // NOTE: Even though the key is not changing, we have to acknowledge
414       // the change request, as clients may rely on this acknowledgement for
415       // caching behavior.
416       for (const auto& observer : observers_)
417         observer->KeyDeleted(key, base::nullopt, source);
418       std::move(callback).Run(true);
419       return;
420     }
421     old_value.swap(found->second);
422     keys_values_map_.erase(found);
423     memory_used_ -= key.size() + old_value.size();
424     storage_used_ -= key.size() + old_value.size();
425     if (commit_batch_)
426       commit_batch_->changed_keys.insert(key);
427   }
428 
429   for (auto& observer : observers_)
430     observer->KeyDeleted(key, old_value, source);
431   std::move(callback).Run(true);
432 }
433 
DeleteAll(const std::string & source,mojo::PendingRemote<blink::mojom::StorageAreaObserver> new_observer,DeleteAllCallback callback)434 void StorageAreaImpl::DeleteAll(
435     const std::string& source,
436     mojo::PendingRemote<blink::mojom::StorageAreaObserver> new_observer,
437     DeleteAllCallback callback) {
438   // Don't check if a map upgrade is needed here and instead just create an
439   // empty map ourself.
440   if (!IsMapLoaded()) {
441     LoadMap(base::BindOnce(&StorageAreaImpl::DeleteAll,
442                            weak_ptr_factory_.GetWeakPtr(), source,
443                            std::move(new_observer), std::move(callback)));
444     return;
445   }
446 
447   bool already_empty = IsMapLoadedAndEmpty();
448 
449   // Upgrade map state if needed.
450   if (IsMapUpgradeNeeded()) {
451     DCHECK(keys_values_map_.empty());
452     map_state_ = MapState::LOADED_KEYS_AND_VALUES;
453   }
454 
455   if (new_observer)
456     AddObserver(std::move(new_observer));
457 
458   if (already_empty) {
459     for (const auto& observer : observers_)
460       observer->AllDeleted(/*was_nonempty=*/false, source);
461     std::move(callback).Run(true);
462     return;
463   }
464 
465   if (database_) {
466     CreateCommitBatchIfNeeded();
467     commit_batch_->clear_all_first = true;
468     commit_batch_->changed_values.clear();
469     commit_batch_->changed_keys.clear();
470   }
471 
472   keys_only_map_.clear();
473   keys_values_map_.clear();
474 
475   storage_used_ = 0;
476   memory_used_ = 0;
477   for (const auto& observer : observers_)
478     observer->AllDeleted(/*was_nonempty=*/true, source);
479   std::move(callback).Run(/*success=*/true);
480 }
481 
Get(const std::vector<uint8_t> & key,GetCallback callback)482 void StorageAreaImpl::Get(const std::vector<uint8_t>& key,
483                           GetCallback callback) {
484   // TODO(ssid): Remove this method since it is not supported in only keys mode,
485   // crbug.com/764127.
486   if (cache_mode_ == CacheMode::KEYS_ONLY_WHEN_POSSIBLE) {
487     NOTREACHED();
488     return;
489   }
490   if (!IsMapLoaded() || IsMapUpgradeNeeded()) {
491     LoadMap(base::BindOnce(&StorageAreaImpl::Get,
492                            weak_ptr_factory_.GetWeakPtr(), key,
493                            std::move(callback)));
494     return;
495   }
496 
497   auto found = keys_values_map_.find(key);
498   if (found == keys_values_map_.end()) {
499     std::move(callback).Run(false, std::vector<uint8_t>());
500     return;
501   }
502   std::move(callback).Run(true, found->second);
503 }
504 
GetAll(mojo::PendingRemote<blink::mojom::StorageAreaObserver> new_observer,GetAllCallback callback)505 void StorageAreaImpl::GetAll(
506     mojo::PendingRemote<blink::mojom::StorageAreaObserver> new_observer,
507     GetAllCallback callback) {
508   // If the map is keys-only and empty, then no loading is necessary.
509   if (IsMapLoadedAndEmpty()) {
510     std::move(callback).Run(std::vector<blink::mojom::KeyValuePtr>());
511     if (new_observer)
512       AddObserver(std::move(new_observer));
513     return;
514   }
515 
516   // The map must always be loaded for the KEYS_ONLY_WHEN_POSSIBLE mode.
517   if (map_state_ != MapState::LOADED_KEYS_AND_VALUES) {
518     LoadMap(base::BindOnce(&StorageAreaImpl::GetAll,
519                            weak_ptr_factory_.GetWeakPtr(),
520                            std::move(new_observer), std::move(callback)));
521     return;
522   }
523 
524   std::vector<blink::mojom::KeyValuePtr> all;
525   for (const auto& it : keys_values_map_) {
526     auto kv = blink::mojom::KeyValue::New();
527     kv->key = it.first;
528     kv->value = it.second;
529     all.push_back(std::move(kv));
530   }
531   std::move(callback).Run(std::move(all));
532   if (new_observer)
533     AddObserver(std::move(new_observer));
534 }
535 
SetCacheMode(CacheMode cache_mode)536 void StorageAreaImpl::SetCacheMode(CacheMode cache_mode) {
537   if (cache_mode_ == cache_mode ||
538       (!database_ && cache_mode == CacheMode::KEYS_ONLY_WHEN_POSSIBLE)) {
539     return;
540   }
541   cache_mode_ = cache_mode;
542   bool should_send_values = cache_mode == CacheMode::KEYS_ONLY_WHEN_POSSIBLE;
543   for (auto& observer : observers_)
544     observer->ShouldSendOldValueOnMutations(should_send_values);
545 
546   // If the |keys_only_map_| is loaded and desired state needs values, no point
547   // keeping around the map since the next change would require reload. On the
548   // other hand if only keys are desired, the keys and values map can still be
549   // used. Consider not unloading when the map is still useful.
550   UnloadMapIfPossible();
551 }
552 
OnConnectionError()553 void StorageAreaImpl::OnConnectionError() {
554   if (!receivers_.empty())
555     return;
556   // If any tasks are waiting for load to complete, delay calling the
557   // no_bindings_callback_ until all those tasks have completed.
558   if (!on_load_complete_tasks_.empty())
559     return;
560   delegate_->OnNoBindings();
561 }
562 
LoadMap(base::OnceClosure completion_callback)563 void StorageAreaImpl::LoadMap(base::OnceClosure completion_callback) {
564   DCHECK_NE(map_state_, MapState::LOADED_KEYS_AND_VALUES);
565   DCHECK(keys_values_map_.empty());
566 
567   // Current commit batch needs to be applied before re-loading the map. The
568   // re-load of map occurs only when GetAll() is called or CacheMode is set to
569   // keys and values, and the |keys_only_map_| is already loaded. In this case
570   // commit batch needs to be committed before reading the database.
571   if (map_state_ == MapState::LOADED_KEYS_ONLY) {
572     DCHECK(on_load_complete_tasks_.empty());
573     DCHECK(database_);
574     if (commit_batch_)
575       CommitChanges();
576     // Make sure the keys only map is not used when on load tasks are in queue.
577     // The changes to the area will be queued to on load tasks.
578     keys_only_map_.clear();
579     map_state_ = MapState::UNLOADED;
580   }
581 
582   on_load_complete_tasks_.push_back(std::move(completion_callback));
583   if (map_state_ == MapState::LOADING_FROM_DATABASE ||
584       map_state_ == MapState::LOADING_FROM_FORK) {
585     return;
586   }
587 
588   map_state_ = MapState::LOADING_FROM_DATABASE;
589 
590   if (!database_) {
591     OnMapLoaded(leveldb::Status::IOError(""), {});
592     return;
593   }
594 
595   database_->RunDatabaseTask(
596       base::BindOnce(
597           [](const DomStorageDatabase::Key& prefix,
598              const DomStorageDatabase& db) {
599             std::vector<DomStorageDatabase::KeyValuePair> data;
600             leveldb::Status status = db.GetPrefixed(prefix, &data);
601             return std::make_tuple(status, std::move(data));
602           },
603           prefix_),
604       base::BindOnce(&StorageAreaImpl::OnMapLoaded,
605                      weak_ptr_factory_.GetWeakPtr()));
606 }
607 
OnMapLoaded(leveldb::Status status,std::vector<DomStorageDatabase::KeyValuePair> data)608 void StorageAreaImpl::OnMapLoaded(
609     leveldb::Status status,
610     std::vector<DomStorageDatabase::KeyValuePair> data) {
611   DCHECK(keys_values_map_.empty());
612   DCHECK_EQ(map_state_, MapState::LOADING_FROM_DATABASE);
613 
614   if (data.empty() && status.ok()) {
615     delegate_->MigrateData(base::BindOnce(&StorageAreaImpl::OnGotMigrationData,
616                                           weak_ptr_factory_.GetWeakPtr()));
617     return;
618   }
619 
620   keys_only_map_.clear();
621   map_state_ = MapState::LOADED_KEYS_AND_VALUES;
622 
623   keys_values_map_.clear();
624   for (auto& entry : data) {
625     DCHECK_GE(entry.key.size(), prefix_.size());
626     keys_values_map_[DomStorageDatabase::Key(entry.key.begin() + prefix_.size(),
627                                              entry.key.end())] =
628         std::move(entry.value);
629   }
630   CalculateStorageAndMemoryUsed();
631 
632   std::vector<Change> changes = delegate_->FixUpData(keys_values_map_);
633   if (!changes.empty()) {
634     DCHECK(database_);
635     CreateCommitBatchIfNeeded();
636     for (auto& change : changes) {
637       auto it = keys_values_map_.find(change.first);
638       if (!change.second) {
639         DCHECK(it != keys_values_map_.end());
640         keys_values_map_.erase(it);
641       } else {
642         if (it != keys_values_map_.end()) {
643           it->second = std::move(*change.second);
644         } else {
645           keys_values_map_[change.first] = std::move(*change.second);
646         }
647       }
648       // No need to store values in |commit_batch_| if values are already
649       // available in |keys_values_map_|, since CommitChanges() will take values
650       // from there.
651       commit_batch_->changed_keys.insert(std::move(change.first));
652     }
653     CalculateStorageAndMemoryUsed();
654     CommitChanges();
655   }
656 
657   // We proceed without using a backing store, nothing will be persisted but the
658   // class is functional for the lifetime of the object.
659   delegate_->OnMapLoaded(status);
660   if (!status.ok()) {
661     database_ = nullptr;
662     SetCacheMode(CacheMode::KEYS_AND_VALUES);
663   }
664 
665   if (on_load_callback_for_testing_)
666     std::move(on_load_callback_for_testing_).Run();
667 
668   OnLoadComplete();
669 }
670 
OnGotMigrationData(std::unique_ptr<ValueMap> data)671 void StorageAreaImpl::OnGotMigrationData(std::unique_ptr<ValueMap> data) {
672   keys_only_map_.clear();
673   keys_values_map_ = data ? std::move(*data) : ValueMap();
674   map_state_ = MapState::LOADED_KEYS_AND_VALUES;
675   CalculateStorageAndMemoryUsed();
676   delegate_->OnMapLoaded(leveldb::Status::OK());
677 
678   if (database_ && !empty()) {
679     CreateCommitBatchIfNeeded();
680     // CommitChanges() will take values from |keys_values_map_|.
681     for (const auto& it : keys_values_map_)
682       commit_batch_->changed_keys.insert(it.first);
683     CommitChanges();
684   }
685   OnLoadComplete();
686 }
687 
CalculateStorageAndMemoryUsed()688 void StorageAreaImpl::CalculateStorageAndMemoryUsed() {
689   memory_used_ = 0;
690   storage_used_ = 0;
691 
692   for (auto& it : keys_values_map_)
693     memory_used_ += it.first.size() + it.second.size();
694   storage_used_ = memory_used_;
695 
696   for (auto& it : keys_only_map_) {
697     memory_used_ += it.first.size() + sizeof(size_t);
698     storage_used_ += it.first.size() + it.second;
699   }
700 }
701 
OnLoadComplete()702 void StorageAreaImpl::OnLoadComplete() {
703   DCHECK(IsMapLoaded());
704 
705   std::vector<base::OnceClosure> tasks;
706   on_load_complete_tasks_.swap(tasks);
707   for (auto it = tasks.begin(); it != tasks.end(); ++it) {
708     // Some tasks (like GetAll) can require a reload if they need a different
709     // map type. If this happens, stop our task execution. Appending tasks is
710     // required (instead of replacing) because the task that required the
711     // reload-requesting-task put itself on the task queue and it still needs
712     // to be executed before the rest of the tasks.
713     if (!IsMapLoaded()) {
714       on_load_complete_tasks_.reserve(on_load_complete_tasks_.size() +
715                                       (tasks.end() - it));
716       std::move(it, tasks.end(), std::back_inserter(on_load_complete_tasks_));
717       return;
718     }
719     std::move(*it).Run();
720   }
721 
722   // Call before |OnNoBindings| as delegate can destroy this object.
723   UnloadMapIfPossible();
724 
725   // We might need to call the no_bindings_callback_ here if bindings became
726   // empty while waiting for load to complete.
727   if (receivers_.empty())
728     delegate_->OnNoBindings();
729 }
730 
CreateCommitBatchIfNeeded()731 void StorageAreaImpl::CreateCommitBatchIfNeeded() {
732   if (commit_batch_)
733     return;
734   DCHECK(database_);
735 
736   commit_batch_.reset(new CommitBatch());
737   StartCommitTimer();
738 }
739 
StartCommitTimer()740 void StorageAreaImpl::StartCommitTimer() {
741   if (!commit_batch_)
742     return;
743 
744   // Start a timer to commit any changes that accrue in the batch, but only if
745   // no commits are currently in flight. In that case the timer will be
746   // started after the commits have happened.
747   if (commit_batches_in_flight_)
748     return;
749 
750   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
751       FROM_HERE,
752       base::BindOnce(&StorageAreaImpl::CommitChanges,
753                      weak_ptr_factory_.GetWeakPtr(), base::OnceClosure()),
754       ComputeCommitDelay());
755 }
756 
ComputeCommitDelay() const757 base::TimeDelta StorageAreaImpl::ComputeCommitDelay() const {
758   if (s_aggressive_flushing_enabled_)
759     return base::TimeDelta::FromSeconds(1);
760 
761   base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time_;
762   base::TimeDelta delay =
763       std::max(default_commit_delay_,
764                std::max(commit_rate_limiter_.ComputeDelayNeeded(elapsed_time),
765                         data_rate_limiter_.ComputeDelayNeeded(elapsed_time)));
766   // TODO(mek): Rename histogram to match class name, or eliminate histogram
767   // entirely.
768   UMA_HISTOGRAM_LONG_TIMES("LevelDBWrapper.CommitDelay", delay);
769   return delay;
770 }
771 
CommitChanges(base::OnceClosure callback)772 void StorageAreaImpl::CommitChanges(base::OnceClosure callback) {
773   // Note: commit_batch_ may be null if ScheduleImmediateCommit was called
774   // after a delayed commit task was scheduled.
775   if (!commit_batch_) {
776     if (callback)
777       std::move(callback).Run();
778     return;
779   }
780 
781   DCHECK(database_);
782   DCHECK(IsMapLoaded()) << static_cast<int>(map_state_);
783 
784   commit_rate_limiter_.add_samples(1);
785 
786   // Commit all our changes in a single batch.
787   struct Commit {
788     DomStorageDatabase::Key prefix;
789     bool clear_all_first;
790     std::vector<DomStorageDatabase::KeyValuePair> entries_to_add;
791     std::vector<DomStorageDatabase::Key> keys_to_delete;
792     base::Optional<DomStorageDatabase::Key> copy_to_prefix;
793   };
794 
795   Commit commit;
796   commit.prefix = prefix_;
797   commit.clear_all_first = commit_batch_->clear_all_first;
798   delegate_->PrepareToCommit(&commit.entries_to_add, &commit.keys_to_delete);
799 
800   const bool has_changes = !commit.entries_to_add.empty() ||
801                            !commit.keys_to_delete.empty() ||
802                            !commit_batch_->changed_values.empty() ||
803                            !commit_batch_->changed_keys.empty();
804   size_t data_size = 0;
805   if (map_state_ == MapState::LOADED_KEYS_AND_VALUES) {
806     DCHECK(commit_batch_->changed_values.empty())
807         << "Map state and commit state out of sync.";
808     for (const auto& key : commit_batch_->changed_keys) {
809       data_size += key.size();
810       DomStorageDatabase::Key prefixed_key;
811       prefixed_key.reserve(prefix_.size() + key.size());
812       prefixed_key.insert(prefixed_key.end(), prefix_.begin(), prefix_.end());
813       prefixed_key.insert(prefixed_key.end(), key.begin(), key.end());
814       auto it = keys_values_map_.find(key);
815       if (it != keys_values_map_.end()) {
816         data_size += it->second.size();
817         commit.entries_to_add.emplace_back(std::move(prefixed_key), it->second);
818       } else {
819         commit.keys_to_delete.push_back(std::move(prefixed_key));
820       }
821     }
822   } else {
823     DCHECK(commit_batch_->changed_keys.empty())
824         << "Map state and commit state out of sync.";
825     DCHECK_EQ(map_state_, MapState::LOADED_KEYS_ONLY);
826     for (auto& entry : commit_batch_->changed_values) {
827       const auto& key = entry.first;
828       data_size += key.size();
829       DomStorageDatabase::Key prefixed_key;
830       prefixed_key.reserve(prefix_.size() + key.size());
831       prefixed_key.insert(prefixed_key.end(), prefix_.begin(), prefix_.end());
832       prefixed_key.insert(prefixed_key.end(), key.begin(), key.end());
833       auto it = keys_only_map_.find(key);
834       if (it != keys_only_map_.end()) {
835         data_size += entry.second.size();
836         commit.entries_to_add.emplace_back(std::move(prefixed_key),
837                                            std::move(entry.second));
838       } else {
839         commit.keys_to_delete.push_back(std::move(prefixed_key));
840       }
841     }
842   }
843   // Schedule the copy, and ignore if |clear_all_first| is specified and there
844   // are no changing keys.
845   if (commit_batch_->copy_to_prefix) {
846     DCHECK(!has_changes);
847     DCHECK(!commit_batch_->clear_all_first);
848     commit.copy_to_prefix = std::move(commit_batch_->copy_to_prefix);
849   }
850   commit_batch_.reset();
851 
852   data_rate_limiter_.add_samples(data_size);
853 
854   ++commit_batches_in_flight_;
855 
856   database_->RunDatabaseTask(
857       base::BindOnce(
858           [](Commit commit, const DomStorageDatabase& db) {
859             leveldb::WriteBatch batch;
860             if (commit.clear_all_first)
861               db.DeletePrefixed(commit.prefix, &batch);
862             for (const auto& entry : commit.entries_to_add) {
863               batch.Put(leveldb_env::MakeSlice(entry.key),
864                         leveldb_env::MakeSlice(entry.value));
865             }
866             for (const auto& key : commit.keys_to_delete)
867               batch.Delete(leveldb_env::MakeSlice(key));
868             if (commit.copy_to_prefix) {
869               db.CopyPrefixed(commit.prefix, commit.copy_to_prefix.value(),
870                               &batch);
871             }
872             return db.Commit(&batch);
873           },
874           std::move(commit)),
875       base::BindOnce(&StorageAreaImpl::OnCommitComplete,
876                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
877 }
878 
OnCommitComplete(base::OnceClosure callback,leveldb::Status status)879 void StorageAreaImpl::OnCommitComplete(base::OnceClosure callback,
880                                        leveldb::Status status) {
881   has_committed_data_ = true;
882   --commit_batches_in_flight_;
883   StartCommitTimer();
884 
885   if (!status.ok())
886     SetCacheMode(CacheMode::KEYS_AND_VALUES);
887 
888   // Call before |DidCommit| as delegate can destroy this object.
889   UnloadMapIfPossible();
890 
891   delegate_->DidCommit(status);
892   if (callback)
893     std::move(callback).Run();
894 }
895 
UnloadMapIfPossible()896 void StorageAreaImpl::UnloadMapIfPossible() {
897   // Do not unload the map if:
898   // * The desired cache mode isn't key-only,
899   // * The map isn't a loaded key-value map,
900   // * There are pending tasks waiting on the key-value map being loaded, or
901   // * There is no database connection.
902   // * We have commit batches in-flight.
903   // * We haven't committed data yet.
904   if (cache_mode_ != CacheMode::KEYS_ONLY_WHEN_POSSIBLE ||
905       map_state_ != MapState::LOADED_KEYS_AND_VALUES ||
906       has_pending_load_tasks() || !database_ || commit_batches_in_flight_ > 0 ||
907       !has_committed_data_) {
908     return;
909   }
910 
911   keys_only_map_.clear();
912   memory_used_ = 0;
913   for (auto& it : keys_values_map_) {
914     keys_only_map_.insert(std::make_pair(it.first, it.second.size()));
915   }
916   if (commit_batch_) {
917     for (const auto& key : commit_batch_->changed_keys) {
918       auto value_it = keys_values_map_.find(key);
919       commit_batch_->changed_values[key] = value_it == keys_values_map_.end()
920                                                ? std::vector<uint8_t>()
921                                                : std::move(value_it->second);
922     }
923     commit_batch_->changed_keys.clear();
924   }
925 
926   keys_values_map_.clear();
927   map_state_ = MapState::LOADED_KEYS_ONLY;
928 
929   CalculateStorageAndMemoryUsed();
930 }
931 
DoForkOperation(const base::WeakPtr<StorageAreaImpl> & forked_area)932 void StorageAreaImpl::DoForkOperation(
933     const base::WeakPtr<StorageAreaImpl>& forked_area) {
934   if (!forked_area)
935     return;
936 
937   DCHECK(IsMapLoaded());
938   // TODO(dmurph): If these commits fails, then the disk could be in an
939   // inconsistant state. Ideally all further operations will fail and the code
940   // will correctly delete the database?
941   if (database_) {
942     // All changes must be stored in the database before the copy operation.
943     if (has_changes_to_commit())
944       CommitChanges();
945     CreateCommitBatchIfNeeded();
946     commit_batch_->copy_to_prefix = forked_area->prefix_;
947     CommitChanges();
948   }
949 
950   forked_area->OnForkStateLoaded(database_ != nullptr, keys_values_map_,
951                                  keys_only_map_);
952 }
953 
OnForkStateLoaded(bool database_enabled,const ValueMap & value_map,const KeysOnlyMap & keys_only_map)954 void StorageAreaImpl::OnForkStateLoaded(bool database_enabled,
955                                         const ValueMap& value_map,
956                                         const KeysOnlyMap& keys_only_map) {
957   // This callback can get either the value map or the key only map depending
958   // on parent operations and other things. So handle both.
959   if (!value_map.empty() || keys_only_map.empty()) {
960     keys_values_map_ = value_map;
961     map_state_ = MapState::LOADED_KEYS_AND_VALUES;
962   } else {
963     keys_only_map_ = keys_only_map;
964     map_state_ = MapState::LOADED_KEYS_ONLY;
965   }
966 
967   if (!database_enabled) {
968     database_ = nullptr;
969     cache_mode_ = CacheMode::KEYS_AND_VALUES;
970   }
971 
972   CalculateStorageAndMemoryUsed();
973   OnLoadComplete();
974 }
975 
976 }  // namespace storage
977