diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/offline/nsDOMOfflineResourceList.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/offline/nsDOMOfflineResourceList.cpp')
-rw-r--r-- | dom/offline/nsDOMOfflineResourceList.cpp | 860 |
1 files changed, 860 insertions, 0 deletions
diff --git a/dom/offline/nsDOMOfflineResourceList.cpp b/dom/offline/nsDOMOfflineResourceList.cpp new file mode 100644 index 0000000000..43bc6bf582 --- /dev/null +++ b/dom/offline/nsDOMOfflineResourceList.cpp @@ -0,0 +1,860 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsDOMOfflineResourceList.h" +#include "nsError.h" +#include "mozilla/Components.h" +#include "mozilla/dom/DOMStringList.h" +#include "nsMemory.h" +#include "nsNetUtil.h" +#include "nsNetCID.h" +#include "nsServiceManagerUtils.h" +#include "nsICookieJarSettings.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIOfflineCacheUpdate.h" +#include "nsContentUtils.h" +#include "nsILoadContext.h" +#include "nsIObserverService.h" +#include "nsIScriptGlobalObject.h" +#include "nsIWebNavigation.h" +#include "nsOfflineCacheUpdate.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/OfflineResourceListBinding.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/EventListenerManager.h" +#include "mozilla/Preferences.h" +#include "mozilla/BasePrincipal.h" +#include "mozilla/StaticPrefs_browser.h" + +#include "nsXULAppAPI.h" +#define IS_CHILD_PROCESS() (GeckoProcessType_Default != XRE_GetProcessType()) + +using namespace mozilla; +using namespace mozilla::dom; + +// Event names + +#define CHECKING_STR u"checking" +#define ERROR_STR u"error" +#define NOUPDATE_STR u"noupdate" +#define DOWNLOADING_STR u"downloading" +#define PROGRESS_STR u"progress" +#define CACHED_STR u"cached" +#define UPDATEREADY_STR u"updateready" +#define OBSOLETE_STR u"obsolete" + +// To prevent abuse of the resource list for data storage, the number +// of offline urls and their length are limited. + +static const char kMaxEntriesPref[] = "offline.max_site_resources"; +#define DEFAULT_MAX_ENTRIES 100 +#define MAX_URI_LENGTH 2048 + +// +// nsDOMOfflineResourceList +// + +NS_IMPL_CYCLE_COLLECTION_WEAK_INHERITED(nsDOMOfflineResourceList, + DOMEventTargetHelper, mCacheUpdate, + mPendingEvents) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMOfflineResourceList) + NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdateObserver) + NS_INTERFACE_MAP_ENTRY(nsIObserver) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_ADDREF_INHERITED(nsDOMOfflineResourceList, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(nsDOMOfflineResourceList, DOMEventTargetHelper) + +nsDOMOfflineResourceList::nsDOMOfflineResourceList( + nsIURI* aManifestURI, nsIURI* aDocumentURI, nsIPrincipal* aLoadingPrincipal, + nsPIDOMWindowInner* aWindow) + : DOMEventTargetHelper(aWindow), + mInitialized(false), + mManifestURI(aManifestURI), + mDocumentURI(aDocumentURI), + mLoadingPrincipal(aLoadingPrincipal), + mExposeCacheUpdateStatus(true), + mStatus(OfflineResourceList_Binding::IDLE) {} + +nsDOMOfflineResourceList::~nsDOMOfflineResourceList() { ClearCachedKeys(); } + +JSObject* nsDOMOfflineResourceList::WrapObject( + JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { + return OfflineResourceList_Binding::Wrap(aCx, this, aGivenProto); +} + +nsresult nsDOMOfflineResourceList::Init() { + if (mInitialized) { + return NS_OK; + } + + if (!mManifestURI) { + return NS_ERROR_DOM_INVALID_STATE_ERR; + } + + mManifestURI->GetAsciiSpec(mManifestSpec); + bool isPrivateWin = + mLoadingPrincipal + ? mLoadingPrincipal->OriginAttributesRef().mPrivateBrowsingId > 0 + : false; + + nsresult rv = nsContentUtils::GetSecurityManager()->CheckSameOriginURI( + mManifestURI, mDocumentURI, true, isPrivateWin); + NS_ENSURE_SUCCESS(rv, rv); + + // Dynamically-managed resources are stored as a separate ownership list + // from the manifest. + nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(mDocumentURI); + if (!innerURI) return NS_ERROR_FAILURE; + + if (!IS_CHILD_PROCESS()) { + mApplicationCacheService = + do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // Check for in-progress cache updates + nsCOMPtr<nsIOfflineCacheUpdateService> cacheUpdateService = + components::OfflineCacheUpdate::Service(); + NS_ENSURE_TRUE(cacheUpdateService, NS_ERROR_UNEXPECTED); + + uint32_t numUpdates; + rv = cacheUpdateService->GetNumUpdates(&numUpdates); + NS_ENSURE_SUCCESS(rv, rv); + + for (uint32_t i = 0; i < numUpdates; i++) { + nsCOMPtr<nsIOfflineCacheUpdate> cacheUpdate; + rv = cacheUpdateService->GetUpdate(i, getter_AddRefs(cacheUpdate)); + NS_ENSURE_SUCCESS(rv, rv); + + UpdateAdded(cacheUpdate); + } + } + + // watch for new offline cache updates + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + if (!observerService) return NS_ERROR_FAILURE; + + rv = observerService->AddObserver(this, "offline-cache-update-added", true); + NS_ENSURE_SUCCESS(rv, rv); + rv = observerService->AddObserver(this, "offline-cache-update-completed", + true); + NS_ENSURE_SUCCESS(rv, rv); + + mInitialized = true; + + return NS_OK; +} + +void nsDOMOfflineResourceList::Disconnect() { + mPendingEvents.Clear(); + + if (mListenerManager) { + mListenerManager->Disconnect(); + mListenerManager = nullptr; + } +} + +already_AddRefed<DOMStringList> nsDOMOfflineResourceList::GetMozItems( + ErrorResult& aRv) { + if (IS_CHILD_PROCESS()) { + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); + return nullptr; + } + + RefPtr<DOMStringList> items = new DOMStringList(); + + // If we are not associated with an application cache, return an + // empty list. + nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache(); + if (!appCache) { + return items.forget(); + } + + aRv = Init(); + if (aRv.Failed()) { + return nullptr; + } + + nsTArray<nsCString> keys; + aRv = appCache->GatherEntries(nsIApplicationCache::ITEM_DYNAMIC, keys); + if (aRv.Failed()) { + return nullptr; + } + + for (auto& key : keys) { + items->Add(NS_ConvertUTF8toUTF16(key)); + } + + return items.forget(); +} + +bool nsDOMOfflineResourceList::MozHasItem(const nsAString& aURI, + ErrorResult& aRv) { + if (IS_CHILD_PROCESS()) { + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); + return false; + } + + nsresult rv = Init(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return false; + } + + nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache(); + if (!appCache) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return false; + } + + nsAutoCString key; + rv = GetCacheKey(aURI, key); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return false; + } + + uint32_t types; + rv = appCache->GetTypes(key, &types); + if (rv == NS_ERROR_CACHE_KEY_NOT_FOUND) { + return false; + } + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return false; + } + + return types & nsIApplicationCache::ITEM_DYNAMIC; +} + +uint32_t nsDOMOfflineResourceList::GetMozLength(ErrorResult& aRv) { + if (IS_CHILD_PROCESS()) { + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); + return 0; + } + + if (!mManifestURI) { + return 0; + } + + nsresult rv = Init(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return 0; + } + + rv = CacheKeys(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return 0; + } + + return mCachedKeys->Length(); +} + +void nsDOMOfflineResourceList::MozItem(uint32_t aIndex, nsAString& aURI, + ErrorResult& aRv) { + bool found; + IndexedGetter(aIndex, found, aURI, aRv); + if (!aRv.Failed() && !found) { + aRv.Throw(NS_ERROR_NOT_AVAILABLE); + } +} + +void nsDOMOfflineResourceList::IndexedGetter(uint32_t aIndex, bool& aFound, + nsAString& aURI, + ErrorResult& aRv) { + if (IS_CHILD_PROCESS()) { + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); + return; + } + + nsresult rv = Init(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } + + rv = CacheKeys(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } + + if (aIndex >= mCachedKeys->Length()) { + aFound = false; + return; + } + + aFound = true; + CopyUTF8toUTF16(mCachedKeys->ElementAt(aIndex), aURI); +} + +void nsDOMOfflineResourceList::MozAdd(const nsAString& aURI, ErrorResult& aRv) { + if (IS_CHILD_PROCESS()) { + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); + return; + } + + nsresult rv = Init(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } + + if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return; + } + + nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache(); + if (!appCache) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + + if (aURI.Length() > MAX_URI_LENGTH) { + aRv.Throw(NS_ERROR_DOM_BAD_URI); + return; + } + + // this will fail if the URI is not absolute + nsCOMPtr<nsIURI> requestedURI; + rv = NS_NewURI(getter_AddRefs(requestedURI), aURI); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } + + nsAutoCString scheme; + rv = requestedURI->GetScheme(scheme); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } + + bool match; + rv = mManifestURI->SchemeIs(scheme.get(), &match); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } + + if (!match) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return; + } + + uint32_t length = GetMozLength(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + uint32_t maxEntries = + Preferences::GetUint(kMaxEntriesPref, DEFAULT_MAX_ENTRIES); + + if (length > maxEntries) { + aRv.Throw(NS_ERROR_NOT_AVAILABLE); + return; + } + + ClearCachedKeys(); + + nsCOMPtr<nsIOfflineCacheUpdate> update = new nsOfflineCacheUpdate(); + + nsAutoCString clientID; + rv = appCache->GetClientID(clientID); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } + + RefPtr<Document> doc = GetOwner()->GetExtantDoc(); + nsCOMPtr<nsICookieJarSettings> cjs = doc ? doc->CookieJarSettings() : nullptr; + + rv = update->InitPartial(mManifestURI, clientID, mDocumentURI, + mLoadingPrincipal, cjs); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } + + rv = update->AddDynamicURI(requestedURI); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } + + rv = update->Schedule(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } +} + +void nsDOMOfflineResourceList::MozRemove(const nsAString& aURI, + ErrorResult& aRv) { + if (IS_CHILD_PROCESS()) { + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); + return; + } + + nsresult rv = Init(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } + + if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return; + } + + nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache(); + if (!appCache) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + + nsAutoCString key; + rv = GetCacheKey(aURI, key); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } + + ClearCachedKeys(); + + // XXX: This is a race condition. remove() is specced to remove + // from the currently associated application cache, but if this + // happens during an update (or after an update, if we haven't + // swapped yet), that remove() will be lost when the next update is + // finished. Need to bring this issue up. + + rv = appCache->UnmarkEntry(key, nsIApplicationCache::ITEM_DYNAMIC); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } +} + +uint16_t nsDOMOfflineResourceList::GetStatus(ErrorResult& aRv) { + nsresult rv = Init(); + + // Init may fail with INVALID_STATE_ERR if there is no manifest URI. + // The status attribute should not throw that exception, convert it + // to an UNCACHED. + if (rv == NS_ERROR_DOM_INVALID_STATE_ERR || + !nsContentUtils::OfflineAppAllowed(mDocumentURI)) { + return OfflineResourceList_Binding::UNCACHED; + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return 0; + } + + // If this object is not associated with a cache, return UNCACHED + nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache(); + if (!appCache) { + return OfflineResourceList_Binding::UNCACHED; + } + + // If there is an update in process, use its status. + if (mCacheUpdate && mExposeCacheUpdateStatus) { + uint16_t status; + rv = mCacheUpdate->GetStatus(&status); + if (NS_SUCCEEDED(rv) && status != OfflineResourceList_Binding::IDLE) { + return status; + } + } + + if (mAvailableApplicationCache) { + return OfflineResourceList_Binding::UPDATEREADY; + } + + return mStatus; +} + +void nsDOMOfflineResourceList::Update(ErrorResult& aRv) { + nsresult rv = Init(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } + + // If the storage is disabled this operation should be ignored. + if (!StaticPrefs::browser_cache_offline_storage_enable()) { + return; + } + + if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return; + } + + nsCOMPtr<nsIOfflineCacheUpdateService> updateService = + components::OfflineCacheUpdate::Service(); + if (NS_WARN_IF(!updateService)) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return; + } + + nsCOMPtr<nsPIDOMWindowInner> window = GetOwner(); + + nsCOMPtr<nsIOfflineCacheUpdate> update; + rv = updateService->ScheduleUpdate(mManifestURI, mDocumentURI, + mLoadingPrincipal, window, + getter_AddRefs(update)); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } +} + +void nsDOMOfflineResourceList::SwapCache(ErrorResult& aRv) { + nsresult rv = Init(); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } + + if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return; + } + + nsCOMPtr<nsIApplicationCache> currentAppCache = GetDocumentAppCache(); + if (!currentAppCache) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + + // Check the current and potentially newly available cache are not identical. + if (mAvailableApplicationCache == currentAppCache) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + + if (mAvailableApplicationCache) { + nsCString currClientId, availClientId; + currentAppCache->GetClientID(currClientId); + mAvailableApplicationCache->GetClientID(availClientId); + if (availClientId == currClientId) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + } else if (mStatus != OfflineResourceList_Binding::OBSOLETE) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + + ClearCachedKeys(); + + nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer = + GetDocumentAppCacheContainer(); + + // In the case of an obsolete cache group, newAppCache might be null. + // We will disassociate from the cache in that case. + if (appCacheContainer) { + rv = appCacheContainer->SetApplicationCache(mAvailableApplicationCache); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.Throw(rv); + return; + } + } + + mAvailableApplicationCache = nullptr; + mStatus = OfflineResourceList_Binding::IDLE; +} + +void nsDOMOfflineResourceList::FirePendingEvents() { + for (int32_t i = 0; i < mPendingEvents.Count(); ++i) { + RefPtr<Event> event = mPendingEvents[i]; + DispatchEvent(*event); + } + mPendingEvents.Clear(); +} + +void nsDOMOfflineResourceList::SendEvent(const nsAString& aEventName) { + // Don't send events to closed windows + if (!GetOwner()) { + return; + } + + if (!GetOwner()->GetDocShell()) { + return; + } + + RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr); + event->InitEvent(aEventName, false, true); + + // We assume anyone that managed to call SendEvent is trusted + event->SetTrusted(true); + + // If the window is frozen or we're still catching up on events that were + // queued while frozen, save the event for later. + if (GetOwner()->IsFrozen() || mPendingEvents.Count() > 0) { + mPendingEvents.AppendObject(event); + return; + } + + DispatchEvent(*event); +} + +// +// nsDOMOfflineResourceList::nsIObserver +// +NS_IMETHODIMP +nsDOMOfflineResourceList::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + if (!strcmp(aTopic, "offline-cache-update-added")) { + nsCOMPtr<nsIOfflineCacheUpdate> update = do_QueryInterface(aSubject); + if (update) { + UpdateAdded(update); + } + } else if (!strcmp(aTopic, "offline-cache-update-completed")) { + nsCOMPtr<nsIOfflineCacheUpdate> update = do_QueryInterface(aSubject); + if (update) { + UpdateCompleted(update); + } + } + + return NS_OK; +} + +// +// nsDOMOfflineResourceList::nsIOfflineCacheUpdateObserver +// +NS_IMETHODIMP +nsDOMOfflineResourceList::UpdateStateChanged(nsIOfflineCacheUpdate* aUpdate, + uint32_t event) { + mExposeCacheUpdateStatus = + (event == STATE_CHECKING) || (event == STATE_DOWNLOADING) || + (event == STATE_ITEMSTARTED) || (event == STATE_ITEMCOMPLETED) || + // During notification of "obsolete" we must expose state of the update + (event == STATE_OBSOLETE); + + switch (event) { + case STATE_ERROR: + SendEvent(nsLiteralString(ERROR_STR)); + break; + case STATE_CHECKING: + SendEvent(nsLiteralString(CHECKING_STR)); + break; + case STATE_NOUPDATE: + SendEvent(nsLiteralString(NOUPDATE_STR)); + break; + case STATE_OBSOLETE: + mStatus = OfflineResourceList_Binding::OBSOLETE; + mAvailableApplicationCache = nullptr; + SendEvent(nsLiteralString(OBSOLETE_STR)); + break; + case STATE_DOWNLOADING: + SendEvent(nsLiteralString(DOWNLOADING_STR)); + break; + case STATE_ITEMSTARTED: + SendEvent(nsLiteralString(PROGRESS_STR)); + break; + case STATE_ITEMCOMPLETED: + // Nothing to do here... + break; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMOfflineResourceList::ApplicationCacheAvailable( + nsIApplicationCache* aApplicationCache) { + nsCOMPtr<nsIApplicationCache> currentAppCache = GetDocumentAppCache(); + if (currentAppCache) { + // Document already has a cache, we cannot override it. swapCache is + // here to do it on demand. + + // If the newly available cache is identical to the current cache, then + // just ignore this event. + if (aApplicationCache == currentAppCache) { + return NS_OK; + } + + nsCString currClientId, availClientId; + currentAppCache->GetClientID(currClientId); + aApplicationCache->GetClientID(availClientId); + if (availClientId == currClientId) { + return NS_OK; + } + + mAvailableApplicationCache = aApplicationCache; + return NS_OK; + } + + nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer = + GetDocumentAppCacheContainer(); + + if (appCacheContainer) { + appCacheContainer->SetApplicationCache(aApplicationCache); + } + + mAvailableApplicationCache = nullptr; + return NS_OK; +} + +nsresult nsDOMOfflineResourceList::GetCacheKey(const nsAString& aURI, + nsCString& aKey) { + nsCOMPtr<nsIURI> requestedURI; + nsresult rv = NS_NewURI(getter_AddRefs(requestedURI), aURI); + NS_ENSURE_SUCCESS(rv, rv); + + return GetCacheKey(requestedURI, aKey); +} + +void nsDOMOfflineResourceList::UpdateAdded(nsIOfflineCacheUpdate* aUpdate) { + // Ignore partial updates. + bool partial; + nsresult rv = aUpdate->GetPartial(&partial); + NS_ENSURE_SUCCESS_VOID(rv); + if (partial) { + return; + } + + nsCOMPtr<nsIURI> updateURI; + rv = aUpdate->GetManifestURI(getter_AddRefs(updateURI)); + NS_ENSURE_SUCCESS_VOID(rv); + + bool equals; + rv = updateURI->Equals(mManifestURI, &equals); + NS_ENSURE_SUCCESS_VOID(rv); + + if (!equals) { + // This update doesn't belong to us + return; + } + + if (mCacheUpdate) { + return; + } + + // We don't need to emit signals here. Updates are either added + // when they are scheduled (in which case they are always IDLE) or + // they are added when the applicationCache object is initialized, so there + // are no listeners to accept signals anyway. + + mCacheUpdate = aUpdate; + mCacheUpdate->AddObserver(this, true); +} + +already_AddRefed<nsIApplicationCacheContainer> +nsDOMOfflineResourceList::GetDocumentAppCacheContainer() { + nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(GetOwner()); + if (!webnav) { + return nullptr; + } + + nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer = + do_GetInterface(webnav); + return appCacheContainer.forget(); +} + +already_AddRefed<nsIApplicationCache> +nsDOMOfflineResourceList::GetDocumentAppCache() { + nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer = + GetDocumentAppCacheContainer(); + + if (appCacheContainer) { + nsCOMPtr<nsIApplicationCache> applicationCache; + appCacheContainer->GetApplicationCache(getter_AddRefs(applicationCache)); + return applicationCache.forget(); + } + + return nullptr; +} + +void nsDOMOfflineResourceList::UpdateCompleted(nsIOfflineCacheUpdate* aUpdate) { + if (aUpdate != mCacheUpdate) { + // This isn't the update we're watching. + return; + } + + bool partial; + mCacheUpdate->GetPartial(&partial); + bool isUpgrade; + mCacheUpdate->GetIsUpgrade(&isUpgrade); + + bool succeeded; + nsresult rv = mCacheUpdate->GetSucceeded(&succeeded); + + mCacheUpdate->RemoveObserver(this); + mCacheUpdate = nullptr; + + if (NS_SUCCEEDED(rv) && succeeded && !partial) { + mStatus = OfflineResourceList_Binding::IDLE; + if (isUpgrade) { + SendEvent(nsLiteralString(UPDATEREADY_STR)); + } else { + SendEvent(nsLiteralString(CACHED_STR)); + } + } +} + +nsresult nsDOMOfflineResourceList::GetCacheKey(nsIURI* aURI, nsCString& aKey) { + nsresult rv = aURI->GetAsciiSpec(aKey); + NS_ENSURE_SUCCESS(rv, rv); + + // url fragments aren't used in cache keys + nsAutoCString::const_iterator specStart, specEnd; + aKey.BeginReading(specStart); + aKey.EndReading(specEnd); + if (FindCharInReadable('#', specStart, specEnd)) { + aKey.BeginReading(specEnd); + aKey = Substring(specEnd, specStart); + } + + return NS_OK; +} + +nsresult nsDOMOfflineResourceList::CacheKeys() { + if (IS_CHILD_PROCESS()) return NS_ERROR_NOT_IMPLEMENTED; + + if (mCachedKeys) return NS_OK; + + nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(GetOwner()); + nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window); + nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav); + + nsAutoCString originSuffix; + if (loadContext) { + mozilla::OriginAttributes oa; + loadContext->GetOriginAttributes(oa); + + oa.CreateSuffix(originSuffix); + } + + nsAutoCString groupID; + mApplicationCacheService->BuildGroupIDForSuffix(mManifestURI, originSuffix, + groupID); + + nsCOMPtr<nsIApplicationCache> appCache; + mApplicationCacheService->GetActiveCache(groupID, getter_AddRefs(appCache)); + + if (!appCache) { + return NS_ERROR_DOM_INVALID_STATE_ERR; + } + + mCachedKeys.emplace(); + nsresult rv = + appCache->GatherEntries(nsIApplicationCache::ITEM_DYNAMIC, *mCachedKeys); + if (NS_FAILED(rv)) { + mCachedKeys.reset(); + } + + return rv; +} + +void nsDOMOfflineResourceList::ClearCachedKeys() { mCachedKeys.reset(); } |