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