Bumping manifests a=b2g-bump
[gecko.git] / uriloader / prefetch / OfflineCacheUpdateChild.cpp
blob0b443f83e520572d509ace9c36650c6b013504c6
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"
23 #include "nsIURL.h"
24 #include "nsITabChild.h"
25 #include "nsNetCID.h"
26 #include "nsNetUtil.h"
27 #include "nsServiceManagerUtils.h"
28 #include "nsStreamUtils.h"
29 #include "nsThreadUtils.h"
30 #include "nsProxyRelease.h"
31 #include "prlog.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;
49 #endif
51 #undef LOG
52 #define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args)
54 #undef LOG_ENABLED
55 #define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4)
57 namespace mozilla {
58 namespace docshell {
60 //-----------------------------------------------------------------------------
61 // OfflineCacheUpdateChild::nsISupports
62 //-----------------------------------------------------------------------------
64 NS_INTERFACE_MAP_BEGIN(OfflineCacheUpdateChild)
65 NS_INTERFACE_MAP_ENTRY(nsISupports)
66 NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdate)
67 NS_INTERFACE_MAP_END
69 NS_IMPL_ADDREF(OfflineCacheUpdateChild)
70 NS_IMPL_RELEASE(OfflineCacheUpdateChild)
72 //-----------------------------------------------------------------------------
73 // OfflineCacheUpdateChild <public>
74 //-----------------------------------------------------------------------------
76 OfflineCacheUpdateChild::OfflineCacheUpdateChild(nsIDOMWindow* aWindow)
77 : mState(STATE_UNINITIALIZED)
78 , mIsUpgrade(false)
79 , mAppID(NECKO_NO_APP_ID)
80 , mInBrowser(false)
81 , mWindow(aWindow)
82 , mByteProgress(0)
86 OfflineCacheUpdateChild::~OfflineCacheUpdateChild()
88 LOG(("OfflineCacheUpdateChild::~OfflineCacheUpdateChild [%p]", this));
91 void
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]);
97 if (observer)
98 aObservers.AppendObject(observer);
99 else
100 mWeakObservers.RemoveObjectAt(i--);
103 for (int32_t i = 0; i < mObservers.Count(); i++) {
104 aObservers.AppendObject(mObservers[i]);
108 void
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);
121 if (!document)
122 return;
124 nsIChannel* channel = document->GetChannel();
125 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
126 do_QueryInterface(channel);
127 if (!appCacheChannel)
128 return;
130 bool loadedFromAppCache;
131 appCacheChannel->GetLoadedFromApplicationCache(&loadedFromAppCache);
132 if (loadedFromAppCache)
133 return;
135 mDocument = aDocument;
138 nsresult
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);
147 if (!container)
148 return NS_OK;
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)
156 if (LOG_ENABLED()) {
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));
164 #endif
166 rv = container->SetApplicationCache(aApplicationCache);
167 NS_ENSURE_SUCCESS(rv, rv);
170 return NS_OK;
173 //-----------------------------------------------------------------------------
174 // OfflineCacheUpdateChild::nsIOfflineCacheUpdate
175 //-----------------------------------------------------------------------------
177 NS_IMETHODIMP
178 OfflineCacheUpdateChild::Init(nsIURI *aManifestURI,
179 nsIURI *aDocumentURI,
180 nsIDOMDocument *aDocument,
181 nsIFile *aCustomProfileDir,
182 uint32_t aAppID,
183 bool aInBrowser)
185 nsresult rv;
187 // Make sure the service has been initialized
188 nsOfflineCacheUpdateService* service =
189 nsOfflineCacheUpdateService::EnsureService();
190 if (!service)
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.
201 bool match;
202 rv = aManifestURI->SchemeIs("http", &match);
203 NS_ENSURE_SUCCESS(rv, rv);
205 if (!match) {
206 rv = aManifestURI->SchemeIs("https", &match);
207 NS_ENSURE_SUCCESS(rv, rv);
208 if (!match)
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;
221 if (aDocument)
222 SetDocument(aDocument);
224 mAppID = aAppID;
225 mInBrowser = aInBrowser;
227 return NS_OK;
230 NS_IMETHODIMP
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;
241 NS_IMETHODIMP
242 OfflineCacheUpdateChild::InitForUpdateCheck(nsIURI *aManifestURI,
243 uint32_t aAppID,
244 bool aInBrowser,
245 nsIObserver *aObserver)
247 NS_NOTREACHED("Not expected to do only update checks"
248 " from the child process");
249 return NS_ERROR_NOT_IMPLEMENTED;
252 NS_IMETHODIMP
253 OfflineCacheUpdateChild::GetUpdateDomain(nsACString &aUpdateDomain)
255 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
257 aUpdateDomain = mUpdateDomain;
258 return NS_OK;
261 NS_IMETHODIMP
262 OfflineCacheUpdateChild::GetStatus(uint16_t *aStatus)
264 switch (mState) {
265 case STATE_CHECKING :
266 *aStatus = nsIDOMOfflineResourceList::CHECKING;
267 return NS_OK;
268 case STATE_DOWNLOADING :
269 *aStatus = nsIDOMOfflineResourceList::DOWNLOADING;
270 return NS_OK;
271 default :
272 *aStatus = nsIDOMOfflineResourceList::IDLE;
273 return NS_OK;
276 return NS_ERROR_FAILURE;
279 NS_IMETHODIMP
280 OfflineCacheUpdateChild::GetPartial(bool *aPartial)
282 *aPartial = false;
283 return NS_OK;
286 NS_IMETHODIMP
287 OfflineCacheUpdateChild::GetManifestURI(nsIURI **aManifestURI)
289 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
291 NS_IF_ADDREF(*aManifestURI = mManifestURI);
292 return NS_OK;
295 NS_IMETHODIMP
296 OfflineCacheUpdateChild::GetSucceeded(bool *aSucceeded)
298 NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE);
300 *aSucceeded = mSucceeded;
302 return NS_OK;
305 NS_IMETHODIMP
306 OfflineCacheUpdateChild::GetIsUpgrade(bool *aIsUpgrade)
308 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
310 *aIsUpgrade = mIsUpgrade;
312 return NS_OK;
315 NS_IMETHODIMP
316 OfflineCacheUpdateChild::AddDynamicURI(nsIURI *aURI)
318 return NS_ERROR_NOT_IMPLEMENTED;
321 NS_IMETHODIMP
322 OfflineCacheUpdateChild::Cancel()
324 return NS_ERROR_NOT_IMPLEMENTED;
327 NS_IMETHODIMP
328 OfflineCacheUpdateChild::AddObserver(nsIOfflineCacheUpdateObserver *aObserver,
329 bool aHoldWeak)
331 LOG(("OfflineCacheUpdateChild::AddObserver [%p]", this));
333 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
335 if (aHoldWeak) {
336 nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aObserver);
337 mWeakObservers.AppendObject(weakRef);
338 } else {
339 mObservers.AppendObject(aObserver);
342 return NS_OK;
345 NS_IMETHODIMP
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);
357 return NS_OK;
361 for (int32_t i = 0; i < mObservers.Count(); i++) {
362 if (mObservers[i] == aObserver) {
363 mObservers.RemoveObjectAt(i);
364 return NS_OK;
368 return NS_OK;
371 NS_IMETHODIMP
372 OfflineCacheUpdateChild::GetByteProgress(uint64_t * _result)
374 NS_ENSURE_ARG(_result);
376 *_result = mByteProgress;
377 return NS_OK;
380 NS_IMETHODIMP
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);
389 mWindow = nullptr;
391 nsIDocShell *docshell = piWindow->GetDocShell();
393 nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(docshell);
394 if (!item) {
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",
421 nullptr);
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,
437 stickDocument);
439 // TabChild::DeallocPOfflineCacheUpdate will release this.
440 NS_ADDREF_THIS();
442 return NS_OK;
445 bool
446 OfflineCacheUpdateChild::RecvAssociateDocuments(const nsCString &cacheGroupId,
447 const nsCString &cacheClientId)
449 LOG(("OfflineCacheUpdateChild::RecvAssociateDocuments [%p, cache=%s]", this, cacheClientId.get()));
451 nsresult rv;
453 nsCOMPtr<nsIApplicationCache> cache =
454 do_CreateInstance(NS_APPLICATIONCACHE_CONTRACTID, &rv);
455 if (NS_FAILED(rv))
456 return true;
458 cache->InitAsHandle(cacheGroupId, cacheClientId);
460 if (mDocument) {
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);
470 return true;
473 bool
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
482 switch (event) {
483 case nsIOfflineCacheUpdateObserver::STATE_CHECKING:
484 mState = STATE_CHECKING;
485 break;
487 case nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING:
488 mState = STATE_DOWNLOADING;
489 break;
491 default:
492 break;
495 nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
496 GatherObservers(observers);
498 for (int32_t i = 0; i < observers.Count(); i++)
499 observers[i]->UpdateStateChanged(this, event);
501 return true;
504 bool
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",
522 nullptr);
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);
531 return true;