michael@0: /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "OfflineCacheUpdateChild.h" michael@0: #include "nsOfflineCacheUpdate.h" michael@0: #include "mozilla/dom/ContentChild.h" michael@0: #include "mozilla/dom/TabChild.h" michael@0: #include "mozilla/ipc/URIUtils.h" michael@0: #include "mozilla/net/NeckoCommon.h" michael@0: michael@0: #include "nsIApplicationCacheContainer.h" michael@0: #include "nsIApplicationCacheChannel.h" michael@0: #include "nsIApplicationCacheService.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsIDocShellTreeItem.h" michael@0: #include "nsIDocShellTreeOwner.h" michael@0: #include "nsIDOMWindow.h" michael@0: #include "nsIDOMOfflineResourceList.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIURL.h" michael@0: #include "nsITabChild.h" michael@0: #include "nsNetCID.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsStreamUtils.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsProxyRelease.h" michael@0: #include "prlog.h" michael@0: #include "nsIAsyncVerifyRedirectCallback.h" michael@0: michael@0: using namespace mozilla::ipc; michael@0: using namespace mozilla::net; michael@0: using mozilla::dom::TabChild; michael@0: michael@0: #if defined(PR_LOGGING) michael@0: // michael@0: // To enable logging (see prlog.h for full details): michael@0: // michael@0: // set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5 michael@0: // set NSPR_LOG_FILE=offlineupdate.log michael@0: // michael@0: // this enables PR_LOG_ALWAYS level information and places all output in michael@0: // the file offlineupdate.log michael@0: // michael@0: extern PRLogModuleInfo *gOfflineCacheUpdateLog; michael@0: #endif michael@0: michael@0: #undef LOG michael@0: #define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args) michael@0: michael@0: #undef LOG_ENABLED michael@0: #define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4) michael@0: michael@0: namespace mozilla { michael@0: namespace docshell { michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // OfflineCacheUpdateChild::nsISupports michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(OfflineCacheUpdateChild) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdate) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_ADDREF(OfflineCacheUpdateChild) michael@0: NS_IMPL_RELEASE(OfflineCacheUpdateChild) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // OfflineCacheUpdateChild michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: OfflineCacheUpdateChild::OfflineCacheUpdateChild(nsIDOMWindow* aWindow) michael@0: : mState(STATE_UNINITIALIZED) michael@0: , mIsUpgrade(false) michael@0: , mAppID(NECKO_NO_APP_ID) michael@0: , mInBrowser(false) michael@0: , mWindow(aWindow) michael@0: , mByteProgress(0) michael@0: { michael@0: } michael@0: michael@0: OfflineCacheUpdateChild::~OfflineCacheUpdateChild() michael@0: { michael@0: LOG(("OfflineCacheUpdateChild::~OfflineCacheUpdateChild [%p]", this)); michael@0: } michael@0: michael@0: void michael@0: OfflineCacheUpdateChild::GatherObservers(nsCOMArray &aObservers) michael@0: { michael@0: for (int32_t i = 0; i < mWeakObservers.Count(); i++) { michael@0: nsCOMPtr observer = michael@0: do_QueryReferent(mWeakObservers[i]); michael@0: if (observer) michael@0: aObservers.AppendObject(observer); michael@0: else michael@0: mWeakObservers.RemoveObjectAt(i--); michael@0: } michael@0: michael@0: for (int32_t i = 0; i < mObservers.Count(); i++) { michael@0: aObservers.AppendObject(mObservers[i]); michael@0: } michael@0: } michael@0: michael@0: void michael@0: OfflineCacheUpdateChild::SetDocument(nsIDOMDocument *aDocument) michael@0: { michael@0: // The design is one document for one cache update on the content process. michael@0: NS_ASSERTION(!mDocument, "Setting more then a single document on a child offline cache update"); michael@0: michael@0: LOG(("Document %p added to update child %p", aDocument, this)); michael@0: michael@0: // Add document only if it was not loaded from an offline cache. michael@0: // If it were loaded from an offline cache then it has already michael@0: // been associated with it and must not be again cached as michael@0: // implicit (which are the reasons we collect documents here). michael@0: nsCOMPtr document = do_QueryInterface(aDocument); michael@0: if (!document) michael@0: return; michael@0: michael@0: nsIChannel* channel = document->GetChannel(); michael@0: nsCOMPtr appCacheChannel = michael@0: do_QueryInterface(channel); michael@0: if (!appCacheChannel) michael@0: return; michael@0: michael@0: bool loadedFromAppCache; michael@0: appCacheChannel->GetLoadedFromApplicationCache(&loadedFromAppCache); michael@0: if (loadedFromAppCache) michael@0: return; michael@0: michael@0: mDocument = aDocument; michael@0: } michael@0: michael@0: nsresult michael@0: OfflineCacheUpdateChild::AssociateDocument(nsIDOMDocument *aDocument, michael@0: nsIApplicationCache *aApplicationCache) michael@0: { michael@0: // Check that the document that requested this update was michael@0: // previously associated with an application cache. If not, it michael@0: // should be associated with the new one. michael@0: nsCOMPtr container = michael@0: do_QueryInterface(aDocument); michael@0: if (!container) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr existingCache; michael@0: nsresult rv = container->GetApplicationCache(getter_AddRefs(existingCache)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!existingCache) { michael@0: #if defined(PR_LOGGING) michael@0: if (LOG_ENABLED()) { michael@0: nsAutoCString clientID; michael@0: if (aApplicationCache) { michael@0: aApplicationCache->GetClientID(clientID); michael@0: } michael@0: LOG(("Update %p: associating app cache %s to document %p", michael@0: this, clientID.get(), aDocument)); michael@0: } michael@0: #endif michael@0: michael@0: rv = container->SetApplicationCache(aApplicationCache); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // OfflineCacheUpdateChild::nsIOfflineCacheUpdate michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: OfflineCacheUpdateChild::Init(nsIURI *aManifestURI, michael@0: nsIURI *aDocumentURI, michael@0: nsIDOMDocument *aDocument, michael@0: nsIFile *aCustomProfileDir, michael@0: uint32_t aAppID, michael@0: bool aInBrowser) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // Make sure the service has been initialized michael@0: nsOfflineCacheUpdateService* service = michael@0: nsOfflineCacheUpdateService::EnsureService(); michael@0: if (!service) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (aCustomProfileDir) { michael@0: NS_ERROR("Custom Offline Cache Update not supported on child process"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: LOG(("OfflineCacheUpdateChild::Init [%p]", this)); michael@0: michael@0: // Only http and https applications are supported. michael@0: bool match; michael@0: rv = aManifestURI->SchemeIs("http", &match); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!match) { michael@0: rv = aManifestURI->SchemeIs("https", &match); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (!match) michael@0: return NS_ERROR_ABORT; michael@0: } michael@0: michael@0: mManifestURI = aManifestURI; michael@0: michael@0: rv = mManifestURI->GetAsciiHost(mUpdateDomain); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mDocumentURI = aDocumentURI; michael@0: michael@0: mState = STATE_INITIALIZED; michael@0: michael@0: if (aDocument) michael@0: SetDocument(aDocument); michael@0: michael@0: mAppID = aAppID; michael@0: mInBrowser = aInBrowser; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OfflineCacheUpdateChild::InitPartial(nsIURI *aManifestURI, michael@0: const nsACString& clientID, michael@0: nsIURI *aDocumentURI) michael@0: { michael@0: NS_NOTREACHED("Not expected to do partial offline cache updates" michael@0: " on the child process"); michael@0: // For now leaving this method, we may discover we need it. michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OfflineCacheUpdateChild::InitForUpdateCheck(nsIURI *aManifestURI, michael@0: uint32_t aAppID, michael@0: bool aInBrowser, michael@0: nsIObserver *aObserver) michael@0: { michael@0: NS_NOTREACHED("Not expected to do only update checks" michael@0: " from the child process"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OfflineCacheUpdateChild::GetUpdateDomain(nsACString &aUpdateDomain) michael@0: { michael@0: NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: aUpdateDomain = mUpdateDomain; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OfflineCacheUpdateChild::GetStatus(uint16_t *aStatus) michael@0: { michael@0: switch (mState) { michael@0: case STATE_CHECKING : michael@0: *aStatus = nsIDOMOfflineResourceList::CHECKING; michael@0: return NS_OK; michael@0: case STATE_DOWNLOADING : michael@0: *aStatus = nsIDOMOfflineResourceList::DOWNLOADING; michael@0: return NS_OK; michael@0: default : michael@0: *aStatus = nsIDOMOfflineResourceList::IDLE; michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OfflineCacheUpdateChild::GetPartial(bool *aPartial) michael@0: { michael@0: *aPartial = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OfflineCacheUpdateChild::GetManifestURI(nsIURI **aManifestURI) michael@0: { michael@0: NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: NS_IF_ADDREF(*aManifestURI = mManifestURI); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OfflineCacheUpdateChild::GetSucceeded(bool *aSucceeded) michael@0: { michael@0: NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE); michael@0: michael@0: *aSucceeded = mSucceeded; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OfflineCacheUpdateChild::GetIsUpgrade(bool *aIsUpgrade) michael@0: { michael@0: NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: *aIsUpgrade = mIsUpgrade; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OfflineCacheUpdateChild::AddDynamicURI(nsIURI *aURI) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OfflineCacheUpdateChild::Cancel() michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OfflineCacheUpdateChild::AddObserver(nsIOfflineCacheUpdateObserver *aObserver, michael@0: bool aHoldWeak) michael@0: { michael@0: LOG(("OfflineCacheUpdateChild::AddObserver [%p]", this)); michael@0: michael@0: NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: if (aHoldWeak) { michael@0: nsCOMPtr weakRef = do_GetWeakReference(aObserver); michael@0: mWeakObservers.AppendObject(weakRef); michael@0: } else { michael@0: mObservers.AppendObject(aObserver); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OfflineCacheUpdateChild::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver) michael@0: { michael@0: LOG(("OfflineCacheUpdateChild::RemoveObserver [%p]", this)); michael@0: michael@0: NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: for (int32_t i = 0; i < mWeakObservers.Count(); i++) { michael@0: nsCOMPtr observer = michael@0: do_QueryReferent(mWeakObservers[i]); michael@0: if (observer == aObserver) { michael@0: mWeakObservers.RemoveObjectAt(i); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: for (int32_t i = 0; i < mObservers.Count(); i++) { michael@0: if (mObservers[i] == aObserver) { michael@0: mObservers.RemoveObjectAt(i); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OfflineCacheUpdateChild::GetByteProgress(uint64_t * _result) michael@0: { michael@0: NS_ENSURE_ARG(_result); michael@0: michael@0: *_result = mByteProgress; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OfflineCacheUpdateChild::Schedule() michael@0: { michael@0: LOG(("OfflineCacheUpdateChild::Schedule [%p]", this)); michael@0: michael@0: NS_ASSERTION(mWindow, "Window must be provided to the offline cache update child"); michael@0: michael@0: nsCOMPtr piWindow = michael@0: do_QueryInterface(mWindow); michael@0: mWindow = nullptr; michael@0: michael@0: nsIDocShell *docshell = piWindow->GetDocShell(); michael@0: michael@0: nsCOMPtr item = do_QueryInterface(docshell); michael@0: if (!item) { michael@0: NS_WARNING("doc shell tree item is null"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsCOMPtr owner; michael@0: item->GetTreeOwner(getter_AddRefs(owner)); michael@0: michael@0: nsCOMPtr tabchild = do_GetInterface(owner); michael@0: // because owner implements nsITabChild, we can assume that it is michael@0: // the one and only TabChild. michael@0: TabChild* child = tabchild ? static_cast(tabchild.get()) : nullptr; michael@0: michael@0: if (MissingRequiredTabChild(child, "offlinecacheupdate")) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: URIParams manifestURI, documentURI; michael@0: SerializeURI(mManifestURI, manifestURI); michael@0: SerializeURI(mDocumentURI, documentURI); michael@0: michael@0: nsCOMPtr observerService = michael@0: mozilla::services::GetObserverService(); michael@0: if (observerService) { michael@0: LOG(("Calling offline-cache-update-added")); michael@0: observerService->NotifyObservers(static_cast(this), michael@0: "offline-cache-update-added", michael@0: nullptr); michael@0: LOG(("Done offline-cache-update-added")); michael@0: } michael@0: michael@0: // mDocument is non-null if both: michael@0: // 1. this update was initiated by a document that referred a manifest michael@0: // 2. the document has not already been loaded from the application cache michael@0: // This tells the update to cache this document even in case the manifest michael@0: // has not been changed since the last fetch. michael@0: // See also nsOfflineCacheUpdate::ScheduleImplicit. michael@0: bool stickDocument = mDocument != nullptr; michael@0: michael@0: // Need to addref ourself here, because the IPC stack doesn't hold michael@0: // a reference to us. Will be released in RecvFinish() that identifies michael@0: // the work has been done. michael@0: child->SendPOfflineCacheUpdateConstructor(this, manifestURI, documentURI, michael@0: stickDocument); michael@0: michael@0: // TabChild::DeallocPOfflineCacheUpdate will release this. michael@0: NS_ADDREF_THIS(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: OfflineCacheUpdateChild::RecvAssociateDocuments(const nsCString &cacheGroupId, michael@0: const nsCString &cacheClientId) michael@0: { michael@0: LOG(("OfflineCacheUpdateChild::RecvAssociateDocuments [%p, cache=%s]", this, cacheClientId.get())); michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr cache = michael@0: do_CreateInstance(NS_APPLICATIONCACHE_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return true; michael@0: michael@0: cache->InitAsHandle(cacheGroupId, cacheClientId); michael@0: michael@0: if (mDocument) { michael@0: AssociateDocument(mDocument, cache); michael@0: } michael@0: michael@0: nsCOMArray observers; michael@0: GatherObservers(observers); michael@0: michael@0: for (int32_t i = 0; i < observers.Count(); i++) michael@0: observers[i]->ApplicationCacheAvailable(cache); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: OfflineCacheUpdateChild::RecvNotifyStateEvent(const uint32_t &event, michael@0: const uint64_t &byteProgress) michael@0: { michael@0: LOG(("OfflineCacheUpdateChild::RecvNotifyStateEvent [%p]", this)); michael@0: michael@0: mByteProgress = byteProgress; michael@0: michael@0: // Convert the public observer state to our internal state michael@0: switch (event) { michael@0: case nsIOfflineCacheUpdateObserver::STATE_CHECKING: michael@0: mState = STATE_CHECKING; michael@0: break; michael@0: michael@0: case nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING: michael@0: mState = STATE_DOWNLOADING; michael@0: break; michael@0: michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: nsCOMArray observers; michael@0: GatherObservers(observers); michael@0: michael@0: for (int32_t i = 0; i < observers.Count(); i++) michael@0: observers[i]->UpdateStateChanged(this, event); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: OfflineCacheUpdateChild::RecvFinish(const bool &succeeded, michael@0: const bool &isUpgrade) michael@0: { michael@0: LOG(("OfflineCacheUpdateChild::RecvFinish [%p]", this)); michael@0: michael@0: nsRefPtr kungFuDeathGrip(this); michael@0: michael@0: mState = STATE_FINISHED; michael@0: mSucceeded = succeeded; michael@0: mIsUpgrade = isUpgrade; michael@0: michael@0: nsCOMPtr observerService = michael@0: mozilla::services::GetObserverService(); michael@0: if (observerService) { michael@0: LOG(("Calling offline-cache-update-completed")); michael@0: observerService->NotifyObservers(static_cast(this), michael@0: "offline-cache-update-completed", michael@0: nullptr); michael@0: LOG(("Done offline-cache-update-completed")); michael@0: } michael@0: michael@0: // This is by contract the last notification from the parent, release michael@0: // us now. This is corresponding to AddRef in Schedule(). michael@0: // TabChild::DeallocPOfflineCacheUpdate will call Release. michael@0: OfflineCacheUpdateChild::Send__delete__(this); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: } michael@0: }