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