1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/src/offline/nsDOMOfflineResourceList.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,846 @@ 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 "nsDOMOfflineResourceList.h" 1.10 +#include "nsIDOMEvent.h" 1.11 +#include "nsIScriptSecurityManager.h" 1.12 +#include "nsError.h" 1.13 +#include "mozilla/dom/DOMStringList.h" 1.14 +#include "nsIPrefetchService.h" 1.15 +#include "nsCPrefetchService.h" 1.16 +#include "nsNetUtil.h" 1.17 +#include "nsNetCID.h" 1.18 +#include "nsICacheSession.h" 1.19 +#include "nsICacheService.h" 1.20 +#include "nsIOfflineCacheUpdate.h" 1.21 +#include "nsAutoPtr.h" 1.22 +#include "nsContentUtils.h" 1.23 +#include "nsIObserverService.h" 1.24 +#include "nsIScriptGlobalObject.h" 1.25 +#include "nsIWebNavigation.h" 1.26 +#include "mozilla/dom/OfflineResourceListBinding.h" 1.27 +#include "mozilla/EventDispatcher.h" 1.28 +#include "mozilla/Preferences.h" 1.29 + 1.30 +#include "nsXULAppAPI.h" 1.31 +#define IS_CHILD_PROCESS() \ 1.32 + (GeckoProcessType_Default != XRE_GetProcessType()) 1.33 + 1.34 +using namespace mozilla; 1.35 +using namespace mozilla::dom; 1.36 + 1.37 +// Event names 1.38 + 1.39 +#define CHECKING_STR "checking" 1.40 +#define ERROR_STR "error" 1.41 +#define NOUPDATE_STR "noupdate" 1.42 +#define DOWNLOADING_STR "downloading" 1.43 +#define PROGRESS_STR "progress" 1.44 +#define CACHED_STR "cached" 1.45 +#define UPDATEREADY_STR "updateready" 1.46 +#define OBSOLETE_STR "obsolete" 1.47 + 1.48 +// To prevent abuse of the resource list for data storage, the number 1.49 +// of offline urls and their length are limited. 1.50 + 1.51 +static const char kMaxEntriesPref[] = "offline.max_site_resources"; 1.52 +#define DEFAULT_MAX_ENTRIES 100 1.53 +#define MAX_URI_LENGTH 2048 1.54 + 1.55 +// 1.56 +// nsDOMOfflineResourceList 1.57 +// 1.58 + 1.59 +NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDOMOfflineResourceList, 1.60 + DOMEventTargetHelper, 1.61 + mCacheUpdate, 1.62 + mPendingEvents) 1.63 + 1.64 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMOfflineResourceList) 1.65 + NS_INTERFACE_MAP_ENTRY(nsIDOMOfflineResourceList) 1.66 + NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdateObserver) 1.67 + NS_INTERFACE_MAP_ENTRY(nsIObserver) 1.68 + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 1.69 +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 1.70 + 1.71 +NS_IMPL_ADDREF_INHERITED(nsDOMOfflineResourceList, DOMEventTargetHelper) 1.72 +NS_IMPL_RELEASE_INHERITED(nsDOMOfflineResourceList, DOMEventTargetHelper) 1.73 + 1.74 +NS_IMPL_EVENT_HANDLER(nsDOMOfflineResourceList, checking) 1.75 +NS_IMPL_EVENT_HANDLER(nsDOMOfflineResourceList, error) 1.76 +NS_IMPL_EVENT_HANDLER(nsDOMOfflineResourceList, noupdate) 1.77 +NS_IMPL_EVENT_HANDLER(nsDOMOfflineResourceList, downloading) 1.78 +NS_IMPL_EVENT_HANDLER(nsDOMOfflineResourceList, progress) 1.79 +NS_IMPL_EVENT_HANDLER(nsDOMOfflineResourceList, cached) 1.80 +NS_IMPL_EVENT_HANDLER(nsDOMOfflineResourceList, updateready) 1.81 +NS_IMPL_EVENT_HANDLER(nsDOMOfflineResourceList, obsolete) 1.82 + 1.83 +nsDOMOfflineResourceList::nsDOMOfflineResourceList(nsIURI *aManifestURI, 1.84 + nsIURI *aDocumentURI, 1.85 + nsPIDOMWindow *aWindow) 1.86 + : DOMEventTargetHelper(aWindow) 1.87 + , mInitialized(false) 1.88 + , mManifestURI(aManifestURI) 1.89 + , mDocumentURI(aDocumentURI) 1.90 + , mExposeCacheUpdateStatus(true) 1.91 + , mStatus(nsIDOMOfflineResourceList::IDLE) 1.92 + , mCachedKeys(nullptr) 1.93 + , mCachedKeysCount(0) 1.94 +{ 1.95 +} 1.96 + 1.97 +nsDOMOfflineResourceList::~nsDOMOfflineResourceList() 1.98 +{ 1.99 + ClearCachedKeys(); 1.100 +} 1.101 + 1.102 +JSObject* 1.103 +nsDOMOfflineResourceList::WrapObject(JSContext* aCx) 1.104 +{ 1.105 + return OfflineResourceListBinding::Wrap(aCx, this); 1.106 +} 1.107 + 1.108 +nsresult 1.109 +nsDOMOfflineResourceList::Init() 1.110 +{ 1.111 + if (mInitialized) { 1.112 + return NS_OK; 1.113 + } 1.114 + 1.115 + if (!mManifestURI) { 1.116 + return NS_ERROR_DOM_INVALID_STATE_ERR; 1.117 + } 1.118 + 1.119 + mManifestURI->GetAsciiSpec(mManifestSpec); 1.120 + 1.121 + nsresult rv = nsContentUtils::GetSecurityManager()-> 1.122 + CheckSameOriginURI(mManifestURI, mDocumentURI, true); 1.123 + NS_ENSURE_SUCCESS(rv, rv); 1.124 + 1.125 + // Dynamically-managed resources are stored as a separate ownership list 1.126 + // from the manifest. 1.127 + nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(mDocumentURI); 1.128 + if (!innerURI) 1.129 + return NS_ERROR_FAILURE; 1.130 + 1.131 + if (!IS_CHILD_PROCESS()) 1.132 + { 1.133 + mApplicationCacheService = 1.134 + do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); 1.135 + NS_ENSURE_SUCCESS(rv, rv); 1.136 + 1.137 + // Check for in-progress cache updates 1.138 + nsCOMPtr<nsIOfflineCacheUpdateService> cacheUpdateService = 1.139 + do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &rv); 1.140 + NS_ENSURE_SUCCESS(rv, rv); 1.141 + 1.142 + uint32_t numUpdates; 1.143 + rv = cacheUpdateService->GetNumUpdates(&numUpdates); 1.144 + NS_ENSURE_SUCCESS(rv, rv); 1.145 + 1.146 + for (uint32_t i = 0; i < numUpdates; i++) { 1.147 + nsCOMPtr<nsIOfflineCacheUpdate> cacheUpdate; 1.148 + rv = cacheUpdateService->GetUpdate(i, getter_AddRefs(cacheUpdate)); 1.149 + NS_ENSURE_SUCCESS(rv, rv); 1.150 + 1.151 + UpdateAdded(cacheUpdate); 1.152 + NS_ENSURE_SUCCESS(rv, rv); 1.153 + } 1.154 + } 1.155 + 1.156 + // watch for new offline cache updates 1.157 + nsCOMPtr<nsIObserverService> observerService = 1.158 + mozilla::services::GetObserverService(); 1.159 + if (!observerService) 1.160 + return NS_ERROR_FAILURE; 1.161 + 1.162 + rv = observerService->AddObserver(this, "offline-cache-update-added", true); 1.163 + NS_ENSURE_SUCCESS(rv, rv); 1.164 + rv = observerService->AddObserver(this, "offline-cache-update-completed", true); 1.165 + NS_ENSURE_SUCCESS(rv, rv); 1.166 + 1.167 + mInitialized = true; 1.168 + 1.169 + return NS_OK; 1.170 +} 1.171 + 1.172 +void 1.173 +nsDOMOfflineResourceList::Disconnect() 1.174 +{ 1.175 + mPendingEvents.Clear(); 1.176 + 1.177 + if (mListenerManager) { 1.178 + mListenerManager->Disconnect(); 1.179 + mListenerManager = nullptr; 1.180 + } 1.181 +} 1.182 + 1.183 +// 1.184 +// nsDOMOfflineResourceList::nsIDOMOfflineResourceList 1.185 +// 1.186 + 1.187 +already_AddRefed<DOMStringList> 1.188 +nsDOMOfflineResourceList::GetMozItems(ErrorResult& aRv) 1.189 +{ 1.190 + if (IS_CHILD_PROCESS()) { 1.191 + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); 1.192 + return nullptr; 1.193 + } 1.194 + 1.195 + nsRefPtr<DOMStringList> items = new DOMStringList(); 1.196 + 1.197 + // If we are not associated with an application cache, return an 1.198 + // empty list. 1.199 + nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache(); 1.200 + if (!appCache) { 1.201 + return items.forget(); 1.202 + } 1.203 + 1.204 + aRv = Init(); 1.205 + if (aRv.Failed()) { 1.206 + return nullptr; 1.207 + } 1.208 + 1.209 + uint32_t length; 1.210 + char **keys; 1.211 + aRv = appCache->GatherEntries(nsIApplicationCache::ITEM_DYNAMIC, 1.212 + &length, &keys); 1.213 + if (aRv.Failed()) { 1.214 + return nullptr; 1.215 + } 1.216 + 1.217 + for (uint32_t i = 0; i < length; i++) { 1.218 + items->Add(NS_ConvertUTF8toUTF16(keys[i])); 1.219 + } 1.220 + 1.221 + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(length, keys); 1.222 + 1.223 + return items.forget(); 1.224 +} 1.225 + 1.226 +NS_IMETHODIMP 1.227 +nsDOMOfflineResourceList::GetMozItems(nsISupports** aItems) 1.228 +{ 1.229 + ErrorResult rv; 1.230 + nsRefPtr<DOMStringList> items = GetMozItems(rv); 1.231 + items.forget(aItems); 1.232 + return rv.ErrorCode(); 1.233 +} 1.234 + 1.235 +NS_IMETHODIMP 1.236 +nsDOMOfflineResourceList::MozHasItem(const nsAString& aURI, bool* aExists) 1.237 +{ 1.238 + if (IS_CHILD_PROCESS()) 1.239 + return NS_ERROR_NOT_IMPLEMENTED; 1.240 + 1.241 + nsresult rv = Init(); 1.242 + NS_ENSURE_SUCCESS(rv, rv); 1.243 + 1.244 + nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache(); 1.245 + if (!appCache) { 1.246 + return NS_ERROR_DOM_INVALID_STATE_ERR; 1.247 + } 1.248 + 1.249 + nsAutoCString key; 1.250 + rv = GetCacheKey(aURI, key); 1.251 + NS_ENSURE_SUCCESS(rv, rv); 1.252 + 1.253 + uint32_t types; 1.254 + rv = appCache->GetTypes(key, &types); 1.255 + if (rv == NS_ERROR_CACHE_KEY_NOT_FOUND) { 1.256 + *aExists = false; 1.257 + return NS_OK; 1.258 + } 1.259 + NS_ENSURE_SUCCESS(rv, rv); 1.260 + 1.261 + *aExists = ((types & nsIApplicationCache::ITEM_DYNAMIC) != 0); 1.262 + return NS_OK; 1.263 +} 1.264 + 1.265 +NS_IMETHODIMP 1.266 +nsDOMOfflineResourceList::GetMozLength(uint32_t *aLength) 1.267 +{ 1.268 + if (IS_CHILD_PROCESS()) 1.269 + return NS_ERROR_NOT_IMPLEMENTED; 1.270 + 1.271 + if (!mManifestURI) { 1.272 + *aLength = 0; 1.273 + return NS_OK; 1.274 + } 1.275 + 1.276 + nsresult rv = Init(); 1.277 + NS_ENSURE_SUCCESS(rv, rv); 1.278 + 1.279 + rv = CacheKeys(); 1.280 + NS_ENSURE_SUCCESS(rv, rv); 1.281 + 1.282 + *aLength = mCachedKeysCount; 1.283 + return NS_OK; 1.284 +} 1.285 + 1.286 +NS_IMETHODIMP 1.287 +nsDOMOfflineResourceList::MozItem(uint32_t aIndex, nsAString& aURI) 1.288 +{ 1.289 + if (IS_CHILD_PROCESS()) 1.290 + return NS_ERROR_NOT_IMPLEMENTED; 1.291 + 1.292 + nsresult rv = Init(); 1.293 + NS_ENSURE_SUCCESS(rv, rv); 1.294 + 1.295 + SetDOMStringToNull(aURI); 1.296 + 1.297 + rv = CacheKeys(); 1.298 + NS_ENSURE_SUCCESS(rv, rv); 1.299 + 1.300 + if (aIndex >= mCachedKeysCount) 1.301 + return NS_ERROR_NOT_AVAILABLE; 1.302 + 1.303 + CopyUTF8toUTF16(mCachedKeys[aIndex], aURI); 1.304 + 1.305 + return NS_OK; 1.306 +} 1.307 + 1.308 +NS_IMETHODIMP 1.309 +nsDOMOfflineResourceList::MozAdd(const nsAString& aURI) 1.310 +{ 1.311 + if (IS_CHILD_PROCESS()) 1.312 + return NS_ERROR_NOT_IMPLEMENTED; 1.313 + 1.314 + nsresult rv = Init(); 1.315 + NS_ENSURE_SUCCESS(rv, rv); 1.316 + 1.317 + if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) { 1.318 + return NS_ERROR_DOM_SECURITY_ERR; 1.319 + } 1.320 + 1.321 + nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache(); 1.322 + if (!appCache) { 1.323 + return NS_ERROR_DOM_INVALID_STATE_ERR; 1.324 + } 1.325 + 1.326 + if (aURI.Length() > MAX_URI_LENGTH) return NS_ERROR_DOM_BAD_URI; 1.327 + 1.328 + // this will fail if the URI is not absolute 1.329 + nsCOMPtr<nsIURI> requestedURI; 1.330 + rv = NS_NewURI(getter_AddRefs(requestedURI), aURI); 1.331 + NS_ENSURE_SUCCESS(rv, rv); 1.332 + 1.333 + nsAutoCString scheme; 1.334 + rv = requestedURI->GetScheme(scheme); 1.335 + NS_ENSURE_SUCCESS(rv, rv); 1.336 + 1.337 + bool match; 1.338 + rv = mManifestURI->SchemeIs(scheme.get(), &match); 1.339 + NS_ENSURE_SUCCESS(rv, rv); 1.340 + 1.341 + if (!match) { 1.342 + return NS_ERROR_DOM_SECURITY_ERR; 1.343 + } 1.344 + 1.345 + uint32_t length; 1.346 + rv = GetMozLength(&length); 1.347 + NS_ENSURE_SUCCESS(rv, rv); 1.348 + uint32_t maxEntries = 1.349 + Preferences::GetUint(kMaxEntriesPref, DEFAULT_MAX_ENTRIES); 1.350 + 1.351 + if (length > maxEntries) return NS_ERROR_NOT_AVAILABLE; 1.352 + 1.353 + ClearCachedKeys(); 1.354 + 1.355 + nsCOMPtr<nsIOfflineCacheUpdate> update = 1.356 + do_CreateInstance(NS_OFFLINECACHEUPDATE_CONTRACTID, &rv); 1.357 + NS_ENSURE_SUCCESS(rv, rv); 1.358 + 1.359 + nsAutoCString clientID; 1.360 + rv = appCache->GetClientID(clientID); 1.361 + NS_ENSURE_SUCCESS(rv, rv); 1.362 + 1.363 + rv = update->InitPartial(mManifestURI, clientID, mDocumentURI); 1.364 + NS_ENSURE_SUCCESS(rv, rv); 1.365 + 1.366 + rv = update->AddDynamicURI(requestedURI); 1.367 + NS_ENSURE_SUCCESS(rv, rv); 1.368 + 1.369 + rv = update->Schedule(); 1.370 + NS_ENSURE_SUCCESS(rv, rv); 1.371 + 1.372 + return NS_OK; 1.373 +} 1.374 + 1.375 +NS_IMETHODIMP 1.376 +nsDOMOfflineResourceList::MozRemove(const nsAString& aURI) 1.377 +{ 1.378 + if (IS_CHILD_PROCESS()) 1.379 + return NS_ERROR_NOT_IMPLEMENTED; 1.380 + 1.381 + nsresult rv = Init(); 1.382 + NS_ENSURE_SUCCESS(rv, rv); 1.383 + 1.384 + if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) { 1.385 + return NS_ERROR_DOM_SECURITY_ERR; 1.386 + } 1.387 + 1.388 + nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache(); 1.389 + if (!appCache) { 1.390 + return NS_ERROR_DOM_INVALID_STATE_ERR; 1.391 + } 1.392 + 1.393 + nsAutoCString key; 1.394 + rv = GetCacheKey(aURI, key); 1.395 + NS_ENSURE_SUCCESS(rv, rv); 1.396 + 1.397 + ClearCachedKeys(); 1.398 + 1.399 + // XXX: This is a race condition. remove() is specced to remove 1.400 + // from the currently associated application cache, but if this 1.401 + // happens during an update (or after an update, if we haven't 1.402 + // swapped yet), that remove() will be lost when the next update is 1.403 + // finished. Need to bring this issue up. 1.404 + 1.405 + rv = appCache->UnmarkEntry(key, nsIApplicationCache::ITEM_DYNAMIC); 1.406 + NS_ENSURE_SUCCESS(rv, rv); 1.407 + 1.408 + return NS_OK; 1.409 +} 1.410 + 1.411 +NS_IMETHODIMP 1.412 +nsDOMOfflineResourceList::GetStatus(uint16_t *aStatus) 1.413 +{ 1.414 + nsresult rv = Init(); 1.415 + 1.416 + // Init may fail with INVALID_STATE_ERR if there is no manifest URI. 1.417 + // The status attribute should not throw that exception, convert it 1.418 + // to an UNCACHED. 1.419 + if (rv == NS_ERROR_DOM_INVALID_STATE_ERR || 1.420 + !nsContentUtils::OfflineAppAllowed(mDocumentURI)) { 1.421 + *aStatus = nsIDOMOfflineResourceList::UNCACHED; 1.422 + return NS_OK; 1.423 + } 1.424 + 1.425 + NS_ENSURE_SUCCESS(rv, rv); 1.426 + 1.427 + // If this object is not associated with a cache, return UNCACHED 1.428 + nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache(); 1.429 + if (!appCache) { 1.430 + *aStatus = nsIDOMOfflineResourceList::UNCACHED; 1.431 + return NS_OK; 1.432 + } 1.433 + 1.434 + 1.435 + // If there is an update in process, use its status. 1.436 + if (mCacheUpdate && mExposeCacheUpdateStatus) { 1.437 + rv = mCacheUpdate->GetStatus(aStatus); 1.438 + if (NS_SUCCEEDED(rv) && *aStatus != nsIDOMOfflineResourceList::IDLE) { 1.439 + return NS_OK; 1.440 + } 1.441 + } 1.442 + 1.443 + if (mAvailableApplicationCache) { 1.444 + *aStatus = nsIDOMOfflineResourceList::UPDATEREADY; 1.445 + return NS_OK; 1.446 + } 1.447 + 1.448 + *aStatus = mStatus; 1.449 + return NS_OK; 1.450 +} 1.451 + 1.452 +NS_IMETHODIMP 1.453 +nsDOMOfflineResourceList::Update() 1.454 +{ 1.455 + nsresult rv = Init(); 1.456 + NS_ENSURE_SUCCESS(rv, rv); 1.457 + 1.458 + if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) { 1.459 + return NS_ERROR_DOM_SECURITY_ERR; 1.460 + } 1.461 + 1.462 + nsCOMPtr<nsIOfflineCacheUpdateService> updateService = 1.463 + do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &rv); 1.464 + NS_ENSURE_SUCCESS(rv, rv); 1.465 + 1.466 + nsCOMPtr<nsIDOMWindow> window = 1.467 + do_QueryInterface(GetOwner()); 1.468 + 1.469 + nsCOMPtr<nsIOfflineCacheUpdate> update; 1.470 + rv = updateService->ScheduleUpdate(mManifestURI, mDocumentURI, 1.471 + window, getter_AddRefs(update)); 1.472 + NS_ENSURE_SUCCESS(rv, rv); 1.473 + 1.474 + return NS_OK; 1.475 +} 1.476 + 1.477 +NS_IMETHODIMP 1.478 +nsDOMOfflineResourceList::SwapCache() 1.479 +{ 1.480 + nsresult rv = Init(); 1.481 + NS_ENSURE_SUCCESS(rv, rv); 1.482 + 1.483 + if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) { 1.484 + return NS_ERROR_DOM_SECURITY_ERR; 1.485 + } 1.486 + 1.487 + nsCOMPtr<nsIApplicationCache> currentAppCache = GetDocumentAppCache(); 1.488 + if (!currentAppCache) { 1.489 + return NS_ERROR_DOM_INVALID_STATE_ERR; 1.490 + } 1.491 + 1.492 + // Check the current and potentially newly available cache are not identical. 1.493 + if (mAvailableApplicationCache == currentAppCache) { 1.494 + return NS_ERROR_DOM_INVALID_STATE_ERR; 1.495 + } 1.496 + 1.497 + if (mAvailableApplicationCache) { 1.498 + nsCString currClientId, availClientId; 1.499 + currentAppCache->GetClientID(currClientId); 1.500 + mAvailableApplicationCache->GetClientID(availClientId); 1.501 + if (availClientId == currClientId) 1.502 + return NS_ERROR_DOM_INVALID_STATE_ERR; 1.503 + } else if (mStatus != OBSOLETE) { 1.504 + return NS_ERROR_DOM_INVALID_STATE_ERR; 1.505 + } 1.506 + 1.507 + ClearCachedKeys(); 1.508 + 1.509 + nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer = 1.510 + GetDocumentAppCacheContainer(); 1.511 + 1.512 + // In the case of an obsolete cache group, newAppCache might be null. 1.513 + // We will disassociate from the cache in that case. 1.514 + if (appCacheContainer) { 1.515 + rv = appCacheContainer->SetApplicationCache(mAvailableApplicationCache); 1.516 + NS_ENSURE_SUCCESS(rv, rv); 1.517 + } 1.518 + 1.519 + mAvailableApplicationCache = nullptr; 1.520 + mStatus = nsIDOMOfflineResourceList::IDLE; 1.521 + 1.522 + return NS_OK; 1.523 +} 1.524 + 1.525 +// 1.526 +// nsDOMOfflineResourceList::nsIDOMEventTarget 1.527 +// 1.528 + 1.529 +void 1.530 +nsDOMOfflineResourceList::FirePendingEvents() 1.531 +{ 1.532 + for (int32_t i = 0; i < mPendingEvents.Count(); ++i) { 1.533 + bool dummy; 1.534 + nsCOMPtr<nsIDOMEvent> event = mPendingEvents[i]; 1.535 + DispatchEvent(event, &dummy); 1.536 + } 1.537 + mPendingEvents.Clear(); 1.538 +} 1.539 + 1.540 +nsresult 1.541 +nsDOMOfflineResourceList::SendEvent(const nsAString &aEventName) 1.542 +{ 1.543 + // Don't send events to closed windows 1.544 + if (!GetOwner()) { 1.545 + return NS_OK; 1.546 + } 1.547 + 1.548 + if (!GetOwner()->GetDocShell()) { 1.549 + return NS_OK; 1.550 + } 1.551 + 1.552 + nsCOMPtr<nsIDOMEvent> event; 1.553 + nsresult rv = EventDispatcher::CreateEvent(this, nullptr, nullptr, 1.554 + NS_LITERAL_STRING("Events"), 1.555 + getter_AddRefs(event)); 1.556 + NS_ENSURE_SUCCESS(rv, rv); 1.557 + event->InitEvent(aEventName, false, true); 1.558 + 1.559 + // We assume anyone that managed to call SendEvent is trusted 1.560 + event->SetTrusted(true); 1.561 + 1.562 + // If the window is frozen or we're still catching up on events that were 1.563 + // queued while frozen, save the event for later. 1.564 + if (GetOwner()->IsFrozen() || mPendingEvents.Count() > 0) { 1.565 + mPendingEvents.AppendObject(event); 1.566 + return NS_OK; 1.567 + } 1.568 + 1.569 + bool dummy; 1.570 + DispatchEvent(event, &dummy); 1.571 + 1.572 + return NS_OK; 1.573 +} 1.574 + 1.575 + 1.576 +// 1.577 +// nsDOMOfflineResourceList::nsIObserver 1.578 +// 1.579 +NS_IMETHODIMP 1.580 +nsDOMOfflineResourceList::Observe(nsISupports *aSubject, 1.581 + const char *aTopic, 1.582 + const char16_t *aData) 1.583 +{ 1.584 + if (!strcmp(aTopic, "offline-cache-update-added")) { 1.585 + nsCOMPtr<nsIOfflineCacheUpdate> update = do_QueryInterface(aSubject); 1.586 + if (update) { 1.587 + UpdateAdded(update); 1.588 + } 1.589 + } else if (!strcmp(aTopic, "offline-cache-update-completed")) { 1.590 + nsCOMPtr<nsIOfflineCacheUpdate> update = do_QueryInterface(aSubject); 1.591 + if (update) { 1.592 + UpdateCompleted(update); 1.593 + } 1.594 + } 1.595 + 1.596 + return NS_OK; 1.597 +} 1.598 + 1.599 +// 1.600 +// nsDOMOfflineResourceList::nsIOfflineCacheUpdateObserver 1.601 +// 1.602 +NS_IMETHODIMP 1.603 +nsDOMOfflineResourceList::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate, 1.604 + uint32_t event) 1.605 +{ 1.606 + mExposeCacheUpdateStatus = 1.607 + (event == STATE_CHECKING) || 1.608 + (event == STATE_DOWNLOADING) || 1.609 + (event == STATE_ITEMSTARTED) || 1.610 + (event == STATE_ITEMCOMPLETED) || 1.611 + // During notification of "obsolete" we must expose state of the update 1.612 + (event == STATE_OBSOLETE); 1.613 + 1.614 + switch (event) { 1.615 + case STATE_ERROR: 1.616 + SendEvent(NS_LITERAL_STRING(ERROR_STR)); 1.617 + break; 1.618 + case STATE_CHECKING: 1.619 + SendEvent(NS_LITERAL_STRING(CHECKING_STR)); 1.620 + break; 1.621 + case STATE_NOUPDATE: 1.622 + SendEvent(NS_LITERAL_STRING(NOUPDATE_STR)); 1.623 + break; 1.624 + case STATE_OBSOLETE: 1.625 + mStatus = nsIDOMOfflineResourceList::OBSOLETE; 1.626 + mAvailableApplicationCache = nullptr; 1.627 + SendEvent(NS_LITERAL_STRING(OBSOLETE_STR)); 1.628 + break; 1.629 + case STATE_DOWNLOADING: 1.630 + SendEvent(NS_LITERAL_STRING(DOWNLOADING_STR)); 1.631 + break; 1.632 + case STATE_ITEMSTARTED: 1.633 + SendEvent(NS_LITERAL_STRING(PROGRESS_STR)); 1.634 + break; 1.635 + case STATE_ITEMCOMPLETED: 1.636 + // Nothing to do here... 1.637 + break; 1.638 + } 1.639 + 1.640 + return NS_OK; 1.641 +} 1.642 + 1.643 +NS_IMETHODIMP 1.644 +nsDOMOfflineResourceList::ApplicationCacheAvailable(nsIApplicationCache *aApplicationCache) 1.645 +{ 1.646 + nsCOMPtr<nsIApplicationCache> currentAppCache = GetDocumentAppCache(); 1.647 + if (currentAppCache) { 1.648 + // Document already has a cache, we cannot override it. swapCache is 1.649 + // here to do it on demand. 1.650 + 1.651 + // If the newly available cache is identical to the current cache, then 1.652 + // just ignore this event. 1.653 + if (aApplicationCache == currentAppCache) { 1.654 + return NS_OK; 1.655 + } 1.656 + 1.657 + nsCString currClientId, availClientId; 1.658 + currentAppCache->GetClientID(currClientId); 1.659 + aApplicationCache->GetClientID(availClientId); 1.660 + if (availClientId == currClientId) { 1.661 + return NS_OK; 1.662 + } 1.663 + 1.664 + mAvailableApplicationCache = aApplicationCache; 1.665 + return NS_OK; 1.666 + } 1.667 + 1.668 + nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer = 1.669 + GetDocumentAppCacheContainer(); 1.670 + 1.671 + if (appCacheContainer) { 1.672 + appCacheContainer->SetApplicationCache(aApplicationCache); 1.673 + } 1.674 + 1.675 + mAvailableApplicationCache = nullptr; 1.676 + return NS_OK; 1.677 +} 1.678 + 1.679 +nsresult 1.680 +nsDOMOfflineResourceList::GetCacheKey(const nsAString &aURI, nsCString &aKey) 1.681 +{ 1.682 + nsCOMPtr<nsIURI> requestedURI; 1.683 + nsresult rv = NS_NewURI(getter_AddRefs(requestedURI), aURI); 1.684 + NS_ENSURE_SUCCESS(rv, rv); 1.685 + 1.686 + return GetCacheKey(requestedURI, aKey); 1.687 +} 1.688 + 1.689 +nsresult 1.690 +nsDOMOfflineResourceList::UpdateAdded(nsIOfflineCacheUpdate *aUpdate) 1.691 +{ 1.692 + // Ignore partial updates. 1.693 + bool partial; 1.694 + nsresult rv = aUpdate->GetPartial(&partial); 1.695 + NS_ENSURE_SUCCESS(rv, rv); 1.696 + 1.697 + if (partial) { 1.698 + return NS_OK; 1.699 + } 1.700 + 1.701 + nsCOMPtr<nsIURI> updateURI; 1.702 + rv = aUpdate->GetManifestURI(getter_AddRefs(updateURI)); 1.703 + NS_ENSURE_SUCCESS(rv, rv); 1.704 + 1.705 + bool equals; 1.706 + rv = updateURI->Equals(mManifestURI, &equals); 1.707 + NS_ENSURE_SUCCESS(rv, rv); 1.708 + 1.709 + if (!equals) { 1.710 + // This update doesn't belong to us 1.711 + return NS_OK; 1.712 + } 1.713 + 1.714 + NS_ENSURE_TRUE(!mCacheUpdate, NS_ERROR_FAILURE); 1.715 + 1.716 + // We don't need to emit signals here. Updates are either added 1.717 + // when they are scheduled (in which case they are always IDLE) or 1.718 + // they are added when the applicationCache object is initialized, so there 1.719 + // are no listeners to accept signals anyway. 1.720 + 1.721 + mCacheUpdate = aUpdate; 1.722 + mCacheUpdate->AddObserver(this, true); 1.723 + 1.724 + return NS_OK; 1.725 +} 1.726 + 1.727 +already_AddRefed<nsIApplicationCacheContainer> 1.728 +nsDOMOfflineResourceList::GetDocumentAppCacheContainer() 1.729 +{ 1.730 + nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(GetOwner()); 1.731 + if (!webnav) { 1.732 + return nullptr; 1.733 + } 1.734 + 1.735 + nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer = 1.736 + do_GetInterface(webnav); 1.737 + return appCacheContainer.forget(); 1.738 +} 1.739 + 1.740 +already_AddRefed<nsIApplicationCache> 1.741 +nsDOMOfflineResourceList::GetDocumentAppCache() 1.742 +{ 1.743 + nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer = 1.744 + GetDocumentAppCacheContainer(); 1.745 + 1.746 + if (appCacheContainer) { 1.747 + nsCOMPtr<nsIApplicationCache> applicationCache; 1.748 + appCacheContainer->GetApplicationCache( 1.749 + getter_AddRefs(applicationCache)); 1.750 + return applicationCache.forget(); 1.751 + } 1.752 + 1.753 + return nullptr; 1.754 +} 1.755 + 1.756 +nsresult 1.757 +nsDOMOfflineResourceList::UpdateCompleted(nsIOfflineCacheUpdate *aUpdate) 1.758 +{ 1.759 + if (aUpdate != mCacheUpdate) { 1.760 + // This isn't the update we're watching. 1.761 + return NS_OK; 1.762 + } 1.763 + 1.764 + bool partial; 1.765 + mCacheUpdate->GetPartial(&partial); 1.766 + bool isUpgrade; 1.767 + mCacheUpdate->GetIsUpgrade(&isUpgrade); 1.768 + 1.769 + bool succeeded; 1.770 + nsresult rv = mCacheUpdate->GetSucceeded(&succeeded); 1.771 + 1.772 + mCacheUpdate->RemoveObserver(this); 1.773 + mCacheUpdate = nullptr; 1.774 + 1.775 + if (NS_SUCCEEDED(rv) && succeeded && !partial) { 1.776 + mStatus = nsIDOMOfflineResourceList::IDLE; 1.777 + if (isUpgrade) { 1.778 + SendEvent(NS_LITERAL_STRING(UPDATEREADY_STR)); 1.779 + } else { 1.780 + SendEvent(NS_LITERAL_STRING(CACHED_STR)); 1.781 + } 1.782 + } 1.783 + 1.784 + return NS_OK; 1.785 +} 1.786 + 1.787 +nsresult 1.788 +nsDOMOfflineResourceList::GetCacheKey(nsIURI *aURI, nsCString &aKey) 1.789 +{ 1.790 + nsresult rv = aURI->GetAsciiSpec(aKey); 1.791 + NS_ENSURE_SUCCESS(rv, rv); 1.792 + 1.793 + // url fragments aren't used in cache keys 1.794 + nsAutoCString::const_iterator specStart, specEnd; 1.795 + aKey.BeginReading(specStart); 1.796 + aKey.EndReading(specEnd); 1.797 + if (FindCharInReadable('#', specStart, specEnd)) { 1.798 + aKey.BeginReading(specEnd); 1.799 + aKey = Substring(specEnd, specStart); 1.800 + } 1.801 + 1.802 + return NS_OK; 1.803 +} 1.804 + 1.805 +nsresult 1.806 +nsDOMOfflineResourceList::CacheKeys() 1.807 +{ 1.808 + if (IS_CHILD_PROCESS()) 1.809 + return NS_ERROR_NOT_IMPLEMENTED; 1.810 + 1.811 + if (mCachedKeys) 1.812 + return NS_OK; 1.813 + 1.814 + nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(GetOwner()); 1.815 + nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window); 1.816 + nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav); 1.817 + 1.818 + uint32_t appId = 0; 1.819 + bool inBrowser = false; 1.820 + if (loadContext) { 1.821 + loadContext->GetAppId(&appId); 1.822 + loadContext->GetIsInBrowserElement(&inBrowser); 1.823 + } 1.824 + 1.825 + nsAutoCString groupID; 1.826 + mApplicationCacheService->BuildGroupIDForApp( 1.827 + mManifestURI, appId, inBrowser, groupID); 1.828 + 1.829 + nsCOMPtr<nsIApplicationCache> appCache; 1.830 + mApplicationCacheService->GetActiveCache(groupID, 1.831 + getter_AddRefs(appCache)); 1.832 + 1.833 + if (!appCache) { 1.834 + return NS_ERROR_DOM_INVALID_STATE_ERR; 1.835 + } 1.836 + 1.837 + return appCache->GatherEntries(nsIApplicationCache::ITEM_DYNAMIC, 1.838 + &mCachedKeysCount, &mCachedKeys); 1.839 +} 1.840 + 1.841 +void 1.842 +nsDOMOfflineResourceList::ClearCachedKeys() 1.843 +{ 1.844 + if (mCachedKeys) { 1.845 + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mCachedKeysCount, mCachedKeys); 1.846 + mCachedKeys = nullptr; 1.847 + mCachedKeysCount = 0; 1.848 + } 1.849 +}