Bumping manifests a=b2g-bump
[gecko.git] / dom / storage / DOMStorageObserver.cpp
blobdc7e151718828bc2e6d53f0cd7c64a7abebac561
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "DOMStorageObserver.h"
8 #include "DOMStorageDBThread.h"
9 #include "DOMStorageCache.h"
11 #include "nsIObserverService.h"
12 #include "nsIURI.h"
13 #include "nsIURL.h"
14 #include "nsIScriptSecurityManager.h"
15 #include "nsIPermission.h"
16 #include "nsIIDNService.h"
17 #include "mozIApplicationClearPrivateDataParams.h"
18 #include "nsICookiePermission.h"
20 #include "nsPrintfCString.h"
21 #include "nsXULAppAPI.h"
22 #include "nsEscape.h"
23 #include "nsNetCID.h"
24 #include "mozilla/Services.h"
25 #include "nsServiceManagerUtils.h"
27 namespace mozilla {
28 namespace dom {
30 static const char kStartupTopic[] = "sessionstore-windows-restored";
31 static const uint32_t kStartupDelay = 0;
33 NS_IMPL_ISUPPORTS(DOMStorageObserver,
34 nsIObserver,
35 nsISupportsWeakReference)
37 DOMStorageObserver* DOMStorageObserver::sSelf = nullptr;
39 extern nsresult
40 CreateReversedDomain(const nsACString& aAsciiDomain, nsACString& aKey);
42 // static
43 nsresult
44 DOMStorageObserver::Init()
46 if (sSelf) {
47 return NS_OK;
50 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
51 if (!obs) {
52 return NS_ERROR_UNEXPECTED;
55 sSelf = new DOMStorageObserver();
56 NS_ADDREF(sSelf);
58 // Chrome clear operations.
59 obs->AddObserver(sSelf, kStartupTopic, true);
60 obs->AddObserver(sSelf, "cookie-changed", true);
61 obs->AddObserver(sSelf, "perm-changed", true);
62 obs->AddObserver(sSelf, "browser:purge-domain-data", true);
63 obs->AddObserver(sSelf, "last-pb-context-exited", true);
64 obs->AddObserver(sSelf, "webapps-clear-data", true);
66 // Shutdown
67 obs->AddObserver(sSelf, "profile-after-change", true);
68 obs->AddObserver(sSelf, "profile-before-change", true);
69 obs->AddObserver(sSelf, "xpcom-shutdown", true);
71 // Observe low device storage notifications.
72 obs->AddObserver(sSelf, "disk-space-watcher", true);
74 #ifdef DOM_STORAGE_TESTS
75 // Testing
76 obs->AddObserver(sSelf, "domstorage-test-flush-force", true);
77 if (XRE_GetProcessType() == GeckoProcessType_Default) {
78 // Only to forward to child process.
79 obs->AddObserver(sSelf, "domstorage-test-flushed", true);
82 obs->AddObserver(sSelf, "domstorage-test-reload", true);
83 #endif
85 return NS_OK;
88 // static
89 nsresult
90 DOMStorageObserver::Shutdown()
92 if (!sSelf) {
93 return NS_ERROR_NOT_INITIALIZED;
96 NS_RELEASE(sSelf);
97 return NS_OK;
100 void
101 DOMStorageObserver::AddSink(DOMStorageObserverSink* aObs)
103 mSinks.AppendElement(aObs);
106 void
107 DOMStorageObserver::RemoveSink(DOMStorageObserverSink* aObs)
109 mSinks.RemoveElement(aObs);
112 void
113 DOMStorageObserver::Notify(const char* aTopic, const nsACString& aData)
115 for (uint32_t i = 0; i < mSinks.Length(); ++i) {
116 DOMStorageObserverSink* sink = mSinks[i];
117 sink->Observe(aTopic, aData);
121 NS_IMETHODIMP
122 DOMStorageObserver::Observe(nsISupports* aSubject,
123 const char* aTopic,
124 const char16_t* aData)
126 nsresult rv;
128 // Start the thread that opens the database.
129 if (!strcmp(aTopic, kStartupTopic)) {
130 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
131 obs->RemoveObserver(this, kStartupTopic);
133 mDBThreadStartDelayTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
134 if (!mDBThreadStartDelayTimer) {
135 return NS_ERROR_UNEXPECTED;
138 mDBThreadStartDelayTimer->Init(this, nsITimer::TYPE_ONE_SHOT, kStartupDelay);
140 return NS_OK;
143 // Timer callback used to start the database a short timer after startup
144 if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
145 nsCOMPtr<nsITimer> timer = do_QueryInterface(aSubject);
146 if (!timer) {
147 return NS_ERROR_UNEXPECTED;
150 if (timer == mDBThreadStartDelayTimer) {
151 mDBThreadStartDelayTimer = nullptr;
153 DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
154 NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
157 return NS_OK;
160 // Clear everything, caches + database
161 if (!strcmp(aTopic, "cookie-changed")) {
162 if (!NS_LITERAL_STRING("cleared").Equals(aData)) {
163 return NS_OK;
166 DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
167 NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
169 db->AsyncClearAll();
171 Notify("cookie-cleared");
173 return NS_OK;
176 // Clear from caches everything that has been stored
177 // while in session-only mode
178 if (!strcmp(aTopic, "perm-changed")) {
179 // Check for cookie permission change
180 nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject));
181 if (!perm) {
182 return NS_OK;
185 nsAutoCString type;
186 perm->GetType(type);
187 if (type != NS_LITERAL_CSTRING("cookie")) {
188 return NS_OK;
191 uint32_t cap = 0;
192 perm->GetCapability(&cap);
193 if (!(cap & nsICookiePermission::ACCESS_SESSION) ||
194 !NS_LITERAL_STRING("deleted").Equals(nsDependentString(aData))) {
195 return NS_OK;
198 nsAutoCString host;
199 perm->GetHost(host);
200 if (host.IsEmpty()) {
201 return NS_OK;
204 nsAutoCString scope;
205 rv = CreateReversedDomain(host, scope);
206 NS_ENSURE_SUCCESS(rv, rv);
208 Notify("session-only-cleared", scope);
210 return NS_OK;
213 // Clear everything (including so and pb data) from caches and database
214 // for the gived domain and subdomains.
215 if (!strcmp(aTopic, "browser:purge-domain-data")) {
216 // Convert the domain name to the ACE format
217 nsAutoCString aceDomain;
218 nsCOMPtr<nsIIDNService> converter = do_GetService(NS_IDNSERVICE_CONTRACTID);
219 if (converter) {
220 rv = converter->ConvertUTF8toACE(NS_ConvertUTF16toUTF8(aData), aceDomain);
221 NS_ENSURE_SUCCESS(rv, rv);
222 } else {
223 // In case the IDN service is not available, this is the best we can come up with!
224 NS_EscapeURL(NS_ConvertUTF16toUTF8(aData),
225 esc_OnlyNonASCII | esc_AlwaysCopy,
226 aceDomain);
229 nsAutoCString scopePrefix;
230 rv = CreateReversedDomain(aceDomain, scopePrefix);
231 NS_ENSURE_SUCCESS(rv, rv);
233 DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
234 NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
236 db->AsyncClearMatchingScope(scopePrefix);
238 Notify("domain-data-cleared", scopePrefix);
240 return NS_OK;
243 // Clear all private-browsing caches
244 if (!strcmp(aTopic, "last-pb-context-exited")) {
245 Notify("private-browsing-data-cleared");
247 return NS_OK;
250 // Clear data beloging to an app.
251 if (!strcmp(aTopic, "webapps-clear-data")) {
252 nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
253 do_QueryInterface(aSubject);
254 if (!params) {
255 NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams");
256 return NS_ERROR_UNEXPECTED;
259 uint32_t appId;
260 bool browserOnly;
262 rv = params->GetAppId(&appId);
263 NS_ENSURE_SUCCESS(rv, rv);
265 rv = params->GetBrowserOnly(&browserOnly);
266 NS_ENSURE_SUCCESS(rv, rv);
268 MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
270 DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
271 NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
273 nsAutoCString scope;
274 scope.AppendInt(appId);
275 scope.AppendLiteral(":t:");
276 db->AsyncClearMatchingScope(scope);
277 Notify("app-data-cleared", scope);
279 if (!browserOnly) {
280 scope.Truncate();
281 scope.AppendInt(appId);
282 scope.AppendLiteral(":f:");
283 db->AsyncClearMatchingScope(scope);
284 Notify("app-data-cleared", scope);
287 return NS_OK;
290 if (!strcmp(aTopic, "profile-after-change")) {
291 Notify("profile-change");
293 return NS_OK;
296 if (!strcmp(aTopic, "profile-before-change") ||
297 !strcmp(aTopic, "xpcom-shutdown")) {
298 rv = DOMStorageCache::StopDatabase();
299 if (NS_FAILED(rv)) {
300 NS_WARNING("Error while stopping DOMStorage DB background thread");
303 return NS_OK;
306 if (!strcmp(aTopic, "disk-space-watcher")) {
307 if (NS_LITERAL_STRING("full").Equals(aData)) {
308 Notify("low-disk-space");
309 } else if (NS_LITERAL_STRING("free").Equals(aData)) {
310 Notify("no-low-disk-space");
313 return NS_OK;
316 #ifdef DOM_STORAGE_TESTS
317 if (!strcmp(aTopic, "domstorage-test-flush-force")) {
318 DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
319 if (db) {
320 db->AsyncFlush();
323 return NS_OK;
326 if (!strcmp(aTopic, "domstorage-test-flushed")) {
327 // Only used to propagate to IPC children
328 Notify("test-flushed");
330 return NS_OK;
333 if (!strcmp(aTopic, "domstorage-test-reload")) {
334 Notify("test-reload");
336 return NS_OK;
338 #endif
340 NS_ERROR("Unexpected topic");
341 return NS_ERROR_UNEXPECTED;
344 } // ::dom
345 } // ::mozilla