Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/simpledb/ActorsParent.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 file,
5
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "ActorsParent.h"
8
9
#include "mozilla/Unused.h"
10
#include "mozilla/dom/PBackgroundSDBConnectionParent.h"
11
#include "mozilla/dom/PBackgroundSDBRequestParent.h"
12
#include "mozilla/dom/quota/Client.h"
13
#include "mozilla/dom/quota/FileStreams.h"
14
#include "mozilla/dom/quota/MemoryOutputStream.h"
15
#include "mozilla/dom/quota/QuotaManager.h"
16
#include "mozilla/dom/quota/UsageInfo.h"
17
#include "mozilla/ipc/BackgroundParent.h"
18
#include "mozilla/ipc/PBackgroundParent.h"
19
#include "mozilla/ipc/PBackgroundSharedTypes.h"
20
#include "nsIFileStreams.h"
21
#include "nsIDirectoryEnumerator.h"
22
#include "nsStringStream.h"
23
#include "prio.h"
24
#include "SimpleDBCommon.h"
25
26
#define DISABLE_ASSERTS_FOR_FUZZING 0
27
28
#if DISABLE_ASSERTS_FOR_FUZZING
29
#define ASSERT_UNLESS_FUZZING(...) do { } while (0)
30
#else
31
0
#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
32
#endif
33
34
namespace mozilla {
35
namespace dom {
36
37
using namespace mozilla::dom::quota;
38
using namespace mozilla::ipc;
39
40
namespace {
41
42
/*******************************************************************************
43
 * Constants
44
 ******************************************************************************/
45
46
const uint32_t kCopyBufferSize = 32768;
47
48
/*******************************************************************************
49
 * Actor class declarations
50
 ******************************************************************************/
51
52
class StreamHelper final
53
  : public Runnable
54
{
55
  nsCOMPtr<nsIEventTarget> mOwningEventTarget;
56
  nsCOMPtr<nsIFileStream> mFileStream;
57
  nsCOMPtr<nsIRunnable> mCallback;
58
59
public:
60
  StreamHelper(nsIFileStream* aFileStream,
61
               nsIRunnable* aCallback);
62
63
  void
64
  AsyncClose();
65
66
private:
67
  ~StreamHelper() override;
68
69
  void
70
  RunOnBackgroundThread();
71
72
  void
73
  RunOnIOThread();
74
75
  NS_DECL_NSIRUNNABLE
76
};
77
78
class Connection final
79
  : public PBackgroundSDBConnectionParent
80
{
81
  RefPtr<DirectoryLock> mDirectoryLock;
82
  nsCOMPtr<nsIFileStream> mFileStream;
83
  const PrincipalInfo mPrincipalInfo;
84
  nsCString mOrigin;
85
  nsString mName;
86
87
  bool mRunningRequest;
88
  bool mOpen;
89
  bool mAllowedToClose;
90
  bool mActorDestroyed;
91
92
public:
93
  explicit Connection(const PrincipalInfo& aPrincipalInfo);
94
95
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::Connection)
96
97
  nsIFileStream*
98
  GetFileStream() const
99
0
  {
100
0
    AssertIsOnIOThread();
101
0
102
0
    return mFileStream;
103
0
  }
104
105
  const PrincipalInfo&
106
  GetPrincipalInfo() const
107
0
  {
108
0
    MOZ_ASSERT(NS_IsMainThread());
109
0
110
0
    return mPrincipalInfo;
111
0
  }
112
113
  const nsCString&
114
  Origin() const
115
0
  {
116
0
    AssertIsOnBackgroundThread();
117
0
    MOZ_ASSERT(!mOrigin.IsEmpty());
118
0
119
0
    return mOrigin;
120
0
  }
121
122
  const nsString&
123
  Name() const
124
0
  {
125
0
    AssertIsOnBackgroundThread();
126
0
    MOZ_ASSERT(!mName.IsEmpty());
127
0
128
0
    return mName;
129
0
  }
130
131
  void
132
  OnNewRequest();
133
134
  void
135
  OnRequestFinished();
136
137
  void
138
  OnOpen(const nsACString& aOrigin,
139
         const nsAString& aName,
140
         already_AddRefed<DirectoryLock> aDirectoryLock,
141
         already_AddRefed<nsIFileStream> aFileStream);
142
143
  void
144
  OnClose();
145
146
  void
147
  AllowToClose();
148
149
private:
150
  ~Connection();
151
152
  void
153
  MaybeCloseStream();
154
155
  bool
156
  VerifyRequestParams(const SDBRequestParams& aParams) const;
157
158
  // IPDL methods.
159
  virtual void
160
  ActorDestroy(ActorDestroyReason aWhy) override;
161
162
  mozilla::ipc::IPCResult
163
  RecvDeleteMe() override;
164
165
  virtual PBackgroundSDBRequestParent*
166
  AllocPBackgroundSDBRequestParent(const SDBRequestParams& aParams) override;
167
168
  virtual mozilla::ipc::IPCResult
169
  RecvPBackgroundSDBRequestConstructor(PBackgroundSDBRequestParent* aActor,
170
                                       const SDBRequestParams& aParams)
171
                                       override;
172
173
  virtual bool
174
  DeallocPBackgroundSDBRequestParent(PBackgroundSDBRequestParent* aActor)
175
                                     override;
176
};
177
178
class ConnectionOperationBase
179
  : public Runnable
180
  , public PBackgroundSDBRequestParent
181
{
182
  nsCOMPtr<nsIEventTarget> mOwningEventTarget;
183
  RefPtr<Connection> mConnection;
184
  nsresult mResultCode;
185
  Atomic<bool> mOperationMayProceed;
186
  bool mActorDestroyed;
187
188
public:
189
  nsIEventTarget*
190
  OwningEventTarget() const
191
0
  {
192
0
    MOZ_ASSERT(mOwningEventTarget);
193
0
194
0
    return mOwningEventTarget;
195
0
  }
196
197
  bool
198
  IsOnOwningThread() const
199
0
  {
200
0
    MOZ_ASSERT(mOwningEventTarget);
201
0
202
0
    bool current;
203
0
    return
204
0
      NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) && current;
205
0
  }
206
207
  void
208
  AssertIsOnOwningThread() const
209
0
  {
210
0
    MOZ_ASSERT(IsOnBackgroundThread());
211
0
    MOZ_ASSERT(IsOnOwningThread());
212
0
  }
213
214
  Connection*
215
  GetConnection() const
216
0
  {
217
0
    MOZ_ASSERT(mConnection);
218
0
219
0
    return mConnection;
220
0
  }
221
222
  nsresult
223
  ResultCode() const
224
0
  {
225
0
    return mResultCode;
226
0
  }
227
228
  void
229
  MaybeSetFailureCode(nsresult aErrorCode)
230
0
  {
231
0
    MOZ_ASSERT(NS_FAILED(aErrorCode));
232
0
233
0
    if (NS_SUCCEEDED(mResultCode)) {
234
0
      mResultCode = aErrorCode;
235
0
    }
236
0
  }
237
238
  // May be called on any thread, but you should call IsActorDestroyed() if
239
  // you know you're on the background thread because it is slightly faster.
240
  bool
241
  OperationMayProceed() const
242
0
  {
243
0
    return mOperationMayProceed;
244
0
  }
245
246
  bool
247
  IsActorDestroyed() const
248
0
  {
249
0
    AssertIsOnOwningThread();
250
0
251
0
    return mActorDestroyed;
252
0
  }
253
254
  // May be overridden by subclasses if they need to perform work on the
255
  // background thread before being dispatched but must always call the base
256
  // class implementation. Returning false will kill the child actors and
257
  // prevent dispatch.
258
  virtual bool
259
  Init();
260
261
  virtual nsresult
262
  Dispatch();
263
264
  // This callback will be called on the background thread before releasing the
265
  // final reference to this request object. Subclasses may perform any
266
  // additional cleanup here but must always call the base class implementation.
267
  virtual void
268
  Cleanup();
269
270
protected:
271
  ConnectionOperationBase(Connection* aConnection)
272
    : Runnable("dom::ConnectionOperationBase")
273
    , mOwningEventTarget(GetCurrentThreadEventTarget())
274
    , mConnection(aConnection)
275
    , mResultCode(NS_OK)
276
    , mOperationMayProceed(true)
277
    , mActorDestroyed(false)
278
0
  {
279
0
    AssertIsOnOwningThread();
280
0
  }
281
282
  ~ConnectionOperationBase() override;
283
284
  void
285
  SendResults();
286
287
  void
288
  DatabaseWork();
289
290
  // Methods that subclasses must implement.
291
  virtual nsresult
292
  DoDatabaseWork(nsIFileStream* aFileStream) = 0;
293
294
  // Subclasses use this override to set the IPDL response value.
295
  virtual void
296
  GetResponse(SDBRequestResponse& aResponse) = 0;
297
298
  // A method that subclasses may implement.
299
  virtual void
300
  OnSuccess();
301
302
private:
303
  NS_IMETHOD
304
  Run() override;
305
306
  // IPDL methods.
307
  void
308
  ActorDestroy(ActorDestroyReason aWhy) override;
309
};
310
311
class OpenOp final
312
  : public ConnectionOperationBase
313
  , public OpenDirectoryListener
314
{
315
  enum class State
316
  {
317
    // Just created on the PBackground thread, dispatched to the main thread.
318
    // Next step is FinishOpen.
319
    Initial,
320
321
    // Opening directory or initializing quota manager on the PBackground
322
    // thread. Next step is either DirectoryOpenPending if quota manager is
323
    // already initialized or QuotaManagerPending if quota manager needs to be
324
    // initialized.
325
    FinishOpen,
326
327
    // Waiting for quota manager initialization to complete on the PBackground
328
    // thread. Next step is either SendingResults if initialization failed or
329
    // DirectoryOpenPending if initialization succeeded.
330
    QuotaManagerPending,
331
332
    // Waiting for directory open allowed on the PBackground thread. The next
333
    // step is either SendingResults if directory lock failed to acquire, or
334
    // DatabaseWorkOpen if directory lock is acquired.
335
    DirectoryOpenPending,
336
337
    // Waiting to do/doing work on the QuotaManager IO thread. Its next step is
338
    // SendingResults.
339
    DatabaseWorkOpen,
340
341
    // Waiting to send/sending results on the PBackground thread. Next step is
342
    // Completed.
343
    SendingResults,
344
345
    // All done.
346
    Completed
347
  };
348
349
  const SDBRequestOpenParams mParams;
350
  RefPtr<DirectoryLock> mDirectoryLock;
351
  nsCOMPtr<nsIFileStream> mFileStream;
352
  nsCString mSuffix;
353
  nsCString mGroup;
354
  nsCString mOrigin;
355
  State mState;
356
  bool mFileStreamOpen;
357
358
public:
359
  OpenOp(Connection* aConnection, const SDBRequestParams& aParams);
360
361
  nsresult
362
  Dispatch() override;
363
364
private:
365
  ~OpenOp() override;
366
367
  nsresult
368
  Open();
369
370
  nsresult
371
  FinishOpen();
372
373
  nsresult
374
  QuotaManagerOpen();
375
376
  nsresult
377
  OpenDirectory();
378
379
  nsresult
380
  SendToIOThread();
381
382
  nsresult
383
  DatabaseWork();
384
385
  void
386
  StreamClosedCallback();
387
388
  // ConnectionOperationBase overrides
389
  nsresult
390
  DoDatabaseWork(nsIFileStream* aFileStream) override;
391
392
  void
393
  GetResponse(SDBRequestResponse& aResponse) override;
394
395
  void
396
  OnSuccess() override;
397
398
  void
399
  Cleanup() override;
400
401
  NS_DECL_ISUPPORTS_INHERITED
402
403
  NS_IMETHOD
404
  Run() override;
405
406
  // OpenDirectoryListener overrides.
407
  void
408
  DirectoryLockAcquired(DirectoryLock* aLock) override;
409
410
  void
411
  DirectoryLockFailed() override;
412
};
413
414
class SeekOp final
415
  : public ConnectionOperationBase
416
{
417
  const SDBRequestSeekParams mParams;
418
419
public:
420
  SeekOp(Connection* aConnection,
421
         const SDBRequestParams& aParams);
422
423
private:
424
  ~SeekOp() override = default;
425
426
  nsresult
427
  DoDatabaseWork(nsIFileStream* aFileStream) override;
428
429
  void
430
  GetResponse(SDBRequestResponse& aResponse) override;
431
};
432
433
class ReadOp final
434
  : public ConnectionOperationBase
435
{
436
  const SDBRequestReadParams mParams;
437
438
  RefPtr<MemoryOutputStream> mOutputStream;
439
440
public:
441
  ReadOp(Connection* aConnection,
442
         const SDBRequestParams& aParams);
443
444
  bool
445
  Init() override;
446
447
private:
448
0
  ~ReadOp() override = default;
449
450
  nsresult
451
  DoDatabaseWork(nsIFileStream* aFileStream) override;
452
453
  void
454
  GetResponse(SDBRequestResponse& aResponse) override;
455
};
456
457
class WriteOp final
458
  : public ConnectionOperationBase
459
{
460
  const SDBRequestWriteParams mParams;
461
462
  nsCOMPtr<nsIInputStream> mInputStream;
463
464
  uint64_t mSize;
465
466
public:
467
  WriteOp(Connection* aConnection,
468
          const SDBRequestParams& aParams);
469
470
  bool
471
  Init() override;
472
473
private:
474
0
  ~WriteOp() override = default;
475
476
  nsresult
477
  DoDatabaseWork(nsIFileStream* aFileStream) override;
478
479
  void
480
  GetResponse(SDBRequestResponse& aResponse) override;
481
};
482
483
class CloseOp final
484
  : public ConnectionOperationBase
485
{
486
public:
487
  explicit CloseOp(Connection* aConnection);
488
489
private:
490
  ~CloseOp() override = default;
491
492
  nsresult
493
  DoDatabaseWork(nsIFileStream* aFileStream) override;
494
495
  void
496
  GetResponse(SDBRequestResponse& aResponse) override;
497
498
  void
499
  OnSuccess() override;
500
};
501
502
/*******************************************************************************
503
 * Other class declarations
504
 ******************************************************************************/
505
506
class QuotaClient final
507
  : public mozilla::dom::quota::Client
508
{
509
  static QuotaClient* sInstance;
510
511
  bool mShutdownRequested;
512
513
public:
514
  QuotaClient();
515
516
  static bool
517
  IsShuttingDownOnBackgroundThread()
518
0
  {
519
0
    AssertIsOnBackgroundThread();
520
0
521
0
    if (sInstance) {
522
0
      return sInstance->IsShuttingDown();
523
0
    }
524
0
525
0
    return QuotaManager::IsShuttingDown();
526
0
  }
527
528
  static bool
529
  IsShuttingDownOnNonBackgroundThread()
530
0
  {
531
0
    MOZ_ASSERT(!IsOnBackgroundThread());
532
0
533
0
    return QuotaManager::IsShuttingDown();
534
0
  }
535
536
  bool
537
  IsShuttingDown() const
538
0
  {
539
0
    AssertIsOnBackgroundThread();
540
0
541
0
    return mShutdownRequested;
542
0
  }
543
544
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QuotaClient, override)
545
546
  Type
547
  GetType() override;
548
549
  nsresult
550
  InitOrigin(PersistenceType aPersistenceType,
551
             const nsACString& aGroup,
552
             const nsACString& aOrigin,
553
             const AtomicBool& aCanceled,
554
             UsageInfo* aUsageInfo) override;
555
556
  nsresult
557
  GetUsageForOrigin(PersistenceType aPersistenceType,
558
                    const nsACString& aGroup,
559
                    const nsACString& aOrigin,
560
                    const AtomicBool& aCanceled,
561
                    UsageInfo* aUsageInfo) override;
562
563
  void
564
  OnOriginClearCompleted(PersistenceType aPersistenceType,
565
                         const nsACString& aOrigin)
566
                         override;
567
568
  void
569
  ReleaseIOThreadObjects() override;
570
571
  void
572
  AbortOperations(const nsACString& aOrigin) override;
573
574
  void
575
  AbortOperationsForProcess(ContentParentId aContentParentId) override;
576
577
  void
578
  StartIdleMaintenance() override;
579
580
  void
581
  StopIdleMaintenance() override;
582
583
  void
584
  ShutdownWorkThreads() override;
585
586
private:
587
  ~QuotaClient() override;
588
};
589
590
/*******************************************************************************
591
 * Globals
592
 ******************************************************************************/
593
594
typedef nsTArray<RefPtr<Connection>> ConnectionArray;
595
596
StaticAutoPtr<ConnectionArray> gOpenConnections;
597
598
} // namespace
599
600
/*******************************************************************************
601
 * Exported functions
602
 ******************************************************************************/
603
604
PBackgroundSDBConnectionParent*
605
AllocPBackgroundSDBConnectionParent(const PrincipalInfo& aPrincipalInfo)
606
0
{
607
0
  AssertIsOnBackgroundThread();
608
0
609
0
  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
610
0
    return nullptr;
611
0
  }
612
0
613
0
  if (NS_WARN_IF(aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) {
614
0
    ASSERT_UNLESS_FUZZING();
615
0
    return nullptr;
616
0
  }
617
0
618
0
  RefPtr<Connection> actor = new Connection(aPrincipalInfo);
619
0
620
0
  return actor.forget().take();
621
0
}
622
623
bool
624
RecvPBackgroundSDBConnectionConstructor(PBackgroundSDBConnectionParent* aActor,
625
                                        const PrincipalInfo& aPrincipalInfo)
626
0
{
627
0
  AssertIsOnBackgroundThread();
628
0
  MOZ_ASSERT(aActor);
629
0
  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
630
0
631
0
  return true;
632
0
}
633
634
bool
635
DeallocPBackgroundSDBConnectionParent(PBackgroundSDBConnectionParent* aActor)
636
0
{
637
0
  AssertIsOnBackgroundThread();
638
0
  MOZ_ASSERT(aActor);
639
0
640
0
  RefPtr<Connection> actor = dont_AddRef(static_cast<Connection*>(aActor));
641
0
  return true;
642
0
}
643
644
namespace simpledb {
645
646
already_AddRefed<mozilla::dom::quota::Client>
647
CreateQuotaClient()
648
0
{
649
0
  AssertIsOnBackgroundThread();
650
0
651
0
  RefPtr<QuotaClient> client = new QuotaClient();
652
0
  return client.forget();
653
0
}
654
655
} // namespace simpledb
656
657
/*******************************************************************************
658
 * StreamHelper
659
 ******************************************************************************/
660
661
StreamHelper::StreamHelper(nsIFileStream* aFileStream,
662
                           nsIRunnable* aCallback)
663
  : Runnable("dom::StreamHelper")
664
  , mOwningEventTarget(GetCurrentThreadEventTarget())
665
  , mFileStream(aFileStream)
666
  , mCallback(aCallback)
667
0
{
668
0
  AssertIsOnBackgroundThread();
669
0
  MOZ_ASSERT(aFileStream);
670
0
  MOZ_ASSERT(aCallback);
671
0
}
672
673
StreamHelper::~StreamHelper()
674
0
{
675
0
  MOZ_ASSERT(!mFileStream);
676
0
  MOZ_ASSERT(!mCallback);
677
0
}
678
679
void
680
StreamHelper::AsyncClose()
681
0
{
682
0
  AssertIsOnBackgroundThread();
683
0
684
0
  QuotaManager* quotaManager = QuotaManager::Get();
685
0
  MOZ_ASSERT(quotaManager);
686
0
687
0
  MOZ_ALWAYS_SUCCEEDS(
688
0
    quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL));
689
0
}
690
691
void
692
StreamHelper::RunOnBackgroundThread()
693
0
{
694
0
  AssertIsOnBackgroundThread();
695
0
696
0
  nsCOMPtr<nsIFileStream> fileStream;
697
0
  mFileStream.swap(fileStream);
698
0
699
0
  nsCOMPtr<nsIRunnable> callback;
700
0
  mCallback.swap(callback);
701
0
702
0
  callback->Run();
703
0
}
704
705
void
706
StreamHelper::RunOnIOThread()
707
0
{
708
0
  AssertIsOnIOThread();
709
0
  MOZ_ASSERT(mFileStream);
710
0
711
0
  nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(mFileStream);
712
0
  MOZ_ASSERT(inputStream);
713
0
714
0
  nsresult rv = inputStream->Close();
715
0
  Unused << NS_WARN_IF(NS_FAILED(rv));
716
0
717
0
  MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
718
0
}
719
720
NS_IMETHODIMP
721
StreamHelper::Run()
722
0
{
723
0
  MOZ_ASSERT(mCallback);
724
0
725
0
  if (IsOnBackgroundThread()) {
726
0
    RunOnBackgroundThread();
727
0
  } else {
728
0
    RunOnIOThread();
729
0
  }
730
0
731
0
  return NS_OK;
732
0
}
733
734
/*******************************************************************************
735
 * Connection
736
 ******************************************************************************/
737
738
Connection::Connection(const PrincipalInfo& aPrincipalInfo)
739
  : mPrincipalInfo(aPrincipalInfo)
740
  , mRunningRequest(false)
741
  , mOpen(false)
742
  , mAllowedToClose(false)
743
  , mActorDestroyed(false)
744
0
{
745
0
  AssertIsOnBackgroundThread();
746
0
  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
747
0
}
748
749
Connection::~Connection()
750
0
{
751
0
  MOZ_ASSERT(!mRunningRequest);
752
0
  MOZ_ASSERT(!mOpen);
753
0
  MOZ_ASSERT(mActorDestroyed);
754
0
}
755
756
void
757
Connection::OnNewRequest()
758
0
{
759
0
  AssertIsOnBackgroundThread();
760
0
  MOZ_ASSERT(!mRunningRequest);
761
0
762
0
  mRunningRequest = true;
763
0
}
764
765
void
766
Connection::OnRequestFinished()
767
0
{
768
0
  AssertIsOnBackgroundThread();
769
0
  MOZ_ASSERT(mRunningRequest);
770
0
771
0
  mRunningRequest = false;
772
0
773
0
  MaybeCloseStream();
774
0
}
775
776
void
777
Connection::OnOpen(const nsACString& aOrigin,
778
                   const nsAString& aName,
779
                   already_AddRefed<DirectoryLock> aDirectoryLock,
780
                   already_AddRefed<nsIFileStream> aFileStream)
781
0
{
782
0
  AssertIsOnBackgroundThread();
783
0
  MOZ_ASSERT(!aOrigin.IsEmpty());
784
0
  MOZ_ASSERT(!aName.IsEmpty());
785
0
  MOZ_ASSERT(mOrigin.IsEmpty());
786
0
  MOZ_ASSERT(mName.IsEmpty());
787
0
  MOZ_ASSERT(!mDirectoryLock);
788
0
  MOZ_ASSERT(!mFileStream);
789
0
  MOZ_ASSERT(!mOpen);
790
0
791
0
  mOrigin = aOrigin;
792
0
  mName = aName;
793
0
  mDirectoryLock = aDirectoryLock;
794
0
  mFileStream = aFileStream;
795
0
  mOpen = true;
796
0
797
0
  if (!gOpenConnections) {
798
0
    gOpenConnections = new ConnectionArray();
799
0
  }
800
0
801
0
  gOpenConnections->AppendElement(this);
802
0
}
803
804
void
805
Connection::OnClose()
806
0
{
807
0
  AssertIsOnBackgroundThread();
808
0
  MOZ_ASSERT(!mOrigin.IsEmpty());
809
0
  MOZ_ASSERT(mDirectoryLock);
810
0
  MOZ_ASSERT(mFileStream);
811
0
  MOZ_ASSERT(mOpen);
812
0
813
0
  mOrigin.Truncate();
814
0
  mName.Truncate();
815
0
  mDirectoryLock = nullptr;
816
0
  mFileStream = nullptr;
817
0
  mOpen = false;
818
0
819
0
  MOZ_ASSERT(gOpenConnections);
820
0
  gOpenConnections->RemoveElement(this);
821
0
822
0
  if (gOpenConnections->IsEmpty()) {
823
0
    gOpenConnections = nullptr;
824
0
  }
825
0
826
0
  if (mAllowedToClose && !mActorDestroyed) {
827
0
    Unused << SendClosed();
828
0
  }
829
0
}
830
831
void
832
Connection::AllowToClose()
833
0
{
834
0
  AssertIsOnBackgroundThread();
835
0
836
0
  if (mAllowedToClose) {
837
0
    return;
838
0
  }
839
0
840
0
  mAllowedToClose = true;
841
0
842
0
  if (!mActorDestroyed) {
843
0
    Unused << SendAllowToClose();
844
0
  }
845
0
846
0
  MaybeCloseStream();
847
0
}
848
849
void
850
Connection::MaybeCloseStream()
851
0
{
852
0
  AssertIsOnBackgroundThread();
853
0
854
0
  if (!mRunningRequest &&
855
0
      mOpen &&
856
0
      mAllowedToClose) {
857
0
    nsCOMPtr<nsIRunnable> callback =
858
0
      NewRunnableMethod("dom::Connection::OnClose",
859
0
                        this,
860
0
                        &Connection::OnClose);
861
0
862
0
    RefPtr<StreamHelper> helper = new StreamHelper(mFileStream, callback);
863
0
    helper->AsyncClose();
864
0
  }
865
0
}
866
867
bool
868
Connection::VerifyRequestParams(const SDBRequestParams& aParams) const
869
0
{
870
0
  AssertIsOnBackgroundThread();
871
0
  MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
872
0
873
0
  switch (aParams.type()) {
874
0
    case SDBRequestParams::TSDBRequestOpenParams: {
875
0
      if (NS_WARN_IF(mOpen)) {
876
0
        ASSERT_UNLESS_FUZZING();
877
0
        return false;
878
0
      }
879
0
880
0
      break;
881
0
    }
882
0
883
0
    case SDBRequestParams::TSDBRequestSeekParams:
884
0
    case SDBRequestParams::TSDBRequestReadParams:
885
0
    case SDBRequestParams::TSDBRequestWriteParams:
886
0
    case SDBRequestParams::TSDBRequestCloseParams: {
887
0
      if (NS_WARN_IF(!mOpen)) {
888
0
        ASSERT_UNLESS_FUZZING();
889
0
        return false;
890
0
      }
891
0
892
0
      break;
893
0
    }
894
0
895
0
    default:
896
0
      MOZ_CRASH("Should never get here!");
897
0
  }
898
0
899
0
  return true;
900
0
}
901
902
void
903
Connection::ActorDestroy(ActorDestroyReason aWhy)
904
0
{
905
0
  AssertIsOnBackgroundThread();
906
0
  MOZ_ASSERT(!mActorDestroyed);
907
0
908
0
  mActorDestroyed = true;
909
0
910
0
  AllowToClose();
911
0
}
912
913
mozilla::ipc::IPCResult
914
Connection::RecvDeleteMe()
915
0
{
916
0
  AssertIsOnBackgroundThread();
917
0
  MOZ_ASSERT(!mActorDestroyed);
918
0
919
0
  IProtocol* mgr = Manager();
920
0
  if (!PBackgroundSDBConnectionParent::Send__delete__(this)) {
921
0
    return IPC_FAIL_NO_REASON(mgr);
922
0
  }
923
0
924
0
  return IPC_OK();
925
0
}
926
927
PBackgroundSDBRequestParent*
928
Connection::AllocPBackgroundSDBRequestParent(const SDBRequestParams& aParams)
929
0
{
930
0
  AssertIsOnBackgroundThread();
931
0
  MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
932
0
933
0
  if (aParams.type() == SDBRequestParams::TSDBRequestOpenParams &&
934
0
      NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
935
0
    return nullptr;
936
0
  }
937
0
938
0
  if (mAllowedToClose) {
939
0
    return nullptr;
940
0
  }
941
0
942
#ifdef DEBUG
943
  // Always verify parameters in DEBUG builds!
944
  bool trustParams = false;
945
#else
946
0
  PBackgroundParent* backgroundActor = Manager();
947
0
  MOZ_ASSERT(backgroundActor);
948
0
949
0
  bool trustParams = !BackgroundParent::IsOtherProcessActor(backgroundActor);
950
0
#endif
951
0
952
0
  if (NS_WARN_IF(!trustParams && !VerifyRequestParams(aParams))) {
953
0
    ASSERT_UNLESS_FUZZING();
954
0
    return nullptr;
955
0
  }
956
0
957
0
  if (NS_WARN_IF(mRunningRequest)) {
958
0
    ASSERT_UNLESS_FUZZING();
959
0
    return nullptr;
960
0
  }
961
0
962
0
  RefPtr<ConnectionOperationBase> actor;
963
0
964
0
  switch (aParams.type()) {
965
0
    case SDBRequestParams::TSDBRequestOpenParams:
966
0
      actor = new OpenOp(this, aParams);
967
0
      break;
968
0
969
0
    case SDBRequestParams::TSDBRequestSeekParams:
970
0
      actor = new SeekOp(this, aParams);
971
0
      break;
972
0
973
0
    case SDBRequestParams::TSDBRequestReadParams:
974
0
      actor = new ReadOp(this, aParams);
975
0
      break;
976
0
977
0
    case SDBRequestParams::TSDBRequestWriteParams:
978
0
      actor = new WriteOp(this, aParams);
979
0
      break;
980
0
981
0
    case SDBRequestParams::TSDBRequestCloseParams:
982
0
      actor = new CloseOp(this);
983
0
      break;
984
0
985
0
    default:
986
0
      MOZ_CRASH("Should never get here!");
987
0
  }
988
0
989
0
  // Transfer ownership to IPDL.
990
0
  return actor.forget().take();
991
0
}
992
993
mozilla::ipc::IPCResult
994
Connection::RecvPBackgroundSDBRequestConstructor(
995
                                            PBackgroundSDBRequestParent* aActor,
996
                                            const SDBRequestParams& aParams)
997
0
{
998
0
  AssertIsOnBackgroundThread();
999
0
  MOZ_ASSERT(aActor);
1000
0
  MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
1001
0
  MOZ_ASSERT_IF(aParams.type() == SDBRequestParams::TSDBRequestOpenParams,
1002
0
                !QuotaClient::IsShuttingDownOnBackgroundThread());
1003
0
  MOZ_ASSERT(!mAllowedToClose);
1004
0
  MOZ_ASSERT(!mRunningRequest);
1005
0
1006
0
  auto* op = static_cast<ConnectionOperationBase*>(aActor);
1007
0
1008
0
  if (NS_WARN_IF(!op->Init())) {
1009
0
    op->Cleanup();
1010
0
    return IPC_FAIL_NO_REASON(this);
1011
0
  }
1012
0
1013
0
  if (NS_WARN_IF(NS_FAILED(op->Dispatch()))) {
1014
0
    op->Cleanup();
1015
0
    return IPC_FAIL_NO_REASON(this);
1016
0
  }
1017
0
1018
0
  return IPC_OK();
1019
0
}
1020
1021
bool
1022
Connection::DeallocPBackgroundSDBRequestParent(
1023
                                            PBackgroundSDBRequestParent* aActor)
1024
0
{
1025
0
  AssertIsOnBackgroundThread();
1026
0
  MOZ_ASSERT(aActor);
1027
0
1028
0
  // Transfer ownership back from IPDL.
1029
0
  RefPtr<ConnectionOperationBase> actor =
1030
0
    dont_AddRef(static_cast<ConnectionOperationBase*>(aActor));
1031
0
  return true;
1032
0
}
1033
1034
/*******************************************************************************
1035
 * ConnectionOperationBase
1036
 ******************************************************************************/
1037
1038
ConnectionOperationBase::~ConnectionOperationBase()
1039
0
{
1040
0
  MOZ_ASSERT(!mConnection,
1041
0
             "ConnectionOperationBase::Cleanup() was not called by a subclass!");
1042
0
  MOZ_ASSERT(mActorDestroyed);
1043
0
}
1044
1045
bool
1046
ConnectionOperationBase::Init()
1047
0
{
1048
0
  AssertIsOnBackgroundThread();
1049
0
  MOZ_ASSERT(mConnection);
1050
0
1051
0
  mConnection->OnNewRequest();
1052
0
1053
0
  return true;
1054
0
}
1055
1056
nsresult
1057
ConnectionOperationBase::Dispatch()
1058
0
{
1059
0
  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
1060
0
      IsActorDestroyed()) {
1061
0
    return NS_ERROR_FAILURE;
1062
0
  }
1063
0
1064
0
  QuotaManager* quotaManager = QuotaManager::Get();
1065
0
  MOZ_ASSERT(quotaManager);
1066
0
1067
0
  nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
1068
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1069
0
    return rv;
1070
0
  }
1071
0
1072
0
  return NS_OK;
1073
0
}
1074
1075
void
1076
ConnectionOperationBase::Cleanup()
1077
0
{
1078
0
  AssertIsOnOwningThread();
1079
0
  MOZ_ASSERT(mConnection);
1080
0
1081
0
  mConnection->OnRequestFinished();
1082
0
1083
0
  mConnection = nullptr;
1084
0
}
1085
1086
void
1087
ConnectionOperationBase::SendResults()
1088
0
{
1089
0
  AssertIsOnOwningThread();
1090
0
1091
0
  if (IsActorDestroyed()) {
1092
0
    MaybeSetFailureCode(NS_ERROR_FAILURE);
1093
0
  } else {
1094
0
    SDBRequestResponse response;
1095
0
1096
0
    if (NS_SUCCEEDED(mResultCode)) {
1097
0
      GetResponse(response);
1098
0
1099
0
      MOZ_ASSERT(response.type() != SDBRequestResponse::T__None);
1100
0
      MOZ_ASSERT(response.type() != SDBRequestResponse::Tnsresult);
1101
0
    } else {
1102
0
      response = mResultCode;
1103
0
    }
1104
0
1105
0
    Unused <<
1106
0
      PBackgroundSDBRequestParent::Send__delete__(this, response);
1107
0
1108
0
    if (NS_SUCCEEDED(mResultCode)) {
1109
0
      OnSuccess();
1110
0
    }
1111
0
  }
1112
0
1113
0
  Cleanup();
1114
0
}
1115
1116
void
1117
ConnectionOperationBase::DatabaseWork()
1118
0
{
1119
0
  AssertIsOnIOThread();
1120
0
  MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
1121
0
1122
0
  if (!OperationMayProceed()) {
1123
0
    // The operation was canceled in some way, likely because the child process
1124
0
    // has crashed.
1125
0
    mResultCode = NS_ERROR_FAILURE;
1126
0
  } else {
1127
0
    nsIFileStream* fileStream = mConnection->GetFileStream();
1128
0
    MOZ_ASSERT(fileStream);
1129
0
1130
0
    nsresult rv = DoDatabaseWork(fileStream);
1131
0
    if (NS_FAILED(rv)) {
1132
0
      mResultCode = rv;
1133
0
    }
1134
0
  }
1135
0
1136
0
  MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
1137
0
}
1138
1139
void
1140
ConnectionOperationBase::OnSuccess()
1141
0
{
1142
0
  AssertIsOnOwningThread();
1143
0
}
1144
1145
NS_IMETHODIMP
1146
ConnectionOperationBase::Run()
1147
0
{
1148
0
  if (IsOnBackgroundThread()) {
1149
0
    SendResults();
1150
0
  } else {
1151
0
    DatabaseWork();
1152
0
  }
1153
0
1154
0
  return NS_OK;
1155
0
}
1156
1157
void
1158
ConnectionOperationBase::ActorDestroy(ActorDestroyReason aWhy)
1159
0
{
1160
0
  AssertIsOnBackgroundThread();
1161
0
1162
0
  mOperationMayProceed = false;
1163
0
  mActorDestroyed = true;
1164
0
}
1165
1166
OpenOp::OpenOp(Connection* aConnection, const SDBRequestParams& aParams)
1167
  : ConnectionOperationBase(aConnection)
1168
  , mParams(aParams.get_SDBRequestOpenParams())
1169
  , mState(State::Initial)
1170
  , mFileStreamOpen(false)
1171
0
{
1172
0
  MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestOpenParams);
1173
0
}
1174
1175
OpenOp::~OpenOp()
1176
0
{
1177
0
  MOZ_ASSERT(!mDirectoryLock);
1178
0
  MOZ_ASSERT(!mFileStream);
1179
0
  MOZ_ASSERT(!mFileStreamOpen);
1180
0
  MOZ_ASSERT_IF(OperationMayProceed(),
1181
0
                mState == State::Initial || mState == State::Completed);
1182
0
}
1183
1184
nsresult
1185
OpenOp::Dispatch()
1186
0
{
1187
0
  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
1188
0
1189
0
  return NS_OK;
1190
0
}
1191
1192
nsresult
1193
OpenOp::Open()
1194
0
{
1195
0
  MOZ_ASSERT(NS_IsMainThread());
1196
0
  MOZ_ASSERT(mState == State::Initial);
1197
0
1198
0
  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
1199
0
      !OperationMayProceed()) {
1200
0
    return NS_ERROR_FAILURE;
1201
0
  }
1202
0
1203
0
  if (NS_WARN_IF(!Preferences::GetBool(kPrefSimpleDBEnabled, false))) {
1204
0
    return NS_ERROR_UNEXPECTED;
1205
0
  }
1206
0
1207
0
  const PrincipalInfo& principalInfo = GetConnection()->GetPrincipalInfo();
1208
0
1209
0
  if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
1210
0
    QuotaManager::GetInfoForChrome(&mSuffix, &mGroup, &mOrigin);
1211
0
  } else {
1212
0
    MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
1213
0
1214
0
    nsresult rv;
1215
0
    nsCOMPtr<nsIPrincipal> principal =
1216
0
      PrincipalInfoToPrincipal(principalInfo, &rv);
1217
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1218
0
      return rv;
1219
0
    }
1220
0
1221
0
    rv = QuotaManager::GetInfoFromPrincipal(principal,
1222
0
                                            &mSuffix,
1223
0
                                            &mGroup,
1224
0
                                            &mOrigin);
1225
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1226
0
      return rv;
1227
0
    }
1228
0
  }
1229
0
1230
0
  mState = State::FinishOpen;
1231
0
  MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
1232
0
1233
0
  return NS_OK;
1234
0
}
1235
1236
nsresult
1237
OpenOp::FinishOpen()
1238
0
{
1239
0
  AssertIsOnOwningThread();
1240
0
  MOZ_ASSERT(mState == State::FinishOpen);
1241
0
1242
0
  if (gOpenConnections) {
1243
0
    for (Connection* connection : *gOpenConnections) {
1244
0
      if (connection->Origin() == mOrigin &&
1245
0
          connection->Name() == mParams.name()) {
1246
0
        return NS_ERROR_STORAGE_BUSY;
1247
0
      }
1248
0
    }
1249
0
  }
1250
0
1251
0
  if (QuotaManager::Get()) {
1252
0
    nsresult rv = OpenDirectory();
1253
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1254
0
      return rv;
1255
0
    }
1256
0
1257
0
    return NS_OK;
1258
0
  }
1259
0
1260
0
  mState = State::QuotaManagerPending;
1261
0
  QuotaManager::GetOrCreate(this);
1262
0
1263
0
  return NS_OK;
1264
0
}
1265
1266
nsresult
1267
OpenOp::QuotaManagerOpen()
1268
0
{
1269
0
  AssertIsOnOwningThread();
1270
0
  MOZ_ASSERT(mState == State::QuotaManagerPending);
1271
0
1272
0
  if (NS_WARN_IF(!QuotaManager::Get())) {
1273
0
    return NS_ERROR_FAILURE;
1274
0
  }
1275
0
1276
0
  nsresult rv = OpenDirectory();
1277
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1278
0
    return rv;
1279
0
  }
1280
0
1281
0
  return NS_OK;
1282
0
}
1283
1284
nsresult
1285
OpenOp::OpenDirectory()
1286
0
{
1287
0
  AssertIsOnOwningThread();
1288
0
  MOZ_ASSERT(mState == State::FinishOpen ||
1289
0
             mState == State::QuotaManagerPending);
1290
0
  MOZ_ASSERT(!mOrigin.IsEmpty());
1291
0
  MOZ_ASSERT(!mDirectoryLock);
1292
0
  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
1293
0
  MOZ_ASSERT(QuotaManager::Get());
1294
0
1295
0
  mState = State::DirectoryOpenPending;
1296
0
  QuotaManager::Get()->OpenDirectory(PERSISTENCE_TYPE_DEFAULT,
1297
0
                                     mGroup,
1298
0
                                     mOrigin,
1299
0
                                     mozilla::dom::quota::Client::SDB,
1300
0
                                     /* aExclusive */ false,
1301
0
                                     this);
1302
0
1303
0
  return NS_OK;
1304
0
}
1305
1306
nsresult
1307
OpenOp::SendToIOThread()
1308
0
{
1309
0
  AssertIsOnOwningThread();
1310
0
  MOZ_ASSERT(mState == State::DirectoryOpenPending);
1311
0
1312
0
  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
1313
0
      IsActorDestroyed()) {
1314
0
    return NS_ERROR_FAILURE;
1315
0
  }
1316
0
1317
0
  mFileStream = new FileStream(PERSISTENCE_TYPE_DEFAULT, mGroup, mOrigin);
1318
0
1319
0
  QuotaManager* quotaManager = QuotaManager::Get();
1320
0
  MOZ_ASSERT(quotaManager);
1321
0
1322
0
  // Must set this before dispatching otherwise we will race with the IO thread.
1323
0
  mState = State::DatabaseWorkOpen;
1324
0
1325
0
  nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
1326
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1327
0
    return rv;
1328
0
  }
1329
0
1330
0
  return NS_OK;
1331
0
}
1332
1333
nsresult
1334
OpenOp::DatabaseWork()
1335
0
{
1336
0
  AssertIsOnIOThread();
1337
0
  MOZ_ASSERT(mState == State::DatabaseWorkOpen);
1338
0
  MOZ_ASSERT(mFileStream);
1339
0
  MOZ_ASSERT(!mFileStreamOpen);
1340
0
1341
0
  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
1342
0
      !OperationMayProceed()) {
1343
0
    return NS_ERROR_FAILURE;
1344
0
  }
1345
0
1346
0
  QuotaManager* quotaManager = QuotaManager::Get();
1347
0
  MOZ_ASSERT(quotaManager);
1348
0
1349
0
  nsCOMPtr<nsIFile> dbDirectory;
1350
0
  nsresult rv =
1351
0
    quotaManager->EnsureOriginIsInitialized(PERSISTENCE_TYPE_DEFAULT,
1352
0
                                            mSuffix,
1353
0
                                            mGroup,
1354
0
                                            mOrigin,
1355
0
                                            getter_AddRefs(dbDirectory));
1356
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1357
0
    return rv;
1358
0
  }
1359
0
1360
0
  rv = dbDirectory->Append(NS_LITERAL_STRING(SDB_DIRECTORY_NAME));
1361
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1362
0
    return rv;
1363
0
  }
1364
0
1365
0
  bool exists;
1366
0
  rv = dbDirectory->Exists(&exists);
1367
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1368
0
    return rv;
1369
0
  }
1370
0
1371
0
  if (!exists) {
1372
0
    rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
1373
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1374
0
      return rv;
1375
0
    }
1376
0
  }
1377
#ifdef DEBUG
1378
  else {
1379
    bool isDirectory;
1380
    MOZ_ASSERT(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)));
1381
    MOZ_ASSERT(isDirectory);
1382
  }
1383
#endif
1384
1385
0
  nsCOMPtr<nsIFile> dbFile;
1386
0
  rv = dbDirectory->Clone(getter_AddRefs(dbFile));
1387
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1388
0
    return rv;
1389
0
  }
1390
0
1391
0
  rv = dbFile->Append(mParams.name());
1392
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1393
0
    return rv;
1394
0
  }
1395
0
1396
0
  nsString databaseFilePath;
1397
0
  rv = dbFile->GetPath(databaseFilePath);
1398
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1399
0
    return rv;
1400
0
  }
1401
0
1402
0
  rv = mFileStream->Init(dbFile, PR_RDWR | PR_CREATE_FILE, 0644, 0);
1403
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1404
0
    return rv;
1405
0
  }
1406
0
1407
0
  mFileStreamOpen = true;
1408
0
1409
0
  rv = DoDatabaseWork(mFileStream);
1410
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1411
0
    return rv;
1412
0
  }
1413
0
1414
0
  // Must set mState before dispatching otherwise we will race with the owning
1415
0
  // thread.
1416
0
  mState = State::SendingResults;
1417
0
1418
0
  rv = OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL);
1419
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1420
0
    return rv;
1421
0
  }
1422
0
1423
0
  return NS_OK;
1424
0
}
1425
1426
void
1427
OpenOp::StreamClosedCallback()
1428
0
{
1429
0
  AssertIsOnOwningThread();
1430
0
  MOZ_ASSERT(NS_FAILED(ResultCode()));
1431
0
  MOZ_ASSERT(mDirectoryLock);
1432
0
  MOZ_ASSERT(mFileStream);
1433
0
  MOZ_ASSERT(mFileStreamOpen);
1434
0
1435
0
  mDirectoryLock = nullptr;
1436
0
  mFileStream = nullptr;
1437
0
  mFileStreamOpen = false;
1438
0
}
1439
1440
nsresult
1441
OpenOp::DoDatabaseWork(nsIFileStream* aFileStream)
1442
0
{
1443
0
  AssertIsOnIOThread();
1444
0
1445
0
  return NS_OK;
1446
0
}
1447
1448
void
1449
OpenOp::GetResponse(SDBRequestResponse& aResponse)
1450
0
{
1451
0
  AssertIsOnOwningThread();
1452
0
1453
0
  aResponse = SDBRequestOpenResponse();
1454
0
}
1455
1456
void
1457
OpenOp::OnSuccess()
1458
0
{
1459
0
  AssertIsOnOwningThread();
1460
0
  MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
1461
0
  MOZ_ASSERT(!mOrigin.IsEmpty());
1462
0
  MOZ_ASSERT(mDirectoryLock);
1463
0
  MOZ_ASSERT(mFileStream);
1464
0
  MOZ_ASSERT(mFileStreamOpen);
1465
0
1466
0
  RefPtr<DirectoryLock> directoryLock;
1467
0
  nsCOMPtr<nsIFileStream> fileStream;
1468
0
1469
0
  mDirectoryLock.swap(directoryLock);
1470
0
  mFileStream.swap(fileStream);
1471
0
  mFileStreamOpen = false;
1472
0
1473
0
  GetConnection()->OnOpen(mOrigin,
1474
0
                          mParams.name(),
1475
0
                          directoryLock.forget(),
1476
0
                          fileStream.forget());
1477
0
}
1478
1479
void
1480
OpenOp::Cleanup()
1481
0
{
1482
0
  AssertIsOnOwningThread();
1483
0
  MOZ_ASSERT_IF(mFileStreamOpen, mFileStream);
1484
0
1485
0
  if (mFileStream && mFileStreamOpen) {
1486
0
    // If we have an initialized file stream then the operation must have failed
1487
0
    // and there must be a directory lock too.
1488
0
    MOZ_ASSERT(NS_FAILED(ResultCode()));
1489
0
    MOZ_ASSERT(mDirectoryLock);
1490
0
1491
0
    // We must close the stream on the I/O thread before releasing it on this
1492
0
    // thread. The directory lock can't be released either.
1493
0
    nsCOMPtr<nsIRunnable> callback =
1494
0
      NewRunnableMethod("dom::OpenOp::StreamClosedCallback",
1495
0
                        this,
1496
0
                        &OpenOp::StreamClosedCallback);
1497
0
1498
0
    RefPtr<StreamHelper> helper = new StreamHelper(mFileStream, callback);
1499
0
    helper->AsyncClose();
1500
0
  } else {
1501
0
    MOZ_ASSERT(!mFileStreamOpen);
1502
0
1503
0
    mDirectoryLock = nullptr;
1504
0
    mFileStream = nullptr;
1505
0
  }
1506
0
1507
0
  ConnectionOperationBase::Cleanup();
1508
0
}
1509
1510
NS_IMPL_ISUPPORTS_INHERITED0(OpenOp, ConnectionOperationBase)
1511
1512
NS_IMETHODIMP
1513
OpenOp::Run()
1514
0
{
1515
0
  nsresult rv;
1516
0
1517
0
  switch (mState) {
1518
0
    case State::Initial:
1519
0
      rv = Open();
1520
0
      break;
1521
0
1522
0
    case State::FinishOpen:
1523
0
      rv = FinishOpen();
1524
0
      break;
1525
0
1526
0
    case State::QuotaManagerPending:
1527
0
      rv = QuotaManagerOpen();
1528
0
      break;
1529
0
1530
0
    case State::DatabaseWorkOpen:
1531
0
      rv = DatabaseWork();
1532
0
      break;
1533
0
1534
0
    case State::SendingResults:
1535
0
      SendResults();
1536
0
      return NS_OK;
1537
0
1538
0
    default:
1539
0
      MOZ_CRASH("Bad state!");
1540
0
  }
1541
0
1542
0
  if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingResults) {
1543
0
    MaybeSetFailureCode(rv);
1544
0
1545
0
    // Must set mState before dispatching otherwise we will race with the owning
1546
0
    // thread.
1547
0
    mState = State::SendingResults;
1548
0
1549
0
    if (IsOnOwningThread()) {
1550
0
      SendResults();
1551
0
    } else {
1552
0
      MOZ_ALWAYS_SUCCEEDS(
1553
0
        OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
1554
0
    }
1555
0
  }
1556
0
1557
0
  return NS_OK;
1558
0
}
1559
1560
void
1561
OpenOp::DirectoryLockAcquired(DirectoryLock* aLock)
1562
0
{
1563
0
  AssertIsOnOwningThread();
1564
0
  MOZ_ASSERT(mState == State::DirectoryOpenPending);
1565
0
  MOZ_ASSERT(!mDirectoryLock);
1566
0
1567
0
  mDirectoryLock = aLock;
1568
0
1569
0
  nsresult rv = SendToIOThread();
1570
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1571
0
    MaybeSetFailureCode(rv);
1572
0
1573
0
    // The caller holds a strong reference to us, no need for a self reference
1574
0
    // before calling Run().
1575
0
1576
0
    mState = State::SendingResults;
1577
0
    MOZ_ALWAYS_SUCCEEDS(Run());
1578
0
1579
0
    return;
1580
0
  }
1581
0
}
1582
1583
void
1584
OpenOp::DirectoryLockFailed()
1585
0
{
1586
0
  AssertIsOnOwningThread();
1587
0
  MOZ_ASSERT(mState == State::DirectoryOpenPending);
1588
0
  MOZ_ASSERT(!mDirectoryLock);
1589
0
1590
0
  MaybeSetFailureCode(NS_ERROR_FAILURE);
1591
0
1592
0
  // The caller holds a strong reference to us, no need for a self reference
1593
0
  // before calling Run().
1594
0
1595
0
  mState = State::SendingResults;
1596
0
  MOZ_ALWAYS_SUCCEEDS(Run());
1597
0
}
1598
1599
SeekOp::SeekOp(Connection* aConnection,
1600
               const SDBRequestParams& aParams)
1601
  : ConnectionOperationBase(aConnection)
1602
  , mParams(aParams.get_SDBRequestSeekParams())
1603
0
{
1604
0
  MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestSeekParams);
1605
0
}
1606
1607
nsresult
1608
SeekOp::DoDatabaseWork(nsIFileStream* aFileStream)
1609
0
{
1610
0
  AssertIsOnIOThread();
1611
0
  MOZ_ASSERT(aFileStream);
1612
0
1613
0
  nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aFileStream);
1614
0
  MOZ_ASSERT(seekableStream);
1615
0
1616
0
  nsresult rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_SET,
1617
0
                                     mParams.offset());
1618
0
1619
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1620
0
    return rv;
1621
0
  }
1622
0
1623
0
  return NS_OK;
1624
0
}
1625
1626
void
1627
SeekOp::GetResponse(SDBRequestResponse& aResponse)
1628
0
{
1629
0
  aResponse = SDBRequestSeekResponse();
1630
0
}
1631
1632
ReadOp::ReadOp(Connection* aConnection,
1633
               const SDBRequestParams& aParams)
1634
  : ConnectionOperationBase(aConnection)
1635
  , mParams(aParams.get_SDBRequestReadParams())
1636
0
{
1637
0
  MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestReadParams);
1638
0
}
1639
1640
bool
1641
ReadOp::Init()
1642
0
{
1643
0
  AssertIsOnOwningThread();
1644
0
1645
0
  if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
1646
0
    return false;
1647
0
  }
1648
0
1649
0
  mOutputStream = MemoryOutputStream::Create(mParams.size());
1650
0
  if (NS_WARN_IF(!mOutputStream)) {
1651
0
    return false;
1652
0
  }
1653
0
1654
0
  return true;
1655
0
}
1656
1657
nsresult
1658
ReadOp::DoDatabaseWork(nsIFileStream* aFileStream)
1659
0
{
1660
0
  AssertIsOnIOThread();
1661
0
  MOZ_ASSERT(aFileStream);
1662
0
1663
0
  nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(aFileStream);
1664
0
  MOZ_ASSERT(inputStream);
1665
0
1666
0
  nsresult rv;
1667
0
1668
0
  uint64_t offset = 0;
1669
0
1670
0
  do {
1671
0
    char copyBuffer[kCopyBufferSize];
1672
0
1673
0
    uint64_t max = mParams.size() - offset;
1674
0
    if (max == 0) {
1675
0
      break;
1676
0
    }
1677
0
1678
0
    uint32_t count = sizeof(copyBuffer);
1679
0
    if (count > max) {
1680
0
      count = max;
1681
0
    }
1682
0
1683
0
    uint32_t numRead;
1684
0
    rv = inputStream->Read(copyBuffer, count, &numRead);
1685
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1686
0
      return rv;
1687
0
    }
1688
0
1689
0
    if (!numRead) {
1690
0
      break;
1691
0
    }
1692
0
1693
0
    uint32_t numWrite;
1694
0
    rv = mOutputStream->Write(copyBuffer, numRead, &numWrite);
1695
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1696
0
      return rv;
1697
0
    }
1698
0
1699
0
    if (NS_WARN_IF(numWrite != numRead)) {
1700
0
      return NS_ERROR_FAILURE;
1701
0
    }
1702
0
1703
0
    offset += numWrite;
1704
0
  } while (true);
1705
0
1706
0
  MOZ_ASSERT(offset == mParams.size());
1707
0
1708
0
  MOZ_ALWAYS_SUCCEEDS(mOutputStream->Close());
1709
0
1710
0
  return NS_OK;
1711
0
}
1712
1713
void
1714
ReadOp::GetResponse(SDBRequestResponse& aResponse)
1715
0
{
1716
0
  aResponse = SDBRequestReadResponse(mOutputStream->Data());
1717
0
}
1718
1719
WriteOp::WriteOp(Connection* aConnection,
1720
                 const SDBRequestParams& aParams)
1721
  : ConnectionOperationBase(aConnection)
1722
  , mParams(aParams.get_SDBRequestWriteParams())
1723
  , mSize(0)
1724
0
{
1725
0
  MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestWriteParams);
1726
0
}
1727
1728
bool
1729
WriteOp::Init()
1730
0
{
1731
0
  AssertIsOnOwningThread();
1732
0
1733
0
  if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
1734
0
    return false;
1735
0
  }
1736
0
1737
0
  const nsCString& string = mParams.data();
1738
0
1739
0
  nsCOMPtr<nsIInputStream> inputStream;
1740
0
  nsresult rv =
1741
0
    NS_NewCStringInputStream(getter_AddRefs(inputStream), string);
1742
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1743
0
    return false;
1744
0
  }
1745
0
1746
0
  mInputStream = std::move(inputStream);
1747
0
  mSize = string.Length();
1748
0
1749
0
  return true;
1750
0
}
1751
1752
nsresult
1753
WriteOp::DoDatabaseWork(nsIFileStream* aFileStream)
1754
0
{
1755
0
  AssertIsOnIOThread();
1756
0
  MOZ_ASSERT(aFileStream);
1757
0
1758
0
  nsCOMPtr<nsIOutputStream> outputStream = do_QueryInterface(aFileStream);
1759
0
  MOZ_ASSERT(outputStream);
1760
0
1761
0
  nsresult rv;
1762
0
1763
0
  do {
1764
0
    char copyBuffer[kCopyBufferSize];
1765
0
1766
0
    uint32_t numRead;
1767
0
    rv = mInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead);
1768
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1769
0
      break;
1770
0
    }
1771
0
1772
0
    if (!numRead) {
1773
0
      break;
1774
0
    }
1775
0
1776
0
    uint32_t numWrite;
1777
0
    rv = outputStream->Write(copyBuffer, numRead, &numWrite);
1778
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1779
0
      return rv;
1780
0
    }
1781
0
1782
0
    if (NS_WARN_IF(numWrite != numRead)) {
1783
0
      return NS_ERROR_FAILURE;
1784
0
    }
1785
0
  } while (true);
1786
0
1787
0
  MOZ_ALWAYS_SUCCEEDS(mInputStream->Close());
1788
0
1789
0
  return NS_OK;
1790
0
}
1791
1792
void
1793
WriteOp::GetResponse(SDBRequestResponse& aResponse)
1794
0
{
1795
0
  aResponse = SDBRequestWriteResponse();
1796
0
}
1797
1798
CloseOp::CloseOp(Connection* aConnection)
1799
  : ConnectionOperationBase(aConnection)
1800
0
{ }
1801
1802
nsresult
1803
CloseOp::DoDatabaseWork(nsIFileStream* aFileStream)
1804
0
{
1805
0
  AssertIsOnIOThread();
1806
0
  MOZ_ASSERT(aFileStream);
1807
0
1808
0
  nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(aFileStream);
1809
0
  MOZ_ASSERT(inputStream);
1810
0
1811
0
  nsresult rv = inputStream->Close();
1812
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1813
0
    return rv;
1814
0
  }
1815
0
1816
0
  return NS_OK;
1817
0
}
1818
1819
void
1820
CloseOp::GetResponse(SDBRequestResponse& aResponse)
1821
0
{
1822
0
  aResponse = SDBRequestCloseResponse();
1823
0
}
1824
1825
void
1826
CloseOp::OnSuccess()
1827
0
{
1828
0
  AssertIsOnOwningThread();
1829
0
1830
0
  GetConnection()->OnClose();
1831
0
}
1832
1833
/*******************************************************************************
1834
 * QuotaClient
1835
 ******************************************************************************/
1836
1837
QuotaClient* QuotaClient::sInstance = nullptr;
1838
1839
QuotaClient::QuotaClient()
1840
  : mShutdownRequested(false)
1841
0
{
1842
0
  AssertIsOnBackgroundThread();
1843
0
  MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
1844
0
1845
0
  sInstance = this;
1846
0
}
1847
1848
QuotaClient::~QuotaClient()
1849
0
{
1850
0
  AssertIsOnBackgroundThread();
1851
0
  MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!");
1852
0
1853
0
  sInstance = nullptr;
1854
0
}
1855
1856
mozilla::dom::quota::Client::Type
1857
QuotaClient::GetType()
1858
0
{
1859
0
  return QuotaClient::SDB;
1860
0
}
1861
1862
nsresult
1863
QuotaClient::InitOrigin(PersistenceType aPersistenceType,
1864
                        const nsACString& aGroup,
1865
                        const nsACString& aOrigin,
1866
                        const AtomicBool& aCanceled,
1867
                        UsageInfo* aUsageInfo)
1868
0
{
1869
0
  AssertIsOnIOThread();
1870
0
1871
0
  if (!aUsageInfo) {
1872
0
    return NS_OK;
1873
0
  }
1874
0
1875
0
  return GetUsageForOrigin(aPersistenceType,
1876
0
                           aGroup,
1877
0
                           aOrigin,
1878
0
                           aCanceled,
1879
0
                           aUsageInfo);
1880
0
}
1881
1882
nsresult
1883
QuotaClient::GetUsageForOrigin(PersistenceType aPersistenceType,
1884
                               const nsACString& aGroup,
1885
                               const nsACString& aOrigin,
1886
                               const AtomicBool& aCanceled,
1887
                               UsageInfo* aUsageInfo)
1888
0
{
1889
0
  AssertIsOnIOThread();
1890
0
  MOZ_ASSERT(aUsageInfo);
1891
0
1892
0
  QuotaManager* quotaManager = QuotaManager::Get();
1893
0
  MOZ_ASSERT(quotaManager);
1894
0
1895
0
  nsCOMPtr<nsIFile> directory;
1896
0
  nsresult rv = quotaManager->GetDirectoryForOrigin(aPersistenceType, aOrigin,
1897
0
                                                    getter_AddRefs(directory));
1898
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1899
0
    return rv;
1900
0
  }
1901
0
1902
0
  MOZ_ASSERT(directory);
1903
0
1904
0
  rv = directory->Append(NS_LITERAL_STRING(SDB_DIRECTORY_NAME));
1905
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1906
0
    return rv;
1907
0
  }
1908
0
1909
0
  DebugOnly<bool> exists;
1910
0
  MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)) && exists);
1911
0
1912
0
  nsCOMPtr<nsIDirectoryEnumerator> entries;
1913
0
  rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
1914
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1915
0
    return rv;
1916
0
  }
1917
0
1918
0
  bool hasMore;
1919
0
  while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
1920
0
         hasMore && !aCanceled) {
1921
0
    nsCOMPtr<nsISupports> entry;
1922
0
    rv = entries->GetNext(getter_AddRefs(entry));
1923
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1924
0
      return rv;
1925
0
    }
1926
0
1927
0
    nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
1928
0
    MOZ_ASSERT(file);
1929
0
1930
0
    int64_t fileSize;
1931
0
    rv = file->GetFileSize(&fileSize);
1932
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1933
0
      return rv;
1934
0
    }
1935
0
1936
0
    MOZ_ASSERT(fileSize >= 0);
1937
0
1938
0
    aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
1939
0
  }
1940
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1941
0
    return rv;
1942
0
  }
1943
0
1944
0
  return NS_OK;
1945
0
}
1946
1947
void
1948
QuotaClient::OnOriginClearCompleted(PersistenceType aPersistenceType,
1949
                                    const nsACString& aOrigin)
1950
0
{
1951
0
  AssertIsOnIOThread();
1952
0
}
1953
1954
void
1955
QuotaClient::ReleaseIOThreadObjects()
1956
0
{
1957
0
  AssertIsOnIOThread();
1958
0
}
1959
1960
void
1961
QuotaClient::AbortOperations(const nsACString& aOrigin)
1962
0
{
1963
0
  AssertIsOnBackgroundThread();
1964
0
1965
0
  if (gOpenConnections) {
1966
0
    for (Connection* connection : *gOpenConnections) {
1967
0
      if (aOrigin.IsVoid() || connection->Origin() == aOrigin) {
1968
0
        connection->AllowToClose();
1969
0
      }
1970
0
    }
1971
0
  }
1972
0
}
1973
1974
void
1975
QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId)
1976
0
{
1977
0
  AssertIsOnBackgroundThread();
1978
0
}
1979
1980
void
1981
QuotaClient::StartIdleMaintenance()
1982
0
{
1983
0
  AssertIsOnBackgroundThread();
1984
0
}
1985
1986
void
1987
QuotaClient::StopIdleMaintenance()
1988
0
{
1989
0
  AssertIsOnBackgroundThread();
1990
0
}
1991
1992
void
1993
QuotaClient::ShutdownWorkThreads()
1994
0
{
1995
0
  AssertIsOnBackgroundThread();
1996
0
  MOZ_ASSERT(!mShutdownRequested);
1997
0
1998
0
  mShutdownRequested = true;
1999
0
2000
0
  if (gOpenConnections) {
2001
0
    for (Connection* connection : *gOpenConnections) {
2002
0
      connection->AllowToClose();
2003
0
    }
2004
0
2005
0
    MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return !gOpenConnections; }));
2006
0
  }
2007
0
}
2008
2009
} // namespace dom
2010
} // namespace mozilla