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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : * Dave Camp <dcamp@mozilla.com>
24 : *
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.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "OfflineCacheUpdateChild.h"
40 : #include "nsOfflineCacheUpdate.h"
41 : #include "mozilla/dom/ContentChild.h"
42 : #include "mozilla/dom/TabChild.h"
43 :
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"
64 :
65 : #if defined(PR_LOGGING)
66 : //
67 : // To enable logging (see prlog.h for full details):
68 : //
69 : // set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5
70 : // set NSPR_LOG_FILE=offlineupdate.log
71 : //
72 : // this enables PR_LOG_ALWAYS level information and places all output in
73 : // the file offlineupdate.log
74 : //
75 : extern PRLogModuleInfo *gOfflineCacheUpdateLog;
76 : #endif
77 : #define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args)
78 : #define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4)
79 :
80 : namespace mozilla {
81 : namespace docshell {
82 :
83 : //-----------------------------------------------------------------------------
84 : // OfflineCacheUpdateChild::nsISupports
85 : //-----------------------------------------------------------------------------
86 :
87 0 : NS_INTERFACE_MAP_BEGIN(OfflineCacheUpdateChild)
88 0 : NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdate)
89 0 : NS_INTERFACE_MAP_END
90 :
91 0 : NS_IMPL_ADDREF(OfflineCacheUpdateChild)
92 0 : NS_IMPL_RELEASE_WITH_DESTROY(OfflineCacheUpdateChild, RefcountHitZero())
93 :
94 : void
95 0 : OfflineCacheUpdateChild::RefcountHitZero()
96 : {
97 0 : if (mIPCActivated) {
98 : // ContentChild::DeallocPOfflineCacheUpdate will delete this
99 0 : OfflineCacheUpdateChild::Send__delete__(this);
100 : } else {
101 0 : delete this; // we never opened IPDL channel
102 : }
103 0 : }
104 :
105 : //-----------------------------------------------------------------------------
106 : // OfflineCacheUpdateChild <public>
107 : //-----------------------------------------------------------------------------
108 :
109 0 : OfflineCacheUpdateChild::OfflineCacheUpdateChild(nsIDOMWindow* aWindow)
110 : : mState(STATE_UNINITIALIZED)
111 : , mIsUpgrade(false)
112 : , mIPCActivated(false)
113 0 : , mWindow(aWindow)
114 : {
115 0 : }
116 :
117 0 : OfflineCacheUpdateChild::~OfflineCacheUpdateChild()
118 : {
119 0 : LOG(("OfflineCacheUpdateChild::~OfflineCacheUpdateChild [%p]", this));
120 0 : }
121 :
122 : nsresult
123 0 : OfflineCacheUpdateChild::GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers)
124 : {
125 0 : for (PRInt32 i = 0; i < mWeakObservers.Count(); i++) {
126 : nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
127 0 : do_QueryReferent(mWeakObservers[i]);
128 0 : if (observer)
129 0 : aObservers.AppendObject(observer);
130 : else
131 0 : mWeakObservers.RemoveObjectAt(i--);
132 : }
133 :
134 0 : for (PRInt32 i = 0; i < mObservers.Count(); i++) {
135 0 : aObservers.AppendObject(mObservers[i]);
136 : }
137 :
138 0 : return NS_OK;
139 : }
140 :
141 : void
142 0 : OfflineCacheUpdateChild::SetDocument(nsIDOMDocument *aDocument)
143 : {
144 : // The design is one document for one cache update on the content process.
145 0 : NS_ASSERTION(!mDocument, "Setting more then a single document on a child offline cache update");
146 :
147 0 : LOG(("Document %p added to update child %p", aDocument, this));
148 :
149 : // Add document only if it was not loaded from an offline cache.
150 : // If it were loaded from an offline cache then it has already
151 : // been associated with it and must not be again cached as
152 : // implicit (which are the reasons we collect documents here).
153 0 : nsCOMPtr<nsIDocument> document = do_QueryInterface(aDocument);
154 0 : if (!document)
155 : return;
156 :
157 0 : nsIChannel* channel = document->GetChannel();
158 : nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
159 0 : do_QueryInterface(channel);
160 0 : if (!appCacheChannel)
161 : return;
162 :
163 : bool loadedFromAppCache;
164 0 : appCacheChannel->GetLoadedFromApplicationCache(&loadedFromAppCache);
165 0 : if (loadedFromAppCache)
166 : return;
167 :
168 0 : mDocument = aDocument;
169 : }
170 :
171 : nsresult
172 0 : OfflineCacheUpdateChild::AssociateDocument(nsIDOMDocument *aDocument,
173 : nsIApplicationCache *aApplicationCache)
174 : {
175 : // Check that the document that requested this update was
176 : // previously associated with an application cache. If not, it
177 : // should be associated with the new one.
178 : nsCOMPtr<nsIApplicationCacheContainer> container =
179 0 : do_QueryInterface(aDocument);
180 0 : if (!container)
181 0 : return NS_OK;
182 :
183 0 : nsCOMPtr<nsIApplicationCache> existingCache;
184 0 : nsresult rv = container->GetApplicationCache(getter_AddRefs(existingCache));
185 0 : NS_ENSURE_SUCCESS(rv, rv);
186 :
187 0 : if (!existingCache) {
188 : #if defined(PR_LOGGING)
189 0 : if (LOG_ENABLED()) {
190 0 : nsCAutoString clientID;
191 0 : if (aApplicationCache) {
192 0 : aApplicationCache->GetClientID(clientID);
193 : }
194 0 : LOG(("Update %p: associating app cache %s to document %p",
195 : this, clientID.get(), aDocument));
196 : }
197 : #endif
198 :
199 0 : rv = container->SetApplicationCache(aApplicationCache);
200 0 : NS_ENSURE_SUCCESS(rv, rv);
201 : }
202 :
203 0 : return NS_OK;
204 : }
205 :
206 : //-----------------------------------------------------------------------------
207 : // OfflineCacheUpdateChild::nsIOfflineCacheUpdate
208 : //-----------------------------------------------------------------------------
209 :
210 : NS_IMETHODIMP
211 0 : OfflineCacheUpdateChild::Init(nsIURI *aManifestURI,
212 : nsIURI *aDocumentURI,
213 : nsIDOMDocument *aDocument)
214 : {
215 : nsresult rv;
216 :
217 : // Make sure the service has been initialized
218 : nsOfflineCacheUpdateService* service =
219 0 : nsOfflineCacheUpdateService::EnsureService();
220 0 : if (!service)
221 0 : return NS_ERROR_FAILURE;
222 :
223 0 : LOG(("OfflineCacheUpdateChild::Init [%p]", this));
224 :
225 : // Only http and https applications are supported.
226 : bool match;
227 0 : rv = aManifestURI->SchemeIs("http", &match);
228 0 : NS_ENSURE_SUCCESS(rv, rv);
229 :
230 0 : if (!match) {
231 0 : rv = aManifestURI->SchemeIs("https", &match);
232 0 : NS_ENSURE_SUCCESS(rv, rv);
233 0 : if (!match)
234 0 : return NS_ERROR_ABORT;
235 : }
236 :
237 0 : mManifestURI = aManifestURI;
238 :
239 0 : rv = mManifestURI->GetAsciiHost(mUpdateDomain);
240 0 : NS_ENSURE_SUCCESS(rv, rv);
241 :
242 0 : mDocumentURI = aDocumentURI;
243 :
244 0 : mState = STATE_INITIALIZED;
245 :
246 0 : if (aDocument)
247 0 : SetDocument(aDocument);
248 :
249 0 : return NS_OK;
250 : }
251 :
252 : NS_IMETHODIMP
253 0 : OfflineCacheUpdateChild::InitPartial(nsIURI *aManifestURI,
254 : const nsACString& clientID,
255 : nsIURI *aDocumentURI)
256 : {
257 : NS_NOTREACHED("Not expected to do partial offline cache updates"
258 0 : " on the child process");
259 : // For now leaving this method, we may discover we need it.
260 0 : return NS_ERROR_NOT_IMPLEMENTED;
261 : }
262 :
263 : NS_IMETHODIMP
264 0 : OfflineCacheUpdateChild::GetUpdateDomain(nsACString &aUpdateDomain)
265 : {
266 0 : NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
267 :
268 0 : aUpdateDomain = mUpdateDomain;
269 0 : return NS_OK;
270 : }
271 :
272 : NS_IMETHODIMP
273 0 : OfflineCacheUpdateChild::GetStatus(PRUint16 *aStatus)
274 : {
275 0 : switch (mState) {
276 : case STATE_CHECKING :
277 0 : *aStatus = nsIDOMOfflineResourceList::CHECKING;
278 0 : return NS_OK;
279 : case STATE_DOWNLOADING :
280 0 : *aStatus = nsIDOMOfflineResourceList::DOWNLOADING;
281 0 : return NS_OK;
282 : default :
283 0 : *aStatus = nsIDOMOfflineResourceList::IDLE;
284 0 : return NS_OK;
285 : }
286 :
287 : return NS_ERROR_FAILURE;
288 : }
289 :
290 : NS_IMETHODIMP
291 0 : OfflineCacheUpdateChild::GetPartial(bool *aPartial)
292 : {
293 0 : *aPartial = false;
294 0 : return NS_OK;
295 : }
296 :
297 : NS_IMETHODIMP
298 0 : OfflineCacheUpdateChild::GetManifestURI(nsIURI **aManifestURI)
299 : {
300 0 : NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
301 :
302 0 : NS_IF_ADDREF(*aManifestURI = mManifestURI);
303 0 : return NS_OK;
304 : }
305 :
306 : NS_IMETHODIMP
307 0 : OfflineCacheUpdateChild::GetSucceeded(bool *aSucceeded)
308 : {
309 0 : NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE);
310 :
311 0 : *aSucceeded = mSucceeded;
312 :
313 0 : return NS_OK;
314 : }
315 :
316 : NS_IMETHODIMP
317 0 : OfflineCacheUpdateChild::GetIsUpgrade(bool *aIsUpgrade)
318 : {
319 0 : NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
320 :
321 0 : *aIsUpgrade = mIsUpgrade;
322 :
323 0 : return NS_OK;
324 : }
325 :
326 : NS_IMETHODIMP
327 0 : OfflineCacheUpdateChild::AddDynamicURI(nsIURI *aURI)
328 : {
329 0 : return NS_ERROR_NOT_IMPLEMENTED;
330 : }
331 :
332 : NS_IMETHODIMP
333 0 : OfflineCacheUpdateChild::AddObserver(nsIOfflineCacheUpdateObserver *aObserver,
334 : bool aHoldWeak)
335 : {
336 0 : LOG(("OfflineCacheUpdateChild::AddObserver [%p]", this));
337 :
338 0 : NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
339 :
340 0 : if (aHoldWeak) {
341 0 : nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aObserver);
342 0 : mWeakObservers.AppendObject(weakRef);
343 : } else {
344 0 : mObservers.AppendObject(aObserver);
345 : }
346 :
347 0 : return NS_OK;
348 : }
349 :
350 : NS_IMETHODIMP
351 0 : OfflineCacheUpdateChild::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver)
352 : {
353 0 : LOG(("OfflineCacheUpdateChild::RemoveObserver [%p]", this));
354 :
355 0 : NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
356 :
357 0 : for (PRInt32 i = 0; i < mWeakObservers.Count(); i++) {
358 : nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
359 0 : do_QueryReferent(mWeakObservers[i]);
360 0 : if (observer == aObserver) {
361 0 : mWeakObservers.RemoveObjectAt(i);
362 0 : return NS_OK;
363 : }
364 : }
365 :
366 0 : for (PRInt32 i = 0; i < mObservers.Count(); i++) {
367 0 : if (mObservers[i] == aObserver) {
368 0 : mObservers.RemoveObjectAt(i);
369 0 : return NS_OK;
370 : }
371 : }
372 :
373 0 : return NS_OK;
374 : }
375 :
376 : NS_IMETHODIMP
377 0 : OfflineCacheUpdateChild::Schedule()
378 : {
379 0 : LOG(("OfflineCacheUpdateChild::Schedule [%p]", this));
380 :
381 0 : NS_ASSERTION(mWindow, "Window must be provided to the offline cache update child");
382 :
383 : nsCOMPtr<nsPIDOMWindow> piWindow =
384 0 : do_QueryInterface(mWindow);
385 0 : mWindow = nsnull;
386 :
387 0 : nsIDocShell *docshell = piWindow->GetDocShell();
388 :
389 0 : nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(docshell);
390 0 : if (!item) {
391 0 : NS_WARNING("doc shell tree item is null");
392 0 : return NS_ERROR_FAILURE;
393 : }
394 :
395 0 : nsCOMPtr<nsIDocShellTreeOwner> owner;
396 0 : item->GetTreeOwner(getter_AddRefs(owner));
397 :
398 0 : nsCOMPtr<nsITabChild> tabchild = do_GetInterface(owner);
399 0 : if (!tabchild) {
400 0 : NS_WARNING("tab is null");
401 0 : return NS_ERROR_FAILURE;
402 : }
403 :
404 : // because owner implements nsITabChild, we can assume that it is
405 : // the one and only TabChild.
406 0 : mozilla::dom::TabChild* child = static_cast<mozilla::dom::TabChild*>(tabchild.get());
407 :
408 : nsCOMPtr<nsIObserverService> observerService =
409 0 : mozilla::services::GetObserverService();
410 0 : if (observerService) {
411 0 : LOG(("Calling offline-cache-update-added"));
412 0 : observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(this),
413 : "offline-cache-update-added",
414 0 : nsnull);
415 0 : LOG(("Done offline-cache-update-added"));
416 : }
417 :
418 : // mDocument is non-null if both:
419 : // 1. this update was initiated by a document that referred a manifest
420 : // 2. the document has not already been loaded from the application cache
421 : // This tells the update to cache this document even in case the manifest
422 : // has not been changed since the last fetch.
423 : // See also nsOfflineCacheUpdate::ScheduleImplicit.
424 0 : bool stickDocument = mDocument != nsnull;
425 :
426 : // Need to addref ourself here, because the IPC stack doesn't hold
427 : // a reference to us. Will be released in RecvFinish() that identifies
428 : // the work has been done.
429 : child->SendPOfflineCacheUpdateConstructor(this,
430 0 : IPC::URI(mManifestURI),
431 0 : IPC::URI(mDocumentURI),
432 : mClientID,
433 0 : stickDocument);
434 :
435 0 : mIPCActivated = true;
436 0 : this->AddRef();
437 :
438 0 : return NS_OK;
439 : }
440 :
441 : bool
442 0 : OfflineCacheUpdateChild::RecvAssociateDocuments(const nsCString &cacheGroupId,
443 : const nsCString &cacheClientId)
444 : {
445 0 : LOG(("OfflineCacheUpdateChild::RecvAssociateDocuments [%p, cache=%s]", this, cacheClientId.get()));
446 :
447 : nsresult rv;
448 :
449 : nsCOMPtr<nsIApplicationCache> cache =
450 0 : do_CreateInstance(NS_APPLICATIONCACHE_CONTRACTID, &rv);
451 0 : if (NS_FAILED(rv))
452 0 : return true;
453 :
454 0 : cache->InitAsHandle(cacheGroupId, cacheClientId);
455 :
456 0 : if (mDocument) {
457 0 : AssociateDocument(mDocument, cache);
458 : }
459 :
460 0 : nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
461 0 : rv = GatherObservers(observers);
462 0 : NS_ENSURE_SUCCESS(rv, rv);
463 :
464 0 : for (PRInt32 i = 0; i < observers.Count(); i++)
465 0 : observers[i]->ApplicationCacheAvailable(cache);
466 :
467 0 : return true;
468 : }
469 :
470 : bool
471 0 : OfflineCacheUpdateChild::RecvNotifyStateEvent(const PRUint32 &event)
472 : {
473 0 : LOG(("OfflineCacheUpdateChild::RecvNotifyStateEvent [%p]", this));
474 :
475 : // Convert the public observer state to our internal state
476 0 : switch (event) {
477 : case nsIOfflineCacheUpdateObserver::STATE_CHECKING:
478 0 : mState = STATE_CHECKING;
479 0 : break;
480 :
481 : case nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING:
482 0 : mState = STATE_DOWNLOADING;
483 0 : break;
484 :
485 : default:
486 0 : break;
487 : }
488 :
489 0 : nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
490 0 : nsresult rv = GatherObservers(observers);
491 0 : NS_ENSURE_SUCCESS(rv, rv);
492 :
493 0 : for (PRInt32 i = 0; i < observers.Count(); i++)
494 0 : observers[i]->UpdateStateChanged(this, event);
495 :
496 0 : return true;
497 : }
498 :
499 : bool
500 0 : OfflineCacheUpdateChild::RecvFinish(const bool &succeeded,
501 : const bool &isUpgrade)
502 : {
503 0 : LOG(("OfflineCacheUpdateChild::RecvFinish [%p]", this));
504 :
505 0 : nsRefPtr<OfflineCacheUpdateChild> kungFuDeathGrip(this);
506 :
507 0 : mState = STATE_FINISHED;
508 0 : mSucceeded = succeeded;
509 0 : mIsUpgrade = isUpgrade;
510 :
511 : nsCOMPtr<nsIObserverService> observerService =
512 0 : mozilla::services::GetObserverService();
513 0 : if (observerService) {
514 0 : LOG(("Calling offline-cache-update-completed"));
515 0 : observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(this),
516 : "offline-cache-update-completed",
517 0 : nsnull);
518 0 : LOG(("Done offline-cache-update-completed"));
519 : }
520 :
521 : // This is by contract the last notification from the parent, release
522 : // us now. This is corresponding to AddRef in Schedule().
523 0 : this->Release();
524 :
525 0 : return true;
526 : }
527 :
528 : }
529 : }
|