Bug 627938: Fix nsGlobalChromeWindow cleanup. (r=smaug, a=jst)
[mozilla-central.git] / uriloader / prefetch / OfflineCacheUpdateChild.cpp
blob2a8fc9509da23e1d731a349401bbc1364f85ac5a
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
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Mozilla Corporation
19 * Portions created by the Initial Developer are Copyright (C) 2007
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
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"
54 #include "nsIURL.h"
55 #include "nsITabChild.h"
56 #include "nsNetCID.h"
57 #include "nsNetUtil.h"
58 #include "nsServiceManagerUtils.h"
59 #include "nsStreamUtils.h"
60 #include "nsThreadUtils.h"
61 #include "nsProxyRelease.h"
62 #include "prlog.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;
78 #endif
79 #define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args)
80 #define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4)
82 namespace mozilla {
83 namespace docshell {
85 //-----------------------------------------------------------------------------
86 // OfflineCacheUpdateChild::nsISupports
87 //-----------------------------------------------------------------------------
89 NS_INTERFACE_MAP_BEGIN(OfflineCacheUpdateChild)
90 NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdate)
91 NS_INTERFACE_MAP_END
93 NS_IMPL_ADDREF(OfflineCacheUpdateChild)
94 NS_IMPL_RELEASE_WITH_DESTROY(OfflineCacheUpdateChild, RefcountHitZero())
96 void
97 OfflineCacheUpdateChild::RefcountHitZero()
99 if (mIPCActivated) {
100 // ContentChild::DeallocPOfflineCacheUpdate will delete this
101 OfflineCacheUpdateChild::Send__delete__(this);
102 } else {
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)
115 , mWindow(aWindow)
119 OfflineCacheUpdateChild::~OfflineCacheUpdateChild()
121 LOG(("OfflineCacheUpdateChild::~OfflineCacheUpdateChild [%p]", this));
124 nsresult
125 OfflineCacheUpdateChild::GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers)
127 for (PRInt32 i = 0; i < mWeakObservers.Count(); i++) {
128 nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
129 do_QueryReferent(mWeakObservers[i]);
130 if (observer)
131 aObservers.AppendObject(observer);
132 else
133 mWeakObservers.RemoveObjectAt(i--);
136 for (PRInt32 i = 0; i < mObservers.Count(); i++) {
137 aObservers.AppendObject(mObservers[i]);
140 return NS_OK;
143 void
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);
156 if (!document)
157 return;
159 nsIChannel* channel = document->GetChannel();
160 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
161 do_QueryInterface(channel);
162 if (!appCacheChannel)
163 return;
165 PRBool loadedFromAppCache;
166 appCacheChannel->GetLoadedFromApplicationCache(&loadedFromAppCache);
167 if (loadedFromAppCache)
168 return;
170 mDocument = aDocument;
173 nsresult
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);
182 if (!container)
183 return NS_OK;
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)
191 if (LOG_ENABLED()) {
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));
199 #endif
201 rv = container->SetApplicationCache(aApplicationCache);
202 NS_ENSURE_SUCCESS(rv, rv);
205 return NS_OK;
208 //-----------------------------------------------------------------------------
209 // OfflineCacheUpdateChild::nsIOfflineCacheUpdate
210 //-----------------------------------------------------------------------------
212 NS_IMETHODIMP
213 OfflineCacheUpdateChild::Init(nsIURI *aManifestURI,
214 nsIURI *aDocumentURI,
215 nsIDOMDocument *aDocument)
217 nsresult rv;
219 // Make sure the service has been initialized
220 nsOfflineCacheUpdateService* service =
221 nsOfflineCacheUpdateService::EnsureService();
222 if (!service)
223 return NS_ERROR_FAILURE;
225 LOG(("OfflineCacheUpdateChild::Init [%p]", this));
227 // Only http and https applications are supported.
228 PRBool match;
229 rv = aManifestURI->SchemeIs("http", &match);
230 NS_ENSURE_SUCCESS(rv, rv);
232 if (!match) {
233 rv = aManifestURI->SchemeIs("https", &match);
234 NS_ENSURE_SUCCESS(rv, rv);
235 if (!match)
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;
248 if (aDocument)
249 SetDocument(aDocument);
251 return NS_OK;
254 NS_IMETHODIMP
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;
265 NS_IMETHODIMP
266 OfflineCacheUpdateChild::GetUpdateDomain(nsACString &aUpdateDomain)
268 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
270 aUpdateDomain = mUpdateDomain;
271 return NS_OK;
274 NS_IMETHODIMP
275 OfflineCacheUpdateChild::GetStatus(PRUint16 *aStatus)
277 switch (mState) {
278 case STATE_CHECKING :
279 *aStatus = nsIDOMOfflineResourceList::CHECKING;
280 return NS_OK;
281 case STATE_DOWNLOADING :
282 *aStatus = nsIDOMOfflineResourceList::DOWNLOADING;
283 return NS_OK;
284 default :
285 *aStatus = nsIDOMOfflineResourceList::IDLE;
286 return NS_OK;
289 return NS_ERROR_FAILURE;
292 NS_IMETHODIMP
293 OfflineCacheUpdateChild::GetPartial(PRBool *aPartial)
295 *aPartial = PR_FALSE;
296 return NS_OK;
299 NS_IMETHODIMP
300 OfflineCacheUpdateChild::GetManifestURI(nsIURI **aManifestURI)
302 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
304 NS_IF_ADDREF(*aManifestURI = mManifestURI);
305 return NS_OK;
308 NS_IMETHODIMP
309 OfflineCacheUpdateChild::GetSucceeded(PRBool *aSucceeded)
311 NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE);
313 *aSucceeded = mSucceeded;
315 return NS_OK;
318 NS_IMETHODIMP
319 OfflineCacheUpdateChild::GetIsUpgrade(PRBool *aIsUpgrade)
321 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
323 *aIsUpgrade = mIsUpgrade;
325 return NS_OK;
328 NS_IMETHODIMP
329 OfflineCacheUpdateChild::AddDynamicURI(nsIURI *aURI)
331 return NS_ERROR_NOT_IMPLEMENTED;
334 NS_IMETHODIMP
335 OfflineCacheUpdateChild::AddObserver(nsIOfflineCacheUpdateObserver *aObserver,
336 PRBool aHoldWeak)
338 LOG(("OfflineCacheUpdateChild::AddObserver [%p]", this));
340 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
342 if (aHoldWeak) {
343 nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aObserver);
344 mWeakObservers.AppendObject(weakRef);
345 } else {
346 mObservers.AppendObject(aObserver);
349 return NS_OK;
352 NS_IMETHODIMP
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);
364 return NS_OK;
368 for (PRInt32 i = 0; i < mObservers.Count(); i++) {
369 if (mObservers[i] == aObserver) {
370 mObservers.RemoveObjectAt(i);
371 return NS_OK;
375 return NS_OK;
378 NS_IMETHODIMP
379 OfflineCacheUpdateChild::Schedule()
381 LOG(("OfflineCacheUpdateChild::Schedule [%p]", this));
383 #ifdef MOZ_IPC
384 NS_ASSERTION(mWindow, "Window must be provided to the offline cache update child");
385 #endif
387 nsCOMPtr<nsPIDOMWindow> piWindow =
388 do_QueryInterface(mWindow);
389 mWindow = nsnull;
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 if (!tabchild) {
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",
418 nsnull);
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),
436 mClientID,
437 stickDocument);
439 mIPCActivated = PR_TRUE;
440 this->AddRef();
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 rv = GatherObservers(observers);
466 NS_ENSURE_SUCCESS(rv, rv);
468 for (PRInt32 i = 0; i < observers.Count(); i++)
469 observers[i]->ApplicationCacheAvailable(cache);
471 return true;
474 bool
475 OfflineCacheUpdateChild::RecvNotifyStateEvent(const PRUint32 &event)
477 LOG(("OfflineCacheUpdateChild::RecvNotifyStateEvent [%p]", this));
479 // Convert the public observer state to our internal state
480 switch (event) {
481 case nsIOfflineCacheUpdateObserver::STATE_CHECKING:
482 mState = STATE_CHECKING;
483 break;
485 case nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING:
486 mState = STATE_DOWNLOADING;
487 break;
489 default:
490 break;
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);
500 return true;
503 bool
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",
521 nsnull);
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().
527 this->Release();
529 return true;