Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/cache/Manager.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 "mozilla/dom/cache/Manager.h"
8
9
#include "mozilla/AutoRestore.h"
10
#include "mozilla/Mutex.h"
11
#include "mozilla/StaticMutex.h"
12
#include "mozilla/StaticPtr.h"
13
#include "mozilla/Unused.h"
14
#include "mozilla/dom/cache/Context.h"
15
#include "mozilla/dom/cache/DBAction.h"
16
#include "mozilla/dom/cache/DBSchema.h"
17
#include "mozilla/dom/cache/FileUtils.h"
18
#include "mozilla/dom/cache/ManagerId.h"
19
#include "mozilla/dom/cache/CacheTypes.h"
20
#include "mozilla/dom/cache/SavedTypes.h"
21
#include "mozilla/dom/cache/StreamList.h"
22
#include "mozilla/dom/cache/Types.h"
23
#include "mozilla/dom/cache/QuotaClient.h"
24
#include "mozilla/ipc/BackgroundParent.h"
25
#include "mozStorageHelper.h"
26
#include "nsIInputStream.h"
27
#include "nsID.h"
28
#include "nsIFile.h"
29
#include "nsIThread.h"
30
#include "nsThreadUtils.h"
31
#include "nsTObserverArray.h"
32
33
34
namespace mozilla {
35
namespace dom {
36
namespace cache {
37
38
namespace {
39
40
// An Action that is executed when a Context is first created.  It ensures that
41
// the directory and database are setup properly.  This lets other actions
42
// not worry about these details.
43
class SetupAction final : public SyncDBAction
44
{
45
public:
46
  SetupAction()
47
    : SyncDBAction(DBAction::Create)
48
0
  { }
49
50
  virtual nsresult
51
  RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
52
                        mozIStorageConnection* aConn) override
53
0
  {
54
0
    MOZ_DIAGNOSTIC_ASSERT(aDBDir);
55
0
56
0
    nsresult rv = BodyCreateDir(aDBDir);
57
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
58
0
59
0
    // executes in its own transaction
60
0
    rv = db::CreateOrMigrateSchema(aConn);
61
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
62
0
63
0
    // If the Context marker file exists, then the last session was
64
0
    // not cleanly shutdown.  In these cases sqlite will ensure that
65
0
    // the database is valid, but we might still orphan data.  Both
66
0
    // Cache objects and body files can be referenced by DOM objects
67
0
    // after they are "removed" from their parent.  So we need to
68
0
    // look and see if any of these late access objects have been
69
0
    // orphaned.
70
0
    //
71
0
    // Note, this must be done after any schema version updates to
72
0
    // ensure our DBSchema methods work correctly.
73
0
    if (MarkerFileExists(aQuotaInfo)) {
74
0
      NS_WARNING("Cache not shutdown cleanly! Cleaning up stale data...");
75
0
      mozStorageTransaction trans(aConn, false,
76
0
                                  mozIStorageConnection::TRANSACTION_IMMEDIATE);
77
0
78
0
      // Clean up orphaned Cache objects
79
0
      AutoTArray<CacheId, 8> orphanedCacheIdList;
80
0
      nsresult rv = db::FindOrphanedCacheIds(aConn, orphanedCacheIdList);
81
0
      if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
82
0
83
0
      int64_t overallDeletedPaddingSize = 0;
84
0
      for (uint32_t i = 0; i < orphanedCacheIdList.Length(); ++i) {
85
0
        AutoTArray<nsID, 16> deletedBodyIdList;
86
0
        int64_t deletedPaddingSize = 0;
87
0
        rv = db::DeleteCacheId(aConn, orphanedCacheIdList[i], deletedBodyIdList,
88
0
                               &deletedPaddingSize);
89
0
        if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
90
0
91
0
        rv = BodyDeleteFiles(aQuotaInfo, aDBDir, deletedBodyIdList);
92
0
        if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
93
0
94
0
        if (deletedPaddingSize > 0) {
95
0
          DecreaseUsageForQuotaInfo(aQuotaInfo, deletedPaddingSize);
96
0
        }
97
0
98
0
        MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - deletedPaddingSize >=
99
0
                              overallDeletedPaddingSize);
100
0
        overallDeletedPaddingSize += deletedPaddingSize;
101
0
      }
102
0
103
0
      // Clean up orphaned body objects
104
0
      AutoTArray<nsID, 64> knownBodyIdList;
105
0
      rv = db::GetKnownBodyIds(aConn, knownBodyIdList);
106
0
107
0
      rv = BodyDeleteOrphanedFiles(aQuotaInfo, aDBDir, knownBodyIdList);
108
0
      if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
109
0
110
0
      // Commit() explicitly here, because we want to ensure the padding file
111
0
      // has the correct content.
112
0
      rv = MaybeUpdatePaddingFile(aDBDir, aConn, /* aIncreaceSize */ 0,
113
0
                                  overallDeletedPaddingSize,
114
0
                                  [&trans]() mutable { return trans.Commit(); });
115
0
      // We'll restore padding file below, so just warn here if failure happens.
116
0
      Unused << NS_WARN_IF(NS_FAILED(rv));
117
0
    }
118
0
119
0
    if (DirectoryPaddingFileExists(aDBDir, DirPaddingFile::TMP_FILE) ||
120
0
        !DirectoryPaddingFileExists(aDBDir, DirPaddingFile::FILE)) {
121
0
      rv = RestorePaddingFile(aDBDir, aConn);
122
0
      if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
123
0
    }
124
0
125
0
    return rv;
126
0
  }
127
};
128
129
// ----------------------------------------------------------------------------
130
131
// Action that is executed when we determine that content has stopped using
132
// a body file that has been orphaned.
133
class DeleteOrphanedBodyAction final : public Action
134
{
135
public:
136
  explicit DeleteOrphanedBodyAction(const nsTArray<nsID>& aDeletedBodyIdList)
137
    : mDeletedBodyIdList(aDeletedBodyIdList)
138
0
  { }
139
140
  explicit DeleteOrphanedBodyAction(const nsID& aBodyId)
141
0
  {
142
0
    mDeletedBodyIdList.AppendElement(aBodyId);
143
0
  }
144
145
  virtual void
146
  RunOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo, Data*) override
147
0
  {
148
0
    MOZ_DIAGNOSTIC_ASSERT(aResolver);
149
0
    MOZ_DIAGNOSTIC_ASSERT(aQuotaInfo.mDir);
150
0
151
0
    // Note that since DeleteOrphanedBodyAction isn't used while the context is
152
0
    // being initialized, we don't need to check for cancellation here.
153
0
154
0
    nsCOMPtr<nsIFile> dbDir;
155
0
    nsresult rv = aQuotaInfo.mDir->Clone(getter_AddRefs(dbDir));
156
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
157
0
      aResolver->Resolve(rv);
158
0
      return;
159
0
    }
160
0
161
0
    rv = dbDir->Append(NS_LITERAL_STRING("cache"));
162
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
163
0
      aResolver->Resolve(rv);
164
0
      return;
165
0
    }
166
0
167
0
    rv = BodyDeleteFiles(aQuotaInfo, dbDir, mDeletedBodyIdList);
168
0
    Unused << NS_WARN_IF(NS_FAILED(rv));
169
0
170
0
    aResolver->Resolve(rv);
171
0
  }
172
173
private:
174
  nsTArray<nsID> mDeletedBodyIdList;
175
};
176
177
bool IsHeadRequest(const CacheRequest& aRequest, const CacheQueryParams& aParams)
178
0
{
179
0
  return !aParams.ignoreMethod() && aRequest.method().LowerCaseEqualsLiteral("head");
180
0
}
181
182
bool IsHeadRequest(const CacheRequestOrVoid& aRequest, const CacheQueryParams& aParams)
183
0
{
184
0
  if (aRequest.type() == CacheRequestOrVoid::TCacheRequest) {
185
0
    return !aParams.ignoreMethod() &&
186
0
           aRequest.get_CacheRequest().method().LowerCaseEqualsLiteral("head");
187
0
  }
188
0
  return false;
189
0
}
190
191
} // namespace
192
193
// ----------------------------------------------------------------------------
194
195
// Singleton class to track Manager instances and ensure there is only
196
// one for each unique ManagerId.
197
class Manager::Factory
198
{
199
public:
200
  friend class StaticAutoPtr<Manager::Factory>;
201
202
  static nsresult
203
  GetOrCreate(ManagerId* aManagerId, Manager** aManagerOut)
204
0
  {
205
0
    mozilla::ipc::AssertIsOnBackgroundThread();
206
0
207
0
    // Ensure there is a factory instance.  This forces the Get() call
208
0
    // below to use the same factory.
209
0
    nsresult rv = MaybeCreateInstance();
210
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
211
0
212
0
    RefPtr<Manager> ref = Get(aManagerId);
213
0
    if (!ref) {
214
0
      // TODO: replace this with a thread pool (bug 1119864)
215
0
      nsCOMPtr<nsIThread> ioThread;
216
0
      rv = NS_NewNamedThread("DOMCacheThread", getter_AddRefs(ioThread));
217
0
      if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
218
0
219
0
      ref = new Manager(aManagerId, ioThread);
220
0
221
0
      // There may be an old manager for this origin in the process of
222
0
      // cleaning up.  We need to tell the new manager about this so
223
0
      // that it won't actually start until the old manager is done.
224
0
      RefPtr<Manager> oldManager = Get(aManagerId, Closing);
225
0
      ref->Init(oldManager);
226
0
227
0
      MOZ_ASSERT(!sFactory->mManagerList.Contains(ref));
228
0
      sFactory->mManagerList.AppendElement(ref);
229
0
    }
230
0
231
0
    ref.forget(aManagerOut);
232
0
233
0
    return NS_OK;
234
0
  }
235
236
  static already_AddRefed<Manager>
237
  Get(ManagerId* aManagerId, State aState = Open)
238
0
  {
239
0
    mozilla::ipc::AssertIsOnBackgroundThread();
240
0
241
0
    nsresult rv = MaybeCreateInstance();
242
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; }
243
0
244
0
    // Iterate in reverse to find the most recent, matching Manager.  This
245
0
    // is important when looking for a Closing Manager.  If a new Manager
246
0
    // chains to an old Manager we want it to be the most recent one.
247
0
    ManagerList::BackwardIterator iter(sFactory->mManagerList);
248
0
    while (iter.HasMore()) {
249
0
      RefPtr<Manager> manager = iter.GetNext();
250
0
      if (aState == manager->GetState() && *manager->mManagerId == *aManagerId) {
251
0
        return manager.forget();
252
0
      }
253
0
    }
254
0
255
0
    return nullptr;
256
0
  }
257
258
  static void
259
  Remove(Manager* aManager)
260
0
  {
261
0
    mozilla::ipc::AssertIsOnBackgroundThread();
262
0
    MOZ_DIAGNOSTIC_ASSERT(aManager);
263
0
    MOZ_DIAGNOSTIC_ASSERT(sFactory);
264
0
265
0
    MOZ_ALWAYS_TRUE(sFactory->mManagerList.RemoveElement(aManager));
266
0
267
0
    // clean up the factory singleton if there are no more managers
268
0
    MaybeDestroyInstance();
269
0
  }
270
271
  static void
272
  Abort(const nsACString& aOrigin)
273
0
  {
274
0
    mozilla::ipc::AssertIsOnBackgroundThread();
275
0
276
0
    if (!sFactory) {
277
0
      return;
278
0
    }
279
0
280
0
    MOZ_DIAGNOSTIC_ASSERT(!sFactory->mManagerList.IsEmpty());
281
0
282
0
    {
283
0
      ManagerList::ForwardIterator iter(sFactory->mManagerList);
284
0
      while (iter.HasMore()) {
285
0
        RefPtr<Manager> manager = iter.GetNext();
286
0
        if (aOrigin.IsVoid() ||
287
0
            manager->mManagerId->QuotaOrigin() == aOrigin) {
288
0
          manager->Abort();
289
0
        }
290
0
      }
291
0
    }
292
0
  }
293
294
  static void
295
  ShutdownAll()
296
0
  {
297
0
    mozilla::ipc::AssertIsOnBackgroundThread();
298
0
299
0
    if (!sFactory) {
300
0
      return;
301
0
    }
302
0
303
0
    MOZ_DIAGNOSTIC_ASSERT(!sFactory->mManagerList.IsEmpty());
304
0
305
0
    {
306
0
      // Note that we are synchronously calling shutdown code here.  If any
307
0
      // of the shutdown code synchronously decides to delete the Factory
308
0
      // we need to delay that delete until the end of this method.
309
0
      AutoRestore<bool> restore(sFactory->mInSyncShutdown);
310
0
      sFactory->mInSyncShutdown = true;
311
0
312
0
      ManagerList::ForwardIterator iter(sFactory->mManagerList);
313
0
      while (iter.HasMore()) {
314
0
        RefPtr<Manager> manager = iter.GetNext();
315
0
        manager->Shutdown();
316
0
      }
317
0
    }
318
0
319
0
    MaybeDestroyInstance();
320
0
  }
321
322
  static bool
323
  IsShutdownAllComplete()
324
0
  {
325
0
    mozilla::ipc::AssertIsOnBackgroundThread();
326
0
    return !sFactory;
327
0
  }
328
329
private:
330
  Factory()
331
    : mInSyncShutdown(false)
332
0
  {
333
0
    MOZ_COUNT_CTOR(cache::Manager::Factory);
334
0
  }
335
336
  ~Factory()
337
0
  {
338
0
    MOZ_COUNT_DTOR(cache::Manager::Factory);
339
0
    MOZ_DIAGNOSTIC_ASSERT(mManagerList.IsEmpty());
340
0
    MOZ_DIAGNOSTIC_ASSERT(!mInSyncShutdown);
341
0
  }
342
343
  static nsresult
344
  MaybeCreateInstance()
345
0
  {
346
0
    mozilla::ipc::AssertIsOnBackgroundThread();
347
0
348
0
    if (!sFactory) {
349
0
      // Be clear about what we are locking.  sFactory is bg thread only, so
350
0
      // we don't need to lock it here.  Just protect sFactoryShutdown and
351
0
      // sBackgroundThread.
352
0
      {
353
0
        StaticMutexAutoLock lock(sMutex);
354
0
355
0
        if (sFactoryShutdown) {
356
0
          return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
357
0
        }
358
0
      }
359
0
360
0
      // We cannot use ClearOnShutdown() here because we're not on the main
361
0
      // thread.  Instead, we delete sFactory in Factory::Remove() after the
362
0
      // last manager is removed.  ShutdownObserver ensures this happens
363
0
      // before shutdown.
364
0
      sFactory = new Factory();
365
0
    }
366
0
367
0
    // Never return sFactory to code outside Factory.  We need to delete it
368
0
    // out from under ourselves just before we return from Remove().  This
369
0
    // would be (even more) dangerous if other code had a pointer to the
370
0
    // factory itself.
371
0
372
0
    return NS_OK;
373
0
  }
374
375
  static void
376
  MaybeDestroyInstance()
377
0
  {
378
0
    mozilla::ipc::AssertIsOnBackgroundThread();
379
0
    MOZ_DIAGNOSTIC_ASSERT(sFactory);
380
0
381
0
    // If the factory is is still in use then we cannot delete yet.  This
382
0
    // could be due to managers still existing or because we are in the
383
0
    // middle of shutting down.  We need to be careful not to delete ourself
384
0
    // synchronously during shutdown.
385
0
    if (!sFactory->mManagerList.IsEmpty() || sFactory->mInSyncShutdown) {
386
0
      return;
387
0
    }
388
0
389
0
    sFactory = nullptr;
390
0
  }
391
392
  // Singleton created on demand and deleted when last Manager is cleared
393
  // in Remove().
394
  // PBackground thread only.
395
  static StaticAutoPtr<Factory> sFactory;
396
397
  // protects following static attribute
398
  static StaticMutex sMutex;
399
400
  // Indicate if shutdown has occurred to block re-creation of sFactory.
401
  // Must hold sMutex to access.
402
  static bool sFactoryShutdown;
403
404
  // Weak references as we don't want to keep Manager objects alive forever.
405
  // When a Manager is destroyed it calls Factory::Remove() to clear itself.
406
  // PBackground thread only.
407
  typedef nsTObserverArray<Manager*> ManagerList;
408
  ManagerList mManagerList;
409
410
  // This flag is set when we are looping through the list and calling
411
  // Shutdown() on each Manager.  We need to be careful not to synchronously
412
  // trigger the deletion of the factory while still executing this loop.
413
  bool mInSyncShutdown;
414
};
415
416
// static
417
StaticAutoPtr<Manager::Factory> Manager::Factory::sFactory;
418
419
// static
420
StaticMutex Manager::Factory::sMutex;
421
422
// static
423
bool Manager::Factory::sFactoryShutdown = false;
424
425
// ----------------------------------------------------------------------------
426
427
// Abstract class to help implement the various Actions.  The vast majority
428
// of Actions are synchronous and need to report back to a Listener on the
429
// Manager.
430
class Manager::BaseAction : public SyncDBAction
431
{
432
protected:
433
  BaseAction(Manager* aManager, ListenerId aListenerId)
434
    : SyncDBAction(DBAction::Existing)
435
    , mManager(aManager)
436
    , mListenerId(aListenerId)
437
0
  {
438
0
  }
439
440
  virtual void
441
  Complete(Listener* aListener, ErrorResult&& aRv) = 0;
442
443
  virtual void
444
  CompleteOnInitiatingThread(nsresult aRv) override
445
0
  {
446
0
    NS_ASSERT_OWNINGTHREAD(Manager::BaseAction);
447
0
    Listener* listener = mManager->GetListener(mListenerId);
448
0
    if (listener) {
449
0
      Complete(listener, ErrorResult(aRv));
450
0
    }
451
0
452
0
    // ensure we release the manager on the initiating thread
453
0
    mManager = nullptr;
454
0
  }
455
456
  RefPtr<Manager> mManager;
457
  const ListenerId mListenerId;
458
};
459
460
// ----------------------------------------------------------------------------
461
462
// Action that is executed when we determine that content has stopped using
463
// a Cache object that has been orphaned.
464
class Manager::DeleteOrphanedCacheAction final : public SyncDBAction
465
{
466
public:
467
  DeleteOrphanedCacheAction(Manager* aManager, CacheId aCacheId)
468
    : SyncDBAction(DBAction::Existing)
469
    , mManager(aManager)
470
    , mCacheId(aCacheId)
471
    , mDeletedPaddingSize(0)
472
0
  { }
473
474
  virtual nsresult
475
  RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
476
                        mozIStorageConnection* aConn) override
477
0
  {
478
0
    mQuotaInfo.emplace(aQuotaInfo);
479
0
480
0
    mozStorageTransaction trans(aConn, false,
481
0
                                mozIStorageConnection::TRANSACTION_IMMEDIATE);
482
0
483
0
    nsresult rv = db::DeleteCacheId(aConn, mCacheId, mDeletedBodyIdList,
484
0
                                    &mDeletedPaddingSize);
485
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
486
0
487
0
    rv = MaybeUpdatePaddingFile(aDBDir, aConn, /* aIncreaceSize */ 0,
488
0
                                mDeletedPaddingSize,
489
0
                                [&trans]() mutable { return trans.Commit(); });
490
0
    Unused << NS_WARN_IF(NS_FAILED(rv));
491
0
492
0
    return rv;
493
0
  }
494
495
  virtual void
496
  CompleteOnInitiatingThread(nsresult aRv) override
497
0
  {
498
0
    // If the transaction fails, we shouldn't delete the body files and decrease
499
0
    // their padding size.
500
0
    if (NS_FAILED(aRv)) {
501
0
      mDeletedBodyIdList.Clear();
502
0
      mDeletedPaddingSize = 0;
503
0
    }
504
0
505
0
    mManager->NoteOrphanedBodyIdList(mDeletedBodyIdList);
506
0
507
0
    if (mDeletedPaddingSize > 0) {
508
0
      DecreaseUsageForQuotaInfo(mQuotaInfo.ref(), mDeletedPaddingSize);
509
0
    }
510
0
511
0
    // ensure we release the manager on the initiating thread
512
0
    mManager = nullptr;
513
0
  }
514
515
private:
516
  RefPtr<Manager> mManager;
517
  const CacheId mCacheId;
518
  nsTArray<nsID> mDeletedBodyIdList;
519
  Maybe<QuotaInfo> mQuotaInfo;
520
  // Track any pad amount associated with orphaned entries.
521
  int64_t mDeletedPaddingSize;
522
};
523
524
// ----------------------------------------------------------------------------
525
526
class Manager::CacheMatchAction final : public Manager::BaseAction
527
{
528
public:
529
  CacheMatchAction(Manager* aManager, ListenerId aListenerId,
530
                   CacheId aCacheId, const CacheMatchArgs& aArgs,
531
                   StreamList* aStreamList)
532
    : BaseAction(aManager, aListenerId)
533
    , mCacheId(aCacheId)
534
    , mArgs(aArgs)
535
    , mStreamList(aStreamList)
536
    , mFoundResponse(false)
537
0
  { }
538
539
  virtual nsresult
540
  RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
541
                        mozIStorageConnection* aConn) override
542
0
  {
543
0
    nsresult rv = db::CacheMatch(aConn, mCacheId, mArgs.request(),
544
0
                                 mArgs.params(), &mFoundResponse, &mResponse);
545
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
546
0
547
0
    if (!mFoundResponse || !mResponse.mHasBodyId
548
0
                        || IsHeadRequest(mArgs.request(), mArgs.params())) {
549
0
      mResponse.mHasBodyId = false;
550
0
      return rv;
551
0
    }
552
0
553
0
    nsCOMPtr<nsIInputStream> stream;
554
0
    if (mArgs.openMode() == OpenMode::Eager) {
555
0
      rv = BodyOpen(aQuotaInfo, aDBDir, mResponse.mBodyId, getter_AddRefs(stream));
556
0
      if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
557
0
      if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
558
0
    }
559
0
560
0
    mStreamList->Add(mResponse.mBodyId, std::move(stream));
561
0
562
0
    return rv;
563
0
  }
564
565
  virtual void
566
  Complete(Listener* aListener, ErrorResult&& aRv) override
567
0
  {
568
0
    if (!mFoundResponse) {
569
0
      aListener->OnOpComplete(std::move(aRv), CacheMatchResult(void_t()));
570
0
    } else {
571
0
      mStreamList->Activate(mCacheId);
572
0
      aListener->OnOpComplete(std::move(aRv), CacheMatchResult(void_t()), mResponse,
573
0
                              mStreamList);
574
0
    }
575
0
    mStreamList = nullptr;
576
0
  }
577
578
  virtual bool MatchesCacheId(CacheId aCacheId) const override
579
0
  {
580
0
    return aCacheId == mCacheId;
581
0
  }
582
583
private:
584
  const CacheId mCacheId;
585
  const CacheMatchArgs mArgs;
586
  RefPtr<StreamList> mStreamList;
587
  bool mFoundResponse;
588
  SavedResponse mResponse;
589
};
590
591
// ----------------------------------------------------------------------------
592
593
class Manager::CacheMatchAllAction final : public Manager::BaseAction
594
{
595
public:
596
  CacheMatchAllAction(Manager* aManager, ListenerId aListenerId,
597
                      CacheId aCacheId, const CacheMatchAllArgs& aArgs,
598
                      StreamList* aStreamList)
599
    : BaseAction(aManager, aListenerId)
600
    , mCacheId(aCacheId)
601
    , mArgs(aArgs)
602
    , mStreamList(aStreamList)
603
0
  { }
604
605
  virtual nsresult
606
  RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
607
                        mozIStorageConnection* aConn) override
608
0
  {
609
0
    nsresult rv = db::CacheMatchAll(aConn, mCacheId, mArgs.requestOrVoid(),
610
0
                                    mArgs.params(), mSavedResponses);
611
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
612
0
613
0
    for (uint32_t i = 0; i < mSavedResponses.Length(); ++i) {
614
0
      if (!mSavedResponses[i].mHasBodyId
615
0
          || IsHeadRequest(mArgs.requestOrVoid(), mArgs.params())) {
616
0
        mSavedResponses[i].mHasBodyId = false;
617
0
        continue;
618
0
      }
619
0
620
0
      nsCOMPtr<nsIInputStream> stream;
621
0
      if (mArgs.openMode() == OpenMode::Eager) {
622
0
        rv = BodyOpen(aQuotaInfo, aDBDir, mSavedResponses[i].mBodyId,
623
0
                      getter_AddRefs(stream));
624
0
        if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
625
0
        if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
626
0
      }
627
0
628
0
      mStreamList->Add(mSavedResponses[i].mBodyId, std::move(stream));
629
0
    }
630
0
631
0
    return rv;
632
0
  }
633
634
  virtual void
635
  Complete(Listener* aListener, ErrorResult&& aRv) override
636
0
  {
637
0
    mStreamList->Activate(mCacheId);
638
0
    aListener->OnOpComplete(std::move(aRv), CacheMatchAllResult(), mSavedResponses,
639
0
                            mStreamList);
640
0
    mStreamList = nullptr;
641
0
  }
642
643
  virtual bool MatchesCacheId(CacheId aCacheId) const override
644
0
  {
645
0
    return aCacheId == mCacheId;
646
0
  }
647
648
private:
649
  const CacheId mCacheId;
650
  const CacheMatchAllArgs mArgs;
651
  RefPtr<StreamList> mStreamList;
652
  nsTArray<SavedResponse> mSavedResponses;
653
};
654
655
// ----------------------------------------------------------------------------
656
657
// This is the most complex Action.  It puts a request/response pair into the
658
// Cache.  It does not complete until all of the body data has been saved to
659
// disk.  This means its an asynchronous Action.
660
class Manager::CachePutAllAction final : public DBAction
661
{
662
public:
663
  CachePutAllAction(Manager* aManager, ListenerId aListenerId,
664
                    CacheId aCacheId,
665
                    const nsTArray<CacheRequestResponse>& aPutList,
666
                    const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
667
                    const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList)
668
    : DBAction(DBAction::Existing)
669
    , mManager(aManager)
670
    , mListenerId(aListenerId)
671
    , mCacheId(aCacheId)
672
    , mList(aPutList.Length())
673
    , mExpectedAsyncCopyCompletions(1)
674
    , mAsyncResult(NS_OK)
675
    , mMutex("cache::Manager::CachePutAllAction")
676
    , mUpdatedPaddingSize(0)
677
    , mDeletedPaddingSize(0)
678
0
  {
679
0
    MOZ_DIAGNOSTIC_ASSERT(!aPutList.IsEmpty());
680
0
    MOZ_DIAGNOSTIC_ASSERT(aPutList.Length() == aRequestStreamList.Length());
681
0
    MOZ_DIAGNOSTIC_ASSERT(aPutList.Length() == aResponseStreamList.Length());
682
0
683
0
    for (uint32_t i = 0; i < aPutList.Length(); ++i) {
684
0
      Entry* entry = mList.AppendElement();
685
0
      entry->mRequest = aPutList[i].request();
686
0
      entry->mRequestStream = aRequestStreamList[i];
687
0
      entry->mResponse = aPutList[i].response();
688
0
      entry->mResponseStream = aResponseStreamList[i];
689
0
    }
690
0
  }
691
692
private:
693
0
  ~CachePutAllAction() { }
694
695
  virtual void
696
  RunWithDBOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo,
697
                    nsIFile* aDBDir, mozIStorageConnection* aConn) override
698
0
  {
699
0
    MOZ_DIAGNOSTIC_ASSERT(aResolver);
700
0
    MOZ_DIAGNOSTIC_ASSERT(aDBDir);
701
0
    MOZ_DIAGNOSTIC_ASSERT(aConn);
702
0
    MOZ_DIAGNOSTIC_ASSERT(!mResolver);
703
0
    MOZ_DIAGNOSTIC_ASSERT(!mDBDir);
704
0
    MOZ_DIAGNOSTIC_ASSERT(!mConn);
705
0
706
0
    MOZ_DIAGNOSTIC_ASSERT(!mTarget);
707
0
    mTarget = GetCurrentThreadSerialEventTarget();
708
0
    MOZ_DIAGNOSTIC_ASSERT(mTarget);
709
0
710
0
    // We should be pre-initialized to expect one async completion.  This is
711
0
    // the "manual" completion we call at the end of this method in all
712
0
    // cases.
713
0
    MOZ_DIAGNOSTIC_ASSERT(mExpectedAsyncCopyCompletions == 1);
714
0
715
0
    mResolver = aResolver;
716
0
    mDBDir = aDBDir;
717
0
    mConn = aConn;
718
0
    mQuotaInfo.emplace(aQuotaInfo);
719
0
720
0
    // File bodies are streamed to disk via asynchronous copying.  Start
721
0
    // this copying now.  Each copy will eventually result in a call
722
0
    // to OnAsyncCopyComplete().
723
0
    nsresult rv = NS_OK;
724
0
    for (uint32_t i = 0; i < mList.Length(); ++i) {
725
0
      rv = StartStreamCopy(aQuotaInfo, mList[i], RequestStream,
726
0
                           &mExpectedAsyncCopyCompletions);
727
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
728
0
        break;
729
0
      }
730
0
731
0
      rv = StartStreamCopy(aQuotaInfo, mList[i], ResponseStream,
732
0
                           &mExpectedAsyncCopyCompletions);
733
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
734
0
        break;
735
0
      }
736
0
    }
737
0
738
0
    // Always call OnAsyncCopyComplete() manually here.  This covers the
739
0
    // case where there is no async copying and also reports any startup
740
0
    // errors correctly.  If we hit an error, then OnAsyncCopyComplete()
741
0
    // will cancel any async copying.
742
0
    OnAsyncCopyComplete(rv);
743
0
  }
744
745
  // Called once for each asynchronous file copy whether it succeeds or
746
  // fails.  If a file copy is canceled, it still calls this method with
747
  // an error code.
748
  void
749
  OnAsyncCopyComplete(nsresult aRv)
750
0
  {
751
0
    MOZ_ASSERT(mTarget->IsOnCurrentThread());
752
0
    MOZ_DIAGNOSTIC_ASSERT(mConn);
753
0
    MOZ_DIAGNOSTIC_ASSERT(mResolver);
754
0
    MOZ_DIAGNOSTIC_ASSERT(mExpectedAsyncCopyCompletions > 0);
755
0
756
0
    // Explicitly check for cancellation here to catch a race condition.
757
0
    // Consider:
758
0
    //
759
0
    // 1) NS_AsyncCopy() executes on IO thread, but has not saved its
760
0
    //    copy context yet.
761
0
    // 2) CancelAllStreamCopying() occurs on PBackground thread
762
0
    // 3) Copy context from (1) is saved on IO thread.
763
0
    //
764
0
    // Checking for cancellation here catches this condition when we
765
0
    // first call OnAsyncCopyComplete() manually from RunWithDBOnTarget().
766
0
    //
767
0
    // This explicit cancellation check also handles the case where we
768
0
    // are canceled just after all stream copying completes.  We should
769
0
    // abort the synchronous DB operations in this case if we have not
770
0
    // started them yet.
771
0
    if (NS_SUCCEEDED(aRv) && IsCanceled()) {
772
0
      aRv = NS_ERROR_ABORT;
773
0
    }
774
0
775
0
    // If any of the async copies fail, we need to still wait for them all to
776
0
    // complete.  Cancel any other streams still working and remember the
777
0
    // error.  All canceled streams will call OnAsyncCopyComplete().
778
0
    if (NS_FAILED(aRv) && NS_SUCCEEDED(mAsyncResult)) {
779
0
      CancelAllStreamCopying();
780
0
      mAsyncResult = aRv;
781
0
    }
782
0
783
0
    // Check to see if async copying is still on-going.  If so, then simply
784
0
    // return for now.  We must wait for a later OnAsyncCopyComplete() call.
785
0
    mExpectedAsyncCopyCompletions -= 1;
786
0
    if (mExpectedAsyncCopyCompletions > 0) {
787
0
      return;
788
0
    }
789
0
790
0
    // We have finished with all async copying.  Indicate this by clearing all
791
0
    // our copy contexts.
792
0
    {
793
0
      MutexAutoLock lock(mMutex);
794
0
      mCopyContextList.Clear();
795
0
    }
796
0
797
0
    // An error occurred while async copying.  Terminate the Action.
798
0
    // DoResolve() will clean up any files we may have written.
799
0
    if (NS_FAILED(mAsyncResult)) {
800
0
      DoResolve(mAsyncResult);
801
0
      return;
802
0
    }
803
0
804
0
    mozStorageTransaction trans(mConn, false,
805
0
                                mozIStorageConnection::TRANSACTION_IMMEDIATE);
806
0
807
0
    nsresult rv = NS_OK;
808
0
    for (uint32_t i = 0; i < mList.Length(); ++i) {
809
0
      Entry& e = mList[i];
810
0
      if (e.mRequestStream) {
811
0
        rv = BodyFinalizeWrite(mDBDir, e.mRequestBodyId);
812
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
813
0
          DoResolve(rv);
814
0
          return;
815
0
        }
816
0
      }
817
0
      if (e.mResponseStream) {
818
0
        // Gerenate padding size for opaque response if needed.
819
0
        if (e.mResponse.type() == ResponseType::Opaque) {
820
0
          // It'll generate padding if we've not set it yet.
821
0
          rv = BodyMaybeUpdatePaddingSize(mQuotaInfo.ref(), mDBDir,
822
0
                                          e.mResponseBodyId,
823
0
                                          e.mResponse.paddingInfo(),
824
0
                                          &e.mResponse.paddingSize());
825
0
          if (NS_WARN_IF(NS_FAILED(rv))) {
826
0
            DoResolve(rv);
827
0
            return;
828
0
          }
829
0
830
0
          MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - e.mResponse.paddingSize() >=
831
0
                                mUpdatedPaddingSize);
832
0
          mUpdatedPaddingSize += e.mResponse.paddingSize();
833
0
        }
834
0
835
0
        rv = BodyFinalizeWrite(mDBDir, e.mResponseBodyId);
836
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
837
0
          DoResolve(rv);
838
0
          return;
839
0
        }
840
0
      }
841
0
842
0
      int64_t deletedPaddingSize = 0;
843
0
      rv = db::CachePut(mConn, mCacheId, e.mRequest,
844
0
                        e.mRequestStream ? &e.mRequestBodyId : nullptr,
845
0
                        e.mResponse,
846
0
                        e.mResponseStream ? &e.mResponseBodyId : nullptr,
847
0
                        mDeletedBodyIdList, &deletedPaddingSize);
848
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
849
0
        DoResolve(rv);
850
0
        return;
851
0
      }
852
0
853
0
      MOZ_DIAGNOSTIC_ASSERT(INT64_MAX - mDeletedPaddingSize >=
854
0
                            deletedPaddingSize);
855
0
      mDeletedPaddingSize += deletedPaddingSize;
856
0
    }
857
0
858
0
    // Update padding file when it's necessary
859
0
    rv = MaybeUpdatePaddingFile(mDBDir, mConn, mUpdatedPaddingSize,
860
0
                                mDeletedPaddingSize,
861
0
                                [&trans]() mutable { return trans.Commit(); });
862
0
    Unused << NS_WARN_IF(NS_FAILED(rv));
863
0
864
0
    DoResolve(rv);
865
0
  }
866
867
  virtual void
868
  CompleteOnInitiatingThread(nsresult aRv) override
869
0
  {
870
0
    NS_ASSERT_OWNINGTHREAD(Action);
871
0
872
0
    for (uint32_t i = 0; i < mList.Length(); ++i) {
873
0
      mList[i].mRequestStream = nullptr;
874
0
      mList[i].mResponseStream = nullptr;
875
0
    }
876
0
877
0
    // If the transaction fails, we shouldn't delete the body files and decrease
878
0
    // their padding size.
879
0
    if (NS_FAILED(aRv)) {
880
0
      mDeletedBodyIdList.Clear();
881
0
      mDeletedPaddingSize = 0;
882
0
    }
883
0
884
0
    mManager->NoteOrphanedBodyIdList(mDeletedBodyIdList);
885
0
886
0
    if (mDeletedPaddingSize > 0) {
887
0
      DecreaseUsageForQuotaInfo(mQuotaInfo.ref(), mDeletedPaddingSize);
888
0
    }
889
0
890
0
    Listener* listener = mManager->GetListener(mListenerId);
891
0
    mManager = nullptr;
892
0
    if (listener) {
893
0
      listener->OnOpComplete(ErrorResult(aRv), CachePutAllResult());
894
0
    }
895
0
  }
896
897
  virtual void
898
  CancelOnInitiatingThread() override
899
0
  {
900
0
    NS_ASSERT_OWNINGTHREAD(Action);
901
0
    Action::CancelOnInitiatingThread();
902
0
    CancelAllStreamCopying();
903
0
  }
904
905
  virtual bool MatchesCacheId(CacheId aCacheId) const override
906
0
  {
907
0
    NS_ASSERT_OWNINGTHREAD(Action);
908
0
    return aCacheId == mCacheId;
909
0
  }
910
911
  struct Entry
912
  {
913
    CacheRequest mRequest;
914
    nsCOMPtr<nsIInputStream> mRequestStream;
915
    nsID mRequestBodyId;
916
    nsCOMPtr<nsISupports> mRequestCopyContext;
917
918
    CacheResponse mResponse;
919
    nsCOMPtr<nsIInputStream> mResponseStream;
920
    nsID mResponseBodyId;
921
    nsCOMPtr<nsISupports> mResponseCopyContext;
922
  };
923
924
  enum StreamId
925
  {
926
    RequestStream,
927
    ResponseStream
928
  };
929
930
  nsresult
931
  StartStreamCopy(const QuotaInfo& aQuotaInfo, Entry& aEntry,
932
                  StreamId aStreamId, uint32_t* aCopyCountOut)
933
0
  {
934
0
    MOZ_ASSERT(mTarget->IsOnCurrentThread());
935
0
    MOZ_DIAGNOSTIC_ASSERT(aCopyCountOut);
936
0
937
0
    if (IsCanceled()) {
938
0
      return NS_ERROR_ABORT;
939
0
    }
940
0
941
0
    nsCOMPtr<nsIInputStream> source;
942
0
    nsID* bodyId;
943
0
944
0
    if (aStreamId == RequestStream) {
945
0
      source = aEntry.mRequestStream;
946
0
      bodyId = &aEntry.mRequestBodyId;
947
0
    } else {
948
0
      MOZ_DIAGNOSTIC_ASSERT(aStreamId == ResponseStream);
949
0
      source = aEntry.mResponseStream;
950
0
      bodyId = &aEntry.mResponseBodyId;
951
0
    }
952
0
953
0
    if (!source) {
954
0
      return NS_OK;
955
0
    }
956
0
957
0
    nsCOMPtr<nsISupports> copyContext;
958
0
959
0
    nsresult rv = BodyStartWriteStream(aQuotaInfo, mDBDir, source, this,
960
0
                                       AsyncCopyCompleteFunc, bodyId,
961
0
                                       getter_AddRefs(copyContext));
962
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
963
0
964
0
    mBodyIdWrittenList.AppendElement(*bodyId);
965
0
966
0
    if (copyContext) {
967
0
      MutexAutoLock lock(mMutex);
968
0
      mCopyContextList.AppendElement(copyContext);
969
0
    }
970
0
971
0
    *aCopyCountOut += 1;
972
0
973
0
    return rv;
974
0
  }
975
976
  void
977
  CancelAllStreamCopying()
978
0
  {
979
0
    // May occur on either owning thread or target thread
980
0
    MutexAutoLock lock(mMutex);
981
0
    for (uint32_t i = 0; i < mCopyContextList.Length(); ++i) {
982
0
      BodyCancelWrite(mDBDir, mCopyContextList[i]);
983
0
    }
984
0
    mCopyContextList.Clear();
985
0
  }
986
987
  static void
988
  AsyncCopyCompleteFunc(void* aClosure, nsresult aRv)
989
0
  {
990
0
    // May be on any thread, including STS event target.
991
0
    MOZ_DIAGNOSTIC_ASSERT(aClosure);
992
0
    // Weak ref as we are guaranteed to the action is alive until
993
0
    // CompleteOnInitiatingThread is called.
994
0
    CachePutAllAction* action = static_cast<CachePutAllAction*>(aClosure);
995
0
    action->CallOnAsyncCopyCompleteOnTargetThread(aRv);
996
0
  }
997
998
  void
999
  CallOnAsyncCopyCompleteOnTargetThread(nsresult aRv)
1000
0
  {
1001
0
    // May be on any thread, including STS event target.  Non-owning runnable
1002
0
    // here since we are guaranteed the Action will survive until
1003
0
    // CompleteOnInitiatingThread is called.
1004
0
    nsCOMPtr<nsIRunnable> runnable = NewNonOwningRunnableMethod<nsresult>(
1005
0
      "dom::cache::Manager::CachePutAllAction::OnAsyncCopyComplete",
1006
0
      this,
1007
0
      &CachePutAllAction::OnAsyncCopyComplete,
1008
0
      aRv);
1009
0
    MOZ_ALWAYS_SUCCEEDS(
1010
0
      mTarget->Dispatch(runnable.forget(), nsIThread::DISPATCH_NORMAL));
1011
0
  }
1012
1013
  void
1014
  DoResolve(nsresult aRv)
1015
0
  {
1016
0
    MOZ_ASSERT(mTarget->IsOnCurrentThread());
1017
0
1018
0
    // DoResolve() must not be called until all async copying has completed.
1019
#ifdef DEBUG
1020
    {
1021
      MutexAutoLock lock(mMutex);
1022
      MOZ_ASSERT(mCopyContextList.IsEmpty());
1023
    }
1024
#endif
1025
1026
0
    // Clean up any files we might have written before hitting the error.
1027
0
    if (NS_FAILED(aRv)) {
1028
0
      BodyDeleteFiles(mQuotaInfo.ref(), mDBDir, mBodyIdWrittenList);
1029
0
      if (mUpdatedPaddingSize > 0) {
1030
0
        DecreaseUsageForQuotaInfo(mQuotaInfo.ref(), mUpdatedPaddingSize);
1031
0
      }
1032
0
    }
1033
0
1034
0
    // Must be released on the target thread where it was opened.
1035
0
    mConn = nullptr;
1036
0
1037
0
    // Drop our ref to the target thread as we are done with this thread.
1038
0
    // Also makes our thread assertions catch any incorrect method calls
1039
0
    // after resolve.
1040
0
    mTarget = nullptr;
1041
0
1042
0
    // Make sure to de-ref the resolver per the Action API contract.
1043
0
    RefPtr<Action::Resolver> resolver;
1044
0
    mResolver.swap(resolver);
1045
0
    resolver->Resolve(aRv);
1046
0
  }
1047
1048
  // initiating thread only
1049
  RefPtr<Manager> mManager;
1050
  const ListenerId mListenerId;
1051
1052
  // Set on initiating thread, read on target thread.  State machine guarantees
1053
  // these are not modified while being read by the target thread.
1054
  const CacheId mCacheId;
1055
  nsTArray<Entry> mList;
1056
  uint32_t mExpectedAsyncCopyCompletions;
1057
1058
  // target thread only
1059
  RefPtr<Resolver> mResolver;
1060
  nsCOMPtr<nsIFile> mDBDir;
1061
  nsCOMPtr<mozIStorageConnection> mConn;
1062
  nsCOMPtr<nsISerialEventTarget> mTarget;
1063
  nsresult mAsyncResult;
1064
  nsTArray<nsID> mBodyIdWrittenList;
1065
1066
  // Written to on target thread, accessed on initiating thread after target
1067
  // thread activity is guaranteed complete
1068
  nsTArray<nsID> mDeletedBodyIdList;
1069
1070
  // accessed from any thread while mMutex locked
1071
  Mutex mMutex;
1072
  nsTArray<nsCOMPtr<nsISupports>> mCopyContextList;
1073
1074
  Maybe<QuotaInfo> mQuotaInfo;
1075
  // Track how much pad amount has been added for new entries so that it can be
1076
  // removed if an error occurs.
1077
  int64_t mUpdatedPaddingSize;
1078
  // Track any pad amount associated with overwritten entries.
1079
  int64_t mDeletedPaddingSize;
1080
};
1081
1082
// ----------------------------------------------------------------------------
1083
1084
class Manager::CacheDeleteAction final : public Manager::BaseAction
1085
{
1086
public:
1087
  CacheDeleteAction(Manager* aManager, ListenerId aListenerId,
1088
                    CacheId aCacheId, const CacheDeleteArgs& aArgs)
1089
    : BaseAction(aManager, aListenerId)
1090
    , mCacheId(aCacheId)
1091
    , mArgs(aArgs)
1092
    , mSuccess(false)
1093
    , mDeletedPaddingSize(0)
1094
0
  { }
1095
1096
  virtual nsresult
1097
  RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
1098
                        mozIStorageConnection* aConn) override
1099
0
  {
1100
0
    mQuotaInfo.emplace(aQuotaInfo);
1101
0
1102
0
    mozStorageTransaction trans(aConn, false,
1103
0
                                mozIStorageConnection::TRANSACTION_IMMEDIATE);
1104
0
1105
0
    nsresult rv = db::CacheDelete(aConn, mCacheId, mArgs.request(),
1106
0
                                  mArgs.params(), mDeletedBodyIdList,
1107
0
                                  &mDeletedPaddingSize, &mSuccess);
1108
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
1109
0
1110
0
    rv = MaybeUpdatePaddingFile(aDBDir, aConn, /* aIncreaceSize */ 0,
1111
0
                                mDeletedPaddingSize,
1112
0
                                [&trans]() mutable { return trans.Commit(); });
1113
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1114
0
      mSuccess = false;
1115
0
      return rv;
1116
0
    }
1117
0
1118
0
    return rv;
1119
0
  }
1120
1121
  virtual void
1122
  Complete(Listener* aListener, ErrorResult&& aRv) override
1123
0
  {
1124
0
    // If the transaction fails, we shouldn't delete the body files and decrease
1125
0
    // their padding size.
1126
0
    if (aRv.Failed()) {
1127
0
      mDeletedBodyIdList.Clear();
1128
0
      mDeletedPaddingSize = 0;
1129
0
    }
1130
0
1131
0
    mManager->NoteOrphanedBodyIdList(mDeletedBodyIdList);
1132
0
1133
0
    if (mDeletedPaddingSize > 0) {
1134
0
      DecreaseUsageForQuotaInfo(mQuotaInfo.ref(), mDeletedPaddingSize);
1135
0
    }
1136
0
1137
0
    aListener->OnOpComplete(std::move(aRv), CacheDeleteResult(mSuccess));
1138
0
  }
1139
1140
  virtual bool MatchesCacheId(CacheId aCacheId) const override
1141
0
  {
1142
0
    return aCacheId == mCacheId;
1143
0
  }
1144
1145
private:
1146
  const CacheId mCacheId;
1147
  const CacheDeleteArgs mArgs;
1148
  bool mSuccess;
1149
  nsTArray<nsID> mDeletedBodyIdList;
1150
  Maybe<QuotaInfo> mQuotaInfo;
1151
  // Track any pad amount associated with deleted entries.
1152
  int64_t mDeletedPaddingSize;
1153
};
1154
1155
// ----------------------------------------------------------------------------
1156
1157
class Manager::CacheKeysAction final : public Manager::BaseAction
1158
{
1159
public:
1160
  CacheKeysAction(Manager* aManager, ListenerId aListenerId,
1161
                  CacheId aCacheId, const CacheKeysArgs& aArgs,
1162
                  StreamList* aStreamList)
1163
    : BaseAction(aManager, aListenerId)
1164
    , mCacheId(aCacheId)
1165
    , mArgs(aArgs)
1166
    , mStreamList(aStreamList)
1167
0
  { }
1168
1169
  virtual nsresult
1170
  RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
1171
                        mozIStorageConnection* aConn) override
1172
0
  {
1173
0
    nsresult rv = db::CacheKeys(aConn, mCacheId, mArgs.requestOrVoid(),
1174
0
                                mArgs.params(), mSavedRequests);
1175
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
1176
0
1177
0
    for (uint32_t i = 0; i < mSavedRequests.Length(); ++i) {
1178
0
      if (!mSavedRequests[i].mHasBodyId
1179
0
          || IsHeadRequest(mArgs.requestOrVoid(), mArgs.params())) {
1180
0
        mSavedRequests[i].mHasBodyId = false;
1181
0
        continue;
1182
0
      }
1183
0
1184
0
      nsCOMPtr<nsIInputStream> stream;
1185
0
      if (mArgs.openMode() == OpenMode::Eager) {
1186
0
        rv = BodyOpen(aQuotaInfo, aDBDir, mSavedRequests[i].mBodyId,
1187
0
                      getter_AddRefs(stream));
1188
0
        if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
1189
0
        if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
1190
0
      }
1191
0
1192
0
      mStreamList->Add(mSavedRequests[i].mBodyId, std::move(stream));
1193
0
    }
1194
0
1195
0
    return rv;
1196
0
  }
1197
1198
  virtual void
1199
  Complete(Listener* aListener, ErrorResult&& aRv) override
1200
0
  {
1201
0
    mStreamList->Activate(mCacheId);
1202
0
    aListener->OnOpComplete(std::move(aRv), CacheKeysResult(), mSavedRequests,
1203
0
                            mStreamList);
1204
0
    mStreamList = nullptr;
1205
0
  }
1206
1207
  virtual bool MatchesCacheId(CacheId aCacheId) const override
1208
0
  {
1209
0
    return aCacheId == mCacheId;
1210
0
  }
1211
1212
private:
1213
  const CacheId mCacheId;
1214
  const CacheKeysArgs mArgs;
1215
  RefPtr<StreamList> mStreamList;
1216
  nsTArray<SavedRequest> mSavedRequests;
1217
};
1218
1219
// ----------------------------------------------------------------------------
1220
1221
class Manager::StorageMatchAction final : public Manager::BaseAction
1222
{
1223
public:
1224
  StorageMatchAction(Manager* aManager, ListenerId aListenerId,
1225
                     Namespace aNamespace,
1226
                     const StorageMatchArgs& aArgs,
1227
                     StreamList* aStreamList)
1228
    : BaseAction(aManager, aListenerId)
1229
    , mNamespace(aNamespace)
1230
    , mArgs(aArgs)
1231
    , mStreamList(aStreamList)
1232
    , mFoundResponse(false)
1233
0
  { }
1234
1235
  virtual nsresult
1236
  RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
1237
                        mozIStorageConnection* aConn) override
1238
0
  {
1239
0
    nsresult rv = db::StorageMatch(aConn, mNamespace, mArgs.request(),
1240
0
                                   mArgs.params(), &mFoundResponse,
1241
0
                                   &mSavedResponse);
1242
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
1243
0
1244
0
    if (!mFoundResponse || !mSavedResponse.mHasBodyId
1245
0
                        || IsHeadRequest(mArgs.request(), mArgs.params())) {
1246
0
      mSavedResponse.mHasBodyId = false;
1247
0
      return rv;
1248
0
    }
1249
0
1250
0
    nsCOMPtr<nsIInputStream> stream;
1251
0
    if (mArgs.openMode() == OpenMode::Eager) {
1252
0
      rv = BodyOpen(aQuotaInfo, aDBDir, mSavedResponse.mBodyId,
1253
0
                    getter_AddRefs(stream));
1254
0
      if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
1255
0
      if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
1256
0
    }
1257
0
1258
0
    mStreamList->Add(mSavedResponse.mBodyId, std::move(stream));
1259
0
1260
0
    return rv;
1261
0
  }
1262
1263
  virtual void
1264
  Complete(Listener* aListener, ErrorResult&& aRv) override
1265
0
  {
1266
0
    if (!mFoundResponse) {
1267
0
      aListener->OnOpComplete(std::move(aRv), StorageMatchResult(void_t()));
1268
0
    } else {
1269
0
      mStreamList->Activate(mSavedResponse.mCacheId);
1270
0
      aListener->OnOpComplete(std::move(aRv), StorageMatchResult(void_t()), mSavedResponse,
1271
0
                              mStreamList);
1272
0
    }
1273
0
    mStreamList = nullptr;
1274
0
  }
1275
1276
private:
1277
  const Namespace mNamespace;
1278
  const StorageMatchArgs mArgs;
1279
  RefPtr<StreamList> mStreamList;
1280
  bool mFoundResponse;
1281
  SavedResponse mSavedResponse;
1282
};
1283
1284
// ----------------------------------------------------------------------------
1285
1286
class Manager::StorageHasAction final : public Manager::BaseAction
1287
{
1288
public:
1289
  StorageHasAction(Manager* aManager, ListenerId aListenerId,
1290
                   Namespace aNamespace, const StorageHasArgs& aArgs)
1291
    : BaseAction(aManager, aListenerId)
1292
    , mNamespace(aNamespace)
1293
    , mArgs(aArgs)
1294
    , mCacheFound(false)
1295
0
  { }
1296
1297
  virtual nsresult
1298
  RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
1299
                        mozIStorageConnection* aConn) override
1300
0
  {
1301
0
    CacheId cacheId;
1302
0
    return db::StorageGetCacheId(aConn, mNamespace, mArgs.key(),
1303
0
                                 &mCacheFound, &cacheId);
1304
0
  }
1305
1306
  virtual void
1307
  Complete(Listener* aListener, ErrorResult&& aRv) override
1308
0
  {
1309
0
    aListener->OnOpComplete(std::move(aRv), StorageHasResult(mCacheFound));
1310
0
  }
1311
1312
private:
1313
  const Namespace mNamespace;
1314
  const StorageHasArgs mArgs;
1315
  bool mCacheFound;
1316
};
1317
1318
// ----------------------------------------------------------------------------
1319
1320
class Manager::StorageOpenAction final : public Manager::BaseAction
1321
{
1322
public:
1323
  StorageOpenAction(Manager* aManager, ListenerId aListenerId,
1324
                    Namespace aNamespace, const StorageOpenArgs& aArgs)
1325
    : BaseAction(aManager, aListenerId)
1326
    , mNamespace(aNamespace)
1327
    , mArgs(aArgs)
1328
    , mCacheId(INVALID_CACHE_ID)
1329
0
  { }
1330
1331
  virtual nsresult
1332
  RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
1333
                        mozIStorageConnection* aConn) override
1334
0
  {
1335
0
    // Cache does not exist, create it instead
1336
0
    mozStorageTransaction trans(aConn, false,
1337
0
                                mozIStorageConnection::TRANSACTION_IMMEDIATE);
1338
0
1339
0
    // Look for existing cache
1340
0
    bool cacheFound;
1341
0
    nsresult rv = db::StorageGetCacheId(aConn, mNamespace, mArgs.key(),
1342
0
                                        &cacheFound, &mCacheId);
1343
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
1344
0
    if (cacheFound) {
1345
0
      MOZ_DIAGNOSTIC_ASSERT(mCacheId != INVALID_CACHE_ID);
1346
0
      return rv;
1347
0
    }
1348
0
1349
0
    rv = db::CreateCacheId(aConn, &mCacheId);
1350
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
1351
0
1352
0
    rv = db::StoragePutCache(aConn, mNamespace, mArgs.key(), mCacheId);
1353
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
1354
0
1355
0
    rv = trans.Commit();
1356
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
1357
0
1358
0
    MOZ_DIAGNOSTIC_ASSERT(mCacheId != INVALID_CACHE_ID);
1359
0
    return rv;
1360
0
  }
1361
1362
  virtual void
1363
  Complete(Listener* aListener, ErrorResult&& aRv) override
1364
0
  {
1365
0
    MOZ_DIAGNOSTIC_ASSERT(aRv.Failed() || mCacheId != INVALID_CACHE_ID);
1366
0
    aListener->OnOpComplete(std::move(aRv),
1367
0
                            StorageOpenResult(nullptr, nullptr, mNamespace),
1368
0
                            mCacheId);
1369
0
  }
1370
1371
private:
1372
  const Namespace mNamespace;
1373
  const StorageOpenArgs mArgs;
1374
  CacheId mCacheId;
1375
};
1376
1377
// ----------------------------------------------------------------------------
1378
1379
class Manager::StorageDeleteAction final : public Manager::BaseAction
1380
{
1381
public:
1382
  StorageDeleteAction(Manager* aManager, ListenerId aListenerId,
1383
                      Namespace aNamespace, const StorageDeleteArgs& aArgs)
1384
    : BaseAction(aManager, aListenerId)
1385
    , mNamespace(aNamespace)
1386
    , mArgs(aArgs)
1387
    , mCacheDeleted(false)
1388
    , mCacheId(INVALID_CACHE_ID)
1389
0
  { }
1390
1391
  virtual nsresult
1392
  RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
1393
                        mozIStorageConnection* aConn) override
1394
0
  {
1395
0
    mozStorageTransaction trans(aConn, false,
1396
0
                                mozIStorageConnection::TRANSACTION_IMMEDIATE);
1397
0
1398
0
    bool exists;
1399
0
    nsresult rv = db::StorageGetCacheId(aConn, mNamespace, mArgs.key(),
1400
0
                                        &exists, &mCacheId);
1401
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
1402
0
1403
0
    if (!exists) {
1404
0
      mCacheDeleted = false;
1405
0
      return NS_OK;
1406
0
    }
1407
0
1408
0
    // Don't delete the removing padding size here, we'll delete it on
1409
0
    // DeleteOrphanedCacheAction.
1410
0
    rv = db::StorageForgetCache(aConn, mNamespace, mArgs.key());
1411
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
1412
0
1413
0
    rv = trans.Commit();
1414
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
1415
0
1416
0
    mCacheDeleted = true;
1417
0
    return rv;
1418
0
  }
1419
1420
  virtual void
1421
  Complete(Listener* aListener, ErrorResult&& aRv) override
1422
0
  {
1423
0
    if (mCacheDeleted) {
1424
0
      // If content is referencing this cache, mark it orphaned to be
1425
0
      // deleted later.
1426
0
      if (!mManager->SetCacheIdOrphanedIfRefed(mCacheId)) {
1427
0
1428
0
        // no outstanding references, delete immediately
1429
0
        RefPtr<Context> context = mManager->mContext;
1430
0
1431
0
        if (context->IsCanceled()) {
1432
0
          context->NoteOrphanedData();
1433
0
        } else {
1434
0
          context->CancelForCacheId(mCacheId);
1435
0
          RefPtr<Action> action =
1436
0
            new DeleteOrphanedCacheAction(mManager, mCacheId);
1437
0
          context->Dispatch(action);
1438
0
        }
1439
0
      }
1440
0
    }
1441
0
1442
0
    aListener->OnOpComplete(std::move(aRv), StorageDeleteResult(mCacheDeleted));
1443
0
  }
1444
1445
private:
1446
  const Namespace mNamespace;
1447
  const StorageDeleteArgs mArgs;
1448
  bool mCacheDeleted;
1449
  CacheId mCacheId;
1450
};
1451
1452
// ----------------------------------------------------------------------------
1453
1454
class Manager::StorageKeysAction final : public Manager::BaseAction
1455
{
1456
public:
1457
  StorageKeysAction(Manager* aManager, ListenerId aListenerId,
1458
                    Namespace aNamespace)
1459
    : BaseAction(aManager, aListenerId)
1460
    , mNamespace(aNamespace)
1461
0
  { }
1462
1463
  virtual nsresult
1464
  RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
1465
                        mozIStorageConnection* aConn) override
1466
0
  {
1467
0
    return db::StorageGetKeys(aConn, mNamespace, mKeys);
1468
0
  }
1469
1470
  virtual void
1471
  Complete(Listener* aListener, ErrorResult&& aRv) override
1472
0
  {
1473
0
    if (aRv.Failed()) {
1474
0
      mKeys.Clear();
1475
0
    }
1476
0
    aListener->OnOpComplete(std::move(aRv), StorageKeysResult(mKeys));
1477
0
  }
1478
1479
private:
1480
  const Namespace mNamespace;
1481
  nsTArray<nsString> mKeys;
1482
};
1483
1484
// ----------------------------------------------------------------------------
1485
1486
class Manager::OpenStreamAction final : public Manager::BaseAction
1487
{
1488
public:
1489
  OpenStreamAction(Manager* aManager, ListenerId aListenerId,
1490
                   InputStreamResolver&& aResolver, const nsID& aBodyId)
1491
    : BaseAction(aManager, aListenerId)
1492
    , mResolver(std::move(aResolver))
1493
    , mBodyId(aBodyId)
1494
0
  { }
1495
1496
  virtual nsresult
1497
  RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
1498
                        mozIStorageConnection* aConn) override
1499
0
  {
1500
0
    nsresult rv = BodyOpen(aQuotaInfo, aDBDir, mBodyId,
1501
0
                           getter_AddRefs(mBodyStream));
1502
0
    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
1503
0
    if (NS_WARN_IF(!mBodyStream)) { return NS_ERROR_FILE_NOT_FOUND; }
1504
0
1505
0
    return rv;
1506
0
  }
1507
1508
  virtual void
1509
  Complete(Listener* aListener, ErrorResult&& aRv) override
1510
0
  {
1511
0
    mResolver(std::move(mBodyStream));
1512
0
    mResolver = nullptr;
1513
0
  }
1514
1515
private:
1516
  InputStreamResolver mResolver;
1517
  const nsID mBodyId;
1518
  nsCOMPtr<nsIInputStream> mBodyStream;
1519
};
1520
1521
// ----------------------------------------------------------------------------
1522
1523
//static
1524
Manager::ListenerId Manager::sNextListenerId = 0;
1525
1526
void
1527
Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult)
1528
0
{
1529
0
  OnOpComplete(std::move(aRv), aResult, INVALID_CACHE_ID, nsTArray<SavedResponse>(),
1530
0
               nsTArray<SavedRequest>(), nullptr);
1531
0
}
1532
1533
void
1534
Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
1535
                                CacheId aOpenedCacheId)
1536
0
{
1537
0
  OnOpComplete(std::move(aRv), aResult, aOpenedCacheId, nsTArray<SavedResponse>(),
1538
0
               nsTArray<SavedRequest>(), nullptr);
1539
0
}
1540
1541
void
1542
Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
1543
                                const SavedResponse& aSavedResponse,
1544
                                StreamList* aStreamList)
1545
0
{
1546
0
  AutoTArray<SavedResponse, 1> responseList;
1547
0
  responseList.AppendElement(aSavedResponse);
1548
0
  OnOpComplete(std::move(aRv), aResult, INVALID_CACHE_ID, responseList,
1549
0
               nsTArray<SavedRequest>(), aStreamList);
1550
0
}
1551
1552
void
1553
Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
1554
                                const nsTArray<SavedResponse>& aSavedResponseList,
1555
                                StreamList* aStreamList)
1556
0
{
1557
0
  OnOpComplete(std::move(aRv), aResult, INVALID_CACHE_ID, aSavedResponseList,
1558
0
               nsTArray<SavedRequest>(), aStreamList);
1559
0
}
1560
1561
void
1562
Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
1563
                                const nsTArray<SavedRequest>& aSavedRequestList,
1564
                                StreamList* aStreamList)
1565
0
{
1566
0
  OnOpComplete(std::move(aRv), aResult, INVALID_CACHE_ID, nsTArray<SavedResponse>(),
1567
0
               aSavedRequestList, aStreamList);
1568
0
}
1569
1570
// static
1571
nsresult
1572
Manager::GetOrCreate(ManagerId* aManagerId, Manager** aManagerOut)
1573
0
{
1574
0
  mozilla::ipc::AssertIsOnBackgroundThread();
1575
0
  return Factory::GetOrCreate(aManagerId, aManagerOut);
1576
0
}
1577
1578
// static
1579
already_AddRefed<Manager>
1580
Manager::Get(ManagerId* aManagerId)
1581
0
{
1582
0
  mozilla::ipc::AssertIsOnBackgroundThread();
1583
0
  return Factory::Get(aManagerId);
1584
0
}
1585
1586
// static
1587
void
1588
Manager::ShutdownAll()
1589
0
{
1590
0
  mozilla::ipc::AssertIsOnBackgroundThread();
1591
0
1592
0
  Factory::ShutdownAll();
1593
0
1594
0
  if (!mozilla::SpinEventLoopUntil([]() {
1595
0
        return Factory::IsShutdownAllComplete();
1596
0
      })) {
1597
0
    NS_WARNING("Something bad happened!");
1598
0
  }
1599
0
}
1600
1601
// static
1602
void
1603
Manager::Abort(const nsACString& aOrigin)
1604
0
{
1605
0
  mozilla::ipc::AssertIsOnBackgroundThread();
1606
0
1607
0
  Factory::Abort(aOrigin);
1608
0
}
1609
1610
void
1611
Manager::RemoveListener(Listener* aListener)
1612
0
{
1613
0
  NS_ASSERT_OWNINGTHREAD(Manager);
1614
0
  // There may not be a listener here in the case where an actor is killed
1615
0
  // before it can perform any actual async requests on Manager.
1616
0
  mListeners.RemoveElement(aListener, ListenerEntryListenerComparator());
1617
0
  MOZ_ASSERT(!mListeners.Contains(aListener,
1618
0
                                  ListenerEntryListenerComparator()));
1619
0
  MaybeAllowContextToClose();
1620
0
}
1621
1622
void
1623
Manager::RemoveContext(Context* aContext)
1624
0
{
1625
0
  NS_ASSERT_OWNINGTHREAD(Manager);
1626
0
  MOZ_DIAGNOSTIC_ASSERT(mContext);
1627
0
  MOZ_DIAGNOSTIC_ASSERT(mContext == aContext);
1628
0
1629
0
  // Whether the Context destruction was triggered from the Manager going
1630
0
  // idle or the underlying storage being invalidated, we should know we
1631
0
  // are closing before the Context is destroyed.
1632
0
  MOZ_DIAGNOSTIC_ASSERT(mState == Closing);
1633
0
1634
0
  // Before forgetting the Context, check to see if we have any outstanding
1635
0
  // cache or body objects waiting for deletion.  If so, note that we've
1636
0
  // orphaned data so it will be cleaned up on the next open.
1637
0
  for (uint32_t i = 0; i < mCacheIdRefs.Length(); ++i) {
1638
0
    if (mCacheIdRefs[i].mOrphaned) {
1639
0
      aContext->NoteOrphanedData();
1640
0
      break;
1641
0
    }
1642
0
  }
1643
0
1644
0
  for (uint32_t i = 0; i < mBodyIdRefs.Length(); ++i) {
1645
0
    if (mBodyIdRefs[i].mOrphaned) {
1646
0
      aContext->NoteOrphanedData();
1647
0
      break;
1648
0
    }
1649
0
  }
1650
0
1651
0
  mContext = nullptr;
1652
0
1653
0
  // Once the context is gone, we can immediately remove ourself from the
1654
0
  // Factory list.  We don't need to block shutdown by staying in the list
1655
0
  // any more.
1656
0
  Factory::Remove(this);
1657
0
}
1658
1659
void
1660
Manager::NoteClosing()
1661
0
{
1662
0
  NS_ASSERT_OWNINGTHREAD(Manager);
1663
0
  // This can be called more than once legitimately through different paths.
1664
0
  mState = Closing;
1665
0
}
1666
1667
Manager::State
1668
Manager::GetState() const
1669
0
{
1670
0
  NS_ASSERT_OWNINGTHREAD(Manager);
1671
0
  return mState;
1672
0
}
1673
1674
void
1675
Manager::AddRefCacheId(CacheId aCacheId)
1676
0
{
1677
0
  NS_ASSERT_OWNINGTHREAD(Manager);
1678
0
  for (uint32_t i = 0; i < mCacheIdRefs.Length(); ++i) {
1679
0
    if (mCacheIdRefs[i].mCacheId == aCacheId) {
1680
0
      mCacheIdRefs[i].mCount += 1;
1681
0
      return;
1682
0
    }
1683
0
  }
1684
0
  CacheIdRefCounter* entry = mCacheIdRefs.AppendElement();
1685
0
  entry->mCacheId = aCacheId;
1686
0
  entry->mCount = 1;
1687
0
  entry->mOrphaned = false;
1688
0
}
1689
1690
void
1691
Manager::ReleaseCacheId(CacheId aCacheId)
1692
0
{
1693
0
  NS_ASSERT_OWNINGTHREAD(Manager);
1694
0
  for (uint32_t i = 0; i < mCacheIdRefs.Length(); ++i) {
1695
0
    if (mCacheIdRefs[i].mCacheId == aCacheId) {
1696
0
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1697
0
      uint32_t oldRef = mCacheIdRefs[i].mCount;
1698
0
#endif
1699
0
      mCacheIdRefs[i].mCount -= 1;
1700
0
      MOZ_DIAGNOSTIC_ASSERT(mCacheIdRefs[i].mCount < oldRef);
1701
0
      if (mCacheIdRefs[i].mCount == 0) {
1702
0
        bool orphaned = mCacheIdRefs[i].mOrphaned;
1703
0
        mCacheIdRefs.RemoveElementAt(i);
1704
0
        RefPtr<Context> context = mContext;
1705
0
        // If the context is already gone, then orphan flag should have been
1706
0
        // set in RemoveContext().
1707
0
        if (orphaned && context) {
1708
0
          if (context->IsCanceled()) {
1709
0
            context->NoteOrphanedData();
1710
0
          } else {
1711
0
            context->CancelForCacheId(aCacheId);
1712
0
            RefPtr<Action> action = new DeleteOrphanedCacheAction(this,
1713
0
                                                                    aCacheId);
1714
0
            context->Dispatch(action);
1715
0
          }
1716
0
        }
1717
0
      }
1718
0
      MaybeAllowContextToClose();
1719
0
      return;
1720
0
    }
1721
0
  }
1722
0
  MOZ_ASSERT_UNREACHABLE("Attempt to release CacheId that is not referenced!");
1723
0
}
1724
1725
void
1726
Manager::AddRefBodyId(const nsID& aBodyId)
1727
0
{
1728
0
  NS_ASSERT_OWNINGTHREAD(Manager);
1729
0
  for (uint32_t i = 0; i < mBodyIdRefs.Length(); ++i) {
1730
0
    if (mBodyIdRefs[i].mBodyId == aBodyId) {
1731
0
      mBodyIdRefs[i].mCount += 1;
1732
0
      return;
1733
0
    }
1734
0
  }
1735
0
  BodyIdRefCounter* entry = mBodyIdRefs.AppendElement();
1736
0
  entry->mBodyId = aBodyId;
1737
0
  entry->mCount = 1;
1738
0
  entry->mOrphaned = false;
1739
0
}
1740
1741
void
1742
Manager::ReleaseBodyId(const nsID& aBodyId)
1743
0
{
1744
0
  NS_ASSERT_OWNINGTHREAD(Manager);
1745
0
  for (uint32_t i = 0; i < mBodyIdRefs.Length(); ++i) {
1746
0
    if (mBodyIdRefs[i].mBodyId == aBodyId) {
1747
0
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1748
0
      uint32_t oldRef = mBodyIdRefs[i].mCount;
1749
0
#endif
1750
0
      mBodyIdRefs[i].mCount -= 1;
1751
0
      MOZ_DIAGNOSTIC_ASSERT(mBodyIdRefs[i].mCount < oldRef);
1752
0
      if (mBodyIdRefs[i].mCount < 1) {
1753
0
        bool orphaned = mBodyIdRefs[i].mOrphaned;
1754
0
        mBodyIdRefs.RemoveElementAt(i);
1755
0
        RefPtr<Context> context = mContext;
1756
0
        // If the context is already gone, then orphan flag should have been
1757
0
        // set in RemoveContext().
1758
0
        if (orphaned && context) {
1759
0
          if (context->IsCanceled()) {
1760
0
            context->NoteOrphanedData();
1761
0
          } else {
1762
0
            RefPtr<Action> action = new DeleteOrphanedBodyAction(aBodyId);
1763
0
            context->Dispatch(action);
1764
0
          }
1765
0
        }
1766
0
      }
1767
0
      MaybeAllowContextToClose();
1768
0
      return;
1769
0
    }
1770
0
  }
1771
0
  MOZ_ASSERT_UNREACHABLE("Attempt to release BodyId that is not referenced!");
1772
0
}
1773
1774
already_AddRefed<ManagerId>
1775
Manager::GetManagerId() const
1776
0
{
1777
0
  RefPtr<ManagerId> ref = mManagerId;
1778
0
  return ref.forget();
1779
0
}
1780
1781
void
1782
Manager::AddStreamList(StreamList* aStreamList)
1783
0
{
1784
0
  NS_ASSERT_OWNINGTHREAD(Manager);
1785
0
  MOZ_DIAGNOSTIC_ASSERT(aStreamList);
1786
0
  mStreamLists.AppendElement(aStreamList);
1787
0
}
1788
1789
void
1790
Manager::RemoveStreamList(StreamList* aStreamList)
1791
0
{
1792
0
  NS_ASSERT_OWNINGTHREAD(Manager);
1793
0
  MOZ_DIAGNOSTIC_ASSERT(aStreamList);
1794
0
  mStreamLists.RemoveElement(aStreamList);
1795
0
}
1796
1797
void
1798
Manager::ExecuteCacheOp(Listener* aListener, CacheId aCacheId,
1799
                        const CacheOpArgs& aOpArgs)
1800
0
{
1801
0
  NS_ASSERT_OWNINGTHREAD(Manager);
1802
0
  MOZ_DIAGNOSTIC_ASSERT(aListener);
1803
0
  MOZ_DIAGNOSTIC_ASSERT(aOpArgs.type() != CacheOpArgs::TCachePutAllArgs);
1804
0
1805
0
  if (NS_WARN_IF(mState == Closing)) {
1806
0
    aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), void_t());
1807
0
    return;
1808
0
  }
1809
0
1810
0
  RefPtr<Context> context = mContext;
1811
0
  MOZ_DIAGNOSTIC_ASSERT(!context->IsCanceled());
1812
0
1813
0
  RefPtr<StreamList> streamList = new StreamList(this, context);
1814
0
  ListenerId listenerId = SaveListener(aListener);
1815
0
1816
0
  RefPtr<Action> action;
1817
0
  switch(aOpArgs.type()) {
1818
0
    case CacheOpArgs::TCacheMatchArgs:
1819
0
      action = new CacheMatchAction(this, listenerId, aCacheId,
1820
0
                                    aOpArgs.get_CacheMatchArgs(), streamList);
1821
0
      break;
1822
0
    case CacheOpArgs::TCacheMatchAllArgs:
1823
0
      action = new CacheMatchAllAction(this, listenerId, aCacheId,
1824
0
                                       aOpArgs.get_CacheMatchAllArgs(),
1825
0
                                       streamList);
1826
0
      break;
1827
0
    case CacheOpArgs::TCacheDeleteArgs:
1828
0
      action = new CacheDeleteAction(this, listenerId, aCacheId,
1829
0
                                     aOpArgs.get_CacheDeleteArgs());
1830
0
      break;
1831
0
    case CacheOpArgs::TCacheKeysArgs:
1832
0
      action = new CacheKeysAction(this, listenerId, aCacheId,
1833
0
                                   aOpArgs.get_CacheKeysArgs(), streamList);
1834
0
      break;
1835
0
    default:
1836
0
      MOZ_CRASH("Unknown Cache operation!");
1837
0
  }
1838
0
1839
0
  context->Dispatch(action);
1840
0
}
1841
1842
void
1843
Manager::ExecuteStorageOp(Listener* aListener, Namespace aNamespace,
1844
                          const CacheOpArgs& aOpArgs)
1845
0
{
1846
0
  NS_ASSERT_OWNINGTHREAD(Manager);
1847
0
  MOZ_DIAGNOSTIC_ASSERT(aListener);
1848
0
1849
0
  if (NS_WARN_IF(mState == Closing)) {
1850
0
    aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), void_t());
1851
0
    return;
1852
0
  }
1853
0
1854
0
  RefPtr<Context> context = mContext;
1855
0
  MOZ_DIAGNOSTIC_ASSERT(!context->IsCanceled());
1856
0
1857
0
  RefPtr<StreamList> streamList = new StreamList(this, context);
1858
0
  ListenerId listenerId = SaveListener(aListener);
1859
0
1860
0
  RefPtr<Action> action;
1861
0
  switch(aOpArgs.type()) {
1862
0
    case CacheOpArgs::TStorageMatchArgs:
1863
0
      action = new StorageMatchAction(this, listenerId, aNamespace,
1864
0
                                      aOpArgs.get_StorageMatchArgs(),
1865
0
                                      streamList);
1866
0
      break;
1867
0
    case CacheOpArgs::TStorageHasArgs:
1868
0
      action = new StorageHasAction(this, listenerId, aNamespace,
1869
0
                                    aOpArgs.get_StorageHasArgs());
1870
0
      break;
1871
0
    case CacheOpArgs::TStorageOpenArgs:
1872
0
      action = new StorageOpenAction(this, listenerId, aNamespace,
1873
0
                                     aOpArgs.get_StorageOpenArgs());
1874
0
      break;
1875
0
    case CacheOpArgs::TStorageDeleteArgs:
1876
0
      action = new StorageDeleteAction(this, listenerId, aNamespace,
1877
0
                                       aOpArgs.get_StorageDeleteArgs());
1878
0
      break;
1879
0
    case CacheOpArgs::TStorageKeysArgs:
1880
0
      action = new StorageKeysAction(this, listenerId, aNamespace);
1881
0
      break;
1882
0
    default:
1883
0
      MOZ_CRASH("Unknown CacheStorage operation!");
1884
0
  }
1885
0
1886
0
  context->Dispatch(action);
1887
0
}
1888
1889
void
1890
Manager::ExecuteOpenStream(Listener* aListener, InputStreamResolver&& aResolver,
1891
                           const nsID& aBodyId)
1892
0
{
1893
0
  NS_ASSERT_OWNINGTHREAD(Manager);
1894
0
  MOZ_DIAGNOSTIC_ASSERT(aListener);
1895
0
  MOZ_DIAGNOSTIC_ASSERT(aResolver);
1896
0
1897
0
  if (NS_WARN_IF(mState == Closing)) {
1898
0
    aResolver(nullptr);
1899
0
    return;
1900
0
  }
1901
0
1902
0
  RefPtr<Context> context = mContext;
1903
0
  MOZ_DIAGNOSTIC_ASSERT(!context->IsCanceled());
1904
0
1905
0
  // We save the listener simply to track the existence of the caller here.
1906
0
  // Our returned value will really be passed to the resolver when the
1907
0
  // operation completes.  In the future we should remove the Listener
1908
0
  // mechanism in favor of std::function or MozPromise.
1909
0
  ListenerId listenerId = SaveListener(aListener);
1910
0
1911
0
  RefPtr<Action> action =
1912
0
    new OpenStreamAction(this, listenerId, std::move(aResolver), aBodyId);
1913
0
1914
0
  context->Dispatch(action);
1915
0
}
1916
1917
void
1918
Manager::ExecutePutAll(Listener* aListener, CacheId aCacheId,
1919
                       const nsTArray<CacheRequestResponse>& aPutList,
1920
                       const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
1921
                       const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList)
1922
0
{
1923
0
  NS_ASSERT_OWNINGTHREAD(Manager);
1924
0
  MOZ_DIAGNOSTIC_ASSERT(aListener);
1925
0
1926
0
  if (NS_WARN_IF(mState == Closing)) {
1927
0
    aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), CachePutAllResult());
1928
0
    return;
1929
0
  }
1930
0
1931
0
  RefPtr<Context> context = mContext;
1932
0
  MOZ_DIAGNOSTIC_ASSERT(!context->IsCanceled());
1933
0
1934
0
  ListenerId listenerId = SaveListener(aListener);
1935
0
1936
0
  RefPtr<Action> action = new CachePutAllAction(this, listenerId, aCacheId,
1937
0
                                                  aPutList, aRequestStreamList,
1938
0
                                                  aResponseStreamList);
1939
0
1940
0
  context->Dispatch(action);
1941
0
}
1942
1943
Manager::Manager(ManagerId* aManagerId, nsIThread* aIOThread)
1944
  : mManagerId(aManagerId)
1945
  , mIOThread(aIOThread)
1946
  , mContext(nullptr)
1947
  , mShuttingDown(false)
1948
  , mState(Open)
1949
0
{
1950
0
  MOZ_DIAGNOSTIC_ASSERT(mManagerId);
1951
0
  MOZ_DIAGNOSTIC_ASSERT(mIOThread);
1952
0
}
1953
1954
Manager::~Manager()
1955
0
{
1956
0
  NS_ASSERT_OWNINGTHREAD(Manager);
1957
0
  MOZ_DIAGNOSTIC_ASSERT(mState == Closing);
1958
0
  MOZ_DIAGNOSTIC_ASSERT(!mContext);
1959
0
1960
0
  nsCOMPtr<nsIThread> ioThread;
1961
0
  mIOThread.swap(ioThread);
1962
0
1963
0
  // Don't spin the event loop in the destructor waiting for the thread to
1964
0
  // shutdown.  Defer this to the main thread, instead.
1965
0
  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewRunnableMethod("nsIThread::Shutdown",
1966
0
                                                                ioThread, &nsIThread::Shutdown)));
1967
0
}
1968
1969
void
1970
Manager::Init(Manager* aOldManager)
1971
0
{
1972
0
  NS_ASSERT_OWNINGTHREAD(Manager);
1973
0
1974
0
  RefPtr<Context> oldContext;
1975
0
  if (aOldManager) {
1976
0
    oldContext = aOldManager->mContext;
1977
0
  }
1978
0
1979
0
  // Create the context immediately.  Since there can at most be one Context
1980
0
  // per Manager now, this lets us cleanly call Factory::Remove() once the
1981
0
  // Context goes away.
1982
0
  RefPtr<Action> setupAction = new SetupAction();
1983
0
  RefPtr<Context> ref = Context::Create(this, mIOThread->SerialEventTarget(), setupAction,
1984
0
                                        oldContext);
1985
0
  mContext = ref;
1986
0
}
1987
1988
void
1989
Manager::Shutdown()
1990
0
{
1991
0
  NS_ASSERT_OWNINGTHREAD(Manager);
1992
0
1993
0
  // Ignore duplicate attempts to shutdown.  This can occur when we start
1994
0
  // a browser initiated shutdown and then run ~Manager() which also
1995
0
  // calls Shutdown().
1996
0
  if (mShuttingDown) {
1997
0
    return;
1998
0
  }
1999
0
2000
0
  mShuttingDown = true;
2001
0
2002
0
  // Note that we are closing to prevent any new requests from coming in and
2003
0
  // creating a new Context.  We must ensure all Contexts and IO operations are
2004
0
  // complete before shutdown proceeds.
2005
0
  NoteClosing();
2006
0
2007
0
  // If there is a context, then cancel and only note that we are done after
2008
0
  // its cleaned up.
2009
0
  if (mContext) {
2010
0
    RefPtr<Context> context = mContext;
2011
0
    context->CancelAll();
2012
0
    return;
2013
0
  }
2014
0
}
2015
2016
void
2017
Manager::Abort()
2018
0
{
2019
0
  NS_ASSERT_OWNINGTHREAD(Manager);
2020
0
  MOZ_DIAGNOSTIC_ASSERT(mContext);
2021
0
2022
0
  // Note that we are closing to prevent any new requests from coming in and
2023
0
  // creating a new Context.  We must ensure all Contexts and IO operations are
2024
0
  // complete before origin clear proceeds.
2025
0
  NoteClosing();
2026
0
2027
0
  // Cancel and only note that we are done after the context is cleaned up.
2028
0
  RefPtr<Context> context = mContext;
2029
0
  context->CancelAll();
2030
0
}
2031
2032
Manager::ListenerId
2033
Manager::SaveListener(Listener* aListener)
2034
0
{
2035
0
  NS_ASSERT_OWNINGTHREAD(Manager);
2036
0
2037
0
  // Once a Listener is added, we keep a reference to it until its
2038
0
  // removed.  Since the same Listener might make multiple requests,
2039
0
  // ensure we only have a single reference in our list.
2040
0
  ListenerList::index_type index =
2041
0
    mListeners.IndexOf(aListener, 0, ListenerEntryListenerComparator());
2042
0
  if (index != ListenerList::NoIndex) {
2043
0
    return mListeners[index].mId;
2044
0
  }
2045
0
2046
0
  ListenerId id = sNextListenerId;
2047
0
  sNextListenerId += 1;
2048
0
2049
0
  mListeners.AppendElement(ListenerEntry(id, aListener));
2050
0
  return id;
2051
0
}
2052
2053
Manager::Listener*
2054
Manager::GetListener(ListenerId aListenerId) const
2055
0
{
2056
0
  NS_ASSERT_OWNINGTHREAD(Manager);
2057
0
  ListenerList::index_type index =
2058
0
    mListeners.IndexOf(aListenerId, 0, ListenerEntryIdComparator());
2059
0
  if (index != ListenerList::NoIndex) {
2060
0
    return mListeners[index].mListener;
2061
0
  }
2062
0
2063
0
  // This can legitimately happen if the actor is deleted while a request is
2064
0
  // in process.  For example, the child process OOMs.
2065
0
  return nullptr;
2066
0
}
2067
2068
bool
2069
Manager::SetCacheIdOrphanedIfRefed(CacheId aCacheId)
2070
0
{
2071
0
  NS_ASSERT_OWNINGTHREAD(Manager);
2072
0
  for (uint32_t i = 0; i < mCacheIdRefs.Length(); ++i) {
2073
0
    if (mCacheIdRefs[i].mCacheId == aCacheId) {
2074
0
      MOZ_DIAGNOSTIC_ASSERT(mCacheIdRefs[i].mCount > 0);
2075
0
      MOZ_DIAGNOSTIC_ASSERT(!mCacheIdRefs[i].mOrphaned);
2076
0
      mCacheIdRefs[i].mOrphaned = true;
2077
0
      return true;
2078
0
    }
2079
0
  }
2080
0
  return false;
2081
0
}
2082
2083
// TODO: provide way to set body non-orphaned if its added back to a cache (bug 1110479)
2084
2085
bool
2086
Manager::SetBodyIdOrphanedIfRefed(const nsID& aBodyId)
2087
0
{
2088
0
  NS_ASSERT_OWNINGTHREAD(Manager);
2089
0
  for (uint32_t i = 0; i < mBodyIdRefs.Length(); ++i) {
2090
0
    if (mBodyIdRefs[i].mBodyId == aBodyId) {
2091
0
      MOZ_DIAGNOSTIC_ASSERT(mBodyIdRefs[i].mCount > 0);
2092
0
      MOZ_DIAGNOSTIC_ASSERT(!mBodyIdRefs[i].mOrphaned);
2093
0
      mBodyIdRefs[i].mOrphaned = true;
2094
0
      return true;
2095
0
    }
2096
0
  }
2097
0
  return false;
2098
0
}
2099
2100
void
2101
Manager::NoteOrphanedBodyIdList(const nsTArray<nsID>& aDeletedBodyIdList)
2102
0
{
2103
0
  NS_ASSERT_OWNINGTHREAD(Manager);
2104
0
2105
0
  AutoTArray<nsID, 64> deleteNowList;
2106
0
  deleteNowList.SetCapacity(aDeletedBodyIdList.Length());
2107
0
2108
0
  for (uint32_t i = 0; i < aDeletedBodyIdList.Length(); ++i) {
2109
0
    if (!SetBodyIdOrphanedIfRefed(aDeletedBodyIdList[i])) {
2110
0
      deleteNowList.AppendElement(aDeletedBodyIdList[i]);
2111
0
    }
2112
0
  }
2113
0
2114
0
  // TODO: note that we need to check these bodies for staleness on startup (bug 1110446)
2115
0
  RefPtr<Context> context = mContext;
2116
0
  if (!deleteNowList.IsEmpty() && context && !context->IsCanceled()) {
2117
0
    RefPtr<Action> action = new DeleteOrphanedBodyAction(deleteNowList);
2118
0
    context->Dispatch(action);
2119
0
  }
2120
0
}
2121
2122
void
2123
Manager::MaybeAllowContextToClose()
2124
0
{
2125
0
  NS_ASSERT_OWNINGTHREAD(Manager);
2126
0
2127
0
  // If we have an active context, but we have no more users of the Manager,
2128
0
  // then let it shut itself down.  We must wait for all possible users of
2129
0
  // Cache state information to complete before doing this.  Once we allow
2130
0
  // the Context to close we may not reliably get notified of storage
2131
0
  // invalidation.
2132
0
  RefPtr<Context> context = mContext;
2133
0
  if (context && mListeners.IsEmpty()
2134
0
              && mCacheIdRefs.IsEmpty()
2135
0
              && mBodyIdRefs.IsEmpty()) {
2136
0
2137
0
    // Mark this Manager as invalid so that it won't get used again.  We don't
2138
0
    // want to start any new operations once we allow the Context to close since
2139
0
    // it may race with the underlying storage getting invalidated.
2140
0
    NoteClosing();
2141
0
2142
0
    context->AllowToClose();
2143
0
  }
2144
0
}
2145
2146
} // namespace cache
2147
} // namespace dom
2148
} // namespace mozilla