tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ServiceWorkerJob.cpp (7212B)


      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 "ServiceWorkerJob.h"
      8 
      9 #include "ServiceWorkerManager.h"
     10 #include "mozilla/dom/WorkerCommon.h"
     11 #include "nsIPrincipal.h"
     12 #include "nsProxyRelease.h"
     13 #include "nsThreadUtils.h"
     14 
     15 namespace mozilla::dom {
     16 
     17 ServiceWorkerJob::Type ServiceWorkerJob::GetType() const { return mType; }
     18 
     19 ServiceWorkerJob::State ServiceWorkerJob::GetState() const { return mState; }
     20 
     21 bool ServiceWorkerJob::Canceled() const { return mCanceled; }
     22 
     23 bool ServiceWorkerJob::ResultCallbacksInvoked() const {
     24  return mResultCallbacksInvoked;
     25 }
     26 
     27 bool ServiceWorkerJob::IsEquivalentTo(ServiceWorkerJob* aJob) const {
     28  MOZ_ASSERT(NS_IsMainThread());
     29  MOZ_ASSERT(aJob);
     30  return mType == aJob->mType && mScope.Equals(aJob->mScope) &&
     31         mScriptSpec.Equals(aJob->mScriptSpec) &&
     32         mPrincipal->Equals(aJob->mPrincipal);
     33 }
     34 
     35 void ServiceWorkerJob::AppendResultCallback(Callback* aCallback) {
     36  MOZ_ASSERT(NS_IsMainThread());
     37  MOZ_DIAGNOSTIC_ASSERT(mState != State::Finished);
     38  MOZ_DIAGNOSTIC_ASSERT(aCallback);
     39  MOZ_DIAGNOSTIC_ASSERT(mFinalCallback != aCallback);
     40  MOZ_ASSERT(!mResultCallbackList.Contains(aCallback));
     41  MOZ_DIAGNOSTIC_ASSERT(!mResultCallbacksInvoked);
     42  mResultCallbackList.AppendElement(aCallback);
     43 }
     44 
     45 void ServiceWorkerJob::StealResultCallbacksFrom(ServiceWorkerJob* aJob) {
     46  MOZ_ASSERT(NS_IsMainThread());
     47  MOZ_ASSERT(aJob);
     48  MOZ_ASSERT(aJob->mState == State::Initial);
     49 
     50  // Take the callbacks from the other job immediately to avoid the
     51  // any possibility of them existing on both jobs at once.
     52  nsTArray<RefPtr<Callback>> callbackList =
     53      std::move(aJob->mResultCallbackList);
     54 
     55  for (RefPtr<Callback>& callback : callbackList) {
     56    // Use AppendResultCallback() so that assertion checking is performed on
     57    // each callback.
     58    AppendResultCallback(callback);
     59  }
     60 }
     61 
     62 void ServiceWorkerJob::Start(Callback* aFinalCallback) {
     63  MOZ_ASSERT(NS_IsMainThread());
     64  MOZ_DIAGNOSTIC_ASSERT(!mCanceled);
     65 
     66  MOZ_DIAGNOSTIC_ASSERT(aFinalCallback);
     67  MOZ_DIAGNOSTIC_ASSERT(!mFinalCallback);
     68  MOZ_ASSERT(!mResultCallbackList.Contains(aFinalCallback));
     69  mFinalCallback = aFinalCallback;
     70 
     71  MOZ_DIAGNOSTIC_ASSERT(mState == State::Initial);
     72  mState = State::Started;
     73 
     74  nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(
     75      "ServiceWorkerJob::AsyncExecute", this, &ServiceWorkerJob::AsyncExecute);
     76 
     77  // We may have to wait for the PBackground actor to be initialized
     78  // before proceeding.  We should always be able to get a ServiceWorkerManager,
     79  // however, since Start() should not be called during shutdown.
     80  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     81  if (!swm) {
     82    // browser shutdown
     83    return;
     84  }
     85 
     86  // Otherwise start asynchronously.  We should never run a job synchronously.
     87  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable.forget())));
     88 }
     89 
     90 void ServiceWorkerJob::Cancel() {
     91  MOZ_ASSERT(NS_IsMainThread());
     92  MOZ_ASSERT(!mCanceled);
     93  mCanceled = true;
     94 
     95  if (GetState() != State::Started) {
     96    MOZ_ASSERT(GetState() == State::Initial);
     97 
     98    ErrorResult error(NS_ERROR_DOM_ABORT_ERR);
     99    InvokeResultCallbacks(error);
    100 
    101    // The callbacks might not consume the error, which is fine.
    102    error.SuppressException();
    103  }
    104 }
    105 
    106 ServiceWorkerJob::ServiceWorkerJob(Type aType, nsIPrincipal* aPrincipal,
    107                                   const nsACString& aScope,
    108                                   nsCString aScriptSpec)
    109    : mType(aType),
    110      mPrincipal(aPrincipal),
    111      mScope(aScope),
    112      mScriptSpec(std::move(aScriptSpec)),
    113      mState(State::Initial),
    114      mCanceled(false),
    115      mResultCallbacksInvoked(false) {
    116  MOZ_ASSERT(NS_IsMainThread());
    117  MOZ_ASSERT(mPrincipal);
    118  MOZ_ASSERT(!mScope.IsEmpty());
    119 
    120  // Empty script URL if and only if this is an unregister job.
    121  MOZ_ASSERT((mType == Type::Unregister) == mScriptSpec.IsEmpty());
    122 }
    123 
    124 ServiceWorkerJob::~ServiceWorkerJob() {
    125  MOZ_ASSERT(NS_IsMainThread());
    126  // Jobs must finish or never be started.  Destroying an actively running
    127  // job is an error.
    128  MOZ_ASSERT(mState != State::Started);
    129  MOZ_ASSERT_IF(mState == State::Finished, mResultCallbacksInvoked);
    130 }
    131 
    132 void ServiceWorkerJob::InvokeResultCallbacks(ErrorResult& aRv) {
    133  MOZ_ASSERT(NS_IsMainThread());
    134  MOZ_DIAGNOSTIC_ASSERT(mState != State::Finished);
    135  MOZ_DIAGNOSTIC_ASSERT_IF(mState == State::Initial, Canceled());
    136 
    137  MOZ_DIAGNOSTIC_ASSERT(!mResultCallbacksInvoked);
    138  mResultCallbacksInvoked = true;
    139 
    140  nsTArray<RefPtr<Callback>> callbackList = std::move(mResultCallbackList);
    141 
    142  for (RefPtr<Callback>& callback : callbackList) {
    143    // The callback might consume an exception on the ErrorResult, so we need
    144    // to clone in order to maintain the error for the next callback.
    145    ErrorResult rv;
    146    aRv.CloneTo(rv);
    147 
    148    if (GetState() == State::Started) {
    149      callback->JobFinished(this, rv);
    150    } else {
    151      callback->JobDiscarded(rv);
    152    }
    153 
    154    // The callback might not consume the error.
    155    rv.SuppressException();
    156  }
    157 }
    158 
    159 void ServiceWorkerJob::InvokeResultCallbacks(nsresult aRv) {
    160  ErrorResult converted(aRv);
    161  InvokeResultCallbacks(converted);
    162 }
    163 
    164 void ServiceWorkerJob::Finish(ErrorResult& aRv) {
    165  MOZ_ASSERT(NS_IsMainThread());
    166 
    167  // Finishing a job is idempotent and potentially expected by the error
    168  // handling path for ServiceWorkerUpdateJob, so this is not an error.
    169  if (mState != State::Started) {
    170    return;
    171  }
    172 
    173  // Ensure that we only surface SecurityErr, TypeErr or InvalidStateErr to
    174  // script.
    175  if (aRv.Failed() && !aRv.ErrorCodeIs(NS_ERROR_DOM_SECURITY_ERR) &&
    176      !aRv.ErrorCodeIs(NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR) &&
    177      !aRv.ErrorCodeIs(NS_ERROR_DOM_INVALID_STATE_ERR)) {
    178    // Remove the old error code so we can replace it with a TypeError.
    179    aRv.SuppressException();
    180 
    181    // Throw the type error with a generic error message.  We use a stack
    182    // reference to bypass the normal static analysis for "return right after
    183    // throwing", since it's not the right check here: this ErrorResult came in
    184    // pre-thrown.
    185    ErrorResult& rv = aRv;
    186    rv.ThrowTypeError<MSG_SW_INSTALL_ERROR>(mScriptSpec, mScope);
    187  }
    188 
    189  // The final callback may drop the last ref to this object.
    190  RefPtr<ServiceWorkerJob> kungFuDeathGrip = this;
    191 
    192  if (!mResultCallbacksInvoked) {
    193    InvokeResultCallbacks(aRv);
    194  }
    195 
    196  mState = State::Finished;
    197 
    198  MOZ_DIAGNOSTIC_ASSERT(mFinalCallback);
    199  if (mFinalCallback) {
    200    mFinalCallback->JobFinished(this, aRv);
    201    mFinalCallback = nullptr;
    202  }
    203 
    204  // The callback might not consume the error.
    205  aRv.SuppressException();
    206 
    207  // Async release this object to ensure that our caller methods complete
    208  // as well.
    209  NS_ReleaseOnMainThread("ServiceWorkerJobProxyRunnable",
    210                         kungFuDeathGrip.forget(), true /* always proxy */);
    211 }
    212 
    213 void ServiceWorkerJob::Finish(nsresult aRv) {
    214  ErrorResult converted(aRv);
    215  Finish(converted);
    216 }
    217 
    218 }  // namespace mozilla::dom