1 /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
19 * Portions created by the Initial Developer are Copyright (C) 2007
20 * the Initial Developer. All Rights Reserved.
23 * Dave Camp <dcamp@mozilla.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "OfflineCacheUpdateChild.h"
40 #include "nsOfflineCacheUpdate.h"
41 #include "mozilla/dom/ContentChild.h"
42 #include "mozilla/dom/TabChild.h"
44 #include "nsIApplicationCacheContainer.h"
45 #include "nsIApplicationCacheChannel.h"
46 #include "nsIApplicationCacheService.h"
47 #include "nsIDocShell.h"
48 #include "nsIDocShellTreeItem.h"
49 #include "nsIDocShellTreeOwner.h"
50 #include "nsIDOMWindow.h"
51 #include "nsIDOMOfflineResourceList.h"
52 #include "nsIDocument.h"
53 #include "nsIObserverService.h"
55 #include "nsITabChild.h"
57 #include "nsNetUtil.h"
58 #include "nsServiceManagerUtils.h"
59 #include "nsStreamUtils.h"
60 #include "nsThreadUtils.h"
61 #include "nsProxyRelease.h"
63 #include "nsIAsyncVerifyRedirectCallback.h"
65 static nsOfflineCacheUpdateService
*gOfflineCacheUpdateService
= nsnull
;
67 #if defined(PR_LOGGING)
69 // To enable logging (see prlog.h for full details):
71 // set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5
72 // set NSPR_LOG_FILE=offlineupdate.log
74 // this enables PR_LOG_ALWAYS level information and places all output in
75 // the file offlineupdate.log
77 extern PRLogModuleInfo
*gOfflineCacheUpdateLog
;
79 #define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args)
80 #define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4)
85 //-----------------------------------------------------------------------------
86 // OfflineCacheUpdateChild::nsISupports
87 //-----------------------------------------------------------------------------
89 NS_INTERFACE_MAP_BEGIN(OfflineCacheUpdateChild
)
90 NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdate
)
93 NS_IMPL_ADDREF(OfflineCacheUpdateChild
)
94 NS_IMPL_RELEASE_WITH_DESTROY(OfflineCacheUpdateChild
, RefcountHitZero())
97 OfflineCacheUpdateChild::RefcountHitZero()
100 // ContentChild::DeallocPOfflineCacheUpdate will delete this
101 OfflineCacheUpdateChild::Send__delete__(this);
103 delete this; // we never opened IPDL channel
107 //-----------------------------------------------------------------------------
108 // OfflineCacheUpdateChild <public>
109 //-----------------------------------------------------------------------------
111 OfflineCacheUpdateChild::OfflineCacheUpdateChild(nsIDOMWindow
* aWindow
)
112 : mState(STATE_UNINITIALIZED
)
113 , mIsUpgrade(PR_FALSE
)
114 , mIPCActivated(PR_FALSE
)
119 OfflineCacheUpdateChild::~OfflineCacheUpdateChild()
121 LOG(("OfflineCacheUpdateChild::~OfflineCacheUpdateChild [%p]", this));
125 OfflineCacheUpdateChild::GatherObservers(nsCOMArray
<nsIOfflineCacheUpdateObserver
> &aObservers
)
127 for (PRInt32 i
= 0; i
< mWeakObservers
.Count(); i
++) {
128 nsCOMPtr
<nsIOfflineCacheUpdateObserver
> observer
=
129 do_QueryReferent(mWeakObservers
[i
]);
131 aObservers
.AppendObject(observer
);
133 mWeakObservers
.RemoveObjectAt(i
--);
136 for (PRInt32 i
= 0; i
< mObservers
.Count(); i
++) {
137 aObservers
.AppendObject(mObservers
[i
]);
144 OfflineCacheUpdateChild::SetDocument(nsIDOMDocument
*aDocument
)
146 // The design is one document for one cache update on the content process.
147 NS_ASSERTION(!mDocument
, "Setting more then a single document on a child offline cache update");
149 LOG(("Document %p added to update child %p", aDocument
, this));
151 // Add document only if it was not loaded from an offline cache.
152 // If it were loaded from an offline cache then it has already
153 // been associated with it and must not be again cached as
154 // implicit (which are the reasons we collect documents here).
155 nsCOMPtr
<nsIDocument
> document
= do_QueryInterface(aDocument
);
159 nsIChannel
* channel
= document
->GetChannel();
160 nsCOMPtr
<nsIApplicationCacheChannel
> appCacheChannel
=
161 do_QueryInterface(channel
);
162 if (!appCacheChannel
)
165 PRBool loadedFromAppCache
;
166 appCacheChannel
->GetLoadedFromApplicationCache(&loadedFromAppCache
);
167 if (loadedFromAppCache
)
170 mDocument
= aDocument
;
174 OfflineCacheUpdateChild::AssociateDocument(nsIDOMDocument
*aDocument
,
175 nsIApplicationCache
*aApplicationCache
)
177 // Check that the document that requested this update was
178 // previously associated with an application cache. If not, it
179 // should be associated with the new one.
180 nsCOMPtr
<nsIApplicationCacheContainer
> container
=
181 do_QueryInterface(aDocument
);
185 nsCOMPtr
<nsIApplicationCache
> existingCache
;
186 nsresult rv
= container
->GetApplicationCache(getter_AddRefs(existingCache
));
187 NS_ENSURE_SUCCESS(rv
, rv
);
189 if (!existingCache
) {
190 #if defined(PR_LOGGING)
192 nsCAutoString clientID
;
193 if (aApplicationCache
) {
194 aApplicationCache
->GetClientID(clientID
);
196 LOG(("Update %p: associating app cache %s to document %p",
197 this, clientID
.get(), aDocument
));
201 rv
= container
->SetApplicationCache(aApplicationCache
);
202 NS_ENSURE_SUCCESS(rv
, rv
);
208 //-----------------------------------------------------------------------------
209 // OfflineCacheUpdateChild::nsIOfflineCacheUpdate
210 //-----------------------------------------------------------------------------
213 OfflineCacheUpdateChild::Init(nsIURI
*aManifestURI
,
214 nsIURI
*aDocumentURI
,
215 nsIDOMDocument
*aDocument
)
219 // Make sure the service has been initialized
220 nsOfflineCacheUpdateService
* service
=
221 nsOfflineCacheUpdateService::EnsureService();
223 return NS_ERROR_FAILURE
;
225 LOG(("OfflineCacheUpdateChild::Init [%p]", this));
227 // Only http and https applications are supported.
229 rv
= aManifestURI
->SchemeIs("http", &match
);
230 NS_ENSURE_SUCCESS(rv
, rv
);
233 rv
= aManifestURI
->SchemeIs("https", &match
);
234 NS_ENSURE_SUCCESS(rv
, rv
);
236 return NS_ERROR_ABORT
;
239 mManifestURI
= aManifestURI
;
241 rv
= mManifestURI
->GetAsciiHost(mUpdateDomain
);
242 NS_ENSURE_SUCCESS(rv
, rv
);
244 mDocumentURI
= aDocumentURI
;
246 mState
= STATE_INITIALIZED
;
249 SetDocument(aDocument
);
255 OfflineCacheUpdateChild::InitPartial(nsIURI
*aManifestURI
,
256 const nsACString
& clientID
,
257 nsIURI
*aDocumentURI
)
259 NS_NOTREACHED("Not expected to do partial offline cache updates"
260 " on the child process");
261 // For now leaving this method, we may discover we need it.
262 return NS_ERROR_NOT_IMPLEMENTED
;
266 OfflineCacheUpdateChild::GetUpdateDomain(nsACString
&aUpdateDomain
)
268 NS_ENSURE_TRUE(mState
>= STATE_INITIALIZED
, NS_ERROR_NOT_INITIALIZED
);
270 aUpdateDomain
= mUpdateDomain
;
275 OfflineCacheUpdateChild::GetStatus(PRUint16
*aStatus
)
278 case STATE_CHECKING
:
279 *aStatus
= nsIDOMOfflineResourceList::CHECKING
;
281 case STATE_DOWNLOADING
:
282 *aStatus
= nsIDOMOfflineResourceList::DOWNLOADING
;
285 *aStatus
= nsIDOMOfflineResourceList::IDLE
;
289 return NS_ERROR_FAILURE
;
293 OfflineCacheUpdateChild::GetPartial(PRBool
*aPartial
)
295 *aPartial
= PR_FALSE
;
300 OfflineCacheUpdateChild::GetManifestURI(nsIURI
**aManifestURI
)
302 NS_ENSURE_TRUE(mState
>= STATE_INITIALIZED
, NS_ERROR_NOT_INITIALIZED
);
304 NS_IF_ADDREF(*aManifestURI
= mManifestURI
);
309 OfflineCacheUpdateChild::GetSucceeded(PRBool
*aSucceeded
)
311 NS_ENSURE_TRUE(mState
== STATE_FINISHED
, NS_ERROR_NOT_AVAILABLE
);
313 *aSucceeded
= mSucceeded
;
319 OfflineCacheUpdateChild::GetIsUpgrade(PRBool
*aIsUpgrade
)
321 NS_ENSURE_TRUE(mState
>= STATE_INITIALIZED
, NS_ERROR_NOT_INITIALIZED
);
323 *aIsUpgrade
= mIsUpgrade
;
329 OfflineCacheUpdateChild::AddDynamicURI(nsIURI
*aURI
)
331 return NS_ERROR_NOT_IMPLEMENTED
;
335 OfflineCacheUpdateChild::AddObserver(nsIOfflineCacheUpdateObserver
*aObserver
,
338 LOG(("OfflineCacheUpdateChild::AddObserver [%p]", this));
340 NS_ENSURE_TRUE(mState
>= STATE_INITIALIZED
, NS_ERROR_NOT_INITIALIZED
);
343 nsCOMPtr
<nsIWeakReference
> weakRef
= do_GetWeakReference(aObserver
);
344 mWeakObservers
.AppendObject(weakRef
);
346 mObservers
.AppendObject(aObserver
);
353 OfflineCacheUpdateChild::RemoveObserver(nsIOfflineCacheUpdateObserver
*aObserver
)
355 LOG(("OfflineCacheUpdateChild::RemoveObserver [%p]", this));
357 NS_ENSURE_TRUE(mState
>= STATE_INITIALIZED
, NS_ERROR_NOT_INITIALIZED
);
359 for (PRInt32 i
= 0; i
< mWeakObservers
.Count(); i
++) {
360 nsCOMPtr
<nsIOfflineCacheUpdateObserver
> observer
=
361 do_QueryReferent(mWeakObservers
[i
]);
362 if (observer
== aObserver
) {
363 mWeakObservers
.RemoveObjectAt(i
);
368 for (PRInt32 i
= 0; i
< mObservers
.Count(); i
++) {
369 if (mObservers
[i
] == aObserver
) {
370 mObservers
.RemoveObjectAt(i
);
379 OfflineCacheUpdateChild::Schedule()
381 LOG(("OfflineCacheUpdateChild::Schedule [%p]", this));
384 NS_ASSERTION(mWindow
, "Window must be provided to the offline cache update child");
387 nsCOMPtr
<nsPIDOMWindow
> piWindow
=
388 do_QueryInterface(mWindow
);
391 nsIDocShell
*docshell
= piWindow
->GetDocShell();
393 nsCOMPtr
<nsIDocShellTreeItem
> item
= do_QueryInterface(docshell
);
395 NS_WARNING("doc shell tree item is null");
396 return NS_ERROR_FAILURE
;
399 nsCOMPtr
<nsIDocShellTreeOwner
> owner
;
400 item
->GetTreeOwner(getter_AddRefs(owner
));
402 nsCOMPtr
<nsITabChild
> tabchild
= do_GetInterface(owner
);
404 NS_WARNING("tab is null");
405 return NS_ERROR_FAILURE
;
408 // because owner implements nsITabChild, we can assume that it is
409 // the one and only TabChild.
410 mozilla::dom::TabChild
* child
= static_cast<mozilla::dom::TabChild
*>(tabchild
.get());
412 nsCOMPtr
<nsIObserverService
> observerService
=
413 mozilla::services::GetObserverService();
414 if (observerService
) {
415 LOG(("Calling offline-cache-update-added"));
416 observerService
->NotifyObservers(static_cast<nsIOfflineCacheUpdate
*>(this),
417 "offline-cache-update-added",
419 LOG(("Done offline-cache-update-added"));
422 // mDocument is non-null if both:
423 // 1. this update was initiated by a document that referred a manifest
424 // 2. the document has not already been loaded from the application cache
425 // This tells the update to cache this document even in case the manifest
426 // has not been changed since the last fetch.
427 // See also nsOfflineCacheUpdate::ScheduleImplicit.
428 bool stickDocument
= mDocument
!= nsnull
;
430 // Need to addref ourself here, because the IPC stack doesn't hold
431 // a reference to us. Will be released in RecvFinish() that identifies
432 // the work has been done.
433 child
->SendPOfflineCacheUpdateConstructor(this,
434 IPC::URI(mManifestURI
),
435 IPC::URI(mDocumentURI
),
439 mIPCActivated
= PR_TRUE
;
446 OfflineCacheUpdateChild::RecvAssociateDocuments(const nsCString
&cacheGroupId
,
447 const nsCString
&cacheClientId
)
449 LOG(("OfflineCacheUpdateChild::RecvAssociateDocuments [%p, cache=%s]", this, cacheClientId
.get()));
453 nsCOMPtr
<nsIApplicationCache
> cache
=
454 do_CreateInstance(NS_APPLICATIONCACHE_CONTRACTID
, &rv
);
458 cache
->InitAsHandle(cacheGroupId
, cacheClientId
);
461 AssociateDocument(mDocument
, cache
);
464 nsCOMArray
<nsIOfflineCacheUpdateObserver
> observers
;
465 rv
= GatherObservers(observers
);
466 NS_ENSURE_SUCCESS(rv
, rv
);
468 for (PRInt32 i
= 0; i
< observers
.Count(); i
++)
469 observers
[i
]->ApplicationCacheAvailable(cache
);
475 OfflineCacheUpdateChild::RecvNotifyStateEvent(const PRUint32
&event
)
477 LOG(("OfflineCacheUpdateChild::RecvNotifyStateEvent [%p]", this));
479 // Convert the public observer state to our internal state
481 case nsIOfflineCacheUpdateObserver::STATE_CHECKING
:
482 mState
= STATE_CHECKING
;
485 case nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING
:
486 mState
= STATE_DOWNLOADING
;
493 nsCOMArray
<nsIOfflineCacheUpdateObserver
> observers
;
494 nsresult rv
= GatherObservers(observers
);
495 NS_ENSURE_SUCCESS(rv
, rv
);
497 for (PRInt32 i
= 0; i
< observers
.Count(); i
++)
498 observers
[i
]->UpdateStateChanged(this, event
);
504 OfflineCacheUpdateChild::RecvFinish(const bool &succeeded
,
505 const bool &isUpgrade
)
507 LOG(("OfflineCacheUpdateChild::RecvFinish [%p]", this));
509 nsRefPtr
<OfflineCacheUpdateChild
> kungFuDeathGrip(this);
511 mState
= STATE_FINISHED
;
512 mSucceeded
= succeeded
;
513 mIsUpgrade
= isUpgrade
;
515 nsCOMPtr
<nsIObserverService
> observerService
=
516 mozilla::services::GetObserverService();
517 if (observerService
) {
518 LOG(("Calling offline-cache-update-completed"));
519 observerService
->NotifyObservers(static_cast<nsIOfflineCacheUpdate
*>(this),
520 "offline-cache-update-completed",
522 LOG(("Done offline-cache-update-completed"));
525 // This is by contract the last notification from the parent, release
526 // us now. This is corresponding to AddRef in Schedule().