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