1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/src/storage/DOMStorageObserver.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,345 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "DOMStorageObserver.h" 1.10 + 1.11 +#include "DOMStorageDBThread.h" 1.12 +#include "DOMStorageCache.h" 1.13 + 1.14 +#include "nsIObserverService.h" 1.15 +#include "nsIURI.h" 1.16 +#include "nsIURL.h" 1.17 +#include "nsIScriptSecurityManager.h" 1.18 +#include "nsIPermission.h" 1.19 +#include "nsIIDNService.h" 1.20 +#include "mozIApplicationClearPrivateDataParams.h" 1.21 +#include "nsICookiePermission.h" 1.22 + 1.23 +#include "nsPrintfCString.h" 1.24 +#include "nsXULAppAPI.h" 1.25 +#include "nsEscape.h" 1.26 +#include "nsNetCID.h" 1.27 +#include "mozilla/Services.h" 1.28 +#include "nsServiceManagerUtils.h" 1.29 + 1.30 +namespace mozilla { 1.31 +namespace dom { 1.32 + 1.33 +static const char kStartupTopic[] = "sessionstore-windows-restored"; 1.34 +static const uint32_t kStartupDelay = 0; 1.35 + 1.36 +NS_IMPL_ISUPPORTS(DOMStorageObserver, 1.37 + nsIObserver, 1.38 + nsISupportsWeakReference) 1.39 + 1.40 +DOMStorageObserver* DOMStorageObserver::sSelf = nullptr; 1.41 + 1.42 +extern nsresult 1.43 +CreateReversedDomain(const nsACString& aAsciiDomain, nsACString& aKey); 1.44 + 1.45 +// static 1.46 +nsresult 1.47 +DOMStorageObserver::Init() 1.48 +{ 1.49 + if (sSelf) { 1.50 + return NS_OK; 1.51 + } 1.52 + 1.53 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.54 + if (!obs) { 1.55 + return NS_ERROR_UNEXPECTED; 1.56 + } 1.57 + 1.58 + sSelf = new DOMStorageObserver(); 1.59 + NS_ADDREF(sSelf); 1.60 + 1.61 + // Chrome clear operations. 1.62 + obs->AddObserver(sSelf, kStartupTopic, true); 1.63 + obs->AddObserver(sSelf, "cookie-changed", true); 1.64 + obs->AddObserver(sSelf, "perm-changed", true); 1.65 + obs->AddObserver(sSelf, "browser:purge-domain-data", true); 1.66 + obs->AddObserver(sSelf, "last-pb-context-exited", true); 1.67 + obs->AddObserver(sSelf, "webapps-clear-data", true); 1.68 + 1.69 + // Shutdown 1.70 + obs->AddObserver(sSelf, "profile-after-change", true); 1.71 + obs->AddObserver(sSelf, "profile-before-change", true); 1.72 + obs->AddObserver(sSelf, "xpcom-shutdown", true); 1.73 + 1.74 + // Observe low device storage notifications. 1.75 + obs->AddObserver(sSelf, "disk-space-watcher", true); 1.76 + 1.77 +#ifdef DOM_STORAGE_TESTS 1.78 + // Testing 1.79 + obs->AddObserver(sSelf, "domstorage-test-flush-force", true); 1.80 + if (XRE_GetProcessType() == GeckoProcessType_Default) { 1.81 + // Only to forward to child process. 1.82 + obs->AddObserver(sSelf, "domstorage-test-flushed", true); 1.83 + } 1.84 + 1.85 + obs->AddObserver(sSelf, "domstorage-test-reload", true); 1.86 +#endif 1.87 + 1.88 + return NS_OK; 1.89 +} 1.90 + 1.91 +// static 1.92 +nsresult 1.93 +DOMStorageObserver::Shutdown() 1.94 +{ 1.95 + if (!sSelf) { 1.96 + return NS_ERROR_NOT_INITIALIZED; 1.97 + } 1.98 + 1.99 + NS_RELEASE(sSelf); 1.100 + return NS_OK; 1.101 +} 1.102 + 1.103 +void 1.104 +DOMStorageObserver::AddSink(DOMStorageObserverSink* aObs) 1.105 +{ 1.106 + mSinks.AppendElement(aObs); 1.107 +} 1.108 + 1.109 +void 1.110 +DOMStorageObserver::RemoveSink(DOMStorageObserverSink* aObs) 1.111 +{ 1.112 + mSinks.RemoveElement(aObs); 1.113 +} 1.114 + 1.115 +void 1.116 +DOMStorageObserver::Notify(const char* aTopic, const nsACString& aData) 1.117 +{ 1.118 + for (uint32_t i = 0; i < mSinks.Length(); ++i) { 1.119 + DOMStorageObserverSink* sink = mSinks[i]; 1.120 + sink->Observe(aTopic, aData); 1.121 + } 1.122 +} 1.123 + 1.124 +NS_IMETHODIMP 1.125 +DOMStorageObserver::Observe(nsISupports* aSubject, 1.126 + const char* aTopic, 1.127 + const char16_t* aData) 1.128 +{ 1.129 + nsresult rv; 1.130 + 1.131 + // Start the thread that opens the database. 1.132 + if (!strcmp(aTopic, kStartupTopic)) { 1.133 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.134 + obs->RemoveObserver(this, kStartupTopic); 1.135 + 1.136 + mDBThreadStartDelayTimer = do_CreateInstance(NS_TIMER_CONTRACTID); 1.137 + if (!mDBThreadStartDelayTimer) { 1.138 + return NS_ERROR_UNEXPECTED; 1.139 + } 1.140 + 1.141 + mDBThreadStartDelayTimer->Init(this, nsITimer::TYPE_ONE_SHOT, kStartupDelay); 1.142 + 1.143 + return NS_OK; 1.144 + } 1.145 + 1.146 + // Timer callback used to start the database a short timer after startup 1.147 + if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) { 1.148 + nsCOMPtr<nsITimer> timer = do_QueryInterface(aSubject); 1.149 + if (!timer) { 1.150 + return NS_ERROR_UNEXPECTED; 1.151 + } 1.152 + 1.153 + if (timer == mDBThreadStartDelayTimer) { 1.154 + mDBThreadStartDelayTimer = nullptr; 1.155 + 1.156 + DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); 1.157 + NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); 1.158 + } 1.159 + 1.160 + return NS_OK; 1.161 + } 1.162 + 1.163 + // Clear everything, caches + database 1.164 + if (!strcmp(aTopic, "cookie-changed")) { 1.165 + if (!NS_LITERAL_STRING("cleared").Equals(aData)) { 1.166 + return NS_OK; 1.167 + } 1.168 + 1.169 + DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); 1.170 + NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); 1.171 + 1.172 + db->AsyncClearAll(); 1.173 + 1.174 + Notify("cookie-cleared"); 1.175 + 1.176 + return NS_OK; 1.177 + } 1.178 + 1.179 + // Clear from caches everything that has been stored 1.180 + // while in session-only mode 1.181 + if (!strcmp(aTopic, "perm-changed")) { 1.182 + // Check for cookie permission change 1.183 + nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject)); 1.184 + if (!perm) { 1.185 + return NS_OK; 1.186 + } 1.187 + 1.188 + nsAutoCString type; 1.189 + perm->GetType(type); 1.190 + if (type != NS_LITERAL_CSTRING("cookie")) { 1.191 + return NS_OK; 1.192 + } 1.193 + 1.194 + uint32_t cap = 0; 1.195 + perm->GetCapability(&cap); 1.196 + if (!(cap & nsICookiePermission::ACCESS_SESSION) || 1.197 + !NS_LITERAL_STRING("deleted").Equals(nsDependentString(aData))) { 1.198 + return NS_OK; 1.199 + } 1.200 + 1.201 + nsAutoCString host; 1.202 + perm->GetHost(host); 1.203 + if (host.IsEmpty()) { 1.204 + return NS_OK; 1.205 + } 1.206 + 1.207 + nsAutoCString scope; 1.208 + rv = CreateReversedDomain(host, scope); 1.209 + NS_ENSURE_SUCCESS(rv, rv); 1.210 + 1.211 + Notify("session-only-cleared", scope); 1.212 + 1.213 + return NS_OK; 1.214 + } 1.215 + 1.216 + // Clear everything (including so and pb data) from caches and database 1.217 + // for the gived domain and subdomains. 1.218 + if (!strcmp(aTopic, "browser:purge-domain-data")) { 1.219 + // Convert the domain name to the ACE format 1.220 + nsAutoCString aceDomain; 1.221 + nsCOMPtr<nsIIDNService> converter = do_GetService(NS_IDNSERVICE_CONTRACTID); 1.222 + if (converter) { 1.223 + rv = converter->ConvertUTF8toACE(NS_ConvertUTF16toUTF8(aData), aceDomain); 1.224 + NS_ENSURE_SUCCESS(rv, rv); 1.225 + } else { 1.226 + // In case the IDN service is not available, this is the best we can come up with! 1.227 + NS_EscapeURL(NS_ConvertUTF16toUTF8(aData), 1.228 + esc_OnlyNonASCII | esc_AlwaysCopy, 1.229 + aceDomain); 1.230 + } 1.231 + 1.232 + nsAutoCString scopePrefix; 1.233 + rv = CreateReversedDomain(aceDomain, scopePrefix); 1.234 + NS_ENSURE_SUCCESS(rv, rv); 1.235 + 1.236 + DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); 1.237 + NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); 1.238 + 1.239 + db->AsyncClearMatchingScope(scopePrefix); 1.240 + 1.241 + Notify("domain-data-cleared", scopePrefix); 1.242 + 1.243 + return NS_OK; 1.244 + } 1.245 + 1.246 + // Clear all private-browsing caches 1.247 + if (!strcmp(aTopic, "last-pb-context-exited")) { 1.248 + Notify("private-browsing-data-cleared"); 1.249 + 1.250 + return NS_OK; 1.251 + } 1.252 + 1.253 + // Clear data beloging to an app. 1.254 + if (!strcmp(aTopic, "webapps-clear-data")) { 1.255 + nsCOMPtr<mozIApplicationClearPrivateDataParams> params = 1.256 + do_QueryInterface(aSubject); 1.257 + if (!params) { 1.258 + NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams"); 1.259 + return NS_ERROR_UNEXPECTED; 1.260 + } 1.261 + 1.262 + uint32_t appId; 1.263 + bool browserOnly; 1.264 + 1.265 + rv = params->GetAppId(&appId); 1.266 + NS_ENSURE_SUCCESS(rv, rv); 1.267 + 1.268 + rv = params->GetBrowserOnly(&browserOnly); 1.269 + NS_ENSURE_SUCCESS(rv, rv); 1.270 + 1.271 + MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID); 1.272 + 1.273 + DOMStorageDBBridge* db = DOMStorageCache::StartDatabase(); 1.274 + NS_ENSURE_TRUE(db, NS_ERROR_FAILURE); 1.275 + 1.276 + nsAutoCString scope; 1.277 + scope.AppendInt(appId); 1.278 + scope.Append(NS_LITERAL_CSTRING(":t:")); 1.279 + db->AsyncClearMatchingScope(scope); 1.280 + Notify("app-data-cleared", scope); 1.281 + 1.282 + if (!browserOnly) { 1.283 + scope.Truncate(); 1.284 + scope.AppendInt(appId); 1.285 + scope.Append(NS_LITERAL_CSTRING(":f:")); 1.286 + db->AsyncClearMatchingScope(scope); 1.287 + Notify("app-data-cleared", scope); 1.288 + } 1.289 + 1.290 + return NS_OK; 1.291 + } 1.292 + 1.293 + if (!strcmp(aTopic, "profile-after-change")) { 1.294 + Notify("profile-change"); 1.295 + 1.296 + return NS_OK; 1.297 + } 1.298 + 1.299 + if (!strcmp(aTopic, "profile-before-change") || 1.300 + !strcmp(aTopic, "xpcom-shutdown")) { 1.301 + rv = DOMStorageCache::StopDatabase(); 1.302 + if (NS_FAILED(rv)) { 1.303 + NS_WARNING("Error while stopping DOMStorage DB background thread"); 1.304 + } 1.305 + 1.306 + return NS_OK; 1.307 + } 1.308 + 1.309 + if (!strcmp(aTopic, "disk-space-watcher")) { 1.310 + if (NS_LITERAL_STRING("full").Equals(aData)) { 1.311 + Notify("low-disk-space"); 1.312 + } else if (NS_LITERAL_STRING("free").Equals(aData)) { 1.313 + Notify("no-low-disk-space"); 1.314 + } 1.315 + 1.316 + return NS_OK; 1.317 + } 1.318 + 1.319 +#ifdef DOM_STORAGE_TESTS 1.320 + if (!strcmp(aTopic, "domstorage-test-flush-force")) { 1.321 + DOMStorageDBBridge* db = DOMStorageCache::GetDatabase(); 1.322 + if (db) { 1.323 + db->AsyncFlush(); 1.324 + } 1.325 + 1.326 + return NS_OK; 1.327 + } 1.328 + 1.329 + if (!strcmp(aTopic, "domstorage-test-flushed")) { 1.330 + // Only used to propagate to IPC children 1.331 + Notify("test-flushed"); 1.332 + 1.333 + return NS_OK; 1.334 + } 1.335 + 1.336 + if (!strcmp(aTopic, "domstorage-test-reload")) { 1.337 + Notify("test-reload"); 1.338 + 1.339 + return NS_OK; 1.340 + } 1.341 +#endif 1.342 + 1.343 + NS_ERROR("Unexpected topic"); 1.344 + return NS_ERROR_UNEXPECTED; 1.345 +} 1.346 + 1.347 +} // ::dom 1.348 +} // ::mozilla