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 "LocalStorage.h"
8 #include "LocalStorageCache.h"
9 #include "LocalStorageManager.h"
10 #include "StorageUtils.h"
11 
12 #include "nsIPrincipal.h"
13 
14 #include "mozilla/dom/PermissionMessageUtils.h"
15 #include "mozilla/dom/StorageBinding.h"
16 #include "mozilla/dom/StorageEvent.h"
17 #include "mozilla/dom/StorageEventBinding.h"
18 #include "mozilla/ipc/BackgroundChild.h"
19 #include "mozilla/ipc/PBackgroundChild.h"
20 #include "mozilla/Preferences.h"
21 #include "mozilla/EnumSet.h"
22 #include "nsThreadUtils.h"
23 #include "nsContentUtils.h"
24 #include "nsServiceManagerUtils.h"
25 
26 namespace mozilla {
27 
28 using namespace ipc;
29 
30 namespace dom {
31 
32 NS_IMPL_CYCLE_COLLECTION_CLASS(LocalStorage)
33 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(LocalStorage, Storage)
34   NS_IMPL_CYCLE_COLLECTION_UNLINK(mManager)
35   NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
36 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
37 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(LocalStorage, Storage)
38   CycleCollectionNoteChild(
39       cb, NS_ISUPPORTS_CAST(nsIDOMStorageManager*, tmp->mManager.get()),
40       "mManager");
41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
42 
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LocalStorage)43 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LocalStorage)
44   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
45 NS_INTERFACE_MAP_END_INHERITING(Storage)
46 
47 NS_IMPL_ADDREF_INHERITED(LocalStorage, Storage)
48 NS_IMPL_RELEASE_INHERITED(LocalStorage, Storage)
49 
50 LocalStorage::LocalStorage(nsPIDOMWindowInner* aWindow,
51                            LocalStorageManager* aManager,
52                            LocalStorageCache* aCache,
53                            const nsAString& aDocumentURI,
54                            nsIPrincipal* aPrincipal,
55                            nsIPrincipal* aStoragePrincipal, bool aIsPrivate)
56     : Storage(aWindow, aPrincipal, aStoragePrincipal),
57       mManager(aManager),
58       mCache(aCache),
59       mDocumentURI(aDocumentURI),
60       mIsPrivate(aIsPrivate) {
61   mCache->Preload();
62 }
63 
64 LocalStorage::~LocalStorage() = default;
65 
GetOriginQuotaUsage() const66 int64_t LocalStorage::GetOriginQuotaUsage() const {
67   return mCache->GetOriginQuotaUsage(this);
68 }
69 
GetLength(nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)70 uint32_t LocalStorage::GetLength(nsIPrincipal& aSubjectPrincipal,
71                                  ErrorResult& aRv) {
72   if (!CanUseStorage(aSubjectPrincipal)) {
73     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
74     return 0;
75   }
76 
77   uint32_t length;
78   aRv = mCache->GetLength(this, &length);
79   return length;
80 }
81 
Key(uint32_t aIndex,nsAString & aResult,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)82 void LocalStorage::Key(uint32_t aIndex, nsAString& aResult,
83                        nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
84   if (!CanUseStorage(aSubjectPrincipal)) {
85     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
86     return;
87   }
88 
89   aRv = mCache->GetKey(this, aIndex, aResult);
90 }
91 
GetItem(const nsAString & aKey,nsAString & aResult,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)92 void LocalStorage::GetItem(const nsAString& aKey, nsAString& aResult,
93                            nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
94   if (!CanUseStorage(aSubjectPrincipal)) {
95     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
96     return;
97   }
98 
99   aRv = mCache->GetItem(this, aKey, aResult);
100 }
101 
SetItem(const nsAString & aKey,const nsAString & aData,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)102 void LocalStorage::SetItem(const nsAString& aKey, const nsAString& aData,
103                            nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
104   if (!CanUseStorage(aSubjectPrincipal)) {
105     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
106     return;
107   }
108 
109   nsString data;
110   bool ok = data.Assign(aData, fallible);
111   if (!ok) {
112     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
113     return;
114   }
115 
116   nsString old;
117   aRv = mCache->SetItem(this, aKey, data, old);
118   if (aRv.Failed()) {
119     return;
120   }
121 
122   if (!aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION)) {
123     OnChange(aKey, old, aData);
124   }
125 }
126 
RemoveItem(const nsAString & aKey,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)127 void LocalStorage::RemoveItem(const nsAString& aKey,
128                               nsIPrincipal& aSubjectPrincipal,
129                               ErrorResult& aRv) {
130   if (!CanUseStorage(aSubjectPrincipal)) {
131     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
132     return;
133   }
134 
135   nsAutoString old;
136   aRv = mCache->RemoveItem(this, aKey, old);
137   if (aRv.Failed()) {
138     return;
139   }
140 
141   if (!aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION)) {
142     OnChange(aKey, old, VoidString());
143   }
144 }
145 
Clear(nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)146 void LocalStorage::Clear(nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
147   if (!CanUseStorage(aSubjectPrincipal)) {
148     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
149     return;
150   }
151 
152   aRv = mCache->Clear(this);
153   if (NS_WARN_IF(aRv.Failed())) {
154     return;
155   }
156 
157   if (!aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION)) {
158     OnChange(VoidString(), VoidString(), VoidString());
159   }
160 }
161 
OnChange(const nsAString & aKey,const nsAString & aOldValue,const nsAString & aNewValue)162 void LocalStorage::OnChange(const nsAString& aKey, const nsAString& aOldValue,
163                             const nsAString& aNewValue) {
164   NotifyChange(/* aStorage */ this, StoragePrincipal(), aKey, aOldValue,
165                aNewValue, /* aStorageType */ u"localStorage", mDocumentURI,
166                mIsPrivate, /* aImmediateDispatch */ false);
167 }
168 
ApplyEvent(StorageEvent * aStorageEvent)169 void LocalStorage::ApplyEvent(StorageEvent* aStorageEvent) {
170   MOZ_ASSERT(aStorageEvent);
171 
172   nsAutoString key;
173   nsAutoString old;
174   nsAutoString value;
175 
176   aStorageEvent->GetKey(key);
177   aStorageEvent->GetNewValue(value);
178 
179   // No key means clearing the full storage.
180   if (key.IsVoid()) {
181     MOZ_ASSERT(value.IsVoid());
182     mCache->Clear(this, LocalStorageCache::E10sPropagated);
183     return;
184   }
185 
186   // No new value means removing the key.
187   if (value.IsVoid()) {
188     mCache->RemoveItem(this, key, old, LocalStorageCache::E10sPropagated);
189     return;
190   }
191 
192   // Otherwise, we set the new value.
193   mCache->SetItem(this, key, value, old, LocalStorageCache::E10sPropagated);
194 }
195 
GetSupportedNames(nsTArray<nsString> & aKeys)196 void LocalStorage::GetSupportedNames(nsTArray<nsString>& aKeys) {
197   if (!CanUseStorage(*nsContentUtils::SubjectPrincipal())) {
198     // return just an empty array
199     aKeys.Clear();
200     return;
201   }
202 
203   mCache->GetKeys(this, aKeys);
204 }
205 
IsForkOf(const Storage * aOther) const206 bool LocalStorage::IsForkOf(const Storage* aOther) const {
207   MOZ_ASSERT(aOther);
208   if (aOther->Type() != eLocalStorage) {
209     return false;
210   }
211 
212   return mCache == static_cast<const LocalStorage*>(aOther)->mCache;
213 }
214 
215 }  // namespace dom
216 }  // namespace mozilla
217