зеркало из https://github.com/mozilla/pjs.git
533 строки
16 KiB
C++
533 строки
16 KiB
C++
/* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Mozilla Corporation
|
|
* Portions created by the Initial Developer are Copyright (C) 2007
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Dave Camp <dcamp@mozilla.com>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#include "OfflineCacheUpdateChild.h"
|
|
#include "nsOfflineCacheUpdate.h"
|
|
#include "mozilla/dom/ContentChild.h"
|
|
#include "mozilla/dom/TabChild.h"
|
|
|
|
#include "nsIApplicationCacheContainer.h"
|
|
#include "nsIApplicationCacheChannel.h"
|
|
#include "nsIApplicationCacheService.h"
|
|
#include "nsIDocShell.h"
|
|
#include "nsIDocShellTreeItem.h"
|
|
#include "nsIDocShellTreeOwner.h"
|
|
#include "nsIDOMWindow.h"
|
|
#include "nsIDOMOfflineResourceList.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIObserverService.h"
|
|
#include "nsIURL.h"
|
|
#include "nsITabChild.h"
|
|
#include "nsNetCID.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "nsStreamUtils.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "nsProxyRelease.h"
|
|
#include "prlog.h"
|
|
#include "nsIAsyncVerifyRedirectCallback.h"
|
|
|
|
static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nsnull;
|
|
|
|
#if defined(PR_LOGGING)
|
|
//
|
|
// To enable logging (see prlog.h for full details):
|
|
//
|
|
// set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5
|
|
// set NSPR_LOG_FILE=offlineupdate.log
|
|
//
|
|
// this enables PR_LOG_ALWAYS level information and places all output in
|
|
// the file offlineupdate.log
|
|
//
|
|
extern PRLogModuleInfo *gOfflineCacheUpdateLog;
|
|
#endif
|
|
#define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args)
|
|
#define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4)
|
|
|
|
namespace mozilla {
|
|
namespace docshell {
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OfflineCacheUpdateChild::nsISupports
|
|
//-----------------------------------------------------------------------------
|
|
|
|
NS_INTERFACE_MAP_BEGIN(OfflineCacheUpdateChild)
|
|
NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdate)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
NS_IMPL_ADDREF(OfflineCacheUpdateChild)
|
|
NS_IMPL_RELEASE_WITH_DESTROY(OfflineCacheUpdateChild, RefcountHitZero())
|
|
|
|
void
|
|
OfflineCacheUpdateChild::RefcountHitZero()
|
|
{
|
|
if (mIPCActivated) {
|
|
// ContentChild::DeallocPOfflineCacheUpdate will delete this
|
|
OfflineCacheUpdateChild::Send__delete__(this);
|
|
} else {
|
|
delete this; // we never opened IPDL channel
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OfflineCacheUpdateChild <public>
|
|
//-----------------------------------------------------------------------------
|
|
|
|
OfflineCacheUpdateChild::OfflineCacheUpdateChild(nsIDOMWindow* aWindow)
|
|
: mState(STATE_UNINITIALIZED)
|
|
, mIsUpgrade(PR_FALSE)
|
|
, mIPCActivated(PR_FALSE)
|
|
, mWindow(aWindow)
|
|
{
|
|
}
|
|
|
|
OfflineCacheUpdateChild::~OfflineCacheUpdateChild()
|
|
{
|
|
LOG(("OfflineCacheUpdateChild::~OfflineCacheUpdateChild [%p]", this));
|
|
}
|
|
|
|
nsresult
|
|
OfflineCacheUpdateChild::GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers)
|
|
{
|
|
for (PRInt32 i = 0; i < mWeakObservers.Count(); i++) {
|
|
nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
|
|
do_QueryReferent(mWeakObservers[i]);
|
|
if (observer)
|
|
aObservers.AppendObject(observer);
|
|
else
|
|
mWeakObservers.RemoveObjectAt(i--);
|
|
}
|
|
|
|
for (PRInt32 i = 0; i < mObservers.Count(); i++) {
|
|
aObservers.AppendObject(mObservers[i]);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
OfflineCacheUpdateChild::SetDocument(nsIDOMDocument *aDocument)
|
|
{
|
|
// The design is one document for one cache update on the content process.
|
|
NS_ASSERTION(!mDocument, "Setting more then a single document on a child offline cache update");
|
|
|
|
LOG(("Document %p added to update child %p", aDocument, this));
|
|
|
|
// Add document only if it was not loaded from an offline cache.
|
|
// If it were loaded from an offline cache then it has already
|
|
// been associated with it and must not be again cached as
|
|
// implicit (which are the reasons we collect documents here).
|
|
nsCOMPtr<nsIDocument> document = do_QueryInterface(aDocument);
|
|
if (!document)
|
|
return;
|
|
|
|
nsIChannel* channel = document->GetChannel();
|
|
nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
|
|
do_QueryInterface(channel);
|
|
if (!appCacheChannel)
|
|
return;
|
|
|
|
PRBool loadedFromAppCache;
|
|
appCacheChannel->GetLoadedFromApplicationCache(&loadedFromAppCache);
|
|
if (loadedFromAppCache)
|
|
return;
|
|
|
|
mDocument = aDocument;
|
|
}
|
|
|
|
nsresult
|
|
OfflineCacheUpdateChild::AssociateDocument(nsIDOMDocument *aDocument,
|
|
nsIApplicationCache *aApplicationCache)
|
|
{
|
|
// Check that the document that requested this update was
|
|
// previously associated with an application cache. If not, it
|
|
// should be associated with the new one.
|
|
nsCOMPtr<nsIApplicationCacheContainer> container =
|
|
do_QueryInterface(aDocument);
|
|
if (!container)
|
|
return NS_OK;
|
|
|
|
nsCOMPtr<nsIApplicationCache> existingCache;
|
|
nsresult rv = container->GetApplicationCache(getter_AddRefs(existingCache));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (!existingCache) {
|
|
#if defined(PR_LOGGING)
|
|
if (LOG_ENABLED()) {
|
|
nsCAutoString clientID;
|
|
if (aApplicationCache) {
|
|
aApplicationCache->GetClientID(clientID);
|
|
}
|
|
LOG(("Update %p: associating app cache %s to document %p",
|
|
this, clientID.get(), aDocument));
|
|
}
|
|
#endif
|
|
|
|
rv = container->SetApplicationCache(aApplicationCache);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// OfflineCacheUpdateChild::nsIOfflineCacheUpdate
|
|
//-----------------------------------------------------------------------------
|
|
|
|
NS_IMETHODIMP
|
|
OfflineCacheUpdateChild::Init(nsIURI *aManifestURI,
|
|
nsIURI *aDocumentURI,
|
|
nsIDOMDocument *aDocument)
|
|
{
|
|
nsresult rv;
|
|
|
|
// Make sure the service has been initialized
|
|
nsOfflineCacheUpdateService* service =
|
|
nsOfflineCacheUpdateService::EnsureService();
|
|
if (!service)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
LOG(("OfflineCacheUpdateChild::Init [%p]", this));
|
|
|
|
// Only http and https applications are supported.
|
|
PRBool match;
|
|
rv = aManifestURI->SchemeIs("http", &match);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (!match) {
|
|
rv = aManifestURI->SchemeIs("https", &match);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
if (!match)
|
|
return NS_ERROR_ABORT;
|
|
}
|
|
|
|
mManifestURI = aManifestURI;
|
|
|
|
rv = mManifestURI->GetAsciiHost(mUpdateDomain);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
mDocumentURI = aDocumentURI;
|
|
|
|
mState = STATE_INITIALIZED;
|
|
|
|
if (aDocument)
|
|
SetDocument(aDocument);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
OfflineCacheUpdateChild::InitPartial(nsIURI *aManifestURI,
|
|
const nsACString& clientID,
|
|
nsIURI *aDocumentURI)
|
|
{
|
|
NS_NOTREACHED("Not expected to do partial offline cache updates"
|
|
" on the child process");
|
|
// For now leaving this method, we may discover we need it.
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
OfflineCacheUpdateChild::GetUpdateDomain(nsACString &aUpdateDomain)
|
|
{
|
|
NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
aUpdateDomain = mUpdateDomain;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
OfflineCacheUpdateChild::GetStatus(PRUint16 *aStatus)
|
|
{
|
|
switch (mState) {
|
|
case STATE_CHECKING :
|
|
*aStatus = nsIDOMOfflineResourceList::CHECKING;
|
|
return NS_OK;
|
|
case STATE_DOWNLOADING :
|
|
*aStatus = nsIDOMOfflineResourceList::DOWNLOADING;
|
|
return NS_OK;
|
|
default :
|
|
*aStatus = nsIDOMOfflineResourceList::IDLE;
|
|
return NS_OK;
|
|
}
|
|
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
OfflineCacheUpdateChild::GetPartial(PRBool *aPartial)
|
|
{
|
|
*aPartial = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
OfflineCacheUpdateChild::GetManifestURI(nsIURI **aManifestURI)
|
|
{
|
|
NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
NS_IF_ADDREF(*aManifestURI = mManifestURI);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
OfflineCacheUpdateChild::GetSucceeded(PRBool *aSucceeded)
|
|
{
|
|
NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE);
|
|
|
|
*aSucceeded = mSucceeded;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
OfflineCacheUpdateChild::GetIsUpgrade(PRBool *aIsUpgrade)
|
|
{
|
|
NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
*aIsUpgrade = mIsUpgrade;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
OfflineCacheUpdateChild::AddDynamicURI(nsIURI *aURI)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
OfflineCacheUpdateChild::AddObserver(nsIOfflineCacheUpdateObserver *aObserver,
|
|
PRBool aHoldWeak)
|
|
{
|
|
LOG(("OfflineCacheUpdateChild::AddObserver [%p]", this));
|
|
|
|
NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
if (aHoldWeak) {
|
|
nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aObserver);
|
|
mWeakObservers.AppendObject(weakRef);
|
|
} else {
|
|
mObservers.AppendObject(aObserver);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
OfflineCacheUpdateChild::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver)
|
|
{
|
|
LOG(("OfflineCacheUpdateChild::RemoveObserver [%p]", this));
|
|
|
|
NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
for (PRInt32 i = 0; i < mWeakObservers.Count(); i++) {
|
|
nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
|
|
do_QueryReferent(mWeakObservers[i]);
|
|
if (observer == aObserver) {
|
|
mWeakObservers.RemoveObjectAt(i);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
for (PRInt32 i = 0; i < mObservers.Count(); i++) {
|
|
if (mObservers[i] == aObserver) {
|
|
mObservers.RemoveObjectAt(i);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
OfflineCacheUpdateChild::Schedule()
|
|
{
|
|
LOG(("OfflineCacheUpdateChild::Schedule [%p]", this));
|
|
|
|
#ifdef MOZ_IPC
|
|
NS_ASSERTION(mWindow, "Window must be provided to the offline cache update child");
|
|
#endif
|
|
|
|
nsCOMPtr<nsPIDOMWindow> piWindow =
|
|
do_QueryInterface(mWindow);
|
|
mWindow = nsnull;
|
|
|
|
nsIDocShell *docshell = piWindow->GetDocShell();
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(docshell);
|
|
if (!item) {
|
|
NS_WARNING("doc shell tree item is null");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsCOMPtr<nsIDocShellTreeOwner> owner;
|
|
item->GetTreeOwner(getter_AddRefs(owner));
|
|
|
|
nsCOMPtr<nsITabChild> tabchild = do_GetInterface(owner);
|
|
if (!tabchild) {
|
|
NS_WARNING("tab is null");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// because owner implements nsITabChild, we can assume that it is
|
|
// the one and only TabChild.
|
|
mozilla::dom::TabChild* child = static_cast<mozilla::dom::TabChild*>(tabchild.get());
|
|
|
|
nsCOMPtr<nsIObserverService> observerService =
|
|
mozilla::services::GetObserverService();
|
|
if (observerService) {
|
|
LOG(("Calling offline-cache-update-added"));
|
|
observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(this),
|
|
"offline-cache-update-added",
|
|
nsnull);
|
|
LOG(("Done offline-cache-update-added"));
|
|
}
|
|
|
|
// mDocument is non-null if both:
|
|
// 1. this update was initiated by a document that referred a manifest
|
|
// 2. the document has not already been loaded from the application cache
|
|
// This tells the update to cache this document even in case the manifest
|
|
// has not been changed since the last fetch.
|
|
// See also nsOfflineCacheUpdate::ScheduleImplicit.
|
|
bool stickDocument = mDocument != nsnull;
|
|
|
|
// Need to addref ourself here, because the IPC stack doesn't hold
|
|
// a reference to us. Will be released in RecvFinish() that identifies
|
|
// the work has been done.
|
|
child->SendPOfflineCacheUpdateConstructor(this,
|
|
IPC::URI(mManifestURI),
|
|
IPC::URI(mDocumentURI),
|
|
mClientID,
|
|
stickDocument);
|
|
|
|
mIPCActivated = PR_TRUE;
|
|
this->AddRef();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool
|
|
OfflineCacheUpdateChild::RecvAssociateDocuments(const nsCString &cacheGroupId,
|
|
const nsCString &cacheClientId)
|
|
{
|
|
LOG(("OfflineCacheUpdateChild::RecvAssociateDocuments [%p, cache=%s]", this, cacheClientId.get()));
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsIApplicationCache> cache =
|
|
do_CreateInstance(NS_APPLICATIONCACHE_CONTRACTID, &rv);
|
|
if (NS_FAILED(rv))
|
|
return true;
|
|
|
|
cache->InitAsHandle(cacheGroupId, cacheClientId);
|
|
|
|
if (mDocument) {
|
|
AssociateDocument(mDocument, cache);
|
|
}
|
|
|
|
nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
|
|
rv = GatherObservers(observers);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
for (PRInt32 i = 0; i < observers.Count(); i++)
|
|
observers[i]->ApplicationCacheAvailable(cache);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
OfflineCacheUpdateChild::RecvNotifyStateEvent(const PRUint32 &event)
|
|
{
|
|
LOG(("OfflineCacheUpdateChild::RecvNotifyStateEvent [%p]", this));
|
|
|
|
// Convert the public observer state to our internal state
|
|
switch (event) {
|
|
case nsIOfflineCacheUpdateObserver::STATE_CHECKING:
|
|
mState = STATE_CHECKING;
|
|
break;
|
|
|
|
case nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING:
|
|
mState = STATE_DOWNLOADING;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
|
|
nsresult rv = GatherObservers(observers);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
for (PRInt32 i = 0; i < observers.Count(); i++)
|
|
observers[i]->UpdateStateChanged(this, event);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
OfflineCacheUpdateChild::RecvFinish(const bool &succeeded,
|
|
const bool &isUpgrade)
|
|
{
|
|
LOG(("OfflineCacheUpdateChild::RecvFinish [%p]", this));
|
|
|
|
nsRefPtr<OfflineCacheUpdateChild> kungFuDeathGrip(this);
|
|
|
|
mState = STATE_FINISHED;
|
|
mSucceeded = succeeded;
|
|
mIsUpgrade = isUpgrade;
|
|
|
|
nsCOMPtr<nsIObserverService> observerService =
|
|
mozilla::services::GetObserverService();
|
|
if (observerService) {
|
|
LOG(("Calling offline-cache-update-completed"));
|
|
observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(this),
|
|
"offline-cache-update-completed",
|
|
nsnull);
|
|
LOG(("Done offline-cache-update-completed"));
|
|
}
|
|
|
|
// This is by contract the last notification from the parent, release
|
|
// us now. This is corresponding to AddRef in Schedule().
|
|
this->Release();
|
|
|
|
return true;
|
|
}
|
|
|
|
}
|
|
} |