1 // Copyright 2018 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/session_storage_area_impl.h"
6 
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/memory/ptr_util.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "components/services/storage/dom_storage/session_storage_data_map.h"
12 #include "mojo/public/cpp/bindings/remote.h"
13 #include "third_party/leveldatabase/env_chromium.h"
14 
15 namespace storage {
16 
SessionStorageAreaImpl(SessionStorageMetadata::NamespaceEntry namespace_entry,url::Origin origin,scoped_refptr<SessionStorageDataMap> data_map,RegisterNewAreaMap register_new_map_callback)17 SessionStorageAreaImpl::SessionStorageAreaImpl(
18     SessionStorageMetadata::NamespaceEntry namespace_entry,
19     url::Origin origin,
20     scoped_refptr<SessionStorageDataMap> data_map,
21     RegisterNewAreaMap register_new_map_callback)
22     : namespace_entry_(namespace_entry),
23       origin_(std::move(origin)),
24       shared_data_map_(std::move(data_map)),
25       register_new_map_callback_(std::move(register_new_map_callback)) {}
26 
~SessionStorageAreaImpl()27 SessionStorageAreaImpl::~SessionStorageAreaImpl() {
28   if (receiver_.is_bound())
29     shared_data_map_->RemoveBindingReference();
30 }
31 
Bind(mojo::PendingReceiver<blink::mojom::StorageArea> receiver)32 void SessionStorageAreaImpl::Bind(
33     mojo::PendingReceiver<blink::mojom::StorageArea> receiver) {
34   if (IsBound()) {
35     receiver_.reset();
36   } else {
37     shared_data_map_->AddBindingReference();
38   }
39   receiver_.Bind(std::move(receiver));
40   receiver_.set_disconnect_handler(base::BindOnce(
41       &SessionStorageAreaImpl::OnConnectionError, base::Unretained(this)));
42 }
43 
Clone(SessionStorageMetadata::NamespaceEntry namespace_entry)44 std::unique_ptr<SessionStorageAreaImpl> SessionStorageAreaImpl::Clone(
45     SessionStorageMetadata::NamespaceEntry namespace_entry) {
46   DCHECK(namespace_entry_ != namespace_entry);
47   return base::WrapUnique(new SessionStorageAreaImpl(
48       namespace_entry, origin_, shared_data_map_, register_new_map_callback_));
49 }
50 
NotifyObserversAllDeleted()51 void SessionStorageAreaImpl::NotifyObserversAllDeleted() {
52   for (auto& observer : observers_) {
53     // Renderer process expects |source| to always be two newline separated
54     // strings. Note that we don't bother checking if storage was actually
55     // empty since that might require loading the map where we otherwise
56     // wouldn't need to. A side-effect is that browser-initiated storage removal
57     // may result in a redundant "clear" StorageEvent on an already-empty
58     // StorageArea.
59     observer->AllDeleted(/*was_nonempty=*/true, "\n");
60   };
61 }
62 
63 // blink::mojom::StorageArea:
AddObserver(mojo::PendingRemote<blink::mojom::StorageAreaObserver> observer)64 void SessionStorageAreaImpl::AddObserver(
65     mojo::PendingRemote<blink::mojom::StorageAreaObserver> observer) {
66   observers_.Add(std::move(observer));
67 }
68 
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)69 void SessionStorageAreaImpl::Put(
70     const std::vector<uint8_t>& key,
71     const std::vector<uint8_t>& value,
72     const base::Optional<std::vector<uint8_t>>& client_old_value,
73     const std::string& source,
74     PutCallback callback) {
75   DCHECK(IsBound());
76   DCHECK_NE(0, shared_data_map_->map_data()->ReferenceCount());
77   if (shared_data_map_->map_data()->ReferenceCount() > 1)
78     CreateNewMap(NewMapType::FORKED, base::nullopt);
79   shared_data_map_->storage_area()->Put(key, value, client_old_value, source,
80                                         std::move(callback));
81 }
82 
Delete(const std::vector<uint8_t> & key,const base::Optional<std::vector<uint8_t>> & client_old_value,const std::string & source,DeleteCallback callback)83 void SessionStorageAreaImpl::Delete(
84     const std::vector<uint8_t>& key,
85     const base::Optional<std::vector<uint8_t>>& client_old_value,
86     const std::string& source,
87     DeleteCallback callback) {
88   DCHECK(IsBound());
89   DCHECK_NE(0, shared_data_map_->map_data()->ReferenceCount());
90   if (shared_data_map_->map_data()->ReferenceCount() > 1)
91     CreateNewMap(NewMapType::FORKED, base::nullopt);
92   shared_data_map_->storage_area()->Delete(key, client_old_value, source,
93                                            std::move(callback));
94 }
95 
DeleteAll(const std::string & source,mojo::PendingRemote<blink::mojom::StorageAreaObserver> new_observer,DeleteAllCallback callback)96 void SessionStorageAreaImpl::DeleteAll(
97     const std::string& source,
98     mojo::PendingRemote<blink::mojom::StorageAreaObserver> new_observer,
99     DeleteAllCallback callback) {
100   // Note: This can be called by the Clear Browsing Data flow, and thus doesn't
101   // have to be bound.
102   if (shared_data_map_->map_data()->ReferenceCount() > 1) {
103     CreateNewMap(NewMapType::EMPTY_FROM_DELETE_ALL, source);
104     if (new_observer)
105       AddObserver(std::move(new_observer));
106     std::move(callback).Run(true);
107     return;
108   }
109   shared_data_map_->storage_area()->DeleteAll(
110       source, /*new_observer=*/mojo::NullRemote(),
111       base::BindOnce(&SessionStorageAreaImpl::OnDeleteAllResult,
112                      weak_ptr_factory_.GetWeakPtr(), std::move(new_observer),
113                      std::move(callback)));
114 }
115 
Get(const std::vector<uint8_t> & key,GetCallback callback)116 void SessionStorageAreaImpl::Get(const std::vector<uint8_t>& key,
117                                  GetCallback callback) {
118   DCHECK(IsBound());
119   DCHECK_NE(0, shared_data_map_->map_data()->ReferenceCount());
120   shared_data_map_->storage_area()->Get(key, std::move(callback));
121 }
122 
GetAll(mojo::PendingRemote<blink::mojom::StorageAreaObserver> new_observer,GetAllCallback callback)123 void SessionStorageAreaImpl::GetAll(
124     mojo::PendingRemote<blink::mojom::StorageAreaObserver> new_observer,
125     GetAllCallback callback) {
126   DCHECK(IsBound());
127   DCHECK_NE(0, shared_data_map_->map_data()->ReferenceCount());
128   shared_data_map_->storage_area()->GetAll(
129       /*new_observer=*/mojo::NullRemote(),
130       base::BindOnce(&SessionStorageAreaImpl::OnGetAllResult,
131                      weak_ptr_factory_.GetWeakPtr(), std::move(new_observer),
132                      std::move(callback)));
133 }
134 
FlushForTesting()135 void SessionStorageAreaImpl::FlushForTesting() {
136   receiver_.FlushForTesting();
137 }
138 
139 // Note: this can be called after invalidation of the |namespace_entry_|.
OnConnectionError()140 void SessionStorageAreaImpl::OnConnectionError() {
141   shared_data_map_->RemoveBindingReference();
142   // Make sure we totally unbind the receiver - this doesn't seem to happen
143   // automatically on connection error. The bound status is used in the
144   // destructor to know if |RemoveBindingReference| was already called.
145   if (receiver_.is_bound())
146     receiver_.reset();
147 }
148 
OnGetAllResult(mojo::PendingRemote<blink::mojom::StorageAreaObserver> new_observer,GetAllCallback callback,std::vector<blink::mojom::KeyValuePtr> entries)149 void SessionStorageAreaImpl::OnGetAllResult(
150     mojo::PendingRemote<blink::mojom::StorageAreaObserver> new_observer,
151     GetAllCallback callback,
152     std::vector<blink::mojom::KeyValuePtr> entries) {
153   std::move(callback).Run(std::move(entries));
154   if (new_observer)
155     AddObserver(std::move(new_observer));
156 }
157 
OnDeleteAllResult(mojo::PendingRemote<blink::mojom::StorageAreaObserver> new_observer,DeleteAllCallback callback,bool was_nonempty)158 void SessionStorageAreaImpl::OnDeleteAllResult(
159     mojo::PendingRemote<blink::mojom::StorageAreaObserver> new_observer,
160     DeleteAllCallback callback,
161     bool was_nonempty) {
162   std::move(callback).Run(true);
163   if (new_observer)
164     AddObserver(std::move(new_observer));
165 }
166 
CreateNewMap(NewMapType map_type,const base::Optional<std::string> & delete_all_source)167 void SessionStorageAreaImpl::CreateNewMap(
168     NewMapType map_type,
169     const base::Optional<std::string>& delete_all_source) {
170   bool bound = IsBound();
171   if (bound)
172     shared_data_map_->RemoveBindingReference();
173   switch (map_type) {
174     case NewMapType::FORKED:
175       shared_data_map_ = SessionStorageDataMap::CreateClone(
176           shared_data_map_->listener(),
177           register_new_map_callback_.Run(namespace_entry_, origin_),
178           shared_data_map_);
179       break;
180     case NewMapType::EMPTY_FROM_DELETE_ALL: {
181       // The code optimizes the 'delete all' for shared maps by just creating
182       // a new map instead of forking. However, we still need the observers to
183       // be correctly called. To do that, we manually call them here.
184       shared_data_map_ = SessionStorageDataMap::CreateEmpty(
185           shared_data_map_->listener(),
186           register_new_map_callback_.Run(namespace_entry_, origin_),
187           shared_data_map_->storage_area()->database());
188       break;
189     }
190   }
191   if (bound)
192     shared_data_map_->AddBindingReference();
193 }
194 
195 }  // namespace storage
196