1 /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "OfflineCacheUpdateChild.h"
7 #include "nsOfflineCacheUpdate.h"
8 #include "mozilla/dom/ContentChild.h"
9 #include "mozilla/dom/TabChild.h"
10 #include "mozilla/ipc/URIUtils.h"
11 #include "mozilla/net/NeckoCommon.h"
13 #include "nsIApplicationCacheContainer.h"
14 #include "nsIApplicationCacheChannel.h"
15 #include "nsIApplicationCacheService.h"
16 #include "nsIDocShell.h"
17 #include "nsIDocShellTreeItem.h"
18 #include "nsIDocShellTreeOwner.h"
19 #include "nsIDOMWindow.h"
20 #include "nsIDOMOfflineResourceList.h"
21 #include "nsIDocument.h"
22 #include "nsIObserverService.h"
24 #include "nsITabChild.h"
26 #include "nsNetUtil.h"
27 #include "nsServiceManagerUtils.h"
28 #include "nsStreamUtils.h"
29 #include "nsThreadUtils.h"
30 #include "nsProxyRelease.h"
32 #include "nsIAsyncVerifyRedirectCallback.h"
34 using namespace mozilla::ipc
;
35 using namespace mozilla::net
;
36 using mozilla::dom::TabChild
;
38 #if defined(PR_LOGGING)
40 // To enable logging (see prlog.h for full details):
42 // set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5
43 // set NSPR_LOG_FILE=offlineupdate.log
45 // this enables PR_LOG_ALWAYS level information and places all output in
46 // the file offlineupdate.log
48 extern PRLogModuleInfo
*gOfflineCacheUpdateLog
;
52 #define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args)
55 #define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4)
60 //-----------------------------------------------------------------------------
61 // OfflineCacheUpdateChild::nsISupports
62 //-----------------------------------------------------------------------------
64 NS_INTERFACE_MAP_BEGIN(OfflineCacheUpdateChild
)
65 NS_INTERFACE_MAP_ENTRY(nsISupports
)
66 NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdate
)
69 NS_IMPL_ADDREF(OfflineCacheUpdateChild
)
70 NS_IMPL_RELEASE(OfflineCacheUpdateChild
)
72 //-----------------------------------------------------------------------------
73 // OfflineCacheUpdateChild <public>
74 //-----------------------------------------------------------------------------
76 OfflineCacheUpdateChild::OfflineCacheUpdateChild(nsIDOMWindow
* aWindow
)
77 : mState(STATE_UNINITIALIZED
)
79 , mAppID(NECKO_NO_APP_ID
)
86 OfflineCacheUpdateChild::~OfflineCacheUpdateChild()
88 LOG(("OfflineCacheUpdateChild::~OfflineCacheUpdateChild [%p]", this));
92 OfflineCacheUpdateChild::GatherObservers(nsCOMArray
<nsIOfflineCacheUpdateObserver
> &aObservers
)
94 for (int32_t i
= 0; i
< mWeakObservers
.Count(); i
++) {
95 nsCOMPtr
<nsIOfflineCacheUpdateObserver
> observer
=
96 do_QueryReferent(mWeakObservers
[i
]);
98 aObservers
.AppendObject(observer
);
100 mWeakObservers
.RemoveObjectAt(i
--);
103 for (int32_t i
= 0; i
< mObservers
.Count(); i
++) {
104 aObservers
.AppendObject(mObservers
[i
]);
109 OfflineCacheUpdateChild::SetDocument(nsIDOMDocument
*aDocument
)
111 // The design is one document for one cache update on the content process.
112 NS_ASSERTION(!mDocument
, "Setting more then a single document on a child offline cache update");
114 LOG(("Document %p added to update child %p", aDocument
, this));
116 // Add document only if it was not loaded from an offline cache.
117 // If it were loaded from an offline cache then it has already
118 // been associated with it and must not be again cached as
119 // implicit (which are the reasons we collect documents here).
120 nsCOMPtr
<nsIDocument
> document
= do_QueryInterface(aDocument
);
124 nsIChannel
* channel
= document
->GetChannel();
125 nsCOMPtr
<nsIApplicationCacheChannel
> appCacheChannel
=
126 do_QueryInterface(channel
);
127 if (!appCacheChannel
)
130 bool loadedFromAppCache
;
131 appCacheChannel
->GetLoadedFromApplicationCache(&loadedFromAppCache
);
132 if (loadedFromAppCache
)
135 mDocument
= aDocument
;
139 OfflineCacheUpdateChild::AssociateDocument(nsIDOMDocument
*aDocument
,
140 nsIApplicationCache
*aApplicationCache
)
142 // Check that the document that requested this update was
143 // previously associated with an application cache. If not, it
144 // should be associated with the new one.
145 nsCOMPtr
<nsIApplicationCacheContainer
> container
=
146 do_QueryInterface(aDocument
);
150 nsCOMPtr
<nsIApplicationCache
> existingCache
;
151 nsresult rv
= container
->GetApplicationCache(getter_AddRefs(existingCache
));
152 NS_ENSURE_SUCCESS(rv
, rv
);
154 if (!existingCache
) {
155 #if defined(PR_LOGGING)
157 nsAutoCString clientID
;
158 if (aApplicationCache
) {
159 aApplicationCache
->GetClientID(clientID
);
161 LOG(("Update %p: associating app cache %s to document %p",
162 this, clientID
.get(), aDocument
));
166 rv
= container
->SetApplicationCache(aApplicationCache
);
167 NS_ENSURE_SUCCESS(rv
, rv
);
173 //-----------------------------------------------------------------------------
174 // OfflineCacheUpdateChild::nsIOfflineCacheUpdate
175 //-----------------------------------------------------------------------------
178 OfflineCacheUpdateChild::Init(nsIURI
*aManifestURI
,
179 nsIURI
*aDocumentURI
,
180 nsIDOMDocument
*aDocument
,
181 nsIFile
*aCustomProfileDir
,
187 // Make sure the service has been initialized
188 nsOfflineCacheUpdateService
* service
=
189 nsOfflineCacheUpdateService::EnsureService();
191 return NS_ERROR_FAILURE
;
193 if (aCustomProfileDir
) {
194 NS_ERROR("Custom Offline Cache Update not supported on child process");
195 return NS_ERROR_NOT_IMPLEMENTED
;
198 LOG(("OfflineCacheUpdateChild::Init [%p]", this));
200 // Only http and https applications are supported.
202 rv
= aManifestURI
->SchemeIs("http", &match
);
203 NS_ENSURE_SUCCESS(rv
, rv
);
206 rv
= aManifestURI
->SchemeIs("https", &match
);
207 NS_ENSURE_SUCCESS(rv
, rv
);
209 return NS_ERROR_ABORT
;
212 mManifestURI
= aManifestURI
;
214 rv
= mManifestURI
->GetAsciiHost(mUpdateDomain
);
215 NS_ENSURE_SUCCESS(rv
, rv
);
217 mDocumentURI
= aDocumentURI
;
219 mState
= STATE_INITIALIZED
;
222 SetDocument(aDocument
);
225 mInBrowser
= aInBrowser
;
231 OfflineCacheUpdateChild::InitPartial(nsIURI
*aManifestURI
,
232 const nsACString
& clientID
,
233 nsIURI
*aDocumentURI
)
235 NS_NOTREACHED("Not expected to do partial offline cache updates"
236 " on the child process");
237 // For now leaving this method, we may discover we need it.
238 return NS_ERROR_NOT_IMPLEMENTED
;
242 OfflineCacheUpdateChild::InitForUpdateCheck(nsIURI
*aManifestURI
,
245 nsIObserver
*aObserver
)
247 NS_NOTREACHED("Not expected to do only update checks"
248 " from the child process");
249 return NS_ERROR_NOT_IMPLEMENTED
;
253 OfflineCacheUpdateChild::GetUpdateDomain(nsACString
&aUpdateDomain
)
255 NS_ENSURE_TRUE(mState
>= STATE_INITIALIZED
, NS_ERROR_NOT_INITIALIZED
);
257 aUpdateDomain
= mUpdateDomain
;
262 OfflineCacheUpdateChild::GetStatus(uint16_t *aStatus
)
265 case STATE_CHECKING
:
266 *aStatus
= nsIDOMOfflineResourceList::CHECKING
;
268 case STATE_DOWNLOADING
:
269 *aStatus
= nsIDOMOfflineResourceList::DOWNLOADING
;
272 *aStatus
= nsIDOMOfflineResourceList::IDLE
;
276 return NS_ERROR_FAILURE
;
280 OfflineCacheUpdateChild::GetPartial(bool *aPartial
)
287 OfflineCacheUpdateChild::GetManifestURI(nsIURI
**aManifestURI
)
289 NS_ENSURE_TRUE(mState
>= STATE_INITIALIZED
, NS_ERROR_NOT_INITIALIZED
);
291 NS_IF_ADDREF(*aManifestURI
= mManifestURI
);
296 OfflineCacheUpdateChild::GetSucceeded(bool *aSucceeded
)
298 NS_ENSURE_TRUE(mState
== STATE_FINISHED
, NS_ERROR_NOT_AVAILABLE
);
300 *aSucceeded
= mSucceeded
;
306 OfflineCacheUpdateChild::GetIsUpgrade(bool *aIsUpgrade
)
308 NS_ENSURE_TRUE(mState
>= STATE_INITIALIZED
, NS_ERROR_NOT_INITIALIZED
);
310 *aIsUpgrade
= mIsUpgrade
;
316 OfflineCacheUpdateChild::AddDynamicURI(nsIURI
*aURI
)
318 return NS_ERROR_NOT_IMPLEMENTED
;
322 OfflineCacheUpdateChild::Cancel()
324 return NS_ERROR_NOT_IMPLEMENTED
;
328 OfflineCacheUpdateChild::AddObserver(nsIOfflineCacheUpdateObserver
*aObserver
,
331 LOG(("OfflineCacheUpdateChild::AddObserver [%p]", this));
333 NS_ENSURE_TRUE(mState
>= STATE_INITIALIZED
, NS_ERROR_NOT_INITIALIZED
);
336 nsCOMPtr
<nsIWeakReference
> weakRef
= do_GetWeakReference(aObserver
);
337 mWeakObservers
.AppendObject(weakRef
);
339 mObservers
.AppendObject(aObserver
);
346 OfflineCacheUpdateChild::RemoveObserver(nsIOfflineCacheUpdateObserver
*aObserver
)
348 LOG(("OfflineCacheUpdateChild::RemoveObserver [%p]", this));
350 NS_ENSURE_TRUE(mState
>= STATE_INITIALIZED
, NS_ERROR_NOT_INITIALIZED
);
352 for (int32_t i
= 0; i
< mWeakObservers
.Count(); i
++) {
353 nsCOMPtr
<nsIOfflineCacheUpdateObserver
> observer
=
354 do_QueryReferent(mWeakObservers
[i
]);
355 if (observer
== aObserver
) {
356 mWeakObservers
.RemoveObjectAt(i
);
361 for (int32_t i
= 0; i
< mObservers
.Count(); i
++) {
362 if (mObservers
[i
] == aObserver
) {
363 mObservers
.RemoveObjectAt(i
);
372 OfflineCacheUpdateChild::GetByteProgress(uint64_t * _result
)
374 NS_ENSURE_ARG(_result
);
376 *_result
= mByteProgress
;
381 OfflineCacheUpdateChild::Schedule()
383 LOG(("OfflineCacheUpdateChild::Schedule [%p]", this));
385 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
);
403 // because owner implements nsITabChild, we can assume that it is
404 // the one and only TabChild.
405 TabChild
* child
= tabchild
? static_cast<TabChild
*>(tabchild
.get()) : nullptr;
407 if (MissingRequiredTabChild(child
, "offlinecacheupdate")) {
408 return NS_ERROR_FAILURE
;
411 URIParams manifestURI
, documentURI
;
412 SerializeURI(mManifestURI
, manifestURI
);
413 SerializeURI(mDocumentURI
, documentURI
);
415 nsCOMPtr
<nsIObserverService
> observerService
=
416 mozilla::services::GetObserverService();
417 if (observerService
) {
418 LOG(("Calling offline-cache-update-added"));
419 observerService
->NotifyObservers(static_cast<nsIOfflineCacheUpdate
*>(this),
420 "offline-cache-update-added",
422 LOG(("Done offline-cache-update-added"));
425 // mDocument is non-null if both:
426 // 1. this update was initiated by a document that referred a manifest
427 // 2. the document has not already been loaded from the application cache
428 // This tells the update to cache this document even in case the manifest
429 // has not been changed since the last fetch.
430 // See also nsOfflineCacheUpdate::ScheduleImplicit.
431 bool stickDocument
= mDocument
!= nullptr;
433 // Need to addref ourself here, because the IPC stack doesn't hold
434 // a reference to us. Will be released in RecvFinish() that identifies
435 // the work has been done.
436 child
->SendPOfflineCacheUpdateConstructor(this, manifestURI
, documentURI
,
439 // TabChild::DeallocPOfflineCacheUpdate will release this.
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 GatherObservers(observers
);
467 for (int32_t i
= 0; i
< observers
.Count(); i
++)
468 observers
[i
]->ApplicationCacheAvailable(cache
);
474 OfflineCacheUpdateChild::RecvNotifyStateEvent(const uint32_t &event
,
475 const uint64_t &byteProgress
)
477 LOG(("OfflineCacheUpdateChild::RecvNotifyStateEvent [%p]", this));
479 mByteProgress
= byteProgress
;
481 // Convert the public observer state to our internal state
483 case nsIOfflineCacheUpdateObserver::STATE_CHECKING
:
484 mState
= STATE_CHECKING
;
487 case nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING
:
488 mState
= STATE_DOWNLOADING
;
495 nsCOMArray
<nsIOfflineCacheUpdateObserver
> observers
;
496 GatherObservers(observers
);
498 for (int32_t i
= 0; i
< observers
.Count(); i
++)
499 observers
[i
]->UpdateStateChanged(this, event
);
505 OfflineCacheUpdateChild::RecvFinish(const bool &succeeded
,
506 const bool &isUpgrade
)
508 LOG(("OfflineCacheUpdateChild::RecvFinish [%p]", this));
510 nsRefPtr
<OfflineCacheUpdateChild
> kungFuDeathGrip(this);
512 mState
= STATE_FINISHED
;
513 mSucceeded
= succeeded
;
514 mIsUpgrade
= isUpgrade
;
516 nsCOMPtr
<nsIObserverService
> observerService
=
517 mozilla::services::GetObserverService();
518 if (observerService
) {
519 LOG(("Calling offline-cache-update-completed"));
520 observerService
->NotifyObservers(static_cast<nsIOfflineCacheUpdate
*>(this),
521 "offline-cache-update-completed",
523 LOG(("Done offline-cache-update-completed"));
526 // This is by contract the last notification from the parent, release
527 // us now. This is corresponding to AddRef in Schedule().
528 // TabChild::DeallocPOfflineCacheUpdate will call Release.
529 OfflineCacheUpdateChild::Send__delete__(this);