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