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