Coverage Report

Created: 2018-09-25 14:53

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