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 "Storage.h"
8 #include "StorageNotifierService.h"
9
10 #include "mozilla/dom/StorageBinding.h"
11 #include "mozilla/dom/StorageEvent.h"
12 #include "mozilla/dom/StorageEventBinding.h"
13 #include "mozilla/BasePrincipal.h"
14 #include "mozilla/Preferences.h"
15 #include "mozilla/SchedulerGroup.h"
16 #include "mozilla/Services.h"
17 #include "mozilla/StorageAccess.h"
18 #include "nsIObserverService.h"
19 #include "nsPIDOMWindow.h"
20
21 namespace mozilla {
22 namespace dom {
23
24 static const char kStorageEnabled[] = "dom.storage.enabled";
25
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Storage,mWindow,mPrincipal,mStoragePrincipal)26 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Storage, mWindow, mPrincipal,
27 mStoragePrincipal)
28
29 NS_IMPL_CYCLE_COLLECTING_ADDREF(Storage)
30 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(Storage, LastRelease())
31
32 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Storage)
33 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
34 NS_INTERFACE_MAP_ENTRY(nsISupports)
35 NS_INTERFACE_MAP_END
36
37 Storage::Storage(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal,
38 nsIPrincipal* aStoragePrincipal)
39 : mWindow(aWindow),
40 mPrincipal(aPrincipal),
41 mStoragePrincipal(aStoragePrincipal),
42 mPrivateBrowsing(false),
43 mSessionScopedOrLess(false) {
44 MOZ_ASSERT(aPrincipal);
45
46 if (mPrincipal->IsSystemPrincipal()) {
47 mPrivateBrowsing = false;
48 mSessionScopedOrLess = false;
49 } else if (mWindow) {
50 uint32_t rejectedReason = 0;
51 StorageAccess access = StorageAllowedForWindow(mWindow, &rejectedReason);
52
53 mPrivateBrowsing = access == StorageAccess::ePrivateBrowsing;
54 mSessionScopedOrLess = access <= StorageAccess::eSessionScoped;
55 }
56 }
57
58 Storage::~Storage() = default;
59
60 /* static */
StoragePrefIsEnabled()61 bool Storage::StoragePrefIsEnabled() {
62 return mozilla::Preferences::GetBool(kStorageEnabled);
63 }
64
CanUseStorage(nsIPrincipal & aSubjectPrincipal)65 bool Storage::CanUseStorage(nsIPrincipal& aSubjectPrincipal) {
66 if (!StoragePrefIsEnabled()) {
67 return false;
68 }
69
70 return aSubjectPrincipal.Subsumes(mPrincipal);
71 }
72
73 /* virtual */
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)74 JSObject* Storage::WrapObject(JSContext* aCx,
75 JS::Handle<JSObject*> aGivenProto) {
76 return Storage_Binding::Wrap(aCx, this, aGivenProto);
77 }
78
79 namespace {
80
81 class StorageNotifierRunnable : public Runnable {
82 public:
StorageNotifierRunnable(nsISupports * aSubject,const char16_t * aStorageType,bool aPrivateBrowsing)83 StorageNotifierRunnable(nsISupports* aSubject, const char16_t* aStorageType,
84 bool aPrivateBrowsing)
85 : Runnable("StorageNotifierRunnable"),
86 mSubject(aSubject),
87 mStorageType(aStorageType),
88 mPrivateBrowsing(aPrivateBrowsing) {}
89
90 NS_IMETHOD
Run()91 Run() override {
92 nsCOMPtr<nsIObserverService> observerService =
93 mozilla::services::GetObserverService();
94 if (observerService) {
95 observerService->NotifyObservers(mSubject,
96 mPrivateBrowsing
97 ? "dom-private-storage2-changed"
98 : "dom-storage2-changed",
99 mStorageType);
100 }
101 return NS_OK;
102 }
103
104 private:
105 nsCOMPtr<nsISupports> mSubject;
106 const char16_t* mStorageType;
107 const bool mPrivateBrowsing;
108 };
109
110 } // namespace
111
112 /* static */
NotifyChange(Storage * aStorage,nsIPrincipal * aPrincipal,const nsAString & aKey,const nsAString & aOldValue,const nsAString & aNewValue,const char16_t * aStorageType,const nsAString & aDocumentURI,bool aIsPrivate,bool aImmediateDispatch)113 void Storage::NotifyChange(Storage* aStorage, nsIPrincipal* aPrincipal,
114 const nsAString& aKey, const nsAString& aOldValue,
115 const nsAString& aNewValue,
116 const char16_t* aStorageType,
117 const nsAString& aDocumentURI, bool aIsPrivate,
118 bool aImmediateDispatch) {
119 StorageEventInit dict;
120 dict.mBubbles = false;
121 dict.mCancelable = false;
122 dict.mKey = aKey;
123 dict.mNewValue = aNewValue;
124 dict.mOldValue = aOldValue;
125 dict.mStorageArea = aStorage;
126 dict.mUrl = aDocumentURI;
127
128 // Note, this DOM event should never reach JS. It is cloned later in
129 // nsGlobalWindow.
130 RefPtr<StorageEvent> event =
131 StorageEvent::Constructor(nullptr, u"storage"_ns, dict);
132
133 event->SetPrincipal(aPrincipal);
134
135 // This will send the event to any registered window.
136 StorageNotifierService::Broadcast(event, aStorageType, aIsPrivate,
137 aImmediateDispatch);
138
139 // This runnable is mainly used by devtools. Windows receive notification by
140 // StorageNotifierService.
141
142 RefPtr<StorageNotifierRunnable> r =
143 new StorageNotifierRunnable(event, aStorageType, aIsPrivate);
144
145 if (aImmediateDispatch) {
146 Unused << r->Run();
147 } else {
148 SchedulerGroup::Dispatch(TaskCategory::Other, r.forget());
149 }
150 }
151
152 } // namespace dom
153 } // namespace mozilla
154