Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/offline/nsDOMOfflineResourceList.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "nsDOMOfflineResourceList.h"
8
#include "nsIScriptSecurityManager.h"
9
#include "nsError.h"
10
#include "mozilla/dom/DOMStringList.h"
11
#include "nsIPrefetchService.h"
12
#include "nsCPrefetchService.h"
13
#include "nsMemory.h"
14
#include "nsNetUtil.h"
15
#include "nsNetCID.h"
16
#include "nsServiceManagerUtils.h"
17
#include "nsIInterfaceRequestorUtils.h"
18
#include "nsIOfflineCacheUpdate.h"
19
#include "nsContentUtils.h"
20
#include "nsILoadContext.h"
21
#include "nsIObserverService.h"
22
#include "nsIScriptGlobalObject.h"
23
#include "nsIWebNavigation.h"
24
#include "nsOfflineCacheUpdate.h"
25
#include "mozilla/dom/Event.h"
26
#include "mozilla/dom/OfflineResourceListBinding.h"
27
#include "mozilla/EventDispatcher.h"
28
#include "mozilla/Preferences.h"
29
#include "mozilla/BasePrincipal.h"
30
31
#include "nsXULAppAPI.h"
32
#define IS_CHILD_PROCESS() \
33
0
    (GeckoProcessType_Default != XRE_GetProcessType())
34
35
using namespace mozilla;
36
using namespace mozilla::dom;
37
38
// Event names
39
40
#define CHECKING_STR    "checking"
41
#define ERROR_STR       "error"
42
#define NOUPDATE_STR    "noupdate"
43
#define DOWNLOADING_STR "downloading"
44
#define PROGRESS_STR    "progress"
45
#define CACHED_STR      "cached"
46
#define UPDATEREADY_STR "updateready"
47
#define OBSOLETE_STR    "obsolete"
48
49
// To prevent abuse of the resource list for data storage, the number
50
// of offline urls and their length are limited.
51
52
static const char kMaxEntriesPref[] =  "offline.max_site_resources";
53
0
#define DEFAULT_MAX_ENTRIES 100
54
0
#define MAX_URI_LENGTH 2048
55
56
//
57
// nsDOMOfflineResourceList
58
//
59
60
NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDOMOfflineResourceList,
61
                                   DOMEventTargetHelper,
62
                                   mCacheUpdate,
63
                                   mPendingEvents)
64
65
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMOfflineResourceList)
66
0
  NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdateObserver)
67
0
  NS_INTERFACE_MAP_ENTRY(nsIObserver)
68
0
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
69
0
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
70
71
NS_IMPL_ADDREF_INHERITED(nsDOMOfflineResourceList, DOMEventTargetHelper)
72
NS_IMPL_RELEASE_INHERITED(nsDOMOfflineResourceList, DOMEventTargetHelper)
73
74
nsDOMOfflineResourceList::nsDOMOfflineResourceList(nsIURI *aManifestURI,
75
                                                   nsIURI *aDocumentURI,
76
                                                   nsIPrincipal *aLoadingPrincipal,
77
                                                   nsPIDOMWindowInner *aWindow)
78
  : DOMEventTargetHelper(aWindow)
79
  , mInitialized(false)
80
  , mManifestURI(aManifestURI)
81
  , mDocumentURI(aDocumentURI)
82
  , mLoadingPrincipal(aLoadingPrincipal)
83
  , mExposeCacheUpdateStatus(true)
84
  , mStatus(OfflineResourceList_Binding::IDLE)
85
  , mCachedKeys(nullptr)
86
  , mCachedKeysCount(0)
87
0
{
88
0
}
89
90
nsDOMOfflineResourceList::~nsDOMOfflineResourceList()
91
0
{
92
0
  ClearCachedKeys();
93
0
}
94
95
JSObject*
96
nsDOMOfflineResourceList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
97
0
{
98
0
  return OfflineResourceList_Binding::Wrap(aCx, this, aGivenProto);
99
0
}
100
101
nsresult
102
nsDOMOfflineResourceList::Init()
103
0
{
104
0
  if (mInitialized) {
105
0
    return NS_OK;
106
0
  }
107
0
108
0
  if (!mManifestURI) {
109
0
    return NS_ERROR_DOM_INVALID_STATE_ERR;
110
0
  }
111
0
112
0
  mManifestURI->GetAsciiSpec(mManifestSpec);
113
0
  bool isPrivateWin = mLoadingPrincipal
114
0
    ? mLoadingPrincipal->OriginAttributesRef().mPrivateBrowsingId > 0
115
0
    : false;
116
0
117
0
  nsresult rv = nsContentUtils::GetSecurityManager()->
118
0
                   CheckSameOriginURI(mManifestURI, mDocumentURI, true, isPrivateWin);
119
0
  NS_ENSURE_SUCCESS(rv, rv);
120
0
121
0
  // Dynamically-managed resources are stored as a separate ownership list
122
0
  // from the manifest.
123
0
  nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(mDocumentURI);
124
0
  if (!innerURI)
125
0
    return NS_ERROR_FAILURE;
126
0
127
0
  if (!IS_CHILD_PROCESS())
128
0
  {
129
0
    mApplicationCacheService =
130
0
      do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
131
0
    NS_ENSURE_SUCCESS(rv, rv);
132
0
133
0
    // Check for in-progress cache updates
134
0
    nsCOMPtr<nsIOfflineCacheUpdateService> cacheUpdateService =
135
0
      do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &rv);
136
0
    NS_ENSURE_SUCCESS(rv, rv);
137
0
138
0
    uint32_t numUpdates;
139
0
    rv = cacheUpdateService->GetNumUpdates(&numUpdates);
140
0
    NS_ENSURE_SUCCESS(rv, rv);
141
0
142
0
    for (uint32_t i = 0; i < numUpdates; i++) {
143
0
      nsCOMPtr<nsIOfflineCacheUpdate> cacheUpdate;
144
0
      rv = cacheUpdateService->GetUpdate(i, getter_AddRefs(cacheUpdate));
145
0
      NS_ENSURE_SUCCESS(rv, rv);
146
0
147
0
      UpdateAdded(cacheUpdate);
148
0
      NS_ENSURE_SUCCESS(rv, rv);
149
0
    }
150
0
  }
151
0
152
0
  // watch for new offline cache updates
153
0
  nsCOMPtr<nsIObserverService> observerService =
154
0
    mozilla::services::GetObserverService();
155
0
  if (!observerService)
156
0
    return NS_ERROR_FAILURE;
157
0
158
0
  rv = observerService->AddObserver(this, "offline-cache-update-added", true);
159
0
  NS_ENSURE_SUCCESS(rv, rv);
160
0
  rv = observerService->AddObserver(this, "offline-cache-update-completed", true);
161
0
  NS_ENSURE_SUCCESS(rv, rv);
162
0
163
0
  mInitialized = true;
164
0
165
0
  return NS_OK;
166
0
}
167
168
void
169
nsDOMOfflineResourceList::Disconnect()
170
0
{
171
0
  mPendingEvents.Clear();
172
0
173
0
  if (mListenerManager) {
174
0
    mListenerManager->Disconnect();
175
0
    mListenerManager = nullptr;
176
0
  }
177
0
}
178
179
already_AddRefed<DOMStringList>
180
nsDOMOfflineResourceList::GetMozItems(ErrorResult& aRv)
181
0
{
182
0
  if (IS_CHILD_PROCESS()) {
183
0
    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
184
0
    return nullptr;
185
0
  }
186
0
187
0
  RefPtr<DOMStringList> items = new DOMStringList();
188
0
189
0
  // If we are not associated with an application cache, return an
190
0
  // empty list.
191
0
  nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
192
0
  if (!appCache) {
193
0
    return items.forget();
194
0
  }
195
0
196
0
  aRv = Init();
197
0
  if (aRv.Failed()) {
198
0
    return nullptr;
199
0
  }
200
0
201
0
  uint32_t length;
202
0
  char **keys;
203
0
  aRv = appCache->GatherEntries(nsIApplicationCache::ITEM_DYNAMIC,
204
0
                                &length, &keys);
205
0
  if (aRv.Failed()) {
206
0
    return nullptr;
207
0
  }
208
0
209
0
  for (uint32_t i = 0; i < length; i++) {
210
0
    items->Add(NS_ConvertUTF8toUTF16(keys[i]));
211
0
  }
212
0
213
0
  NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(length, keys);
214
0
215
0
  return items.forget();
216
0
}
217
218
bool
219
nsDOMOfflineResourceList::MozHasItem(const nsAString& aURI, ErrorResult& aRv)
220
0
{
221
0
  if (IS_CHILD_PROCESS()) {
222
0
    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
223
0
    return false;
224
0
  }
225
0
226
0
  nsresult rv = Init();
227
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
228
0
    aRv.Throw(rv);
229
0
    return false;
230
0
  }
231
0
232
0
  nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
233
0
  if (!appCache) {
234
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
235
0
    return false;
236
0
  }
237
0
238
0
  nsAutoCString key;
239
0
  rv = GetCacheKey(aURI, key);
240
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
241
0
    aRv.Throw(rv);
242
0
    return false;
243
0
  }
244
0
245
0
  uint32_t types;
246
0
  rv = appCache->GetTypes(key, &types);
247
0
  if (rv == NS_ERROR_CACHE_KEY_NOT_FOUND) {
248
0
    return false;
249
0
  }
250
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
251
0
    aRv.Throw(rv);
252
0
    return false;
253
0
  }
254
0
255
0
  return types & nsIApplicationCache::ITEM_DYNAMIC;
256
0
}
257
258
uint32_t
259
nsDOMOfflineResourceList::GetMozLength(ErrorResult& aRv)
260
0
{
261
0
  if (IS_CHILD_PROCESS()) {
262
0
    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
263
0
    return 0;
264
0
  }
265
0
266
0
  if (!mManifestURI) {
267
0
    return 0;
268
0
  }
269
0
270
0
  nsresult rv = Init();
271
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
272
0
    aRv.Throw(rv);
273
0
    return 0;
274
0
  }
275
0
276
0
  rv = CacheKeys();
277
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
278
0
    aRv.Throw(rv);
279
0
    return 0;
280
0
  }
281
0
282
0
  return mCachedKeysCount;
283
0
}
284
285
void
286
nsDOMOfflineResourceList::MozItem(uint32_t aIndex, nsAString& aURI,
287
                                  ErrorResult& aRv)
288
0
{
289
0
  bool found;
290
0
  IndexedGetter(aIndex, found, aURI, aRv);
291
0
  if (!aRv.Failed() && !found) {
292
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
293
0
  }
294
0
}
295
296
void
297
nsDOMOfflineResourceList::IndexedGetter(uint32_t aIndex, bool& aFound,
298
                                        nsAString& aURI, ErrorResult& aRv)
299
0
{
300
0
  if (IS_CHILD_PROCESS()) {
301
0
    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
302
0
    return;
303
0
  }
304
0
305
0
  nsresult rv = Init();
306
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
307
0
    aRv.Throw(rv);
308
0
    return;
309
0
  }
310
0
311
0
  rv = CacheKeys();
312
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
313
0
    aRv.Throw(rv);
314
0
    return;
315
0
  }
316
0
317
0
  if (aIndex >= mCachedKeysCount) {
318
0
    aFound = false;
319
0
    return;
320
0
  }
321
0
322
0
  aFound = true;
323
0
  CopyUTF8toUTF16(mozilla::MakeStringSpan(mCachedKeys[aIndex]), aURI);
324
0
}
325
326
void
327
nsDOMOfflineResourceList::MozAdd(const nsAString& aURI, ErrorResult& aRv)
328
0
{
329
0
  if (IS_CHILD_PROCESS()) {
330
0
    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
331
0
    return;
332
0
  }
333
0
334
0
  nsresult rv = Init();
335
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
336
0
    aRv.Throw(rv);
337
0
    return;
338
0
  }
339
0
340
0
  if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
341
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
342
0
    return;
343
0
  }
344
0
345
0
  nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
346
0
  if (!appCache) {
347
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
348
0
    return;
349
0
  }
350
0
351
0
  if (aURI.Length() > MAX_URI_LENGTH) {
352
0
    aRv.Throw(NS_ERROR_DOM_BAD_URI);
353
0
    return;
354
0
  }
355
0
356
0
  // this will fail if the URI is not absolute
357
0
  nsCOMPtr<nsIURI> requestedURI;
358
0
  rv = NS_NewURI(getter_AddRefs(requestedURI), aURI);
359
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
360
0
    aRv.Throw(rv);
361
0
    return;
362
0
  }
363
0
364
0
  nsAutoCString scheme;
365
0
  rv = requestedURI->GetScheme(scheme);
366
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
367
0
    aRv.Throw(rv);
368
0
    return;
369
0
  }
370
0
371
0
  bool match;
372
0
  rv = mManifestURI->SchemeIs(scheme.get(), &match);
373
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
374
0
    aRv.Throw(rv);
375
0
    return;
376
0
  }
377
0
378
0
  if (!match) {
379
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
380
0
    return;
381
0
  }
382
0
383
0
  uint32_t length = GetMozLength(aRv);
384
0
  if (NS_WARN_IF(aRv.Failed())) {
385
0
    return;
386
0
  }
387
0
  uint32_t maxEntries =
388
0
    Preferences::GetUint(kMaxEntriesPref, DEFAULT_MAX_ENTRIES);
389
0
390
0
  if (length > maxEntries) {
391
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
392
0
    return;
393
0
  }
394
0
395
0
  ClearCachedKeys();
396
0
397
0
  nsCOMPtr<nsIOfflineCacheUpdate> update = new nsOfflineCacheUpdate();
398
0
399
0
  nsAutoCString clientID;
400
0
  rv = appCache->GetClientID(clientID);
401
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
402
0
    aRv.Throw(rv);
403
0
    return;
404
0
  }
405
0
406
0
  rv = update->InitPartial(mManifestURI, clientID,
407
0
                           mDocumentURI, mLoadingPrincipal);
408
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
409
0
    aRv.Throw(rv);
410
0
    return;
411
0
  }
412
0
413
0
  rv = update->AddDynamicURI(requestedURI);
414
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
415
0
    aRv.Throw(rv);
416
0
    return;
417
0
  }
418
0
419
0
  rv = update->Schedule();
420
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
421
0
    aRv.Throw(rv);
422
0
    return;
423
0
  }
424
0
}
425
426
void
427
nsDOMOfflineResourceList::MozRemove(const nsAString& aURI, ErrorResult& aRv)
428
0
{
429
0
  if (IS_CHILD_PROCESS()) {
430
0
    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
431
0
    return;
432
0
  }
433
0
434
0
  nsresult rv = Init();
435
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
436
0
    aRv.Throw(rv);
437
0
    return;
438
0
  }
439
0
440
0
  if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
441
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
442
0
    return;
443
0
  }
444
0
445
0
  nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
446
0
  if (!appCache) {
447
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
448
0
    return;
449
0
  }
450
0
451
0
  nsAutoCString key;
452
0
  rv = GetCacheKey(aURI, key);
453
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
454
0
    aRv.Throw(rv);
455
0
    return;
456
0
  }
457
0
458
0
  ClearCachedKeys();
459
0
460
0
  // XXX: This is a race condition.  remove() is specced to remove
461
0
  // from the currently associated application cache, but if this
462
0
  // happens during an update (or after an update, if we haven't
463
0
  // swapped yet), that remove() will be lost when the next update is
464
0
  // finished.  Need to bring this issue up.
465
0
466
0
  rv = appCache->UnmarkEntry(key, nsIApplicationCache::ITEM_DYNAMIC);
467
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
468
0
    aRv.Throw(rv);
469
0
    return;
470
0
  }
471
0
}
472
473
uint16_t
474
nsDOMOfflineResourceList::GetStatus(ErrorResult& aRv)
475
0
{
476
0
  nsresult rv = Init();
477
0
478
0
  // Init may fail with INVALID_STATE_ERR if there is no manifest URI.
479
0
  // The status attribute should not throw that exception, convert it
480
0
  // to an UNCACHED.
481
0
  if (rv == NS_ERROR_DOM_INVALID_STATE_ERR ||
482
0
      !nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
483
0
    return OfflineResourceList_Binding::UNCACHED;
484
0
  }
485
0
486
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
487
0
    aRv.Throw(rv);
488
0
    return 0;
489
0
  }
490
0
491
0
  // If this object is not associated with a cache, return UNCACHED
492
0
  nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
493
0
  if (!appCache) {
494
0
    return OfflineResourceList_Binding::UNCACHED;
495
0
  }
496
0
497
0
498
0
  // If there is an update in process, use its status.
499
0
  if (mCacheUpdate && mExposeCacheUpdateStatus) {
500
0
    uint16_t status;
501
0
    rv = mCacheUpdate->GetStatus(&status);
502
0
    if (NS_SUCCEEDED(rv) && status != OfflineResourceList_Binding::IDLE) {
503
0
      return status;
504
0
    }
505
0
  }
506
0
507
0
  if (mAvailableApplicationCache) {
508
0
    return OfflineResourceList_Binding::UPDATEREADY;
509
0
  }
510
0
511
0
  return mStatus;
512
0
}
513
514
void
515
nsDOMOfflineResourceList::Update(ErrorResult& aRv)
516
0
{
517
0
  nsresult rv = Init();
518
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
519
0
    aRv.Throw(rv);
520
0
    return;
521
0
  }
522
0
523
0
  if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
524
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
525
0
    return;
526
0
  }
527
0
528
0
  nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
529
0
    do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &rv);
530
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
531
0
    aRv.Throw(rv);
532
0
    return;
533
0
  }
534
0
535
0
  nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
536
0
537
0
  nsCOMPtr<nsIOfflineCacheUpdate> update;
538
0
  rv = updateService->ScheduleUpdate(mManifestURI, mDocumentURI, mLoadingPrincipal,
539
0
                                     window, getter_AddRefs(update));
540
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
541
0
    aRv.Throw(rv);
542
0
    return;
543
0
  }
544
0
}
545
546
void
547
nsDOMOfflineResourceList::SwapCache(ErrorResult& aRv)
548
0
{
549
0
  nsresult rv = Init();
550
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
551
0
    aRv.Throw(rv);
552
0
    return;
553
0
  }
554
0
555
0
  if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
556
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
557
0
    return;
558
0
  }
559
0
560
0
  nsCOMPtr<nsIApplicationCache> currentAppCache = GetDocumentAppCache();
561
0
  if (!currentAppCache) {
562
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
563
0
    return;
564
0
  }
565
0
566
0
  // Check the current and potentially newly available cache are not identical.
567
0
  if (mAvailableApplicationCache == currentAppCache) {
568
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
569
0
    return;
570
0
  }
571
0
572
0
  if (mAvailableApplicationCache) {
573
0
    nsCString currClientId, availClientId;
574
0
    currentAppCache->GetClientID(currClientId);
575
0
    mAvailableApplicationCache->GetClientID(availClientId);
576
0
    if (availClientId == currClientId) {
577
0
      aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
578
0
      return;
579
0
    }
580
0
  } else if (mStatus != OfflineResourceList_Binding::OBSOLETE) {
581
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
582
0
    return;
583
0
  }
584
0
585
0
  ClearCachedKeys();
586
0
587
0
  nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
588
0
    GetDocumentAppCacheContainer();
589
0
590
0
  // In the case of an obsolete cache group, newAppCache might be null.
591
0
  // We will disassociate from the cache in that case.
592
0
  if (appCacheContainer) {
593
0
    rv = appCacheContainer->SetApplicationCache(mAvailableApplicationCache);
594
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
595
0
      aRv.Throw(rv);
596
0
      return;
597
0
    }
598
0
  }
599
0
600
0
  mAvailableApplicationCache = nullptr;
601
0
  mStatus = OfflineResourceList_Binding::IDLE;
602
0
}
603
604
void
605
nsDOMOfflineResourceList::FirePendingEvents()
606
0
{
607
0
  for (int32_t i = 0; i < mPendingEvents.Count(); ++i) {
608
0
    RefPtr<Event> event = mPendingEvents[i];
609
0
    DispatchEvent(*event);
610
0
  }
611
0
  mPendingEvents.Clear();
612
0
}
613
614
nsresult
615
nsDOMOfflineResourceList::SendEvent(const nsAString &aEventName)
616
0
{
617
0
  // Don't send events to closed windows
618
0
  if (!GetOwner()) {
619
0
    return NS_OK;
620
0
  }
621
0
622
0
  if (!GetOwner()->GetDocShell()) {
623
0
    return NS_OK;
624
0
  }
625
0
626
0
  RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
627
0
  event->InitEvent(aEventName, false, true);
628
0
629
0
  // We assume anyone that managed to call SendEvent is trusted
630
0
  event->SetTrusted(true);
631
0
632
0
  // If the window is frozen or we're still catching up on events that were
633
0
  // queued while frozen, save the event for later.
634
0
  if (GetOwner()->IsFrozen() || mPendingEvents.Count() > 0) {
635
0
    mPendingEvents.AppendObject(event);
636
0
    return NS_OK;
637
0
  }
638
0
639
0
  DispatchEvent(*event);
640
0
641
0
  return NS_OK;
642
0
}
643
644
645
//
646
// nsDOMOfflineResourceList::nsIObserver
647
//
648
NS_IMETHODIMP
649
nsDOMOfflineResourceList::Observe(nsISupports *aSubject,
650
                                    const char *aTopic,
651
                                    const char16_t *aData)
652
0
{
653
0
  if (!strcmp(aTopic, "offline-cache-update-added")) {
654
0
    nsCOMPtr<nsIOfflineCacheUpdate> update = do_QueryInterface(aSubject);
655
0
    if (update) {
656
0
      UpdateAdded(update);
657
0
    }
658
0
  } else if (!strcmp(aTopic, "offline-cache-update-completed")) {
659
0
    nsCOMPtr<nsIOfflineCacheUpdate> update = do_QueryInterface(aSubject);
660
0
    if (update) {
661
0
      UpdateCompleted(update);
662
0
    }
663
0
  }
664
0
665
0
  return NS_OK;
666
0
}
667
668
//
669
// nsDOMOfflineResourceList::nsIOfflineCacheUpdateObserver
670
//
671
NS_IMETHODIMP
672
nsDOMOfflineResourceList::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate,
673
                                     uint32_t event)
674
0
{
675
0
  mExposeCacheUpdateStatus =
676
0
      (event == STATE_CHECKING) ||
677
0
      (event == STATE_DOWNLOADING) ||
678
0
      (event == STATE_ITEMSTARTED) ||
679
0
      (event == STATE_ITEMCOMPLETED) ||
680
0
      // During notification of "obsolete" we must expose state of the update
681
0
      (event == STATE_OBSOLETE);
682
0
683
0
  switch (event) {
684
0
    case STATE_ERROR:
685
0
      SendEvent(NS_LITERAL_STRING(ERROR_STR));
686
0
      break;
687
0
    case STATE_CHECKING:
688
0
      SendEvent(NS_LITERAL_STRING(CHECKING_STR));
689
0
      break;
690
0
    case STATE_NOUPDATE:
691
0
      SendEvent(NS_LITERAL_STRING(NOUPDATE_STR));
692
0
      break;
693
0
    case STATE_OBSOLETE:
694
0
      mStatus = OfflineResourceList_Binding::OBSOLETE;
695
0
      mAvailableApplicationCache = nullptr;
696
0
      SendEvent(NS_LITERAL_STRING(OBSOLETE_STR));
697
0
      break;
698
0
    case STATE_DOWNLOADING:
699
0
      SendEvent(NS_LITERAL_STRING(DOWNLOADING_STR));
700
0
      break;
701
0
    case STATE_ITEMSTARTED:
702
0
      SendEvent(NS_LITERAL_STRING(PROGRESS_STR));
703
0
      break;
704
0
    case STATE_ITEMCOMPLETED:
705
0
      // Nothing to do here...
706
0
      break;
707
0
  }
708
0
709
0
  return NS_OK;
710
0
}
711
712
NS_IMETHODIMP
713
nsDOMOfflineResourceList::ApplicationCacheAvailable(nsIApplicationCache *aApplicationCache)
714
0
{
715
0
  nsCOMPtr<nsIApplicationCache> currentAppCache = GetDocumentAppCache();
716
0
  if (currentAppCache) {
717
0
    // Document already has a cache, we cannot override it.  swapCache is
718
0
    // here to do it on demand.
719
0
720
0
    // If the newly available cache is identical to the current cache, then
721
0
    // just ignore this event.
722
0
    if (aApplicationCache == currentAppCache) {
723
0
      return NS_OK;
724
0
    }
725
0
726
0
    nsCString currClientId, availClientId;
727
0
    currentAppCache->GetClientID(currClientId);
728
0
    aApplicationCache->GetClientID(availClientId);
729
0
    if (availClientId == currClientId) {
730
0
      return NS_OK;
731
0
    }
732
0
733
0
    mAvailableApplicationCache = aApplicationCache;
734
0
    return NS_OK;
735
0
  }
736
0
737
0
  nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
738
0
    GetDocumentAppCacheContainer();
739
0
740
0
  if (appCacheContainer) {
741
0
    appCacheContainer->SetApplicationCache(aApplicationCache);
742
0
  }
743
0
744
0
  mAvailableApplicationCache = nullptr;
745
0
  return NS_OK;
746
0
}
747
748
nsresult
749
nsDOMOfflineResourceList::GetCacheKey(const nsAString &aURI, nsCString &aKey)
750
0
{
751
0
  nsCOMPtr<nsIURI> requestedURI;
752
0
  nsresult rv = NS_NewURI(getter_AddRefs(requestedURI), aURI);
753
0
  NS_ENSURE_SUCCESS(rv, rv);
754
0
755
0
  return GetCacheKey(requestedURI, aKey);
756
0
}
757
758
nsresult
759
nsDOMOfflineResourceList::UpdateAdded(nsIOfflineCacheUpdate *aUpdate)
760
0
{
761
0
  // Ignore partial updates.
762
0
  bool partial;
763
0
  nsresult rv = aUpdate->GetPartial(&partial);
764
0
  NS_ENSURE_SUCCESS(rv, rv);
765
0
766
0
  if (partial) {
767
0
    return NS_OK;
768
0
  }
769
0
770
0
  nsCOMPtr<nsIURI> updateURI;
771
0
  rv = aUpdate->GetManifestURI(getter_AddRefs(updateURI));
772
0
  NS_ENSURE_SUCCESS(rv, rv);
773
0
774
0
  bool equals;
775
0
  rv = updateURI->Equals(mManifestURI, &equals);
776
0
  NS_ENSURE_SUCCESS(rv, rv);
777
0
778
0
  if (!equals) {
779
0
    // This update doesn't belong to us
780
0
    return NS_OK;
781
0
  }
782
0
783
0
  NS_ENSURE_TRUE(!mCacheUpdate, NS_ERROR_FAILURE);
784
0
785
0
  // We don't need to emit signals here.  Updates are either added
786
0
  // when they are scheduled (in which case they are always IDLE) or
787
0
  // they are added when the applicationCache object is initialized, so there
788
0
  // are no listeners to accept signals anyway.
789
0
790
0
  mCacheUpdate = aUpdate;
791
0
  mCacheUpdate->AddObserver(this, true);
792
0
793
0
  return NS_OK;
794
0
}
795
796
already_AddRefed<nsIApplicationCacheContainer>
797
nsDOMOfflineResourceList::GetDocumentAppCacheContainer()
798
0
{
799
0
  nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(GetOwner());
800
0
  if (!webnav) {
801
0
    return nullptr;
802
0
  }
803
0
804
0
  nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
805
0
    do_GetInterface(webnav);
806
0
  return appCacheContainer.forget();
807
0
}
808
809
already_AddRefed<nsIApplicationCache>
810
nsDOMOfflineResourceList::GetDocumentAppCache()
811
0
{
812
0
  nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
813
0
    GetDocumentAppCacheContainer();
814
0
815
0
  if (appCacheContainer) {
816
0
    nsCOMPtr<nsIApplicationCache> applicationCache;
817
0
    appCacheContainer->GetApplicationCache(
818
0
      getter_AddRefs(applicationCache));
819
0
    return applicationCache.forget();
820
0
  }
821
0
822
0
  return nullptr;
823
0
}
824
825
nsresult
826
nsDOMOfflineResourceList::UpdateCompleted(nsIOfflineCacheUpdate *aUpdate)
827
0
{
828
0
  if (aUpdate != mCacheUpdate) {
829
0
    // This isn't the update we're watching.
830
0
    return NS_OK;
831
0
  }
832
0
833
0
  bool partial;
834
0
  mCacheUpdate->GetPartial(&partial);
835
0
  bool isUpgrade;
836
0
  mCacheUpdate->GetIsUpgrade(&isUpgrade);
837
0
838
0
  bool succeeded;
839
0
  nsresult rv = mCacheUpdate->GetSucceeded(&succeeded);
840
0
841
0
  mCacheUpdate->RemoveObserver(this);
842
0
  mCacheUpdate = nullptr;
843
0
844
0
  if (NS_SUCCEEDED(rv) && succeeded && !partial) {
845
0
    mStatus = OfflineResourceList_Binding::IDLE;
846
0
    if (isUpgrade) {
847
0
      SendEvent(NS_LITERAL_STRING(UPDATEREADY_STR));
848
0
    } else {
849
0
      SendEvent(NS_LITERAL_STRING(CACHED_STR));
850
0
    }
851
0
  }
852
0
853
0
  return NS_OK;
854
0
}
855
856
nsresult
857
nsDOMOfflineResourceList::GetCacheKey(nsIURI *aURI, nsCString &aKey)
858
0
{
859
0
  nsresult rv = aURI->GetAsciiSpec(aKey);
860
0
  NS_ENSURE_SUCCESS(rv, rv);
861
0
862
0
  // url fragments aren't used in cache keys
863
0
  nsAutoCString::const_iterator specStart, specEnd;
864
0
  aKey.BeginReading(specStart);
865
0
  aKey.EndReading(specEnd);
866
0
  if (FindCharInReadable('#', specStart, specEnd)) {
867
0
    aKey.BeginReading(specEnd);
868
0
    aKey = Substring(specEnd, specStart);
869
0
  }
870
0
871
0
  return NS_OK;
872
0
}
873
874
nsresult
875
nsDOMOfflineResourceList::CacheKeys()
876
0
{
877
0
  if (IS_CHILD_PROCESS())
878
0
    return NS_ERROR_NOT_IMPLEMENTED;
879
0
880
0
  if (mCachedKeys)
881
0
    return NS_OK;
882
0
883
0
  nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(GetOwner());
884
0
  nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window);
885
0
  nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
886
0
887
0
  nsAutoCString originSuffix;
888
0
  if (loadContext) {
889
0
    mozilla::OriginAttributes oa;
890
0
    loadContext->GetOriginAttributes(oa);
891
0
892
0
    oa.CreateSuffix(originSuffix);
893
0
  }
894
0
895
0
  nsAutoCString groupID;
896
0
  mApplicationCacheService->BuildGroupIDForSuffix(
897
0
      mManifestURI, originSuffix, groupID);
898
0
899
0
  nsCOMPtr<nsIApplicationCache> appCache;
900
0
  mApplicationCacheService->GetActiveCache(groupID,
901
0
                                           getter_AddRefs(appCache));
902
0
903
0
  if (!appCache) {
904
0
    return NS_ERROR_DOM_INVALID_STATE_ERR;
905
0
  }
906
0
907
0
  return appCache->GatherEntries(nsIApplicationCache::ITEM_DYNAMIC,
908
0
                                 &mCachedKeysCount, &mCachedKeys);
909
0
}
910
911
void
912
nsDOMOfflineResourceList::ClearCachedKeys()
913
0
{
914
0
  if (mCachedKeys) {
915
0
    NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mCachedKeysCount, mCachedKeys);
916
0
    mCachedKeys = nullptr;
917
0
    mCachedKeysCount = 0;
918
0
  }
919
0
}