Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/uriloader/prefetch/nsOfflineCacheUpdateService.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 "OfflineCacheUpdateChild.h"
7
#include "OfflineCacheUpdateParent.h"
8
#include "nsXULAppAPI.h"
9
#include "OfflineCacheUpdateGlue.h"
10
#include "nsOfflineCacheUpdate.h"
11
12
#include "nsCPrefetchService.h"
13
#include "nsCURILoader.h"
14
#include "nsIApplicationCacheContainer.h"
15
#include "nsIApplicationCacheChannel.h"
16
#include "nsIApplicationCacheService.h"
17
#include "nsICachingChannel.h"
18
#include "nsIContent.h"
19
#include "nsIDocShell.h"
20
#include "nsIDocumentLoader.h"
21
#include "nsIDOMWindow.h"
22
#include "nsIDocument.h"
23
#include "nsIObserverService.h"
24
#include "nsIURL.h"
25
#include "nsIWebProgress.h"
26
#include "nsIWebNavigation.h"
27
#include "nsICryptoHash.h"
28
#include "nsIPermissionManager.h"
29
#include "nsIPrincipal.h"
30
#include "nsNetCID.h"
31
#include "nsServiceManagerUtils.h"
32
#include "nsStreamUtils.h"
33
#include "nsThreadUtils.h"
34
#include "nsProxyRelease.h"
35
#include "mozilla/Logging.h"
36
#include "nsIAsyncVerifyRedirectCallback.h"
37
#include "mozilla/Preferences.h"
38
#include "mozilla/Attributes.h"
39
#include "mozilla/Unused.h"
40
#include "nsIDocShell.h"
41
#include "nsIDocShellTreeItem.h"
42
#include "nsIDocShellTreeOwner.h"
43
#include "mozilla/dom/ContentChild.h"
44
#include "mozilla/dom/PermissionMessageUtils.h"
45
#include "nsContentUtils.h"
46
#include "mozilla/Unused.h"
47
48
using namespace mozilla;
49
using namespace mozilla::dom;
50
51
static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nullptr;
52
static bool sAllowOfflineCache = true;
53
static bool sAllowInsecureOfflineCache = true;
54
55
nsTHashtable<nsCStringHashKey>* nsOfflineCacheUpdateService::mAllowedDomains = nullptr;
56
57
nsTHashtable<nsCStringHashKey>* nsOfflineCacheUpdateService::AllowedDomains()
58
0
{
59
0
    if (!mAllowedDomains)
60
0
        mAllowedDomains = new nsTHashtable<nsCStringHashKey>();
61
0
62
0
    return mAllowedDomains;
63
0
}
64
65
66
typedef mozilla::docshell::OfflineCacheUpdateParent OfflineCacheUpdateParent;
67
typedef mozilla::docshell::OfflineCacheUpdateChild OfflineCacheUpdateChild;
68
typedef mozilla::docshell::OfflineCacheUpdateGlue OfflineCacheUpdateGlue;
69
70
//
71
// To enable logging (see mozilla/Logging.h for full details):
72
//
73
//    set MOZ_LOG=nsOfflineCacheUpdate:5
74
//    set MOZ_LOG_FILE=offlineupdate.log
75
//
76
// this enables LogLevel::Debug level information and places all output in
77
// the file offlineupdate.log
78
//
79
LazyLogModule gOfflineCacheUpdateLog("nsOfflineCacheUpdate");
80
81
#undef LOG
82
0
#define LOG(args) MOZ_LOG(gOfflineCacheUpdateLog, mozilla::LogLevel::Debug, args)
83
84
#undef LOG_ENABLED
85
#define LOG_ENABLED() MOZ_LOG_TEST(gOfflineCacheUpdateLog, mozilla::LogLevel::Debug)
86
87
//-----------------------------------------------------------------------------
88
// nsOfflineCachePendingUpdate
89
//-----------------------------------------------------------------------------
90
91
class nsOfflineCachePendingUpdate final : public nsIWebProgressListener
92
                                        , public nsSupportsWeakReference
93
{
94
public:
95
    NS_DECL_ISUPPORTS
96
    NS_DECL_NSIWEBPROGRESSLISTENER
97
98
    nsOfflineCachePendingUpdate(nsOfflineCacheUpdateService *aService,
99
                                nsIURI *aManifestURI,
100
                                nsIURI *aDocumentURI,
101
                                nsIPrincipal* aLoadingPrincipal,
102
                                nsIDocument *aDocument)
103
        : mService(aService)
104
        , mManifestURI(aManifestURI)
105
        , mDocumentURI(aDocumentURI)
106
        , mLoadingPrincipal(aLoadingPrincipal)
107
        , mDidReleaseThis(false)
108
0
        {
109
0
            mDocument = do_GetWeakReference(aDocument);
110
0
        }
111
112
private:
113
0
    ~nsOfflineCachePendingUpdate() {}
114
115
    RefPtr<nsOfflineCacheUpdateService> mService;
116
    nsCOMPtr<nsIURI> mManifestURI;
117
    nsCOMPtr<nsIURI> mDocumentURI;
118
    nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
119
    nsCOMPtr<nsIWeakReference> mDocument;
120
    bool mDidReleaseThis;
121
};
122
123
NS_IMPL_ISUPPORTS(nsOfflineCachePendingUpdate,
124
                  nsIWebProgressListener,
125
                  nsISupportsWeakReference)
126
127
//-----------------------------------------------------------------------------
128
// nsOfflineCacheUpdateService::nsIWebProgressListener
129
//-----------------------------------------------------------------------------
130
131
NS_IMETHODIMP
132
nsOfflineCachePendingUpdate::OnProgressChange(nsIWebProgress *aProgress,
133
                                              nsIRequest *aRequest,
134
                                              int32_t curSelfProgress,
135
                                              int32_t maxSelfProgress,
136
                                              int32_t curTotalProgress,
137
                                              int32_t maxTotalProgress)
138
0
{
139
0
    MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
140
0
    return NS_OK;
141
0
}
142
143
NS_IMETHODIMP
144
nsOfflineCachePendingUpdate::OnStateChange(nsIWebProgress* aWebProgress,
145
                                           nsIRequest *aRequest,
146
                                           uint32_t progressStateFlags,
147
                                           nsresult aStatus)
148
0
{
149
0
    if (mDidReleaseThis) {
150
0
        return NS_OK;
151
0
    }
152
0
    nsCOMPtr<nsIDocument> updateDoc = do_QueryReferent(mDocument);
153
0
    if (!updateDoc) {
154
0
        // The document that scheduled this update has gone away,
155
0
        // we don't need to listen anymore.
156
0
        aWebProgress->RemoveProgressListener(this);
157
0
        MOZ_ASSERT(!mDidReleaseThis);
158
0
        mDidReleaseThis = true;
159
0
        NS_RELEASE_THIS();
160
0
        return NS_OK;
161
0
    }
162
0
163
0
    if (!(progressStateFlags & STATE_STOP)) {
164
0
        return NS_OK;
165
0
    }
166
0
167
0
    nsCOMPtr<mozIDOMWindowProxy> windowProxy;
168
0
    aWebProgress->GetDOMWindow(getter_AddRefs(windowProxy));
169
0
    if (!windowProxy) return NS_OK;
170
0
171
0
    auto* outerWindow = nsPIDOMWindowOuter::From(windowProxy);
172
0
    nsPIDOMWindowInner* innerWindow = outerWindow->GetCurrentInnerWindow();
173
0
174
0
    nsCOMPtr<nsIDocument> progressDoc = outerWindow->GetDoc();
175
0
    if (!progressDoc) return NS_OK;
176
0
177
0
    if (!SameCOMIdentity(progressDoc, updateDoc)) {
178
0
        return NS_OK;
179
0
    }
180
0
181
0
    LOG(("nsOfflineCachePendingUpdate::OnStateChange [%p, doc=%p]",
182
0
         this, progressDoc.get()));
183
0
184
0
    // Only schedule the update if the document loaded successfully
185
0
    if (NS_SUCCEEDED(aStatus)) {
186
0
        nsCOMPtr<nsIOfflineCacheUpdate> update;
187
0
        mService->Schedule(mManifestURI, mDocumentURI, mLoadingPrincipal, updateDoc, innerWindow,
188
0
                           nullptr, getter_AddRefs(update));
189
0
        if (mDidReleaseThis) {
190
0
            return NS_OK;
191
0
        }
192
0
    }
193
0
194
0
    aWebProgress->RemoveProgressListener(this);
195
0
    MOZ_ASSERT(!mDidReleaseThis);
196
0
    mDidReleaseThis = true;
197
0
    NS_RELEASE_THIS();
198
0
199
0
    return NS_OK;
200
0
}
201
202
NS_IMETHODIMP
203
nsOfflineCachePendingUpdate::OnLocationChange(nsIWebProgress* aWebProgress,
204
                                              nsIRequest* aRequest,
205
                                              nsIURI *location,
206
                                              uint32_t aFlags)
207
0
{
208
0
    MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
209
0
    return NS_OK;
210
0
}
211
212
NS_IMETHODIMP
213
nsOfflineCachePendingUpdate::OnStatusChange(nsIWebProgress* aWebProgress,
214
                                            nsIRequest* aRequest,
215
                                            nsresult aStatus,
216
                                            const char16_t* aMessage)
217
0
{
218
0
    MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
219
0
    return NS_OK;
220
0
}
221
222
NS_IMETHODIMP
223
nsOfflineCachePendingUpdate::OnSecurityChange(nsIWebProgress *aWebProgress,
224
                                              nsIRequest *aRequest,
225
                                              uint32_t state)
226
0
{
227
0
    MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
228
0
    return NS_OK;
229
0
}
230
231
//-----------------------------------------------------------------------------
232
// nsOfflineCacheUpdateService::nsISupports
233
//-----------------------------------------------------------------------------
234
235
NS_IMPL_ISUPPORTS(nsOfflineCacheUpdateService,
236
                  nsIOfflineCacheUpdateService,
237
                  nsIObserver,
238
                  nsISupportsWeakReference)
239
240
//-----------------------------------------------------------------------------
241
// nsOfflineCacheUpdateService <public>
242
//-----------------------------------------------------------------------------
243
244
nsOfflineCacheUpdateService::nsOfflineCacheUpdateService()
245
    : mDisabled(false)
246
    , mUpdateRunning(false)
247
0
{
248
0
    MOZ_ASSERT(NS_IsMainThread());
249
0
    Preferences::AddBoolVarCache(&sAllowOfflineCache,
250
0
                                 "browser.cache.offline.enable",
251
0
                                 true);
252
0
    Preferences::AddBoolVarCache(&sAllowInsecureOfflineCache,
253
0
                                 "browser.cache.offline.insecure.enable",
254
0
                                 true);
255
0
}
256
257
nsOfflineCacheUpdateService::~nsOfflineCacheUpdateService()
258
0
{
259
0
    MOZ_ASSERT(gOfflineCacheUpdateService == this);
260
0
    gOfflineCacheUpdateService = nullptr;
261
0
262
0
    delete mAllowedDomains;
263
0
    mAllowedDomains = nullptr;
264
0
}
265
266
nsresult
267
nsOfflineCacheUpdateService::Init()
268
0
{
269
0
    // Observe xpcom-shutdown event
270
0
    nsCOMPtr<nsIObserverService> observerService =
271
0
      mozilla::services::GetObserverService();
272
0
    if (!observerService)
273
0
      return NS_ERROR_FAILURE;
274
0
275
0
    nsresult rv = observerService->AddObserver(this,
276
0
                                               NS_XPCOM_SHUTDOWN_OBSERVER_ID,
277
0
                                               true);
278
0
    NS_ENSURE_SUCCESS(rv, rv);
279
0
280
0
    gOfflineCacheUpdateService = this;
281
0
282
0
    return NS_OK;
283
0
}
284
285
/* static */
286
already_AddRefed<nsOfflineCacheUpdateService>
287
nsOfflineCacheUpdateService::GetInstance()
288
0
{
289
0
    if (!gOfflineCacheUpdateService) {
290
0
        auto serv = MakeRefPtr<nsOfflineCacheUpdateService>();
291
0
        if (NS_FAILED(serv->Init()))
292
0
            serv = nullptr;
293
0
        MOZ_ASSERT(gOfflineCacheUpdateService == serv.get());
294
0
        return serv.forget();
295
0
    }
296
0
297
0
    return do_AddRef(gOfflineCacheUpdateService);
298
0
}
299
300
/* static */
301
nsOfflineCacheUpdateService *
302
nsOfflineCacheUpdateService::EnsureService()
303
0
{
304
0
    if (!gOfflineCacheUpdateService) {
305
0
        // Make the service manager hold a long-lived reference to the service
306
0
        nsCOMPtr<nsIOfflineCacheUpdateService> service =
307
0
            do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
308
0
    }
309
0
310
0
    return gOfflineCacheUpdateService;
311
0
}
312
313
nsresult
314
nsOfflineCacheUpdateService::ScheduleUpdate(nsOfflineCacheUpdate *aUpdate)
315
0
{
316
0
    LOG(("nsOfflineCacheUpdateService::Schedule [%p, update=%p]",
317
0
         this, aUpdate));
318
0
319
0
    aUpdate->SetOwner(this);
320
0
321
0
    mUpdates.AppendElement(aUpdate);
322
0
    ProcessNextUpdate();
323
0
324
0
    return NS_OK;
325
0
}
326
327
NS_IMETHODIMP
328
nsOfflineCacheUpdateService::ScheduleOnDocumentStop(nsIURI *aManifestURI,
329
                                                    nsIURI *aDocumentURI,
330
                                                    nsIPrincipal* aLoadingPrincipal,
331
                                                    nsIDocument *aDocument)
332
0
{
333
0
    LOG(("nsOfflineCacheUpdateService::ScheduleOnDocumentStop [%p, manifestURI=%p, documentURI=%p doc=%p]",
334
0
         this, aManifestURI, aDocumentURI, aDocument));
335
0
336
0
    nsCOMPtr<nsIWebProgress> progress = do_QueryInterface(aDocument->GetContainer());
337
0
    NS_ENSURE_TRUE(progress, NS_ERROR_INVALID_ARG);
338
0
339
0
    // Proceed with cache update
340
0
    RefPtr<nsOfflineCachePendingUpdate> update =
341
0
        new nsOfflineCachePendingUpdate(this, aManifestURI, aDocumentURI,
342
0
                                        aLoadingPrincipal, aDocument);
343
0
    NS_ENSURE_TRUE(update, NS_ERROR_OUT_OF_MEMORY);
344
0
345
0
    nsresult rv = progress->AddProgressListener
346
0
        (update, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
347
0
    NS_ENSURE_SUCCESS(rv, rv);
348
0
349
0
    // The update will release when it has scheduled itself.
350
0
    Unused << update.forget();
351
0
352
0
    return NS_OK;
353
0
}
354
355
nsresult
356
nsOfflineCacheUpdateService::UpdateFinished(nsOfflineCacheUpdate *aUpdate)
357
0
{
358
0
    LOG(("nsOfflineCacheUpdateService::UpdateFinished [%p, update=%p]",
359
0
         this, aUpdate));
360
0
361
0
    NS_ASSERTION(mUpdates.Length() > 0 &&
362
0
                 mUpdates[0] == aUpdate, "Unknown update completed");
363
0
364
0
    // keep this item alive until we're done notifying observers
365
0
    RefPtr<nsOfflineCacheUpdate> update = mUpdates[0];
366
0
    Unused << update;
367
0
    mUpdates.RemoveElementAt(0);
368
0
    mUpdateRunning = false;
369
0
370
0
    ProcessNextUpdate();
371
0
372
0
    return NS_OK;
373
0
}
374
375
//-----------------------------------------------------------------------------
376
// nsOfflineCacheUpdateService <private>
377
//-----------------------------------------------------------------------------
378
379
nsresult
380
nsOfflineCacheUpdateService::ProcessNextUpdate()
381
0
{
382
0
    LOG(("nsOfflineCacheUpdateService::ProcessNextUpdate [%p, num=%zu]",
383
0
         this, mUpdates.Length()));
384
0
385
0
    if (mDisabled)
386
0
        return NS_ERROR_ABORT;
387
0
388
0
    if (mUpdateRunning)
389
0
        return NS_OK;
390
0
391
0
    if (mUpdates.Length() > 0) {
392
0
        mUpdateRunning = true;
393
0
394
0
        return mUpdates[0]->Begin();
395
0
    }
396
0
397
0
    return NS_OK;
398
0
}
399
400
//-----------------------------------------------------------------------------
401
// nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService
402
//-----------------------------------------------------------------------------
403
404
NS_IMETHODIMP
405
nsOfflineCacheUpdateService::GetNumUpdates(uint32_t *aNumUpdates)
406
0
{
407
0
    LOG(("nsOfflineCacheUpdateService::GetNumUpdates [%p]", this));
408
0
409
0
    *aNumUpdates = mUpdates.Length();
410
0
    return NS_OK;
411
0
}
412
413
NS_IMETHODIMP
414
nsOfflineCacheUpdateService::GetUpdate(uint32_t aIndex,
415
                                       nsIOfflineCacheUpdate **aUpdate)
416
0
{
417
0
    LOG(("nsOfflineCacheUpdateService::GetUpdate [%p, %d]", this, aIndex));
418
0
419
0
    if (aIndex < mUpdates.Length()) {
420
0
        NS_ADDREF(*aUpdate = mUpdates[aIndex]);
421
0
    } else {
422
0
        *aUpdate = nullptr;
423
0
    }
424
0
425
0
    return NS_OK;
426
0
}
427
428
nsresult
429
nsOfflineCacheUpdateService::FindUpdate(nsIURI *aManifestURI,
430
                                        nsACString const &aOriginSuffix,
431
                                        nsIFile *aCustomProfileDir,
432
                                        nsOfflineCacheUpdate **aUpdate)
433
0
{
434
0
    nsresult rv;
435
0
436
0
    nsCOMPtr<nsIApplicationCacheService> cacheService =
437
0
        do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
438
0
    NS_ENSURE_SUCCESS(rv, rv);
439
0
440
0
    nsAutoCString groupID;
441
0
    rv = cacheService->BuildGroupIDForSuffix(aManifestURI, aOriginSuffix, groupID);
442
0
    NS_ENSURE_SUCCESS(rv, rv);
443
0
444
0
    RefPtr<nsOfflineCacheUpdate> update;
445
0
    for (uint32_t i = 0; i < mUpdates.Length(); i++) {
446
0
        update = mUpdates[i];
447
0
448
0
        bool partial;
449
0
        rv = update->GetPartial(&partial);
450
0
        NS_ENSURE_SUCCESS(rv, rv);
451
0
452
0
        if (partial) {
453
0
            // Partial updates aren't considered
454
0
            continue;
455
0
        }
456
0
457
0
        if (update->IsForGroupID(groupID) && update->IsForProfile(aCustomProfileDir)) {
458
0
            update.swap(*aUpdate);
459
0
            return NS_OK;
460
0
        }
461
0
    }
462
0
463
0
    return NS_ERROR_NOT_AVAILABLE;
464
0
}
465
466
nsresult
467
nsOfflineCacheUpdateService::Schedule(nsIURI *aManifestURI,
468
                                      nsIURI *aDocumentURI,
469
                                      nsIPrincipal* aLoadingPrincipal,
470
                                      nsIDocument *aDocument,
471
                                      nsPIDOMWindowInner* aWindow,
472
                                      nsIFile* aCustomProfileDir,
473
                                      nsIOfflineCacheUpdate **aUpdate)
474
0
{
475
0
    nsCOMPtr<nsIOfflineCacheUpdate> update;
476
0
    if (GeckoProcessType_Default != XRE_GetProcessType()) {
477
0
        update = new OfflineCacheUpdateChild(aWindow);
478
0
    }
479
0
    else {
480
0
        update = new OfflineCacheUpdateGlue();
481
0
    }
482
0
483
0
    nsresult rv;
484
0
485
0
    if (aWindow) {
486
0
        // Ensure there is window.applicationCache object that is
487
0
        // responsible for association of the new applicationCache
488
0
        // with the corresponding document.  Just ignore the result.
489
0
        aWindow->GetApplicationCache();
490
0
    }
491
0
492
0
    rv = update->Init(aManifestURI, aDocumentURI, aLoadingPrincipal, aDocument,
493
0
                      aCustomProfileDir);
494
0
    NS_ENSURE_SUCCESS(rv, rv);
495
0
496
0
    rv = update->Schedule();
497
0
    NS_ENSURE_SUCCESS(rv, rv);
498
0
499
0
    NS_ADDREF(*aUpdate = update);
500
0
501
0
    return NS_OK;
502
0
}
503
504
NS_IMETHODIMP
505
nsOfflineCacheUpdateService::ScheduleUpdate(nsIURI *aManifestURI,
506
                                            nsIURI *aDocumentURI,
507
                                            nsIPrincipal* aLoadingPrincipal,
508
                                            mozIDOMWindow* aWindow,
509
                                            nsIOfflineCacheUpdate **aUpdate)
510
0
{
511
0
    return Schedule(aManifestURI, aDocumentURI, aLoadingPrincipal, nullptr,
512
0
                    nsPIDOMWindowInner::From(aWindow), nullptr, aUpdate);
513
0
}
514
515
NS_IMETHODIMP
516
nsOfflineCacheUpdateService::ScheduleAppUpdate(nsIURI *aManifestURI,
517
                                               nsIURI *aDocumentURI,
518
                                               nsIPrincipal* aLoadingPrincipal,
519
                                               nsIFile *aProfileDir,
520
                                               nsIOfflineCacheUpdate **aUpdate)
521
0
{
522
0
    return Schedule(aManifestURI, aDocumentURI, aLoadingPrincipal, nullptr, nullptr,
523
0
                    aProfileDir, aUpdate);
524
0
}
525
526
NS_IMETHODIMP nsOfflineCacheUpdateService::CheckForUpdate(nsIURI *aManifestURI,
527
                                                          nsIPrincipal* aLoadingPrincipal,
528
                                                          nsIObserver *aObserver)
529
0
{
530
0
    if (GeckoProcessType_Default != XRE_GetProcessType()) {
531
0
        // Not intended to support this on child processes
532
0
        return NS_ERROR_NOT_IMPLEMENTED;
533
0
    }
534
0
535
0
    nsCOMPtr<nsIOfflineCacheUpdate> update = new OfflineCacheUpdateGlue();
536
0
537
0
    nsresult rv;
538
0
539
0
    rv = update->InitForUpdateCheck(aManifestURI, aLoadingPrincipal, aObserver);
540
0
    NS_ENSURE_SUCCESS(rv, rv);
541
0
542
0
    rv = update->Schedule();
543
0
    NS_ENSURE_SUCCESS(rv, rv);
544
0
545
0
    return NS_OK;
546
0
}
547
548
//-----------------------------------------------------------------------------
549
// nsOfflineCacheUpdateService::nsIObserver
550
//-----------------------------------------------------------------------------
551
552
NS_IMETHODIMP
553
nsOfflineCacheUpdateService::Observe(nsISupports     *aSubject,
554
                                     const char      *aTopic,
555
                                     const char16_t *aData)
556
0
{
557
0
    if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
558
0
        if (mUpdates.Length() > 0)
559
0
            mUpdates[0]->Cancel();
560
0
        mDisabled = true;
561
0
    }
562
0
563
0
    return NS_OK;
564
0
}
565
566
//-----------------------------------------------------------------------------
567
// nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService
568
//-----------------------------------------------------------------------------
569
570
static nsresult
571
OfflineAppPermForPrincipal(nsIPrincipal *aPrincipal,
572
                           nsIPrefBranch *aPrefBranch,
573
                           bool pinned,
574
                           bool *aAllowed)
575
0
{
576
0
    *aAllowed = false;
577
0
578
0
    if (!sAllowOfflineCache) {
579
0
        return NS_OK;
580
0
    }
581
0
582
0
    if (!aPrincipal)
583
0
        return NS_ERROR_INVALID_ARG;
584
0
585
0
    nsCOMPtr<nsIURI> uri;
586
0
    aPrincipal->GetURI(getter_AddRefs(uri));
587
0
588
0
    if (!uri)
589
0
        return NS_OK;
590
0
591
0
    nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
592
0
    if (!innerURI)
593
0
        return NS_OK;
594
0
595
0
    // only http and https applications can use offline APIs.
596
0
    bool match;
597
0
    nsresult rv = innerURI->SchemeIs("http", &match);
598
0
    NS_ENSURE_SUCCESS(rv, rv);
599
0
600
0
    if (!match) {
601
0
        rv = innerURI->SchemeIs("https", &match);
602
0
        NS_ENSURE_SUCCESS(rv, rv);
603
0
        if (!match) {
604
0
            return NS_OK;
605
0
        }
606
0
    } else {
607
0
        if (!sAllowInsecureOfflineCache) {
608
0
            return NS_OK;
609
0
        }
610
0
    }
611
0
612
0
    nsAutoCString domain;
613
0
    rv = innerURI->GetAsciiHost(domain);
614
0
    NS_ENSURE_SUCCESS(rv, rv);
615
0
616
0
    if (nsOfflineCacheUpdateService::AllowedDomains()->Contains(domain)) {
617
0
        *aAllowed = true;
618
0
        return NS_OK;
619
0
    }
620
0
621
0
    nsCOMPtr<nsIPermissionManager> permissionManager =
622
0
        services::GetPermissionManager();
623
0
    if (!permissionManager) {
624
0
        return NS_OK;
625
0
    }
626
0
627
0
    uint32_t perm;
628
0
    const char *permName = pinned ? "pin-app" : "offline-app";
629
0
    permissionManager->TestExactPermissionFromPrincipal(aPrincipal, permName, &perm);
630
0
631
0
    if (perm == nsIPermissionManager::ALLOW_ACTION ||
632
0
        perm == nsIOfflineCacheUpdateService::ALLOW_NO_WARN) {
633
0
        *aAllowed = true;
634
0
    }
635
0
636
0
    // offline-apps.allow_by_default is now effective at the cache selection
637
0
    // algorithm code (nsContentSink).
638
0
639
0
    return NS_OK;
640
0
}
641
642
NS_IMETHODIMP
643
nsOfflineCacheUpdateService::OfflineAppAllowed(nsIPrincipal *aPrincipal,
644
                                               nsIPrefBranch *aPrefBranch,
645
                                               bool *aAllowed)
646
0
{
647
0
    return OfflineAppPermForPrincipal(aPrincipal, aPrefBranch, false, aAllowed);
648
0
}
649
650
NS_IMETHODIMP
651
nsOfflineCacheUpdateService::OfflineAppAllowedForURI(nsIURI *aURI,
652
                                                     nsIPrefBranch *aPrefBranch,
653
                                                     bool *aAllowed)
654
0
{
655
0
    OriginAttributes attrs;
656
0
    nsCOMPtr<nsIPrincipal> principal =
657
0
        BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
658
0
    return OfflineAppPermForPrincipal(principal, aPrefBranch, false, aAllowed);
659
0
}
660
661
nsresult
662
nsOfflineCacheUpdateService::OfflineAppPinnedForURI(nsIURI *aDocumentURI,
663
                                                    nsIPrefBranch *aPrefBranch,
664
                                                    bool *aPinned)
665
0
{
666
0
    OriginAttributes attrs;
667
0
    nsCOMPtr<nsIPrincipal> principal =
668
0
        BasePrincipal::CreateCodebasePrincipal(aDocumentURI, attrs);
669
0
    return OfflineAppPermForPrincipal(principal, aPrefBranch, true, aPinned);
670
0
}
671
672
NS_IMETHODIMP
673
nsOfflineCacheUpdateService::AllowOfflineApp(nsIPrincipal *aPrincipal)
674
0
{
675
0
    nsresult rv;
676
0
677
0
    if (!sAllowOfflineCache) {
678
0
        return NS_ERROR_NOT_AVAILABLE;
679
0
    }
680
0
681
0
    if (!sAllowInsecureOfflineCache) {
682
0
        nsCOMPtr<nsIURI> uri;
683
0
        aPrincipal->GetURI(getter_AddRefs(uri));
684
0
685
0
        if (!uri) {
686
0
            return NS_ERROR_NOT_AVAILABLE;
687
0
        }
688
0
689
0
        nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
690
0
        if (!innerURI) {
691
0
            return NS_ERROR_NOT_AVAILABLE;
692
0
        }
693
0
694
0
        // if http then we should prevent this cache
695
0
        bool match;
696
0
        rv = innerURI->SchemeIs("http", &match);
697
0
        NS_ENSURE_SUCCESS(rv, rv);
698
0
699
0
        if (match) {
700
0
            return NS_ERROR_NOT_AVAILABLE;
701
0
        }
702
0
    }
703
0
704
0
    if (GeckoProcessType_Default != XRE_GetProcessType()) {
705
0
        ContentChild* child = ContentChild::GetSingleton();
706
0
707
0
        if (!child->SendSetOfflinePermission(IPC::Principal(aPrincipal))) {
708
0
            return NS_ERROR_FAILURE;
709
0
        }
710
0
711
0
        nsAutoCString domain;
712
0
        rv = aPrincipal->GetBaseDomain(domain);
713
0
        NS_ENSURE_SUCCESS(rv, rv);
714
0
715
0
        nsOfflineCacheUpdateService::AllowedDomains()->PutEntry(domain);
716
0
    }
717
0
    else {
718
0
        nsCOMPtr<nsIPermissionManager> permissionManager =
719
0
            services::GetPermissionManager();
720
0
        if (!permissionManager)
721
0
            return NS_ERROR_NOT_AVAILABLE;
722
0
723
0
        rv = permissionManager->AddFromPrincipal(
724
0
            aPrincipal, "offline-app", nsIPermissionManager::ALLOW_ACTION,
725
0
            nsIPermissionManager::EXPIRE_NEVER, 0);
726
0
        NS_ENSURE_SUCCESS(rv, rv);
727
0
    }
728
0
729
0
    return NS_OK;
730
0
}