1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "SessionStorage.h"
8
9 #include "SessionStorageCache.h"
10 #include "SessionStorageManager.h"
11
12 #include "mozilla/dom/StorageBinding.h"
13 #include "mozilla/Preferences.h"
14 #include "nsContentUtils.h"
15 #include "nsIPrincipal.h"
16 #include "nsPIDOMWindow.h"
17 #include "nsThreadUtils.h"
18
19 namespace mozilla {
20 namespace dom {
21
22 NS_IMPL_CYCLE_COLLECTION_INHERITED(SessionStorage, Storage, mManager);
23
24 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SessionStorage)
NS_INTERFACE_MAP_END_INHERITING(Storage)25 NS_INTERFACE_MAP_END_INHERITING(Storage)
26
27 NS_IMPL_ADDREF_INHERITED(SessionStorage, Storage)
28 NS_IMPL_RELEASE_INHERITED(SessionStorage, Storage)
29
30 SessionStorage::SessionStorage(nsPIDOMWindowInner* aWindow,
31 nsIPrincipal* aPrincipal,
32 nsIPrincipal* aStoragePrincipal,
33 SessionStorageCache* aCache,
34 SessionStorageManager* aManager,
35 const nsAString& aDocumentURI, bool aIsPrivate)
36 : Storage(aWindow, aPrincipal, aStoragePrincipal),
37 mCache(aCache),
38 mManager(aManager),
39 mDocumentURI(aDocumentURI),
40 mIsPrivate(aIsPrivate),
41 mHasPendingStableStateCallback(false) {
42 MOZ_ASSERT(aCache);
43 }
44
45 SessionStorage::~SessionStorage() = default;
46
GetOriginQuotaUsage() const47 int64_t SessionStorage::GetOriginQuotaUsage() const {
48 nsresult rv = EnsureCacheLoadedOrCloned();
49 if (NS_WARN_IF(NS_FAILED(rv))) {
50 return 0;
51 }
52
53 return mCache->GetOriginQuotaUsage();
54 }
55
GetLength(nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)56 uint32_t SessionStorage::GetLength(nsIPrincipal& aSubjectPrincipal,
57 ErrorResult& aRv) {
58 if (!CanUseStorage(aSubjectPrincipal)) {
59 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
60 return 0;
61 }
62
63 nsresult rv = EnsureCacheLoadedOrCloned();
64 if (NS_WARN_IF(NS_FAILED(rv))) {
65 aRv.Throw(rv);
66 return 0;
67 }
68
69 return mCache->Length();
70 }
71
Key(uint32_t aIndex,nsAString & aResult,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)72 void SessionStorage::Key(uint32_t aIndex, nsAString& aResult,
73 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
74 if (!CanUseStorage(aSubjectPrincipal)) {
75 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
76 return;
77 }
78
79 nsresult rv = EnsureCacheLoadedOrCloned();
80 if (NS_WARN_IF(NS_FAILED(rv))) {
81 aRv.Throw(rv);
82 return;
83 }
84
85 mCache->Key(aIndex, aResult);
86 }
87
GetItem(const nsAString & aKey,nsAString & aResult,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)88 void SessionStorage::GetItem(const nsAString& aKey, nsAString& aResult,
89 nsIPrincipal& aSubjectPrincipal,
90 ErrorResult& aRv) {
91 if (!CanUseStorage(aSubjectPrincipal)) {
92 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
93 return;
94 }
95
96 nsresult rv = EnsureCacheLoadedOrCloned();
97 if (NS_WARN_IF(NS_FAILED(rv))) {
98 aRv.Throw(rv);
99 return;
100 }
101
102 mCache->GetItem(aKey, aResult);
103 }
104
GetSupportedNames(nsTArray<nsString> & aKeys)105 void SessionStorage::GetSupportedNames(nsTArray<nsString>& aKeys) {
106 if (!CanUseStorage(*nsContentUtils::SubjectPrincipal())) {
107 // return just an empty array
108 aKeys.Clear();
109 return;
110 }
111
112 nsresult rv = EnsureCacheLoadedOrCloned();
113 if (NS_WARN_IF(NS_FAILED(rv))) {
114 // return just an empty array
115 aKeys.Clear();
116 return;
117 }
118
119 mCache->GetKeys(aKeys);
120 }
121
SetItem(const nsAString & aKey,const nsAString & aValue,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)122 void SessionStorage::SetItem(const nsAString& aKey, const nsAString& aValue,
123 nsIPrincipal& aSubjectPrincipal,
124 ErrorResult& aRv) {
125 if (!CanUseStorage(aSubjectPrincipal)) {
126 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
127 return;
128 }
129
130 nsresult rv = EnsureCacheLoadedOrCloned();
131 if (NS_WARN_IF(NS_FAILED(rv))) {
132 aRv.Throw(rv);
133 return;
134 }
135
136 nsString oldValue;
137 rv = mCache->SetItem(aKey, aValue, oldValue);
138 if (NS_WARN_IF(NS_FAILED(rv))) {
139 aRv.Throw(rv);
140 return;
141 }
142
143 if (rv == NS_SUCCESS_DOM_NO_OPERATION) {
144 return;
145 }
146
147 BroadcastChangeNotification(aKey, oldValue, aValue);
148 }
149
RemoveItem(const nsAString & aKey,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)150 void SessionStorage::RemoveItem(const nsAString& aKey,
151 nsIPrincipal& aSubjectPrincipal,
152 ErrorResult& aRv) {
153 if (!CanUseStorage(aSubjectPrincipal)) {
154 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
155 return;
156 }
157
158 nsresult rv = EnsureCacheLoadedOrCloned();
159 if (NS_WARN_IF(NS_FAILED(rv))) {
160 aRv.Throw(rv);
161 return;
162 }
163
164 nsString oldValue;
165 rv = mCache->RemoveItem(aKey, oldValue);
166 MOZ_ASSERT(NS_SUCCEEDED(rv));
167
168 if (rv == NS_SUCCESS_DOM_NO_OPERATION) {
169 return;
170 }
171
172 BroadcastChangeNotification(aKey, oldValue, VoidString());
173 }
174
Clear(nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)175 void SessionStorage::Clear(nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
176 uint32_t length = GetLength(aSubjectPrincipal, aRv);
177 if (!length) {
178 return;
179 }
180
181 nsresult rv = EnsureCacheLoadedOrCloned();
182 if (NS_WARN_IF(NS_FAILED(rv))) {
183 aRv.Throw(rv);
184 return;
185 }
186
187 mCache->Clear();
188 BroadcastChangeNotification(VoidString(), VoidString(), VoidString());
189 }
190
BroadcastChangeNotification(const nsAString & aKey,const nsAString & aOldValue,const nsAString & aNewValue)191 void SessionStorage::BroadcastChangeNotification(const nsAString& aKey,
192 const nsAString& aOldValue,
193 const nsAString& aNewValue) {
194 NotifyChange(this, StoragePrincipal(), aKey, aOldValue, aNewValue,
195 u"sessionStorage", mDocumentURI, mIsPrivate, false);
196
197 // Sync changes on SessionStorageCache at the next statble state.
198 if (mManager->CanLoadData()) {
199 MaybeScheduleStableStateCallback();
200 }
201 }
202
IsForkOf(const Storage * aOther) const203 bool SessionStorage::IsForkOf(const Storage* aOther) const {
204 MOZ_ASSERT(aOther);
205 if (aOther->Type() != eSessionStorage) {
206 return false;
207 }
208
209 return mCache == static_cast<const SessionStorage*>(aOther)->mCache;
210 }
211
MaybeScheduleStableStateCallback()212 void SessionStorage::MaybeScheduleStableStateCallback() {
213 AssertIsOnOwningThread();
214
215 if (!mHasPendingStableStateCallback) {
216 nsContentUtils::RunInStableState(
217 NewRunnableMethod("SessionStorage::StableStateCallback", this,
218 &SessionStorage::StableStateCallback));
219
220 mHasPendingStableStateCallback = true;
221 }
222 }
223
StableStateCallback()224 void SessionStorage::StableStateCallback() {
225 AssertIsOnOwningThread();
226 MOZ_ASSERT(mHasPendingStableStateCallback);
227 MOZ_ASSERT(mManager);
228 MOZ_ASSERT(mCache);
229
230 mHasPendingStableStateCallback = false;
231
232 if (mManager->CanLoadData()) {
233 mManager->CheckpointData(*StoragePrincipal(), *mCache);
234 }
235 }
236
EnsureCacheLoadedOrCloned() const237 nsresult SessionStorage::EnsureCacheLoadedOrCloned() const {
238 AssertIsOnOwningThread();
239 MOZ_ASSERT(mManager);
240
241 if (!mManager->CanLoadData()) {
242 return NS_OK;
243 }
244
245 // Ensure manager actor.
246 nsresult rv = mManager->EnsureManager();
247 if (NS_WARN_IF(NS_FAILED(rv))) {
248 return rv;
249 }
250
251 // Ensure cache is loaded or cloned.
252 if (mCache->WasLoadedOrCloned()) {
253 return NS_OK;
254 }
255
256 return mManager->LoadData(*StoragePrincipal(), *mCache);
257 }
258
259 } // namespace dom
260 } // namespace mozilla
261