Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/uriloader/prefetch/nsOfflineCacheUpdate.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 "nsOfflineCacheUpdate.h"
7
8
#include "nsCPrefetchService.h"
9
#include "nsCURILoader.h"
10
#include "nsIApplicationCacheContainer.h"
11
#include "nsIApplicationCacheChannel.h"
12
#include "nsIApplicationCacheService.h"
13
#include "nsICachingChannel.h"
14
#include "nsIContent.h"
15
#include "mozilla/dom/Element.h"
16
#include "mozilla/dom/OfflineResourceListBinding.h"
17
#include "nsIDocumentLoader.h"
18
#include "nsIDOMWindow.h"
19
#include "nsIDocument.h"
20
#include "nsIObserverService.h"
21
#include "nsIURL.h"
22
#include "nsIURIMutator.h"
23
#include "nsIWebProgress.h"
24
#include "nsICryptoHash.h"
25
#include "nsICacheEntry.h"
26
#include "nsIPermissionManager.h"
27
#include "nsIPrincipal.h"
28
#include "nsNetCID.h"
29
#include "nsNetUtil.h"
30
#include "nsServiceManagerUtils.h"
31
#include "nsStreamUtils.h"
32
#include "nsThreadUtils.h"
33
#include "nsProxyRelease.h"
34
#include "nsIConsoleService.h"
35
#include "mozilla/Logging.h"
36
#include "nsIAsyncVerifyRedirectCallback.h"
37
#include "mozilla/Preferences.h"
38
#include "mozilla/Attributes.h"
39
#include "nsContentUtils.h"
40
#include "nsIPrincipal.h"
41
#include "nsDiskCacheDeviceSQL.h"
42
43
#include "nsXULAppAPI.h"
44
45
using namespace mozilla;
46
47
static const uint32_t kRescheduleLimit = 3;
48
// Max number of retries for every entry of pinned app.
49
static const uint32_t kPinnedEntryRetriesLimit = 3;
50
// Maximum number of parallel items loads
51
static const uint32_t kParallelLoadLimit = 15;
52
53
// Quota for offline apps when preloading
54
static const int32_t  kCustomProfileQuota = 512000;
55
56
//
57
// To enable logging (see mozilla/Logging.h for full details):
58
//
59
//    set MOZ_LOG=nsOfflineCacheUpdate:5
60
//    set MOZ_LOG_FILE=offlineupdate.log
61
//
62
// this enables LogLevel::Debug level information and places all output in
63
// the file offlineupdate.log
64
//
65
extern LazyLogModule gOfflineCacheUpdateLog;
66
67
#undef LOG
68
0
#define LOG(args) MOZ_LOG(gOfflineCacheUpdateLog, mozilla::LogLevel::Debug, args)
69
70
#undef LOG_ENABLED
71
0
#define LOG_ENABLED() MOZ_LOG_TEST(gOfflineCacheUpdateLog, mozilla::LogLevel::Debug)
72
73
class AutoFreeArray {
74
public:
75
    AutoFreeArray(uint32_t count, char **values)
76
0
        : mCount(count), mValues(values) {};
77
0
    ~AutoFreeArray() { NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mCount, mValues); }
78
private:
79
    uint32_t mCount;
80
    char **mValues;
81
};
82
83
namespace {
84
85
nsresult
86
DropReferenceFromURL(nsCOMPtr<nsIURI>& aURI)
87
0
{
88
0
    // XXXdholbert If this SetRef fails, callers of this method probably
89
0
    // want to call aURI->CloneIgnoringRef() and use the result of that.
90
0
    nsCOMPtr<nsIURI> uri(aURI);
91
0
    return NS_GetURIWithoutRef(uri, getter_AddRefs(aURI));
92
0
}
93
94
void
95
LogToConsole(const char * message, nsOfflineCacheUpdateItem * item = nullptr)
96
0
{
97
0
    nsCOMPtr<nsIConsoleService> consoleService =
98
0
        do_GetService(NS_CONSOLESERVICE_CONTRACTID);
99
0
    if (consoleService)
100
0
    {
101
0
        nsAutoString messageUTF16 = NS_ConvertUTF8toUTF16(message);
102
0
        if (item && item->mURI) {
103
0
            messageUTF16.AppendLiteral(", URL=");
104
0
            messageUTF16.Append(
105
0
                NS_ConvertUTF8toUTF16(item->mURI->GetSpecOrDefault()));
106
0
        }
107
0
        consoleService->LogStringMessage(messageUTF16.get());
108
0
    }
109
0
}
110
111
} // namespace
112
113
//-----------------------------------------------------------------------------
114
// nsManifestCheck
115
//-----------------------------------------------------------------------------
116
117
class nsManifestCheck final : public nsIStreamListener
118
                            , public nsIChannelEventSink
119
                            , public nsIInterfaceRequestor
120
{
121
public:
122
    nsManifestCheck(nsOfflineCacheUpdate *aUpdate,
123
                    nsIURI *aURI,
124
                    nsIURI *aReferrerURI,
125
                    nsIPrincipal* aLoadingPrincipal)
126
        : mUpdate(aUpdate)
127
        , mURI(aURI)
128
        , mReferrerURI(aReferrerURI)
129
        , mLoadingPrincipal(aLoadingPrincipal)
130
0
        {}
131
132
    NS_DECL_ISUPPORTS
133
    NS_DECL_NSIREQUESTOBSERVER
134
    NS_DECL_NSISTREAMLISTENER
135
    NS_DECL_NSICHANNELEVENTSINK
136
    NS_DECL_NSIINTERFACEREQUESTOR
137
138
    nsresult Begin();
139
140
private:
141
142
0
    ~nsManifestCheck() {}
143
144
    static nsresult ReadManifest(nsIInputStream *aInputStream,
145
                                 void *aClosure,
146
                                 const char *aFromSegment,
147
                                 uint32_t aOffset,
148
                                 uint32_t aCount,
149
                                 uint32_t *aBytesConsumed);
150
151
    RefPtr<nsOfflineCacheUpdate> mUpdate;
152
    nsCOMPtr<nsIURI> mURI;
153
    nsCOMPtr<nsIURI> mReferrerURI;
154
    nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
155
    nsCOMPtr<nsICryptoHash> mManifestHash;
156
    nsCOMPtr<nsIChannel> mChannel;
157
};
158
159
//-----------------------------------------------------------------------------
160
// nsManifestCheck::nsISupports
161
//-----------------------------------------------------------------------------
162
NS_IMPL_ISUPPORTS(nsManifestCheck,
163
                  nsIRequestObserver,
164
                  nsIStreamListener,
165
                  nsIChannelEventSink,
166
                  nsIInterfaceRequestor)
167
168
//-----------------------------------------------------------------------------
169
// nsManifestCheck <public>
170
//-----------------------------------------------------------------------------
171
172
nsresult
173
nsManifestCheck::Begin()
174
0
{
175
0
    nsresult rv;
176
0
    mManifestHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
177
0
    NS_ENSURE_SUCCESS(rv, rv);
178
0
179
0
    rv = mManifestHash->Init(nsICryptoHash::MD5);
180
0
    NS_ENSURE_SUCCESS(rv, rv);
181
0
    rv = NS_NewChannel(getter_AddRefs(mChannel),
182
0
                       mURI,
183
0
                       mLoadingPrincipal,
184
0
                       nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
185
0
                       nsIContentPolicy::TYPE_OTHER,
186
0
                       nullptr,   // PerformanceStorage
187
0
                       nullptr,   // loadGroup
188
0
                       nullptr,   // aCallbacks
189
0
                       nsIRequest::LOAD_BYPASS_CACHE);
190
0
191
0
    NS_ENSURE_SUCCESS(rv, rv);
192
0
193
0
    // configure HTTP specific stuff
194
0
    nsCOMPtr<nsIHttpChannel> httpChannel =
195
0
        do_QueryInterface(mChannel);
196
0
    if (httpChannel) {
197
0
        rv = httpChannel->SetReferrer(mReferrerURI);
198
0
        MOZ_ASSERT(NS_SUCCEEDED(rv));
199
0
        rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
200
0
                                           NS_LITERAL_CSTRING("offline-resource"),
201
0
                                           false);
202
0
        MOZ_ASSERT(NS_SUCCEEDED(rv));
203
0
    }
204
0
205
0
    return mChannel->AsyncOpen2(this);
206
0
}
207
208
//-----------------------------------------------------------------------------
209
// nsManifestCheck <public>
210
//-----------------------------------------------------------------------------
211
212
/* static */ nsresult
213
nsManifestCheck::ReadManifest(nsIInputStream *aInputStream,
214
                              void *aClosure,
215
                              const char *aFromSegment,
216
                              uint32_t aOffset,
217
                              uint32_t aCount,
218
                              uint32_t *aBytesConsumed)
219
0
{
220
0
    nsManifestCheck *manifestCheck =
221
0
        static_cast<nsManifestCheck*>(aClosure);
222
0
223
0
    nsresult rv;
224
0
    *aBytesConsumed = aCount;
225
0
226
0
    rv = manifestCheck->mManifestHash->Update(
227
0
        reinterpret_cast<const uint8_t *>(aFromSegment), aCount);
228
0
    NS_ENSURE_SUCCESS(rv, rv);
229
0
230
0
    return NS_OK;
231
0
}
232
233
//-----------------------------------------------------------------------------
234
// nsManifestCheck::nsIStreamListener
235
//-----------------------------------------------------------------------------
236
237
NS_IMETHODIMP
238
nsManifestCheck::OnStartRequest(nsIRequest *aRequest,
239
                                nsISupports *aContext)
240
0
{
241
0
    return NS_OK;
242
0
}
243
244
NS_IMETHODIMP
245
nsManifestCheck::OnDataAvailable(nsIRequest *aRequest,
246
                                 nsISupports *aContext,
247
                                 nsIInputStream *aStream,
248
                                 uint64_t aOffset,
249
                                 uint32_t aCount)
250
0
{
251
0
    uint32_t bytesRead;
252
0
    aStream->ReadSegments(ReadManifest, this, aCount, &bytesRead);
253
0
    return NS_OK;
254
0
}
255
256
NS_IMETHODIMP
257
nsManifestCheck::OnStopRequest(nsIRequest *aRequest,
258
                               nsISupports *aContext,
259
                               nsresult aStatus)
260
0
{
261
0
    nsAutoCString manifestHash;
262
0
    if (NS_SUCCEEDED(aStatus)) {
263
0
        mManifestHash->Finish(true, manifestHash);
264
0
    }
265
0
266
0
    mUpdate->ManifestCheckCompleted(aStatus, manifestHash);
267
0
268
0
    return NS_OK;
269
0
}
270
271
//-----------------------------------------------------------------------------
272
// nsManifestCheck::nsIInterfaceRequestor
273
//-----------------------------------------------------------------------------
274
275
NS_IMETHODIMP
276
nsManifestCheck::GetInterface(const nsIID &aIID, void **aResult)
277
0
{
278
0
    if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
279
0
        NS_ADDREF_THIS();
280
0
        *aResult = static_cast<nsIChannelEventSink *>(this);
281
0
        return NS_OK;
282
0
    }
283
0
284
0
    return NS_ERROR_NO_INTERFACE;
285
0
}
286
287
//-----------------------------------------------------------------------------
288
// nsManifestCheck::nsIChannelEventSink
289
//-----------------------------------------------------------------------------
290
291
NS_IMETHODIMP
292
nsManifestCheck::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
293
                                        nsIChannel *aNewChannel,
294
                                        uint32_t aFlags,
295
                                        nsIAsyncVerifyRedirectCallback *callback)
296
0
{
297
0
    // Redirects should cause the load (and therefore the update) to fail.
298
0
    if (aFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
299
0
        callback->OnRedirectVerifyCallback(NS_OK);
300
0
        return NS_OK;
301
0
    }
302
0
303
0
    LogToConsole("Manifest check failed because its response is a redirect");
304
0
305
0
    aOldChannel->Cancel(NS_ERROR_ABORT);
306
0
    return NS_ERROR_ABORT;
307
0
}
308
309
//-----------------------------------------------------------------------------
310
// nsOfflineCacheUpdateItem::nsISupports
311
//-----------------------------------------------------------------------------
312
313
NS_IMPL_ISUPPORTS(nsOfflineCacheUpdateItem,
314
                  nsIRequestObserver,
315
                  nsIStreamListener,
316
                  nsIRunnable,
317
                  nsIInterfaceRequestor,
318
                  nsIChannelEventSink)
319
320
//-----------------------------------------------------------------------------
321
// nsOfflineCacheUpdateItem <public>
322
//-----------------------------------------------------------------------------
323
324
nsOfflineCacheUpdateItem::nsOfflineCacheUpdateItem(nsIURI *aURI,
325
                                                   nsIURI *aReferrerURI,
326
                                                   nsIPrincipal* aLoadingPrincipal,
327
                                                   nsIApplicationCache *aApplicationCache,
328
                                                   nsIApplicationCache *aPreviousApplicationCache,
329
                                                   uint32_t type,
330
                                                   uint32_t loadFlags)
331
    : mURI(aURI)
332
    , mReferrerURI(aReferrerURI)
333
    , mLoadingPrincipal(aLoadingPrincipal)
334
    , mApplicationCache(aApplicationCache)
335
    , mPreviousApplicationCache(aPreviousApplicationCache)
336
    , mItemType(type)
337
    , mLoadFlags(loadFlags)
338
    , mChannel(nullptr)
339
    , mState(LoadStatus::UNINITIALIZED)
340
    , mBytesRead(0)
341
0
{
342
0
}
343
344
nsOfflineCacheUpdateItem::~nsOfflineCacheUpdateItem()
345
0
{
346
0
}
347
348
nsresult
349
nsOfflineCacheUpdateItem::OpenChannel(nsOfflineCacheUpdate *aUpdate)
350
0
{
351
0
    if (LOG_ENABLED()) {
352
0
        LOG(("%p: Opening channel for %s", this,
353
0
             mURI->GetSpecOrDefault().get()));
354
0
    }
355
0
356
0
    if (mUpdate) {
357
0
        // Holding a reference to the update means this item is already
358
0
        // in progress (has a channel, or is just in between OnStopRequest()
359
0
        // and its Run() call.  We must never open channel on this item again.
360
0
        LOG(("  %p is already running! ignoring", this));
361
0
        return NS_ERROR_ALREADY_OPENED;
362
0
    }
363
0
364
0
    nsresult rv = nsOfflineCacheUpdate::GetCacheKey(mURI, mCacheKey);
365
0
    NS_ENSURE_SUCCESS(rv, rv);
366
0
367
0
    uint32_t flags = nsIRequest::LOAD_BACKGROUND |
368
0
                     nsICachingChannel::LOAD_ONLY_IF_MODIFIED;
369
0
370
0
    if (mApplicationCache == mPreviousApplicationCache) {
371
0
        // Same app cache to read from and to write to is used during
372
0
        // an only-update-check procedure.  Here we protect the existing
373
0
        // cache from being modified.
374
0
        flags |= nsIRequest::INHIBIT_CACHING;
375
0
    }
376
0
377
0
    flags |= mLoadFlags;
378
0
379
0
    rv = NS_NewChannel(getter_AddRefs(mChannel),
380
0
                       mURI,
381
0
                       mLoadingPrincipal,
382
0
                       nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
383
0
                       nsIContentPolicy::TYPE_OTHER,
384
0
                       nullptr,   // PerformanceStorage
385
0
                       nullptr,  // aLoadGroup
386
0
                       this,     // aCallbacks
387
0
                       flags);
388
0
389
0
    NS_ENSURE_SUCCESS(rv, rv);
390
0
391
0
    nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
392
0
        do_QueryInterface(mChannel, &rv);
393
0
394
0
    // Support for nsIApplicationCacheChannel is required.
395
0
    NS_ENSURE_SUCCESS(rv, rv);
396
0
397
0
    // Use the existing application cache as the cache to check.
398
0
    rv = appCacheChannel->SetApplicationCache(mPreviousApplicationCache);
399
0
    NS_ENSURE_SUCCESS(rv, rv);
400
0
401
0
    // Set the new application cache as the target for write.
402
0
    rv = appCacheChannel->SetApplicationCacheForWrite(mApplicationCache);
403
0
    NS_ENSURE_SUCCESS(rv, rv);
404
0
405
0
    // configure HTTP specific stuff
406
0
    nsCOMPtr<nsIHttpChannel> httpChannel =
407
0
        do_QueryInterface(mChannel);
408
0
    if (httpChannel) {
409
0
        rv = httpChannel->SetReferrer(mReferrerURI);
410
0
        MOZ_ASSERT(NS_SUCCEEDED(rv));
411
0
        rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
412
0
                                           NS_LITERAL_CSTRING("offline-resource"),
413
0
                                           false);
414
0
        MOZ_ASSERT(NS_SUCCEEDED(rv));
415
0
    }
416
0
417
0
    rv = mChannel->AsyncOpen2(this);
418
0
    NS_ENSURE_SUCCESS(rv, rv);
419
0
420
0
    mUpdate = aUpdate;
421
0
422
0
    mState = LoadStatus::REQUESTED;
423
0
424
0
    return NS_OK;
425
0
}
426
427
nsresult
428
nsOfflineCacheUpdateItem::Cancel()
429
0
{
430
0
    if (mChannel) {
431
0
        mChannel->Cancel(NS_ERROR_ABORT);
432
0
        mChannel = nullptr;
433
0
    }
434
0
435
0
    mState = LoadStatus::UNINITIALIZED;
436
0
437
0
    return NS_OK;
438
0
}
439
440
//-----------------------------------------------------------------------------
441
// nsOfflineCacheUpdateItem::nsIStreamListener
442
//-----------------------------------------------------------------------------
443
444
NS_IMETHODIMP
445
nsOfflineCacheUpdateItem::OnStartRequest(nsIRequest *aRequest,
446
                                         nsISupports *aContext)
447
0
{
448
0
    mState = LoadStatus::RECEIVING;
449
0
450
0
    return NS_OK;
451
0
}
452
453
NS_IMETHODIMP
454
nsOfflineCacheUpdateItem::OnDataAvailable(nsIRequest *aRequest,
455
                                          nsISupports *aContext,
456
                                          nsIInputStream *aStream,
457
                                          uint64_t aOffset,
458
                                          uint32_t aCount)
459
0
{
460
0
    uint32_t bytesRead = 0;
461
0
    aStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &bytesRead);
462
0
    mBytesRead += bytesRead;
463
0
    LOG(("loaded %u bytes into offline cache [offset=%" PRIu64 "]\n",
464
0
         bytesRead, aOffset));
465
0
466
0
    mUpdate->OnByteProgress(bytesRead);
467
0
468
0
    return NS_OK;
469
0
}
470
471
NS_IMETHODIMP
472
nsOfflineCacheUpdateItem::OnStopRequest(nsIRequest *aRequest,
473
                                        nsISupports *aContext,
474
                                        nsresult aStatus)
475
0
{
476
0
    if (LOG_ENABLED()) {
477
0
        LOG(("%p: Done fetching offline item %s [status=%" PRIx32 "]\n",
478
0
             this, mURI->GetSpecOrDefault().get(), static_cast<uint32_t>(aStatus)));
479
0
    }
480
0
481
0
    if (mBytesRead == 0 && aStatus == NS_OK) {
482
0
        // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was
483
0
        // specified), but the object should report loadedSize as if it
484
0
        // did.
485
0
        mChannel->GetContentLength(&mBytesRead);
486
0
        mUpdate->OnByteProgress(mBytesRead);
487
0
    }
488
0
489
0
    if (NS_FAILED(aStatus)) {
490
0
        nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
491
0
        if (httpChannel) {
492
0
            bool isNoStore;
493
0
            if (NS_SUCCEEDED(httpChannel->IsNoStoreResponse(&isNoStore))
494
0
                && isNoStore) {
495
0
                LogToConsole("Offline cache manifest item has Cache-control: no-store header",
496
0
                             this);
497
0
            }
498
0
        }
499
0
    }
500
0
501
0
    // We need to notify the update that the load is complete, but we
502
0
    // want to give the channel a chance to close the cache entries.
503
0
    NS_DispatchToCurrentThread(this);
504
0
505
0
    return NS_OK;
506
0
}
507
508
//-----------------------------------------------------------------------------
509
// nsOfflineCacheUpdateItem::nsIRunnable
510
//-----------------------------------------------------------------------------
511
NS_IMETHODIMP
512
nsOfflineCacheUpdateItem::Run()
513
0
{
514
0
    // Set mState to LOADED here rather than in OnStopRequest to prevent
515
0
    // race condition when checking state of all mItems in ProcessNextURI().
516
0
    // If state would have been set in OnStopRequest we could mistakenly
517
0
    // take this item as already finished and finish the update process too
518
0
    // early when ProcessNextURI() would get called between OnStopRequest()
519
0
    // and Run() of this item.  Finish() would then have been called twice.
520
0
    mState = LoadStatus::LOADED;
521
0
522
0
    RefPtr<nsOfflineCacheUpdate> update;
523
0
    update.swap(mUpdate);
524
0
    update->LoadCompleted(this);
525
0
526
0
    return NS_OK;
527
0
}
528
529
//-----------------------------------------------------------------------------
530
// nsOfflineCacheUpdateItem::nsIInterfaceRequestor
531
//-----------------------------------------------------------------------------
532
533
NS_IMETHODIMP
534
nsOfflineCacheUpdateItem::GetInterface(const nsIID &aIID, void **aResult)
535
0
{
536
0
    if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
537
0
        NS_ADDREF_THIS();
538
0
        *aResult = static_cast<nsIChannelEventSink *>(this);
539
0
        return NS_OK;
540
0
    }
541
0
542
0
    return NS_ERROR_NO_INTERFACE;
543
0
}
544
545
//-----------------------------------------------------------------------------
546
// nsOfflineCacheUpdateItem::nsIChannelEventSink
547
//-----------------------------------------------------------------------------
548
549
NS_IMETHODIMP
550
nsOfflineCacheUpdateItem::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
551
                                                 nsIChannel *aNewChannel,
552
                                                 uint32_t aFlags,
553
                                                 nsIAsyncVerifyRedirectCallback *cb)
554
0
{
555
0
    if (!(aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
556
0
        // Don't allow redirect in case of non-internal redirect and cancel
557
0
        // the channel to clean the cache entry.
558
0
        LogToConsole("Offline cache manifest failed because an item redirects", this);
559
0
560
0
        aOldChannel->Cancel(NS_ERROR_ABORT);
561
0
        return NS_ERROR_ABORT;
562
0
    }
563
0
564
0
    nsCOMPtr<nsIURI> newURI;
565
0
    nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI));
566
0
    if (NS_FAILED(rv))
567
0
        return rv;
568
0
569
0
    nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
570
0
        do_QueryInterface(aNewChannel);
571
0
    if (appCacheChannel) {
572
0
        rv = appCacheChannel->SetApplicationCacheForWrite(mApplicationCache);
573
0
        NS_ENSURE_SUCCESS(rv, rv);
574
0
    }
575
0
576
0
    nsAutoCString oldScheme;
577
0
    mURI->GetScheme(oldScheme);
578
0
579
0
    bool match;
580
0
    if (NS_FAILED(newURI->SchemeIs(oldScheme.get(), &match)) || !match) {
581
0
        LOG(("rejected: redirected to a different scheme\n"));
582
0
        return NS_ERROR_ABORT;
583
0
    }
584
0
585
0
    // HTTP request headers are not automatically forwarded to the new channel.
586
0
    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
587
0
    NS_ENSURE_STATE(httpChannel);
588
0
589
0
    rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
590
0
                                       NS_LITERAL_CSTRING("offline-resource"),
591
0
                                       false);
592
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
593
0
594
0
    mChannel = aNewChannel;
595
0
596
0
    cb->OnRedirectVerifyCallback(NS_OK);
597
0
    return NS_OK;
598
0
}
599
600
nsresult
601
nsOfflineCacheUpdateItem::GetRequestSucceeded(bool * succeeded)
602
0
{
603
0
    *succeeded = false;
604
0
605
0
    if (!mChannel)
606
0
        return NS_OK;
607
0
608
0
    nsresult rv;
609
0
    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
610
0
    NS_ENSURE_SUCCESS(rv, rv);
611
0
612
0
    bool reqSucceeded;
613
0
    rv = httpChannel->GetRequestSucceeded(&reqSucceeded);
614
0
    if (NS_ERROR_NOT_AVAILABLE == rv)
615
0
        return NS_OK;
616
0
    NS_ENSURE_SUCCESS(rv, rv);
617
0
618
0
    if (!reqSucceeded) {
619
0
        LOG(("Request failed"));
620
0
        return NS_OK;
621
0
    }
622
0
623
0
    nsresult channelStatus;
624
0
    rv = httpChannel->GetStatus(&channelStatus);
625
0
    NS_ENSURE_SUCCESS(rv, rv);
626
0
627
0
    if (NS_FAILED(channelStatus)) {
628
0
        LOG(("Channel status=0x%08" PRIx32, static_cast<uint32_t>(channelStatus)));
629
0
        return NS_OK;
630
0
    }
631
0
632
0
    *succeeded = true;
633
0
    return NS_OK;
634
0
}
635
636
bool
637
nsOfflineCacheUpdateItem::IsScheduled()
638
0
{
639
0
    return mState == LoadStatus::UNINITIALIZED;
640
0
}
641
642
bool
643
nsOfflineCacheUpdateItem::IsInProgress()
644
0
{
645
0
    return mState == LoadStatus::REQUESTED ||
646
0
           mState == LoadStatus::RECEIVING;
647
0
}
648
649
bool
650
nsOfflineCacheUpdateItem::IsCompleted()
651
0
{
652
0
    return mState == LoadStatus::LOADED;
653
0
}
654
655
nsresult
656
nsOfflineCacheUpdateItem::GetStatus(uint16_t *aStatus)
657
0
{
658
0
    if (!mChannel) {
659
0
        *aStatus = 0;
660
0
        return NS_OK;
661
0
    }
662
0
663
0
    nsresult rv;
664
0
    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
665
0
    NS_ENSURE_SUCCESS(rv, rv);
666
0
667
0
    uint32_t httpStatus;
668
0
    rv = httpChannel->GetResponseStatus(&httpStatus);
669
0
    if (rv == NS_ERROR_NOT_AVAILABLE) {
670
0
        *aStatus = 0;
671
0
        return NS_OK;
672
0
    }
673
0
674
0
    NS_ENSURE_SUCCESS(rv, rv);
675
0
    *aStatus = uint16_t(httpStatus);
676
0
    return NS_OK;
677
0
}
678
679
//-----------------------------------------------------------------------------
680
// nsOfflineManifestItem
681
//-----------------------------------------------------------------------------
682
683
//-----------------------------------------------------------------------------
684
// nsOfflineManifestItem <public>
685
//-----------------------------------------------------------------------------
686
687
nsOfflineManifestItem::nsOfflineManifestItem(nsIURI *aURI,
688
                                             nsIURI *aReferrerURI,
689
                                             nsIPrincipal* aLoadingPrincipal,
690
                                             nsIApplicationCache *aApplicationCache,
691
                                             nsIApplicationCache *aPreviousApplicationCache)
692
    : nsOfflineCacheUpdateItem(aURI, aReferrerURI, aLoadingPrincipal,
693
                               aApplicationCache, aPreviousApplicationCache,
694
                               nsIApplicationCache::ITEM_MANIFEST, 0)
695
    , mParserState(PARSE_INIT)
696
    , mNeedsUpdate(true)
697
    , mStrictFileOriginPolicy(false)
698
    , mManifestHashInitialized(false)
699
0
{
700
0
    ReadStrictFileOriginPolicyPref();
701
0
}
702
703
nsOfflineManifestItem::~nsOfflineManifestItem()
704
0
{
705
0
}
706
707
//-----------------------------------------------------------------------------
708
// nsOfflineManifestItem <private>
709
//-----------------------------------------------------------------------------
710
711
/* static */
712
nsresult
713
nsOfflineManifestItem::ReadManifest(nsIInputStream *aInputStream,
714
                                    void *aClosure,
715
                                    const char *aFromSegment,
716
                                    uint32_t aOffset,
717
                                    uint32_t aCount,
718
                                    uint32_t *aBytesConsumed)
719
0
{
720
0
    nsOfflineManifestItem *manifest =
721
0
        static_cast<nsOfflineManifestItem*>(aClosure);
722
0
723
0
    nsresult rv;
724
0
725
0
    *aBytesConsumed = aCount;
726
0
727
0
    if (manifest->mParserState == PARSE_ERROR) {
728
0
        // parse already failed, ignore this
729
0
        return NS_OK;
730
0
    }
731
0
732
0
    if (!manifest->mManifestHashInitialized) {
733
0
        // Avoid re-creation of crypto hash when it fails from some reason the first time
734
0
        manifest->mManifestHashInitialized = true;
735
0
736
0
        manifest->mManifestHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
737
0
        if (NS_SUCCEEDED(rv)) {
738
0
            rv = manifest->mManifestHash->Init(nsICryptoHash::MD5);
739
0
            if (NS_FAILED(rv)) {
740
0
                manifest->mManifestHash = nullptr;
741
0
                LOG(("Could not initialize manifest hash for byte-to-byte check, rv=%08" PRIx32,
742
0
                     static_cast<uint32_t>(rv)));
743
0
            }
744
0
        }
745
0
    }
746
0
747
0
    if (manifest->mManifestHash) {
748
0
        rv = manifest->mManifestHash->Update(reinterpret_cast<const uint8_t *>(aFromSegment), aCount);
749
0
        if (NS_FAILED(rv)) {
750
0
            manifest->mManifestHash = nullptr;
751
0
            LOG(("Could not update manifest hash, rv=%08" PRIx32, static_cast<uint32_t>(rv)));
752
0
        }
753
0
    }
754
0
755
0
    manifest->mReadBuf.Append(aFromSegment, aCount);
756
0
757
0
    nsCString::const_iterator begin, iter, end;
758
0
    manifest->mReadBuf.BeginReading(begin);
759
0
    manifest->mReadBuf.EndReading(end);
760
0
761
0
    for (iter = begin; iter != end; iter++) {
762
0
        if (*iter == '\r' || *iter == '\n') {
763
0
            rv = manifest->HandleManifestLine(begin, iter);
764
0
765
0
            if (NS_FAILED(rv)) {
766
0
                LOG(("HandleManifestLine failed with 0x%08" PRIx32, static_cast<uint32_t>(rv)));
767
0
                *aBytesConsumed = 0; // Avoid assertion failure in stream tee
768
0
                return NS_ERROR_ABORT;
769
0
            }
770
0
771
0
            begin = iter;
772
0
            begin++;
773
0
        }
774
0
    }
775
0
776
0
    // any leftovers are saved for next time
777
0
    manifest->mReadBuf = Substring(begin, end);
778
0
779
0
    return NS_OK;
780
0
}
781
782
nsresult
783
nsOfflineManifestItem::AddNamespace(uint32_t namespaceType,
784
                                    const nsCString &namespaceSpec,
785
                                    const nsCString &data)
786
787
0
{
788
0
    nsresult rv;
789
0
    if (!mNamespaces) {
790
0
        mNamespaces = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
791
0
        NS_ENSURE_SUCCESS(rv, rv);
792
0
    }
793
0
794
0
    nsCOMPtr<nsIApplicationCacheNamespace> ns =
795
0
        new nsApplicationCacheNamespace();
796
0
797
0
    rv = ns->Init(namespaceType, namespaceSpec, data);
798
0
    NS_ENSURE_SUCCESS(rv, rv);
799
0
800
0
    rv = mNamespaces->AppendElement(ns);
801
0
    NS_ENSURE_SUCCESS(rv, rv);
802
0
803
0
    return NS_OK;
804
0
}
805
806
static nsresult
807
GetURIDirectory(nsIURI* uri, nsACString &directory)
808
0
{
809
0
  nsresult rv;
810
0
811
0
  nsCOMPtr<nsIURL> url(do_QueryInterface(uri, &rv));
812
0
  NS_ENSURE_SUCCESS(rv, rv);
813
0
814
0
  rv = url->GetDirectory(directory);
815
0
  NS_ENSURE_SUCCESS(rv, rv);
816
0
817
0
  return NS_OK;
818
0
}
819
820
static nsresult
821
CheckFileContainedInPath(nsIURI* file, nsACString const &masterDirectory)
822
0
{
823
0
  nsresult rv;
824
0
825
0
  nsAutoCString directory;
826
0
  rv = GetURIDirectory(file, directory);
827
0
  NS_ENSURE_SUCCESS(rv, rv);
828
0
829
0
  bool contains = StringBeginsWith(directory, masterDirectory);
830
0
  if (!contains) {
831
0
      return NS_ERROR_DOM_BAD_URI;
832
0
  }
833
0
834
0
  return NS_OK;
835
0
}
836
837
nsresult
838
nsOfflineManifestItem::HandleManifestLine(const nsCString::const_iterator &aBegin,
839
                                          const nsCString::const_iterator &aEnd)
840
0
{
841
0
    nsCString::const_iterator begin = aBegin;
842
0
    nsCString::const_iterator end = aEnd;
843
0
844
0
    // all lines ignore trailing spaces and tabs
845
0
    nsCString::const_iterator last = end;
846
0
    --last;
847
0
    while (end != begin && (*last == ' ' || *last == '\t')) {
848
0
        --end;
849
0
        --last;
850
0
    }
851
0
852
0
    if (mParserState == PARSE_INIT) {
853
0
        // Allow a UTF-8 BOM
854
0
        if (begin != end && static_cast<unsigned char>(*begin) == 0xef) {
855
0
            if (++begin == end || static_cast<unsigned char>(*begin) != 0xbb ||
856
0
                ++begin == end || static_cast<unsigned char>(*begin) != 0xbf) {
857
0
                mParserState = PARSE_ERROR;
858
0
                LogToConsole("Offline cache manifest BOM error", this);
859
0
                return NS_OK;
860
0
            }
861
0
            ++begin;
862
0
        }
863
0
864
0
        const nsACString& magic = Substring(begin, end);
865
0
866
0
        if (!magic.EqualsLiteral("CACHE MANIFEST")) {
867
0
            mParserState = PARSE_ERROR;
868
0
            LogToConsole("Offline cache manifest magic incorrect", this);
869
0
            return NS_OK;
870
0
        }
871
0
872
0
        mParserState = PARSE_CACHE_ENTRIES;
873
0
        return NS_OK;
874
0
    }
875
0
876
0
    // lines other than the first ignore leading spaces and tabs
877
0
    while (begin != end && (*begin == ' ' || *begin == '\t'))
878
0
        begin++;
879
0
880
0
    // ignore blank lines and comments
881
0
    if (begin == end || *begin == '#')
882
0
        return NS_OK;
883
0
884
0
    const nsACString& line = Substring(begin, end);
885
0
886
0
    if (line.EqualsLiteral("CACHE:")) {
887
0
        mParserState = PARSE_CACHE_ENTRIES;
888
0
        return NS_OK;
889
0
    }
890
0
891
0
    if (line.EqualsLiteral("FALLBACK:")) {
892
0
        mParserState = PARSE_FALLBACK_ENTRIES;
893
0
        return NS_OK;
894
0
    }
895
0
896
0
    if (line.EqualsLiteral("NETWORK:")) {
897
0
        mParserState = PARSE_BYPASS_ENTRIES;
898
0
        return NS_OK;
899
0
    }
900
0
901
0
    // Every other section type we don't know must be silently ignored.
902
0
    nsCString::const_iterator lastChar = end;
903
0
    if (*(--lastChar) == ':') {
904
0
        mParserState = PARSE_UNKNOWN_SECTION;
905
0
        return NS_OK;
906
0
    }
907
0
908
0
    nsresult rv;
909
0
910
0
    switch(mParserState) {
911
0
    case PARSE_INIT:
912
0
    case PARSE_ERROR: {
913
0
        // this should have been dealt with earlier
914
0
        return NS_ERROR_FAILURE;
915
0
    }
916
0
917
0
    case PARSE_UNKNOWN_SECTION: {
918
0
        // just jump over
919
0
        return NS_OK;
920
0
    }
921
0
922
0
    case PARSE_CACHE_ENTRIES: {
923
0
        nsCOMPtr<nsIURI> uri;
924
0
        rv = NS_NewURI(getter_AddRefs(uri), line, nullptr, mURI);
925
0
        if (NS_FAILED(rv))
926
0
            break;
927
0
        if (NS_FAILED(DropReferenceFromURL(uri)))
928
0
            break;
929
0
930
0
        nsAutoCString scheme;
931
0
        uri->GetScheme(scheme);
932
0
933
0
        // Manifest URIs must have the same scheme as the manifest.
934
0
        bool match;
935
0
        if (NS_FAILED(mURI->SchemeIs(scheme.get(), &match)) || !match)
936
0
            break;
937
0
938
0
        mExplicitURIs.AppendObject(uri);
939
0
940
0
        if (!NS_SecurityCompareURIs(mURI, uri,
941
0
                                    mStrictFileOriginPolicy)) {
942
0
          mAnonymousURIs.AppendObject(uri);
943
0
        }
944
0
945
0
        break;
946
0
    }
947
0
948
0
    case PARSE_FALLBACK_ENTRIES: {
949
0
        int32_t separator = line.FindChar(' ');
950
0
        if (separator == kNotFound) {
951
0
            separator = line.FindChar('\t');
952
0
            if (separator == kNotFound)
953
0
                break;
954
0
        }
955
0
956
0
        nsCString namespaceSpec(Substring(line, 0, separator));
957
0
        nsCString fallbackSpec(Substring(line, separator + 1));
958
0
        namespaceSpec.CompressWhitespace();
959
0
        fallbackSpec.CompressWhitespace();
960
0
961
0
        nsCOMPtr<nsIURI> namespaceURI;
962
0
        rv = NS_NewURI(getter_AddRefs(namespaceURI), namespaceSpec, nullptr, mURI);
963
0
        if (NS_FAILED(rv))
964
0
            break;
965
0
        if (NS_FAILED(DropReferenceFromURL(namespaceURI)))
966
0
            break;
967
0
        rv = namespaceURI->GetAsciiSpec(namespaceSpec);
968
0
        if (NS_FAILED(rv))
969
0
            break;
970
0
971
0
        nsCOMPtr<nsIURI> fallbackURI;
972
0
        rv = NS_NewURI(getter_AddRefs(fallbackURI), fallbackSpec, nullptr, mURI);
973
0
        if (NS_FAILED(rv))
974
0
            break;
975
0
        if (NS_FAILED(DropReferenceFromURL(fallbackURI)))
976
0
            break;
977
0
        rv = fallbackURI->GetAsciiSpec(fallbackSpec);
978
0
        if (NS_FAILED(rv))
979
0
            break;
980
0
981
0
        // The following set of checks is preventing a website under
982
0
        // a subdirectory to add fallback pages for the whole origin
983
0
        // (or a parent directory) to prevent fallback attacks.
984
0
        nsAutoCString manifestDirectory;
985
0
        rv = GetURIDirectory(mURI, manifestDirectory);
986
0
        if (NS_FAILED(rv)) {
987
0
            break;
988
0
        }
989
0
990
0
        rv = CheckFileContainedInPath(namespaceURI, manifestDirectory);
991
0
        if (NS_FAILED(rv)) {
992
0
            break;
993
0
        }
994
0
995
0
        rv = CheckFileContainedInPath(fallbackURI, manifestDirectory);
996
0
        if (NS_FAILED(rv)) {
997
0
            break;
998
0
        }
999
0
1000
0
        // Manifest and namespace must be same origin
1001
0
        if (!NS_SecurityCompareURIs(mURI, namespaceURI,
1002
0
                                    mStrictFileOriginPolicy))
1003
0
            break;
1004
0
1005
0
        // Fallback and namespace must be same origin
1006
0
        if (!NS_SecurityCompareURIs(namespaceURI, fallbackURI,
1007
0
                                    mStrictFileOriginPolicy))
1008
0
            break;
1009
0
1010
0
        mFallbackURIs.AppendObject(fallbackURI);
1011
0
1012
0
        AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_FALLBACK,
1013
0
                     namespaceSpec, fallbackSpec);
1014
0
        break;
1015
0
    }
1016
0
1017
0
    case PARSE_BYPASS_ENTRIES: {
1018
0
        if (line[0] == '*' && (line.Length() == 1 || line[1] == ' ' || line[1] == '\t'))
1019
0
        {
1020
0
          // '*' indicates to make the online whitelist wildcard flag open,
1021
0
          // i.e. do allow load of resources not present in the offline cache
1022
0
          // or not conforming any namespace.
1023
0
          // We achive that simply by adding an 'empty' - i.e. universal
1024
0
          // namespace of BYPASS type into the cache.
1025
0
          AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_BYPASS,
1026
0
                       EmptyCString(), EmptyCString());
1027
0
          break;
1028
0
        }
1029
0
1030
0
        nsCOMPtr<nsIURI> bypassURI;
1031
0
        rv = NS_NewURI(getter_AddRefs(bypassURI), line, nullptr, mURI);
1032
0
        if (NS_FAILED(rv))
1033
0
            break;
1034
0
1035
0
        nsAutoCString scheme;
1036
0
        bypassURI->GetScheme(scheme);
1037
0
        bool equals;
1038
0
        if (NS_FAILED(mURI->SchemeIs(scheme.get(), &equals)) || !equals)
1039
0
            break;
1040
0
        if (NS_FAILED(DropReferenceFromURL(bypassURI)))
1041
0
            break;
1042
0
        nsCString spec;
1043
0
        if (NS_FAILED(bypassURI->GetAsciiSpec(spec)))
1044
0
            break;
1045
0
1046
0
        AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_BYPASS,
1047
0
                     spec, EmptyCString());
1048
0
        break;
1049
0
    }
1050
0
    }
1051
0
1052
0
    return NS_OK;
1053
0
}
1054
1055
nsresult
1056
nsOfflineManifestItem::GetOldManifestContentHash(nsIRequest *aRequest)
1057
0
{
1058
0
    nsresult rv;
1059
0
1060
0
    nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aRequest, &rv);
1061
0
    NS_ENSURE_SUCCESS(rv, rv);
1062
0
1063
0
    // load the main cache token that is actually the old offline cache token and
1064
0
    // read previous manifest content hash value
1065
0
    nsCOMPtr<nsISupports> cacheToken;
1066
0
    cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
1067
0
    if (cacheToken) {
1068
0
        nsCOMPtr<nsICacheEntry> cacheDescriptor(do_QueryInterface(cacheToken, &rv));
1069
0
        NS_ENSURE_SUCCESS(rv, rv);
1070
0
1071
0
        rv = cacheDescriptor->GetMetaDataElement("offline-manifest-hash", getter_Copies(mOldManifestHashValue));
1072
0
        if (NS_FAILED(rv))
1073
0
            mOldManifestHashValue.Truncate();
1074
0
    }
1075
0
1076
0
    return NS_OK;
1077
0
}
1078
1079
nsresult
1080
nsOfflineManifestItem::CheckNewManifestContentHash(nsIRequest *aRequest)
1081
0
{
1082
0
    nsresult rv;
1083
0
1084
0
    if (!mManifestHash) {
1085
0
        // Nothing to compare against...
1086
0
        return NS_OK;
1087
0
    }
1088
0
1089
0
    nsCString newManifestHashValue;
1090
0
    rv = mManifestHash->Finish(true, mManifestHashValue);
1091
0
    mManifestHash = nullptr;
1092
0
1093
0
    if (NS_FAILED(rv)) {
1094
0
        LOG(("Could not finish manifest hash, rv=%08" PRIx32, static_cast<uint32_t>(rv)));
1095
0
        // This is not critical error
1096
0
        return NS_OK;
1097
0
    }
1098
0
1099
0
    if (!ParseSucceeded()) {
1100
0
        // Parsing failed, the hash is not valid
1101
0
        return NS_OK;
1102
0
    }
1103
0
1104
0
    if (mOldManifestHashValue == mManifestHashValue) {
1105
0
        LOG(("Update not needed, downloaded manifest content is byte-for-byte identical"));
1106
0
        mNeedsUpdate = false;
1107
0
    }
1108
0
1109
0
    // Store the manifest content hash value to the new
1110
0
    // offline cache token
1111
0
    nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aRequest, &rv);
1112
0
    NS_ENSURE_SUCCESS(rv, rv);
1113
0
1114
0
    nsCOMPtr<nsISupports> cacheToken;
1115
0
    cachingChannel->GetOfflineCacheToken(getter_AddRefs(cacheToken));
1116
0
    if (cacheToken) {
1117
0
        nsCOMPtr<nsICacheEntry> cacheDescriptor(do_QueryInterface(cacheToken, &rv));
1118
0
        NS_ENSURE_SUCCESS(rv, rv);
1119
0
1120
0
        rv = cacheDescriptor->SetMetaDataElement("offline-manifest-hash", mManifestHashValue.get());
1121
0
        NS_ENSURE_SUCCESS(rv, rv);
1122
0
    }
1123
0
1124
0
    return NS_OK;
1125
0
}
1126
1127
void
1128
nsOfflineManifestItem::ReadStrictFileOriginPolicyPref()
1129
0
{
1130
0
    mStrictFileOriginPolicy =
1131
0
        Preferences::GetBool("security.fileuri.strict_origin_policy", true);
1132
0
}
1133
1134
NS_IMETHODIMP
1135
nsOfflineManifestItem::OnStartRequest(nsIRequest *aRequest,
1136
                                      nsISupports *aContext)
1137
0
{
1138
0
    nsresult rv;
1139
0
1140
0
    nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
1141
0
    NS_ENSURE_SUCCESS(rv, rv);
1142
0
1143
0
    bool succeeded;
1144
0
    rv = channel->GetRequestSucceeded(&succeeded);
1145
0
    NS_ENSURE_SUCCESS(rv, rv);
1146
0
1147
0
    if (!succeeded) {
1148
0
        LOG(("HTTP request failed"));
1149
0
        LogToConsole("Offline cache manifest HTTP request failed", this);
1150
0
        mParserState = PARSE_ERROR;
1151
0
        return NS_ERROR_ABORT;
1152
0
    }
1153
0
1154
0
    rv = GetOldManifestContentHash(aRequest);
1155
0
    NS_ENSURE_SUCCESS(rv, rv);
1156
0
1157
0
    return nsOfflineCacheUpdateItem::OnStartRequest(aRequest, aContext);
1158
0
}
1159
1160
NS_IMETHODIMP
1161
nsOfflineManifestItem::OnDataAvailable(nsIRequest *aRequest,
1162
                                       nsISupports *aContext,
1163
                                       nsIInputStream *aStream,
1164
                                       uint64_t aOffset,
1165
                                       uint32_t aCount)
1166
0
{
1167
0
    uint32_t bytesRead = 0;
1168
0
    aStream->ReadSegments(ReadManifest, this, aCount, &bytesRead);
1169
0
    mBytesRead += bytesRead;
1170
0
1171
0
    if (mParserState == PARSE_ERROR) {
1172
0
        LOG(("OnDataAvailable is canceling the request due a parse error\n"));
1173
0
        return NS_ERROR_ABORT;
1174
0
    }
1175
0
1176
0
    LOG(("loaded %u bytes into offline cache [offset=%" PRIu64 "]\n",
1177
0
         bytesRead, aOffset));
1178
0
1179
0
    // All the parent method does is read and discard, don't bother
1180
0
    // chaining up.
1181
0
1182
0
    return NS_OK;
1183
0
}
1184
1185
NS_IMETHODIMP
1186
nsOfflineManifestItem::OnStopRequest(nsIRequest *aRequest,
1187
                                     nsISupports *aContext,
1188
                                     nsresult aStatus)
1189
0
{
1190
0
    if (mBytesRead == 0) {
1191
0
        // We didn't need to read (because LOAD_ONLY_IF_MODIFIED was
1192
0
        // specified).
1193
0
        mNeedsUpdate = false;
1194
0
    } else {
1195
0
        // Handle any leftover manifest data.
1196
0
        nsCString::const_iterator begin, end;
1197
0
        mReadBuf.BeginReading(begin);
1198
0
        mReadBuf.EndReading(end);
1199
0
        nsresult rv = HandleManifestLine(begin, end);
1200
0
        NS_ENSURE_SUCCESS(rv, rv);
1201
0
1202
0
        rv = CheckNewManifestContentHash(aRequest);
1203
0
        NS_ENSURE_SUCCESS(rv, rv);
1204
0
    }
1205
0
1206
0
    return nsOfflineCacheUpdateItem::OnStopRequest(aRequest, aContext, aStatus);
1207
0
}
1208
1209
//-----------------------------------------------------------------------------
1210
// nsOfflineCacheUpdate::nsISupports
1211
//-----------------------------------------------------------------------------
1212
1213
NS_IMPL_ISUPPORTS(nsOfflineCacheUpdate,
1214
                  nsIOfflineCacheUpdateObserver,
1215
                  nsIOfflineCacheUpdate,
1216
                  nsIRunnable)
1217
1218
//-----------------------------------------------------------------------------
1219
// nsOfflineCacheUpdate <public>
1220
//-----------------------------------------------------------------------------
1221
1222
nsOfflineCacheUpdate::nsOfflineCacheUpdate()
1223
    : mState(STATE_UNINITIALIZED)
1224
    , mAddedItems(false)
1225
    , mPartialUpdate(false)
1226
    , mOnlyCheckUpdate(false)
1227
    , mSucceeded(true)
1228
    , mObsolete(false)
1229
    , mItemsInProgress(0)
1230
    , mRescheduleCount(0)
1231
    , mPinnedEntryRetriesCount(0)
1232
    , mPinned(false)
1233
    , mByteProgress(0)
1234
0
{
1235
0
}
1236
1237
nsOfflineCacheUpdate::~nsOfflineCacheUpdate()
1238
0
{
1239
0
    LOG(("nsOfflineCacheUpdate::~nsOfflineCacheUpdate [%p]", this));
1240
0
}
1241
1242
/* static */
1243
nsresult
1244
nsOfflineCacheUpdate::GetCacheKey(nsIURI *aURI, nsACString &aKey)
1245
0
{
1246
0
    aKey.Truncate();
1247
0
1248
0
    nsCOMPtr<nsIURI> newURI;
1249
0
    nsresult rv = NS_GetURIWithoutRef(aURI, getter_AddRefs(newURI));
1250
0
    NS_ENSURE_SUCCESS(rv, rv);
1251
0
1252
0
    rv = newURI->GetAsciiSpec(aKey);
1253
0
    NS_ENSURE_SUCCESS(rv, rv);
1254
0
1255
0
    return NS_OK;
1256
0
}
1257
1258
nsresult
1259
nsOfflineCacheUpdate::InitInternal(nsIURI *aManifestURI,
1260
                                   nsIPrincipal* aLoadingPrincipal)
1261
0
{
1262
0
    nsresult rv;
1263
0
1264
0
    // Only http and https applications are supported.
1265
0
    bool match;
1266
0
    rv = aManifestURI->SchemeIs("http", &match);
1267
0
    NS_ENSURE_SUCCESS(rv, rv);
1268
0
1269
0
    if (!match) {
1270
0
        rv = aManifestURI->SchemeIs("https", &match);
1271
0
        NS_ENSURE_SUCCESS(rv, rv);
1272
0
        if (!match)
1273
0
            return NS_ERROR_ABORT;
1274
0
    }
1275
0
1276
0
    mManifestURI = aManifestURI;
1277
0
    mLoadingPrincipal = aLoadingPrincipal;
1278
0
1279
0
    rv = mManifestURI->GetAsciiHost(mUpdateDomain);
1280
0
    NS_ENSURE_SUCCESS(rv, rv);
1281
0
1282
0
    mPartialUpdate = false;
1283
0
1284
0
    return NS_OK;
1285
0
}
1286
1287
nsresult
1288
nsOfflineCacheUpdate::Init(nsIURI *aManifestURI,
1289
                           nsIURI *aDocumentURI,
1290
                           nsIPrincipal* aLoadingPrincipal,
1291
                           nsIDocument *aDocument,
1292
                           nsIFile *aCustomProfileDir)
1293
0
{
1294
0
    nsresult rv;
1295
0
1296
0
    // Make sure the service has been initialized
1297
0
    nsOfflineCacheUpdateService* service =
1298
0
        nsOfflineCacheUpdateService::EnsureService();
1299
0
    if (!service)
1300
0
        return NS_ERROR_FAILURE;
1301
0
1302
0
    LOG(("nsOfflineCacheUpdate::Init [%p]", this));
1303
0
1304
0
    rv = InitInternal(aManifestURI, aLoadingPrincipal);
1305
0
    NS_ENSURE_SUCCESS(rv, rv);
1306
0
1307
0
    nsCOMPtr<nsIApplicationCacheService> cacheService =
1308
0
        do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
1309
0
    NS_ENSURE_SUCCESS(rv, rv);
1310
0
1311
0
    nsAutoCString originSuffix;
1312
0
    rv = aLoadingPrincipal->GetOriginSuffix(originSuffix);
1313
0
    NS_ENSURE_SUCCESS(rv, rv);
1314
0
1315
0
    mDocumentURI = aDocumentURI;
1316
0
1317
0
    if (aCustomProfileDir) {
1318
0
        rv = cacheService->BuildGroupIDForSuffix(aManifestURI, originSuffix, mGroupID);
1319
0
        NS_ENSURE_SUCCESS(rv, rv);
1320
0
1321
0
        // Create only a new offline application cache in the custom profile
1322
0
        // This is a preload of a new cache.
1323
0
1324
0
        // XXX Custom updates don't support "updating" of an existing cache
1325
0
        // in the custom profile at the moment.  This support can be, though,
1326
0
        // simply added as well when needed.
1327
0
        mPreviousApplicationCache = nullptr;
1328
0
1329
0
        rv = cacheService->CreateCustomApplicationCache(mGroupID,
1330
0
                                                        aCustomProfileDir,
1331
0
                                                        kCustomProfileQuota,
1332
0
                                                        getter_AddRefs(mApplicationCache));
1333
0
        NS_ENSURE_SUCCESS(rv, rv);
1334
0
1335
0
        mCustomProfileDir = aCustomProfileDir;
1336
0
    }
1337
0
    else {
1338
0
        rv = cacheService->BuildGroupIDForSuffix(aManifestURI, originSuffix, mGroupID);
1339
0
        NS_ENSURE_SUCCESS(rv, rv);
1340
0
1341
0
        rv = cacheService->GetActiveCache(mGroupID,
1342
0
                                          getter_AddRefs(mPreviousApplicationCache));
1343
0
        NS_ENSURE_SUCCESS(rv, rv);
1344
0
1345
0
        rv = cacheService->CreateApplicationCache(mGroupID,
1346
0
                                                  getter_AddRefs(mApplicationCache));
1347
0
        NS_ENSURE_SUCCESS(rv, rv);
1348
0
    }
1349
0
1350
0
    rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aDocumentURI,
1351
0
                                                             nullptr,
1352
0
                                                             &mPinned);
1353
0
    NS_ENSURE_SUCCESS(rv, rv);
1354
0
1355
0
    mState = STATE_INITIALIZED;
1356
0
    return NS_OK;
1357
0
}
1358
1359
nsresult
1360
nsOfflineCacheUpdate::InitForUpdateCheck(nsIURI *aManifestURI,
1361
                                         nsIPrincipal* aLoadingPrincipal,
1362
                                         nsIObserver *aObserver)
1363
0
{
1364
0
    nsresult rv;
1365
0
1366
0
    // Make sure the service has been initialized
1367
0
    nsOfflineCacheUpdateService* service =
1368
0
        nsOfflineCacheUpdateService::EnsureService();
1369
0
    if (!service)
1370
0
        return NS_ERROR_FAILURE;
1371
0
1372
0
    LOG(("nsOfflineCacheUpdate::InitForUpdateCheck [%p]", this));
1373
0
1374
0
    rv = InitInternal(aManifestURI, aLoadingPrincipal);
1375
0
    NS_ENSURE_SUCCESS(rv, rv);
1376
0
1377
0
    nsCOMPtr<nsIApplicationCacheService> cacheService =
1378
0
        do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
1379
0
    NS_ENSURE_SUCCESS(rv, rv);
1380
0
1381
0
    nsAutoCString originSuffix;
1382
0
    rv = aLoadingPrincipal->GetOriginSuffix(originSuffix);
1383
0
    NS_ENSURE_SUCCESS(rv, rv);
1384
0
1385
0
    rv = cacheService->BuildGroupIDForSuffix(aManifestURI, originSuffix, mGroupID);
1386
0
    NS_ENSURE_SUCCESS(rv, rv);
1387
0
1388
0
    rv = cacheService->GetActiveCache(mGroupID,
1389
0
                                      getter_AddRefs(mPreviousApplicationCache));
1390
0
    NS_ENSURE_SUCCESS(rv, rv);
1391
0
1392
0
    // To load the manifest properly using current app cache to satisfy and
1393
0
    // also to compare the cached content hash value we have to set 'some'
1394
0
    // app cache to write to on the channel.  Otherwise the cached version will
1395
0
    // be used and no actual network request will be made.  We use the same
1396
0
    // app cache here.  OpenChannel prevents caching in this case using
1397
0
    // INHIBIT_CACHING load flag.
1398
0
    mApplicationCache = mPreviousApplicationCache;
1399
0
1400
0
    rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aManifestURI,
1401
0
                                                             nullptr,
1402
0
                                                             &mPinned);
1403
0
    NS_ENSURE_SUCCESS(rv, rv);
1404
0
1405
0
    mUpdateAvailableObserver = aObserver;
1406
0
    mOnlyCheckUpdate = true;
1407
0
1408
0
    mState = STATE_INITIALIZED;
1409
0
    return NS_OK;
1410
0
}
1411
1412
nsresult
1413
nsOfflineCacheUpdate::InitPartial(nsIURI *aManifestURI,
1414
                                  const nsACString& clientID,
1415
                                  nsIURI *aDocumentURI,
1416
                                  nsIPrincipal *aLoadingPrincipal)
1417
0
{
1418
0
    nsresult rv;
1419
0
1420
0
    // Make sure the service has been initialized
1421
0
    nsOfflineCacheUpdateService* service =
1422
0
        nsOfflineCacheUpdateService::EnsureService();
1423
0
    if (!service)
1424
0
        return NS_ERROR_FAILURE;
1425
0
1426
0
    LOG(("nsOfflineCacheUpdate::InitPartial [%p]", this));
1427
0
1428
0
    mPartialUpdate = true;
1429
0
    mDocumentURI = aDocumentURI;
1430
0
    mLoadingPrincipal = aLoadingPrincipal;
1431
0
1432
0
    mManifestURI = aManifestURI;
1433
0
    rv = mManifestURI->GetAsciiHost(mUpdateDomain);
1434
0
    NS_ENSURE_SUCCESS(rv, rv);
1435
0
1436
0
    nsCOMPtr<nsIApplicationCacheService> cacheService =
1437
0
        do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
1438
0
    NS_ENSURE_SUCCESS(rv, rv);
1439
0
1440
0
    rv = cacheService->GetApplicationCache(clientID,
1441
0
                                           getter_AddRefs(mApplicationCache));
1442
0
    NS_ENSURE_SUCCESS(rv, rv);
1443
0
1444
0
    if (!mApplicationCache) {
1445
0
        nsAutoCString manifestSpec;
1446
0
        rv = GetCacheKey(mManifestURI, manifestSpec);
1447
0
        NS_ENSURE_SUCCESS(rv, rv);
1448
0
1449
0
        rv = cacheService->CreateApplicationCache
1450
0
            (manifestSpec, getter_AddRefs(mApplicationCache));
1451
0
        NS_ENSURE_SUCCESS(rv, rv);
1452
0
    }
1453
0
1454
0
    rv = mApplicationCache->GetManifestURI(getter_AddRefs(mManifestURI));
1455
0
    NS_ENSURE_SUCCESS(rv, rv);
1456
0
1457
0
    nsAutoCString groupID;
1458
0
    rv = mApplicationCache->GetGroupID(groupID);
1459
0
    NS_ENSURE_SUCCESS(rv, rv);
1460
0
1461
0
    rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aDocumentURI,
1462
0
                                                             nullptr,
1463
0
                                                             &mPinned);
1464
0
    NS_ENSURE_SUCCESS(rv, rv);
1465
0
1466
0
    mState = STATE_INITIALIZED;
1467
0
    return NS_OK;
1468
0
}
1469
1470
nsresult
1471
nsOfflineCacheUpdate::HandleManifest(bool *aDoUpdate)
1472
0
{
1473
0
    // Be pessimistic
1474
0
    *aDoUpdate = false;
1475
0
1476
0
    bool succeeded;
1477
0
    nsresult rv = mManifestItem->GetRequestSucceeded(&succeeded);
1478
0
    NS_ENSURE_SUCCESS(rv, rv);
1479
0
1480
0
    if (!succeeded || !mManifestItem->ParseSucceeded()) {
1481
0
        return NS_ERROR_FAILURE;
1482
0
    }
1483
0
1484
0
    if (!mManifestItem->NeedsUpdate()) {
1485
0
        return NS_OK;
1486
0
    }
1487
0
1488
0
    // Add items requested by the manifest.
1489
0
    const nsCOMArray<nsIURI> &manifestURIs = mManifestItem->GetExplicitURIs();
1490
0
    for (int32_t i = 0; i < manifestURIs.Count(); i++) {
1491
0
        rv = AddURI(manifestURIs[i], nsIApplicationCache::ITEM_EXPLICIT);
1492
0
        NS_ENSURE_SUCCESS(rv, rv);
1493
0
    }
1494
0
1495
0
    const nsCOMArray<nsIURI> &anonURIs = mManifestItem->GetAnonymousURIs();
1496
0
    for (int32_t i = 0; i < anonURIs.Count(); i++) {
1497
0
      rv = AddURI(anonURIs[i], nsIApplicationCache::ITEM_EXPLICIT,
1498
0
                  nsIRequest::LOAD_ANONYMOUS);
1499
0
      NS_ENSURE_SUCCESS(rv, rv);
1500
0
    }
1501
0
1502
0
    const nsCOMArray<nsIURI> &fallbackURIs = mManifestItem->GetFallbackURIs();
1503
0
    for (int32_t i = 0; i < fallbackURIs.Count(); i++) {
1504
0
        rv = AddURI(fallbackURIs[i], nsIApplicationCache::ITEM_FALLBACK);
1505
0
        NS_ENSURE_SUCCESS(rv, rv);
1506
0
    }
1507
0
1508
0
    // The document that requested the manifest is implicitly included
1509
0
    // as part of that manifest update.
1510
0
    rv = AddURI(mDocumentURI, nsIApplicationCache::ITEM_IMPLICIT);
1511
0
    NS_ENSURE_SUCCESS(rv, rv);
1512
0
1513
0
    // Add items previously cached implicitly
1514
0
    rv = AddExistingItems(nsIApplicationCache::ITEM_IMPLICIT);
1515
0
    NS_ENSURE_SUCCESS(rv, rv);
1516
0
1517
0
    // Add items requested by the script API
1518
0
    rv = AddExistingItems(nsIApplicationCache::ITEM_DYNAMIC);
1519
0
    NS_ENSURE_SUCCESS(rv, rv);
1520
0
1521
0
    // Add opportunistically cached items conforming current opportunistic
1522
0
    // namespace list
1523
0
    rv = AddExistingItems(nsIApplicationCache::ITEM_OPPORTUNISTIC,
1524
0
                          &mManifestItem->GetOpportunisticNamespaces());
1525
0
    NS_ENSURE_SUCCESS(rv, rv);
1526
0
1527
0
    *aDoUpdate = true;
1528
0
1529
0
    return NS_OK;
1530
0
}
1531
1532
bool
1533
nsOfflineCacheUpdate::CheckUpdateAvailability()
1534
0
{
1535
0
    nsresult rv;
1536
0
1537
0
    bool succeeded;
1538
0
    rv = mManifestItem->GetRequestSucceeded(&succeeded);
1539
0
    NS_ENSURE_SUCCESS(rv, false);
1540
0
1541
0
    if (!succeeded || !mManifestItem->ParseSucceeded()) {
1542
0
        return false;
1543
0
    }
1544
0
1545
0
    if (!mPinned) {
1546
0
        uint16_t status;
1547
0
        rv = mManifestItem->GetStatus(&status);
1548
0
        NS_ENSURE_SUCCESS(rv, false);
1549
0
1550
0
        // Treat these as there would be an update available,
1551
0
        // since this is indication of demand to remove this
1552
0
        // offline cache.
1553
0
        if (status == 404 || status == 410) {
1554
0
            return true;
1555
0
        }
1556
0
    }
1557
0
1558
0
    return mManifestItem->NeedsUpdate();
1559
0
}
1560
1561
void
1562
nsOfflineCacheUpdate::LoadCompleted(nsOfflineCacheUpdateItem *aItem)
1563
0
{
1564
0
    nsresult rv;
1565
0
1566
0
    LOG(("nsOfflineCacheUpdate::LoadCompleted [%p]", this));
1567
0
1568
0
    if (mState == STATE_FINISHED) {
1569
0
        LOG(("  after completion, ignoring"));
1570
0
        return;
1571
0
    }
1572
0
1573
0
    // Keep the object alive through a Finish() call.
1574
0
    nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
1575
0
1576
0
    if (mState == STATE_CANCELLED) {
1577
0
        NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
1578
0
        Finish();
1579
0
        return;
1580
0
    }
1581
0
1582
0
    if (mState == STATE_CHECKING) {
1583
0
        // Manifest load finished.
1584
0
1585
0
        if (mOnlyCheckUpdate) {
1586
0
            Finish();
1587
0
            NotifyUpdateAvailability(CheckUpdateAvailability());
1588
0
            return;
1589
0
        }
1590
0
1591
0
        NS_ASSERTION(mManifestItem,
1592
0
                     "Must have a manifest item in STATE_CHECKING.");
1593
0
        NS_ASSERTION(mManifestItem == aItem,
1594
0
                     "Unexpected aItem in nsOfflineCacheUpdate::LoadCompleted");
1595
0
1596
0
        // A 404 or 410 is interpreted as an intentional removal of
1597
0
        // the manifest file, rather than a transient server error.
1598
0
        // Obsolete this cache group if one of these is returned.
1599
0
        uint16_t status;
1600
0
        rv = mManifestItem->GetStatus(&status);
1601
0
        if (status == 404 || status == 410) {
1602
0
            LogToConsole("Offline cache manifest removed, cache cleared", mManifestItem);
1603
0
            mSucceeded = false;
1604
0
            if (mPreviousApplicationCache) {
1605
0
                if (mPinned) {
1606
0
                    // Do not obsolete a pinned application.
1607
0
                    NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE);
1608
0
                } else {
1609
0
                    NotifyState(nsIOfflineCacheUpdateObserver::STATE_OBSOLETE);
1610
0
                    mObsolete = true;
1611
0
                }
1612
0
            } else {
1613
0
                NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
1614
0
                mObsolete = true;
1615
0
            }
1616
0
            Finish();
1617
0
            return;
1618
0
        }
1619
0
1620
0
        bool doUpdate;
1621
0
        if (NS_FAILED(HandleManifest(&doUpdate))) {
1622
0
            mSucceeded = false;
1623
0
            NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
1624
0
            Finish();
1625
0
            return;
1626
0
        }
1627
0
1628
0
        if (!doUpdate) {
1629
0
            LogToConsole("Offline cache doesn't need to update", mManifestItem);
1630
0
1631
0
            mSucceeded = false;
1632
0
1633
0
            AssociateDocuments(mPreviousApplicationCache);
1634
0
1635
0
            ScheduleImplicit();
1636
0
1637
0
            // If we didn't need an implicit update, we can
1638
0
            // send noupdate and end the update now.
1639
0
            if (!mImplicitUpdate) {
1640
0
                NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE);
1641
0
                Finish();
1642
0
            }
1643
0
            return;
1644
0
        }
1645
0
1646
0
        rv = mApplicationCache->MarkEntry(mManifestItem->mCacheKey,
1647
0
                                          mManifestItem->mItemType);
1648
0
        if (NS_FAILED(rv)) {
1649
0
            mSucceeded = false;
1650
0
            NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
1651
0
            Finish();
1652
0
            return;
1653
0
        }
1654
0
1655
0
        mState = STATE_DOWNLOADING;
1656
0
        NotifyState(nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING);
1657
0
1658
0
        // Start fetching resources.
1659
0
        ProcessNextURI();
1660
0
1661
0
        return;
1662
0
    }
1663
0
1664
0
    // Normal load finished.
1665
0
    if (mItemsInProgress) // Just to be safe here!
1666
0
      --mItemsInProgress;
1667
0
1668
0
    bool succeeded;
1669
0
    rv = aItem->GetRequestSucceeded(&succeeded);
1670
0
1671
0
    if (mPinned && NS_SUCCEEDED(rv) && succeeded) {
1672
0
        uint32_t dummy_cache_type;
1673
0
        rv = mApplicationCache->GetTypes(aItem->mCacheKey, &dummy_cache_type);
1674
0
        bool item_doomed = NS_FAILED(rv); // can not find it? -> doomed
1675
0
1676
0
        if (item_doomed &&
1677
0
            mPinnedEntryRetriesCount < kPinnedEntryRetriesLimit &&
1678
0
            (aItem->mItemType & (nsIApplicationCache::ITEM_EXPLICIT |
1679
0
                                 nsIApplicationCache::ITEM_FALLBACK))) {
1680
0
            rv = EvictOneNonPinned();
1681
0
            if (NS_FAILED(rv)) {
1682
0
                mSucceeded = false;
1683
0
                NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
1684
0
                Finish();
1685
0
                return;
1686
0
            }
1687
0
1688
0
            // This reverts the item state to UNINITIALIZED that makes it to
1689
0
            // be scheduled for download again.
1690
0
            rv = aItem->Cancel();
1691
0
            if (NS_FAILED(rv)) {
1692
0
                mSucceeded = false;
1693
0
                NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
1694
0
                Finish();
1695
0
                return;
1696
0
            }
1697
0
1698
0
            mPinnedEntryRetriesCount++;
1699
0
1700
0
            LogToConsole("An unpinned offline cache deleted");
1701
0
1702
0
            // Retry this item.
1703
0
            ProcessNextURI();
1704
0
            return;
1705
0
        }
1706
0
    }
1707
0
1708
0
    // According to parallelism this may imply more pinned retries count,
1709
0
    // but that is not critical, since at one moment the algorithm will
1710
0
    // stop anyway.  Also, this code may soon be completely removed
1711
0
    // after we have a separate storage for pinned apps.
1712
0
    mPinnedEntryRetriesCount = 0;
1713
0
1714
0
    // Check for failures.  3XX, 4XX and 5XX errors on items explicitly
1715
0
    // listed in the manifest will cause the update to fail.
1716
0
    if (NS_FAILED(rv) || !succeeded) {
1717
0
        if (aItem->mItemType &
1718
0
            (nsIApplicationCache::ITEM_EXPLICIT |
1719
0
             nsIApplicationCache::ITEM_FALLBACK)) {
1720
0
            LogToConsole("Offline cache manifest item failed to load", aItem);
1721
0
            mSucceeded = false;
1722
0
        }
1723
0
    } else {
1724
0
        rv = mApplicationCache->MarkEntry(aItem->mCacheKey, aItem->mItemType);
1725
0
        if (NS_FAILED(rv)) {
1726
0
            mSucceeded = false;
1727
0
        }
1728
0
    }
1729
0
1730
0
    if (!mSucceeded) {
1731
0
        NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
1732
0
        Finish();
1733
0
        return;
1734
0
    }
1735
0
1736
0
    NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMCOMPLETED);
1737
0
1738
0
    ProcessNextURI();
1739
0
}
1740
1741
void
1742
nsOfflineCacheUpdate::ManifestCheckCompleted(nsresult aStatus,
1743
                                             const nsCString &aManifestHash)
1744
0
{
1745
0
    // Keep the object alive through a Finish() call.
1746
0
    nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
1747
0
1748
0
    if (NS_SUCCEEDED(aStatus)) {
1749
0
        nsAutoCString firstManifestHash;
1750
0
        mManifestItem->GetManifestHash(firstManifestHash);
1751
0
        if (aManifestHash != firstManifestHash) {
1752
0
            LOG(("Manifest has changed during cache items download [%p]", this));
1753
0
            LogToConsole("Offline cache manifest changed during update", mManifestItem);
1754
0
            aStatus = NS_ERROR_FAILURE;
1755
0
        }
1756
0
    }
1757
0
1758
0
    if (NS_FAILED(aStatus)) {
1759
0
        mSucceeded = false;
1760
0
        NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
1761
0
    }
1762
0
1763
0
    if (NS_FAILED(aStatus) && mRescheduleCount < kRescheduleLimit) {
1764
0
        // Do the final stuff but prevent notification of STATE_FINISHED.
1765
0
        // That would disconnect listeners that are responsible for document
1766
0
        // association after a successful update. Forwarding notifications
1767
0
        // from a new update through this dead update to them is absolutely
1768
0
        // correct.
1769
0
        FinishNoNotify();
1770
0
1771
0
        RefPtr<nsOfflineCacheUpdate> newUpdate =
1772
0
            new nsOfflineCacheUpdate();
1773
0
        // Leave aDocument argument null. Only glues and children keep
1774
0
        // document instances.
1775
0
        newUpdate->Init(mManifestURI, mDocumentURI, mLoadingPrincipal, nullptr,
1776
0
                        mCustomProfileDir);
1777
0
1778
0
        // In a rare case the manifest will not be modified on the next refetch
1779
0
        // transfer all master document URIs to the new update to ensure that
1780
0
        // all documents refering it will be properly cached.
1781
0
        for (int32_t i = 0; i < mDocumentURIs.Count(); i++) {
1782
0
            newUpdate->StickDocument(mDocumentURIs[i]);
1783
0
        }
1784
0
1785
0
        newUpdate->mRescheduleCount = mRescheduleCount + 1;
1786
0
        newUpdate->AddObserver(this, false);
1787
0
        newUpdate->Schedule();
1788
0
    }
1789
0
    else {
1790
0
        LogToConsole("Offline cache update done", mManifestItem);
1791
0
        Finish();
1792
0
    }
1793
0
}
1794
1795
nsresult
1796
nsOfflineCacheUpdate::Begin()
1797
0
{
1798
0
    LOG(("nsOfflineCacheUpdate::Begin [%p]", this));
1799
0
1800
0
    // Keep the object alive through a ProcessNextURI()/Finish() call.
1801
0
    nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
1802
0
1803
0
    mItemsInProgress = 0;
1804
0
1805
0
    if (mState == STATE_CANCELLED) {
1806
0
      nsresult rv = NS_DispatchToMainThread(
1807
0
        NewRunnableMethod("nsOfflineCacheUpdate::AsyncFinishWithError",
1808
0
                          this,
1809
0
                          &nsOfflineCacheUpdate::AsyncFinishWithError));
1810
0
      NS_ENSURE_SUCCESS(rv, rv);
1811
0
1812
0
      return NS_OK;
1813
0
    }
1814
0
1815
0
    if (mPartialUpdate) {
1816
0
        mState = STATE_DOWNLOADING;
1817
0
        NotifyState(nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING);
1818
0
        ProcessNextURI();
1819
0
        return NS_OK;
1820
0
    }
1821
0
1822
0
    // Start checking the manifest.
1823
0
    mManifestItem = new nsOfflineManifestItem(mManifestURI,
1824
0
                                              mDocumentURI,
1825
0
                                              mLoadingPrincipal,
1826
0
                                              mApplicationCache,
1827
0
                                              mPreviousApplicationCache);
1828
0
    if (!mManifestItem) {
1829
0
        return NS_ERROR_OUT_OF_MEMORY;
1830
0
    }
1831
0
1832
0
    mState = STATE_CHECKING;
1833
0
    mByteProgress = 0;
1834
0
    NotifyState(nsIOfflineCacheUpdateObserver::STATE_CHECKING);
1835
0
1836
0
    nsresult rv = mManifestItem->OpenChannel(this);
1837
0
    if (NS_FAILED(rv)) {
1838
0
        LoadCompleted(mManifestItem);
1839
0
    }
1840
0
1841
0
    return NS_OK;
1842
0
}
1843
1844
//-----------------------------------------------------------------------------
1845
// nsOfflineCacheUpdate <private>
1846
//-----------------------------------------------------------------------------
1847
1848
nsresult
1849
nsOfflineCacheUpdate::AddExistingItems(uint32_t aType,
1850
                                       nsTArray<nsCString>* namespaceFilter)
1851
0
{
1852
0
    if (!mPreviousApplicationCache) {
1853
0
        return NS_OK;
1854
0
    }
1855
0
1856
0
    if (namespaceFilter && namespaceFilter->Length() == 0) {
1857
0
        // Don't bother to walk entries when there are no namespaces
1858
0
        // defined.
1859
0
        return NS_OK;
1860
0
    }
1861
0
1862
0
    uint32_t count = 0;
1863
0
    char **keys = nullptr;
1864
0
    nsresult rv = mPreviousApplicationCache->GatherEntries(aType,
1865
0
                                                           &count, &keys);
1866
0
    NS_ENSURE_SUCCESS(rv, rv);
1867
0
1868
0
    AutoFreeArray autoFree(count, keys);
1869
0
1870
0
    for (uint32_t i = 0; i < count; i++) {
1871
0
        if (namespaceFilter) {
1872
0
            bool found = false;
1873
0
            for (uint32_t j = 0; j < namespaceFilter->Length() && !found; j++) {
1874
0
                found = StringBeginsWith(nsDependentCString(keys[i]),
1875
0
                                         namespaceFilter->ElementAt(j));
1876
0
            }
1877
0
1878
0
            if (!found)
1879
0
                continue;
1880
0
        }
1881
0
1882
0
        nsCOMPtr<nsIURI> uri;
1883
0
        if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), keys[i]))) {
1884
0
            rv = AddURI(uri, aType);
1885
0
            NS_ENSURE_SUCCESS(rv, rv);
1886
0
        }
1887
0
    }
1888
0
1889
0
    return NS_OK;
1890
0
}
1891
1892
nsresult
1893
nsOfflineCacheUpdate::ProcessNextURI()
1894
0
{
1895
0
    // Keep the object alive through a Finish() call.
1896
0
    nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
1897
0
1898
0
    LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p, inprogress=%d, numItems=%zu]",
1899
0
         this, mItemsInProgress, mItems.Length()));
1900
0
1901
0
    if (mState != STATE_DOWNLOADING) {
1902
0
        LOG(("  should only be called from the DOWNLOADING state, ignoring"));
1903
0
        return NS_ERROR_UNEXPECTED;
1904
0
    }
1905
0
1906
0
    nsOfflineCacheUpdateItem * runItem = nullptr;
1907
0
    uint32_t completedItems = 0;
1908
0
    for (uint32_t i = 0; i < mItems.Length(); ++i) {
1909
0
        nsOfflineCacheUpdateItem * item = mItems[i];
1910
0
1911
0
        if (item->IsScheduled()) {
1912
0
            runItem = item;
1913
0
            break;
1914
0
        }
1915
0
1916
0
        if (item->IsCompleted())
1917
0
            ++completedItems;
1918
0
    }
1919
0
1920
0
    if (completedItems == mItems.Length()) {
1921
0
        LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p]: all items loaded", this));
1922
0
1923
0
        if (mPartialUpdate) {
1924
0
            return Finish();
1925
0
        } else {
1926
0
            // Verify that the manifest wasn't changed during the
1927
0
            // update, to prevent capturing a cache while the server
1928
0
            // is being updated.  The check will call
1929
0
            // ManifestCheckCompleted() when it's done.
1930
0
            RefPtr<nsManifestCheck> manifestCheck =
1931
0
                new nsManifestCheck(this, mManifestURI, mDocumentURI, mLoadingPrincipal);
1932
0
            if (NS_FAILED(manifestCheck->Begin())) {
1933
0
                mSucceeded = false;
1934
0
                NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
1935
0
                return Finish();
1936
0
            }
1937
0
1938
0
            return NS_OK;
1939
0
        }
1940
0
    }
1941
0
1942
0
    if (!runItem) {
1943
0
        LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p]:"
1944
0
             " No more items to include in parallel load", this));
1945
0
        return NS_OK;
1946
0
    }
1947
0
1948
0
    if (LOG_ENABLED()) {
1949
0
        LOG(("%p: Opening channel for %s", this,
1950
0
             runItem->mURI->GetSpecOrDefault().get()));
1951
0
    }
1952
0
1953
0
    ++mItemsInProgress;
1954
0
    NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMSTARTED);
1955
0
1956
0
    nsresult rv = runItem->OpenChannel(this);
1957
0
    if (NS_FAILED(rv)) {
1958
0
        LoadCompleted(runItem);
1959
0
        return rv;
1960
0
    }
1961
0
1962
0
    if (mItemsInProgress >= kParallelLoadLimit) {
1963
0
        LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p]:"
1964
0
             " At parallel load limit", this));
1965
0
        return NS_OK;
1966
0
    }
1967
0
1968
0
    // This calls this method again via a post triggering
1969
0
    // a parallel item load
1970
0
    return NS_DispatchToCurrentThread(this);
1971
0
}
1972
1973
void
1974
nsOfflineCacheUpdate::GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers)
1975
0
{
1976
0
    for (int32_t i = 0; i < mWeakObservers.Count(); i++) {
1977
0
        nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
1978
0
            do_QueryReferent(mWeakObservers[i]);
1979
0
        if (observer)
1980
0
            aObservers.AppendObject(observer);
1981
0
        else
1982
0
            mWeakObservers.RemoveObjectAt(i--);
1983
0
    }
1984
0
1985
0
    for (int32_t i = 0; i < mObservers.Count(); i++) {
1986
0
        aObservers.AppendObject(mObservers[i]);
1987
0
    }
1988
0
}
1989
1990
void
1991
nsOfflineCacheUpdate::NotifyState(uint32_t state)
1992
0
{
1993
0
    LOG(("nsOfflineCacheUpdate::NotifyState [%p, %d]", this, state));
1994
0
1995
0
    if (state == STATE_ERROR) {
1996
0
        LogToConsole("Offline cache update error", mManifestItem);
1997
0
    }
1998
0
1999
0
    nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
2000
0
    GatherObservers(observers);
2001
0
2002
0
    for (int32_t i = 0; i < observers.Count(); i++) {
2003
0
        observers[i]->UpdateStateChanged(this, state);
2004
0
    }
2005
0
}
2006
2007
void
2008
nsOfflineCacheUpdate::NotifyUpdateAvailability(bool updateAvailable)
2009
0
{
2010
0
    if (!mUpdateAvailableObserver)
2011
0
        return;
2012
0
2013
0
    LOG(("nsOfflineCacheUpdate::NotifyUpdateAvailability [this=%p, avail=%d]",
2014
0
         this, updateAvailable));
2015
0
2016
0
    const char* topic = updateAvailable
2017
0
                      ? "offline-cache-update-available"
2018
0
                      : "offline-cache-update-unavailable";
2019
0
2020
0
    nsCOMPtr<nsIObserver> observer;
2021
0
    observer.swap(mUpdateAvailableObserver);
2022
0
    observer->Observe(mManifestURI, topic, nullptr);
2023
0
}
2024
2025
void
2026
nsOfflineCacheUpdate::AssociateDocuments(nsIApplicationCache* cache)
2027
0
{
2028
0
    if (!cache) {
2029
0
        LOG(("nsOfflineCacheUpdate::AssociateDocuments bypassed"
2030
0
             ", no cache provided [this=%p]", this));
2031
0
        return;
2032
0
    }
2033
0
2034
0
    nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
2035
0
    GatherObservers(observers);
2036
0
2037
0
    for (int32_t i = 0; i < observers.Count(); i++) {
2038
0
        observers[i]->ApplicationCacheAvailable(cache);
2039
0
    }
2040
0
}
2041
2042
void
2043
nsOfflineCacheUpdate::StickDocument(nsIURI *aDocumentURI)
2044
0
{
2045
0
    if (!aDocumentURI)
2046
0
      return;
2047
0
2048
0
    mDocumentURIs.AppendObject(aDocumentURI);
2049
0
}
2050
2051
void
2052
nsOfflineCacheUpdate::SetOwner(nsOfflineCacheUpdateOwner *aOwner)
2053
0
{
2054
0
    NS_ASSERTION(!mOwner, "Tried to set cache update owner twice.");
2055
0
    mOwner = aOwner;
2056
0
}
2057
2058
bool
2059
nsOfflineCacheUpdate::IsForGroupID(const nsACString& groupID)
2060
0
{
2061
0
    return mGroupID == groupID;
2062
0
}
2063
2064
bool
2065
nsOfflineCacheUpdate::IsForProfile(nsIFile* aCustomProfileDir)
2066
0
{
2067
0
    if (!mCustomProfileDir && !aCustomProfileDir)
2068
0
        return true;
2069
0
    if (!mCustomProfileDir || !aCustomProfileDir)
2070
0
        return false;
2071
0
2072
0
    bool equals;
2073
0
    nsresult rv = mCustomProfileDir->Equals(aCustomProfileDir, &equals);
2074
0
2075
0
    return NS_SUCCEEDED(rv) && equals;
2076
0
}
2077
2078
nsresult
2079
nsOfflineCacheUpdate::UpdateFinished(nsOfflineCacheUpdate *aUpdate)
2080
0
{
2081
0
    // Keep the object alive through a Finish() call.
2082
0
    nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
2083
0
2084
0
    mImplicitUpdate = nullptr;
2085
0
2086
0
    NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE);
2087
0
    Finish();
2088
0
2089
0
    return NS_OK;
2090
0
}
2091
2092
void
2093
nsOfflineCacheUpdate::OnByteProgress(uint64_t byteIncrement)
2094
0
{
2095
0
    mByteProgress += byteIncrement;
2096
0
    NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMPROGRESS);
2097
0
}
2098
2099
nsresult
2100
nsOfflineCacheUpdate::ScheduleImplicit()
2101
0
{
2102
0
    if (mDocumentURIs.Count() == 0)
2103
0
        return NS_OK;
2104
0
2105
0
    nsresult rv;
2106
0
2107
0
    RefPtr<nsOfflineCacheUpdate> update = new nsOfflineCacheUpdate();
2108
0
    NS_ENSURE_TRUE(update, NS_ERROR_OUT_OF_MEMORY);
2109
0
2110
0
    nsAutoCString clientID;
2111
0
    if (mPreviousApplicationCache) {
2112
0
        rv = mPreviousApplicationCache->GetClientID(clientID);
2113
0
        NS_ENSURE_SUCCESS(rv, rv);
2114
0
    }
2115
0
    else if (mApplicationCache) {
2116
0
        rv = mApplicationCache->GetClientID(clientID);
2117
0
        NS_ENSURE_SUCCESS(rv, rv);
2118
0
    }
2119
0
    else {
2120
0
        NS_ERROR("Offline cache update not having set mApplicationCache?");
2121
0
    }
2122
0
2123
0
    rv = update->InitPartial(mManifestURI, clientID, mDocumentURI, mLoadingPrincipal);
2124
0
    NS_ENSURE_SUCCESS(rv, rv);
2125
0
2126
0
    for (int32_t i = 0; i < mDocumentURIs.Count(); i++) {
2127
0
        rv = update->AddURI(mDocumentURIs[i],
2128
0
              nsIApplicationCache::ITEM_IMPLICIT);
2129
0
        NS_ENSURE_SUCCESS(rv, rv);
2130
0
    }
2131
0
2132
0
    update->SetOwner(this);
2133
0
    rv = update->Begin();
2134
0
    NS_ENSURE_SUCCESS(rv, rv);
2135
0
2136
0
    mImplicitUpdate = update;
2137
0
2138
0
    return NS_OK;
2139
0
}
2140
2141
nsresult
2142
nsOfflineCacheUpdate::FinishNoNotify()
2143
0
{
2144
0
    LOG(("nsOfflineCacheUpdate::Finish [%p]", this));
2145
0
2146
0
    mState = STATE_FINISHED;
2147
0
2148
0
    if (!mPartialUpdate && !mOnlyCheckUpdate) {
2149
0
        if (mSucceeded) {
2150
0
            nsIArray *namespaces = mManifestItem->GetNamespaces();
2151
0
            nsresult rv = mApplicationCache->AddNamespaces(namespaces);
2152
0
            if (NS_FAILED(rv)) {
2153
0
                NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
2154
0
                mSucceeded = false;
2155
0
            }
2156
0
2157
0
            rv = mApplicationCache->Activate();
2158
0
            if (NS_FAILED(rv)) {
2159
0
                NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
2160
0
                mSucceeded = false;
2161
0
            }
2162
0
2163
0
            AssociateDocuments(mApplicationCache);
2164
0
        }
2165
0
2166
0
        if (mObsolete) {
2167
0
            nsCOMPtr<nsIApplicationCacheService> appCacheService =
2168
0
                do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID);
2169
0
            if (appCacheService) {
2170
0
                nsAutoCString groupID;
2171
0
                mApplicationCache->GetGroupID(groupID);
2172
0
                appCacheService->DeactivateGroup(groupID);
2173
0
             }
2174
0
        }
2175
0
2176
0
        if (!mSucceeded) {
2177
0
            // Update was not merged, mark all the loads as failures
2178
0
            for (uint32_t i = 0; i < mItems.Length(); i++) {
2179
0
                mItems[i]->Cancel();
2180
0
            }
2181
0
2182
0
            mApplicationCache->Discard();
2183
0
        }
2184
0
    }
2185
0
2186
0
    nsresult rv = NS_OK;
2187
0
2188
0
    if (mOwner) {
2189
0
        rv = mOwner->UpdateFinished(this);
2190
0
        // mozilla::WeakPtr is missing some key features, like setting it to
2191
0
        // null explicitly.
2192
0
        mOwner = mozilla::WeakPtr<nsOfflineCacheUpdateOwner>();
2193
0
    }
2194
0
2195
0
    return rv;
2196
0
}
2197
2198
nsresult
2199
nsOfflineCacheUpdate::Finish()
2200
0
{
2201
0
    nsresult rv = FinishNoNotify();
2202
0
2203
0
    NotifyState(nsIOfflineCacheUpdateObserver::STATE_FINISHED);
2204
0
2205
0
    return rv;
2206
0
}
2207
2208
void
2209
nsOfflineCacheUpdate::AsyncFinishWithError()
2210
0
{
2211
0
    NotifyState(nsOfflineCacheUpdate::STATE_ERROR);
2212
0
    Finish();
2213
0
}
2214
2215
static nsresult
2216
EvictOneOfCacheGroups(nsIApplicationCacheService *cacheService,
2217
                      uint32_t count, const char * const *groups)
2218
0
{
2219
0
    nsresult rv;
2220
0
    unsigned int i;
2221
0
2222
0
    for (i = 0; i < count; i++) {
2223
0
        nsCOMPtr<nsIURI> uri;
2224
0
        rv = NS_NewURI(getter_AddRefs(uri), groups[i]);
2225
0
        NS_ENSURE_SUCCESS(rv, rv);
2226
0
2227
0
        nsDependentCString group_name(groups[i]);
2228
0
        nsCOMPtr<nsIApplicationCache> cache;
2229
0
        rv = cacheService->GetActiveCache(group_name, getter_AddRefs(cache));
2230
0
        // Maybe someone in another thread or process have deleted it.
2231
0
        if (NS_FAILED(rv) || !cache)
2232
0
            continue;
2233
0
2234
0
        bool pinned;
2235
0
        rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(uri,
2236
0
                                                                 nullptr,
2237
0
                                                                 &pinned);
2238
0
        NS_ENSURE_SUCCESS(rv, rv);
2239
0
2240
0
        if (!pinned) {
2241
0
            rv = cache->Discard();
2242
0
            return NS_OK;
2243
0
        }
2244
0
    }
2245
0
2246
0
    return NS_ERROR_FILE_NOT_FOUND;
2247
0
}
2248
2249
nsresult
2250
nsOfflineCacheUpdate::EvictOneNonPinned()
2251
0
{
2252
0
    nsresult rv;
2253
0
2254
0
    nsCOMPtr<nsIApplicationCacheService> cacheService =
2255
0
        do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
2256
0
    NS_ENSURE_SUCCESS(rv, rv);
2257
0
2258
0
    uint32_t count;
2259
0
    char **groups;
2260
0
    rv = cacheService->GetGroupsTimeOrdered(&count, &groups);
2261
0
    NS_ENSURE_SUCCESS(rv, rv);
2262
0
2263
0
    rv = EvictOneOfCacheGroups(cacheService, count, groups);
2264
0
2265
0
    NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, groups);
2266
0
    return rv;
2267
0
}
2268
2269
//-----------------------------------------------------------------------------
2270
// nsOfflineCacheUpdate::nsIOfflineCacheUpdate
2271
//-----------------------------------------------------------------------------
2272
2273
NS_IMETHODIMP
2274
nsOfflineCacheUpdate::GetUpdateDomain(nsACString &aUpdateDomain)
2275
0
{
2276
0
    NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
2277
0
2278
0
    aUpdateDomain = mUpdateDomain;
2279
0
    return NS_OK;
2280
0
}
2281
2282
NS_IMETHODIMP
2283
nsOfflineCacheUpdate::GetStatus(uint16_t *aStatus)
2284
0
{
2285
0
    switch (mState) {
2286
0
    case STATE_CHECKING :
2287
0
        *aStatus = dom::OfflineResourceList_Binding::CHECKING;
2288
0
        return NS_OK;
2289
0
    case STATE_DOWNLOADING :
2290
0
        *aStatus = dom::OfflineResourceList_Binding::DOWNLOADING;
2291
0
        return NS_OK;
2292
0
    default :
2293
0
        *aStatus = dom::OfflineResourceList_Binding::IDLE;
2294
0
        return NS_OK;
2295
0
    }
2296
0
2297
0
    return NS_ERROR_FAILURE;
2298
0
}
2299
2300
NS_IMETHODIMP
2301
nsOfflineCacheUpdate::GetPartial(bool *aPartial)
2302
0
{
2303
0
    *aPartial = mPartialUpdate || mOnlyCheckUpdate;
2304
0
    return NS_OK;
2305
0
}
2306
2307
NS_IMETHODIMP
2308
nsOfflineCacheUpdate::GetManifestURI(nsIURI **aManifestURI)
2309
0
{
2310
0
    NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
2311
0
2312
0
    NS_IF_ADDREF(*aManifestURI = mManifestURI);
2313
0
    return NS_OK;
2314
0
}
2315
2316
NS_IMETHODIMP
2317
nsOfflineCacheUpdate::GetSucceeded(bool *aSucceeded)
2318
0
{
2319
0
    NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE);
2320
0
2321
0
    *aSucceeded = mSucceeded;
2322
0
2323
0
    return NS_OK;
2324
0
}
2325
2326
NS_IMETHODIMP
2327
nsOfflineCacheUpdate::GetIsUpgrade(bool *aIsUpgrade)
2328
0
{
2329
0
    NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
2330
0
2331
0
    *aIsUpgrade = (mPreviousApplicationCache != nullptr);
2332
0
2333
0
    return NS_OK;
2334
0
}
2335
2336
nsresult
2337
nsOfflineCacheUpdate::AddURI(nsIURI *aURI, uint32_t aType, uint32_t aLoadFlags)
2338
0
{
2339
0
    NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
2340
0
2341
0
    if (mState >= STATE_DOWNLOADING)
2342
0
        return NS_ERROR_NOT_AVAILABLE;
2343
0
2344
0
    // Resource URIs must have the same scheme as the manifest.
2345
0
    nsAutoCString scheme;
2346
0
    aURI->GetScheme(scheme);
2347
0
2348
0
    bool match;
2349
0
    if (NS_FAILED(mManifestURI->SchemeIs(scheme.get(), &match)) || !match)
2350
0
        return NS_ERROR_FAILURE;
2351
0
2352
0
    // Don't fetch the same URI twice.
2353
0
    for (uint32_t i = 0; i < mItems.Length(); i++) {
2354
0
        bool equals;
2355
0
        if (NS_SUCCEEDED(mItems[i]->mURI->Equals(aURI, &equals)) && equals &&
2356
0
            mItems[i]->mLoadFlags == aLoadFlags) {
2357
0
            // retain both types.
2358
0
            mItems[i]->mItemType |= aType;
2359
0
            return NS_OK;
2360
0
        }
2361
0
    }
2362
0
2363
0
    RefPtr<nsOfflineCacheUpdateItem> item =
2364
0
        new nsOfflineCacheUpdateItem(aURI,
2365
0
                                     mDocumentURI,
2366
0
                                     mLoadingPrincipal,
2367
0
                                     mApplicationCache,
2368
0
                                     mPreviousApplicationCache,
2369
0
                                     aType,
2370
0
                                     aLoadFlags);
2371
0
    if (!item) return NS_ERROR_OUT_OF_MEMORY;
2372
0
2373
0
    mItems.AppendElement(item);
2374
0
    mAddedItems = true;
2375
0
2376
0
    return NS_OK;
2377
0
}
2378
2379
NS_IMETHODIMP
2380
nsOfflineCacheUpdate::AddDynamicURI(nsIURI *aURI)
2381
0
{
2382
0
    if (GeckoProcessType_Default != XRE_GetProcessType())
2383
0
        return NS_ERROR_NOT_IMPLEMENTED;
2384
0
2385
0
    // If this is a partial update and the resource is already in the
2386
0
    // cache, we should only mark the entry, not fetch it again.
2387
0
    if (mPartialUpdate) {
2388
0
        nsAutoCString key;
2389
0
        GetCacheKey(aURI, key);
2390
0
2391
0
        uint32_t types;
2392
0
        nsresult rv = mApplicationCache->GetTypes(key, &types);
2393
0
        if (NS_SUCCEEDED(rv)) {
2394
0
            if (!(types & nsIApplicationCache::ITEM_DYNAMIC)) {
2395
0
                mApplicationCache->MarkEntry
2396
0
                    (key, nsIApplicationCache::ITEM_DYNAMIC);
2397
0
            }
2398
0
            return NS_OK;
2399
0
        }
2400
0
    }
2401
0
2402
0
    return AddURI(aURI, nsIApplicationCache::ITEM_DYNAMIC);
2403
0
}
2404
2405
NS_IMETHODIMP
2406
nsOfflineCacheUpdate::Cancel()
2407
0
{
2408
0
    LOG(("nsOfflineCacheUpdate::Cancel [%p]", this));
2409
0
2410
0
    if ((mState == STATE_FINISHED) || (mState == STATE_CANCELLED)) {
2411
0
      return NS_ERROR_NOT_AVAILABLE;
2412
0
    }
2413
0
2414
0
    mState = STATE_CANCELLED;
2415
0
    mSucceeded = false;
2416
0
2417
0
    // Cancel all running downloads
2418
0
    for (uint32_t i = 0; i < mItems.Length(); ++i) {
2419
0
        nsOfflineCacheUpdateItem * item = mItems[i];
2420
0
2421
0
        if (item->IsInProgress())
2422
0
            item->Cancel();
2423
0
    }
2424
0
2425
0
    return NS_OK;
2426
0
}
2427
2428
NS_IMETHODIMP
2429
nsOfflineCacheUpdate::AddObserver(nsIOfflineCacheUpdateObserver *aObserver,
2430
                                  bool aHoldWeak)
2431
0
{
2432
0
    LOG(("nsOfflineCacheUpdate::AddObserver [%p] to update [%p]", aObserver, this));
2433
0
2434
0
    NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
2435
0
2436
0
    if (aHoldWeak) {
2437
0
        nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aObserver);
2438
0
        mWeakObservers.AppendObject(weakRef);
2439
0
    } else {
2440
0
        mObservers.AppendObject(aObserver);
2441
0
    }
2442
0
2443
0
    return NS_OK;
2444
0
}
2445
2446
NS_IMETHODIMP
2447
nsOfflineCacheUpdate::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver)
2448
0
{
2449
0
    LOG(("nsOfflineCacheUpdate::RemoveObserver [%p] from update [%p]", aObserver, this));
2450
0
2451
0
    NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
2452
0
2453
0
    for (int32_t i = 0; i < mWeakObservers.Count(); i++) {
2454
0
        nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
2455
0
            do_QueryReferent(mWeakObservers[i]);
2456
0
        if (observer == aObserver) {
2457
0
            mWeakObservers.RemoveObjectAt(i);
2458
0
            return NS_OK;
2459
0
        }
2460
0
    }
2461
0
2462
0
    for (int32_t i = 0; i < mObservers.Count(); i++) {
2463
0
        if (mObservers[i] == aObserver) {
2464
0
            mObservers.RemoveObjectAt(i);
2465
0
            return NS_OK;
2466
0
        }
2467
0
    }
2468
0
2469
0
    return NS_OK;
2470
0
}
2471
2472
NS_IMETHODIMP
2473
nsOfflineCacheUpdate::GetByteProgress(uint64_t * _result)
2474
0
{
2475
0
    NS_ENSURE_ARG(_result);
2476
0
2477
0
    *_result = mByteProgress;
2478
0
    return NS_OK;
2479
0
}
2480
2481
NS_IMETHODIMP
2482
nsOfflineCacheUpdate::Schedule()
2483
0
{
2484
0
    LOG(("nsOfflineCacheUpdate::Schedule [%p]", this));
2485
0
2486
0
    nsOfflineCacheUpdateService* service =
2487
0
        nsOfflineCacheUpdateService::EnsureService();
2488
0
2489
0
    if (!service) {
2490
0
        return NS_ERROR_FAILURE;
2491
0
    }
2492
0
2493
0
    return service->ScheduleUpdate(this);
2494
0
}
2495
2496
NS_IMETHODIMP
2497
nsOfflineCacheUpdate::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate,
2498
                                         uint32_t aState)
2499
0
{
2500
0
    if (aState == nsIOfflineCacheUpdateObserver::STATE_FINISHED) {
2501
0
        // Take the mSucceeded flag from the underlying update, we will be
2502
0
        // queried for it soon. mSucceeded of this update is false (manifest
2503
0
        // check failed) but the subsequent re-fetch update might succeed
2504
0
        bool succeeded;
2505
0
        aUpdate->GetSucceeded(&succeeded);
2506
0
        mSucceeded = succeeded;
2507
0
    }
2508
0
2509
0
    NotifyState(aState);
2510
0
    if (aState == nsIOfflineCacheUpdateObserver::STATE_FINISHED)
2511
0
        aUpdate->RemoveObserver(this);
2512
0
2513
0
    return NS_OK;
2514
0
}
2515
2516
NS_IMETHODIMP
2517
nsOfflineCacheUpdate::ApplicationCacheAvailable(nsIApplicationCache *applicationCache)
2518
0
{
2519
0
    AssociateDocuments(applicationCache);
2520
0
    return NS_OK;
2521
0
}
2522
2523
//-----------------------------------------------------------------------------
2524
// nsOfflineCacheUpdate::nsIRunable
2525
//-----------------------------------------------------------------------------
2526
2527
NS_IMETHODIMP
2528
nsOfflineCacheUpdate::Run()
2529
0
{
2530
0
    ProcessNextURI();
2531
0
    return NS_OK;
2532
0
}