|
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/. */ |
|
5 |
|
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" |
|
12 |
|
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" |
|
33 |
|
34 using namespace mozilla::ipc; |
|
35 using namespace mozilla::net; |
|
36 using mozilla::dom::TabChild; |
|
37 |
|
38 #if defined(PR_LOGGING) |
|
39 // |
|
40 // To enable logging (see prlog.h for full details): |
|
41 // |
|
42 // set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5 |
|
43 // set NSPR_LOG_FILE=offlineupdate.log |
|
44 // |
|
45 // this enables PR_LOG_ALWAYS level information and places all output in |
|
46 // the file offlineupdate.log |
|
47 // |
|
48 extern PRLogModuleInfo *gOfflineCacheUpdateLog; |
|
49 #endif |
|
50 |
|
51 #undef LOG |
|
52 #define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args) |
|
53 |
|
54 #undef LOG_ENABLED |
|
55 #define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4) |
|
56 |
|
57 namespace mozilla { |
|
58 namespace docshell { |
|
59 |
|
60 //----------------------------------------------------------------------------- |
|
61 // OfflineCacheUpdateChild::nsISupports |
|
62 //----------------------------------------------------------------------------- |
|
63 |
|
64 NS_INTERFACE_MAP_BEGIN(OfflineCacheUpdateChild) |
|
65 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
66 NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdate) |
|
67 NS_INTERFACE_MAP_END |
|
68 |
|
69 NS_IMPL_ADDREF(OfflineCacheUpdateChild) |
|
70 NS_IMPL_RELEASE(OfflineCacheUpdateChild) |
|
71 |
|
72 //----------------------------------------------------------------------------- |
|
73 // OfflineCacheUpdateChild <public> |
|
74 //----------------------------------------------------------------------------- |
|
75 |
|
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) |
|
83 { |
|
84 } |
|
85 |
|
86 OfflineCacheUpdateChild::~OfflineCacheUpdateChild() |
|
87 { |
|
88 LOG(("OfflineCacheUpdateChild::~OfflineCacheUpdateChild [%p]", this)); |
|
89 } |
|
90 |
|
91 void |
|
92 OfflineCacheUpdateChild::GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers) |
|
93 { |
|
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--); |
|
101 } |
|
102 |
|
103 for (int32_t i = 0; i < mObservers.Count(); i++) { |
|
104 aObservers.AppendObject(mObservers[i]); |
|
105 } |
|
106 } |
|
107 |
|
108 void |
|
109 OfflineCacheUpdateChild::SetDocument(nsIDOMDocument *aDocument) |
|
110 { |
|
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"); |
|
113 |
|
114 LOG(("Document %p added to update child %p", aDocument, this)); |
|
115 |
|
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; |
|
123 |
|
124 nsIChannel* channel = document->GetChannel(); |
|
125 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel = |
|
126 do_QueryInterface(channel); |
|
127 if (!appCacheChannel) |
|
128 return; |
|
129 |
|
130 bool loadedFromAppCache; |
|
131 appCacheChannel->GetLoadedFromApplicationCache(&loadedFromAppCache); |
|
132 if (loadedFromAppCache) |
|
133 return; |
|
134 |
|
135 mDocument = aDocument; |
|
136 } |
|
137 |
|
138 nsresult |
|
139 OfflineCacheUpdateChild::AssociateDocument(nsIDOMDocument *aDocument, |
|
140 nsIApplicationCache *aApplicationCache) |
|
141 { |
|
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; |
|
149 |
|
150 nsCOMPtr<nsIApplicationCache> existingCache; |
|
151 nsresult rv = container->GetApplicationCache(getter_AddRefs(existingCache)); |
|
152 NS_ENSURE_SUCCESS(rv, rv); |
|
153 |
|
154 if (!existingCache) { |
|
155 #if defined(PR_LOGGING) |
|
156 if (LOG_ENABLED()) { |
|
157 nsAutoCString clientID; |
|
158 if (aApplicationCache) { |
|
159 aApplicationCache->GetClientID(clientID); |
|
160 } |
|
161 LOG(("Update %p: associating app cache %s to document %p", |
|
162 this, clientID.get(), aDocument)); |
|
163 } |
|
164 #endif |
|
165 |
|
166 rv = container->SetApplicationCache(aApplicationCache); |
|
167 NS_ENSURE_SUCCESS(rv, rv); |
|
168 } |
|
169 |
|
170 return NS_OK; |
|
171 } |
|
172 |
|
173 //----------------------------------------------------------------------------- |
|
174 // OfflineCacheUpdateChild::nsIOfflineCacheUpdate |
|
175 //----------------------------------------------------------------------------- |
|
176 |
|
177 NS_IMETHODIMP |
|
178 OfflineCacheUpdateChild::Init(nsIURI *aManifestURI, |
|
179 nsIURI *aDocumentURI, |
|
180 nsIDOMDocument *aDocument, |
|
181 nsIFile *aCustomProfileDir, |
|
182 uint32_t aAppID, |
|
183 bool aInBrowser) |
|
184 { |
|
185 nsresult rv; |
|
186 |
|
187 // Make sure the service has been initialized |
|
188 nsOfflineCacheUpdateService* service = |
|
189 nsOfflineCacheUpdateService::EnsureService(); |
|
190 if (!service) |
|
191 return NS_ERROR_FAILURE; |
|
192 |
|
193 if (aCustomProfileDir) { |
|
194 NS_ERROR("Custom Offline Cache Update not supported on child process"); |
|
195 return NS_ERROR_NOT_IMPLEMENTED; |
|
196 } |
|
197 |
|
198 LOG(("OfflineCacheUpdateChild::Init [%p]", this)); |
|
199 |
|
200 // Only http and https applications are supported. |
|
201 bool match; |
|
202 rv = aManifestURI->SchemeIs("http", &match); |
|
203 NS_ENSURE_SUCCESS(rv, rv); |
|
204 |
|
205 if (!match) { |
|
206 rv = aManifestURI->SchemeIs("https", &match); |
|
207 NS_ENSURE_SUCCESS(rv, rv); |
|
208 if (!match) |
|
209 return NS_ERROR_ABORT; |
|
210 } |
|
211 |
|
212 mManifestURI = aManifestURI; |
|
213 |
|
214 rv = mManifestURI->GetAsciiHost(mUpdateDomain); |
|
215 NS_ENSURE_SUCCESS(rv, rv); |
|
216 |
|
217 mDocumentURI = aDocumentURI; |
|
218 |
|
219 mState = STATE_INITIALIZED; |
|
220 |
|
221 if (aDocument) |
|
222 SetDocument(aDocument); |
|
223 |
|
224 mAppID = aAppID; |
|
225 mInBrowser = aInBrowser; |
|
226 |
|
227 return NS_OK; |
|
228 } |
|
229 |
|
230 NS_IMETHODIMP |
|
231 OfflineCacheUpdateChild::InitPartial(nsIURI *aManifestURI, |
|
232 const nsACString& clientID, |
|
233 nsIURI *aDocumentURI) |
|
234 { |
|
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; |
|
239 } |
|
240 |
|
241 NS_IMETHODIMP |
|
242 OfflineCacheUpdateChild::InitForUpdateCheck(nsIURI *aManifestURI, |
|
243 uint32_t aAppID, |
|
244 bool aInBrowser, |
|
245 nsIObserver *aObserver) |
|
246 { |
|
247 NS_NOTREACHED("Not expected to do only update checks" |
|
248 " from the child process"); |
|
249 return NS_ERROR_NOT_IMPLEMENTED; |
|
250 } |
|
251 |
|
252 NS_IMETHODIMP |
|
253 OfflineCacheUpdateChild::GetUpdateDomain(nsACString &aUpdateDomain) |
|
254 { |
|
255 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); |
|
256 |
|
257 aUpdateDomain = mUpdateDomain; |
|
258 return NS_OK; |
|
259 } |
|
260 |
|
261 NS_IMETHODIMP |
|
262 OfflineCacheUpdateChild::GetStatus(uint16_t *aStatus) |
|
263 { |
|
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; |
|
274 } |
|
275 |
|
276 return NS_ERROR_FAILURE; |
|
277 } |
|
278 |
|
279 NS_IMETHODIMP |
|
280 OfflineCacheUpdateChild::GetPartial(bool *aPartial) |
|
281 { |
|
282 *aPartial = false; |
|
283 return NS_OK; |
|
284 } |
|
285 |
|
286 NS_IMETHODIMP |
|
287 OfflineCacheUpdateChild::GetManifestURI(nsIURI **aManifestURI) |
|
288 { |
|
289 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); |
|
290 |
|
291 NS_IF_ADDREF(*aManifestURI = mManifestURI); |
|
292 return NS_OK; |
|
293 } |
|
294 |
|
295 NS_IMETHODIMP |
|
296 OfflineCacheUpdateChild::GetSucceeded(bool *aSucceeded) |
|
297 { |
|
298 NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE); |
|
299 |
|
300 *aSucceeded = mSucceeded; |
|
301 |
|
302 return NS_OK; |
|
303 } |
|
304 |
|
305 NS_IMETHODIMP |
|
306 OfflineCacheUpdateChild::GetIsUpgrade(bool *aIsUpgrade) |
|
307 { |
|
308 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); |
|
309 |
|
310 *aIsUpgrade = mIsUpgrade; |
|
311 |
|
312 return NS_OK; |
|
313 } |
|
314 |
|
315 NS_IMETHODIMP |
|
316 OfflineCacheUpdateChild::AddDynamicURI(nsIURI *aURI) |
|
317 { |
|
318 return NS_ERROR_NOT_IMPLEMENTED; |
|
319 } |
|
320 |
|
321 NS_IMETHODIMP |
|
322 OfflineCacheUpdateChild::Cancel() |
|
323 { |
|
324 return NS_ERROR_NOT_IMPLEMENTED; |
|
325 } |
|
326 |
|
327 NS_IMETHODIMP |
|
328 OfflineCacheUpdateChild::AddObserver(nsIOfflineCacheUpdateObserver *aObserver, |
|
329 bool aHoldWeak) |
|
330 { |
|
331 LOG(("OfflineCacheUpdateChild::AddObserver [%p]", this)); |
|
332 |
|
333 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); |
|
334 |
|
335 if (aHoldWeak) { |
|
336 nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aObserver); |
|
337 mWeakObservers.AppendObject(weakRef); |
|
338 } else { |
|
339 mObservers.AppendObject(aObserver); |
|
340 } |
|
341 |
|
342 return NS_OK; |
|
343 } |
|
344 |
|
345 NS_IMETHODIMP |
|
346 OfflineCacheUpdateChild::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver) |
|
347 { |
|
348 LOG(("OfflineCacheUpdateChild::RemoveObserver [%p]", this)); |
|
349 |
|
350 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); |
|
351 |
|
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; |
|
358 } |
|
359 } |
|
360 |
|
361 for (int32_t i = 0; i < mObservers.Count(); i++) { |
|
362 if (mObservers[i] == aObserver) { |
|
363 mObservers.RemoveObjectAt(i); |
|
364 return NS_OK; |
|
365 } |
|
366 } |
|
367 |
|
368 return NS_OK; |
|
369 } |
|
370 |
|
371 NS_IMETHODIMP |
|
372 OfflineCacheUpdateChild::GetByteProgress(uint64_t * _result) |
|
373 { |
|
374 NS_ENSURE_ARG(_result); |
|
375 |
|
376 *_result = mByteProgress; |
|
377 return NS_OK; |
|
378 } |
|
379 |
|
380 NS_IMETHODIMP |
|
381 OfflineCacheUpdateChild::Schedule() |
|
382 { |
|
383 LOG(("OfflineCacheUpdateChild::Schedule [%p]", this)); |
|
384 |
|
385 NS_ASSERTION(mWindow, "Window must be provided to the offline cache update child"); |
|
386 |
|
387 nsCOMPtr<nsPIDOMWindow> piWindow = |
|
388 do_QueryInterface(mWindow); |
|
389 mWindow = nullptr; |
|
390 |
|
391 nsIDocShell *docshell = piWindow->GetDocShell(); |
|
392 |
|
393 nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(docshell); |
|
394 if (!item) { |
|
395 NS_WARNING("doc shell tree item is null"); |
|
396 return NS_ERROR_FAILURE; |
|
397 } |
|
398 |
|
399 nsCOMPtr<nsIDocShellTreeOwner> owner; |
|
400 item->GetTreeOwner(getter_AddRefs(owner)); |
|
401 |
|
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; |
|
406 |
|
407 if (MissingRequiredTabChild(child, "offlinecacheupdate")) { |
|
408 return NS_ERROR_FAILURE; |
|
409 } |
|
410 |
|
411 URIParams manifestURI, documentURI; |
|
412 SerializeURI(mManifestURI, manifestURI); |
|
413 SerializeURI(mDocumentURI, documentURI); |
|
414 |
|
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")); |
|
423 } |
|
424 |
|
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; |
|
432 |
|
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); |
|
438 |
|
439 // TabChild::DeallocPOfflineCacheUpdate will release this. |
|
440 NS_ADDREF_THIS(); |
|
441 |
|
442 return NS_OK; |
|
443 } |
|
444 |
|
445 bool |
|
446 OfflineCacheUpdateChild::RecvAssociateDocuments(const nsCString &cacheGroupId, |
|
447 const nsCString &cacheClientId) |
|
448 { |
|
449 LOG(("OfflineCacheUpdateChild::RecvAssociateDocuments [%p, cache=%s]", this, cacheClientId.get())); |
|
450 |
|
451 nsresult rv; |
|
452 |
|
453 nsCOMPtr<nsIApplicationCache> cache = |
|
454 do_CreateInstance(NS_APPLICATIONCACHE_CONTRACTID, &rv); |
|
455 if (NS_FAILED(rv)) |
|
456 return true; |
|
457 |
|
458 cache->InitAsHandle(cacheGroupId, cacheClientId); |
|
459 |
|
460 if (mDocument) { |
|
461 AssociateDocument(mDocument, cache); |
|
462 } |
|
463 |
|
464 nsCOMArray<nsIOfflineCacheUpdateObserver> observers; |
|
465 GatherObservers(observers); |
|
466 |
|
467 for (int32_t i = 0; i < observers.Count(); i++) |
|
468 observers[i]->ApplicationCacheAvailable(cache); |
|
469 |
|
470 return true; |
|
471 } |
|
472 |
|
473 bool |
|
474 OfflineCacheUpdateChild::RecvNotifyStateEvent(const uint32_t &event, |
|
475 const uint64_t &byteProgress) |
|
476 { |
|
477 LOG(("OfflineCacheUpdateChild::RecvNotifyStateEvent [%p]", this)); |
|
478 |
|
479 mByteProgress = byteProgress; |
|
480 |
|
481 // Convert the public observer state to our internal state |
|
482 switch (event) { |
|
483 case nsIOfflineCacheUpdateObserver::STATE_CHECKING: |
|
484 mState = STATE_CHECKING; |
|
485 break; |
|
486 |
|
487 case nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING: |
|
488 mState = STATE_DOWNLOADING; |
|
489 break; |
|
490 |
|
491 default: |
|
492 break; |
|
493 } |
|
494 |
|
495 nsCOMArray<nsIOfflineCacheUpdateObserver> observers; |
|
496 GatherObservers(observers); |
|
497 |
|
498 for (int32_t i = 0; i < observers.Count(); i++) |
|
499 observers[i]->UpdateStateChanged(this, event); |
|
500 |
|
501 return true; |
|
502 } |
|
503 |
|
504 bool |
|
505 OfflineCacheUpdateChild::RecvFinish(const bool &succeeded, |
|
506 const bool &isUpgrade) |
|
507 { |
|
508 LOG(("OfflineCacheUpdateChild::RecvFinish [%p]", this)); |
|
509 |
|
510 nsRefPtr<OfflineCacheUpdateChild> kungFuDeathGrip(this); |
|
511 |
|
512 mState = STATE_FINISHED; |
|
513 mSucceeded = succeeded; |
|
514 mIsUpgrade = isUpgrade; |
|
515 |
|
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")); |
|
524 } |
|
525 |
|
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); |
|
530 |
|
531 return true; |
|
532 } |
|
533 |
|
534 } |
|
535 } |