ServiceWorkerJob.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "ServiceWorkerJob.h"
  6. #include "nsProxyRelease.h"
  7. #include "nsThreadUtils.h"
  8. #include "Workers.h"
  9. #include "ServiceWorkerManager.h"
  10. namespace mozilla {
  11. namespace dom {
  12. namespace workers {
  13. ServiceWorkerJob::Type
  14. ServiceWorkerJob::GetType() const
  15. {
  16. return mType;
  17. }
  18. ServiceWorkerJob::State
  19. ServiceWorkerJob::GetState() const
  20. {
  21. return mState;
  22. }
  23. bool
  24. ServiceWorkerJob::Canceled() const
  25. {
  26. return mCanceled;
  27. }
  28. bool
  29. ServiceWorkerJob::ResultCallbacksInvoked() const
  30. {
  31. return mResultCallbacksInvoked;
  32. }
  33. bool
  34. ServiceWorkerJob::IsEquivalentTo(ServiceWorkerJob* aJob) const
  35. {
  36. AssertIsOnMainThread();
  37. MOZ_ASSERT(aJob);
  38. return mType == aJob->mType &&
  39. mScope.Equals(aJob->mScope) &&
  40. mScriptSpec.Equals(aJob->mScriptSpec) &&
  41. mPrincipal->Equals(aJob->mPrincipal);
  42. }
  43. void
  44. ServiceWorkerJob::AppendResultCallback(Callback* aCallback)
  45. {
  46. AssertIsOnMainThread();
  47. MOZ_DIAGNOSTIC_ASSERT(mState != State::Finished);
  48. MOZ_DIAGNOSTIC_ASSERT(aCallback);
  49. MOZ_DIAGNOSTIC_ASSERT(mFinalCallback != aCallback);
  50. MOZ_ASSERT(!mResultCallbackList.Contains(aCallback));
  51. MOZ_DIAGNOSTIC_ASSERT(!mResultCallbacksInvoked);
  52. mResultCallbackList.AppendElement(aCallback);
  53. }
  54. void
  55. ServiceWorkerJob::StealResultCallbacksFrom(ServiceWorkerJob* aJob)
  56. {
  57. AssertIsOnMainThread();
  58. MOZ_ASSERT(aJob);
  59. MOZ_ASSERT(aJob->mState == State::Initial);
  60. // Take the callbacks from the other job immediately to avoid the
  61. // any possibility of them existing on both jobs at once.
  62. nsTArray<RefPtr<Callback>> callbackList;
  63. callbackList.SwapElements(aJob->mResultCallbackList);
  64. for (RefPtr<Callback>& callback : callbackList) {
  65. // Use AppendResultCallback() so that assertion checking is performed on
  66. // each callback.
  67. AppendResultCallback(callback);
  68. }
  69. }
  70. void
  71. ServiceWorkerJob::Start(Callback* aFinalCallback)
  72. {
  73. AssertIsOnMainThread();
  74. MOZ_DIAGNOSTIC_ASSERT(!mCanceled);
  75. MOZ_DIAGNOSTIC_ASSERT(aFinalCallback);
  76. MOZ_DIAGNOSTIC_ASSERT(!mFinalCallback);
  77. MOZ_ASSERT(!mResultCallbackList.Contains(aFinalCallback));
  78. mFinalCallback = aFinalCallback;
  79. MOZ_DIAGNOSTIC_ASSERT(mState == State::Initial);
  80. mState = State::Started;
  81. nsCOMPtr<nsIRunnable> runnable =
  82. NewRunnableMethod(this, &ServiceWorkerJob::AsyncExecute);
  83. // We may have to wait for the PBackground actor to be initialized
  84. // before proceeding. We should always be able to get a ServiceWorkerManager,
  85. // however, since Start() should not be called during shutdown.
  86. RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
  87. if (!swm) {
  88. // browser shutdown
  89. return;
  90. }
  91. if (!swm->HasBackgroundActor()) {
  92. // waiting to initialize
  93. swm->AppendPendingOperation(runnable);
  94. return;
  95. }
  96. // Otherwise start asynchronously. We should never run a job synchronously.
  97. MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
  98. NS_DispatchToMainThread(runnable.forget())));
  99. }
  100. void
  101. ServiceWorkerJob::Cancel()
  102. {
  103. AssertIsOnMainThread();
  104. MOZ_ASSERT(!mCanceled);
  105. mCanceled = true;
  106. }
  107. ServiceWorkerJob::ServiceWorkerJob(Type aType,
  108. nsIPrincipal* aPrincipal,
  109. const nsACString& aScope,
  110. const nsACString& aScriptSpec)
  111. : mType(aType)
  112. , mPrincipal(aPrincipal)
  113. , mScope(aScope)
  114. , mScriptSpec(aScriptSpec)
  115. , mState(State::Initial)
  116. , mCanceled(false)
  117. , mResultCallbacksInvoked(false)
  118. {
  119. AssertIsOnMainThread();
  120. MOZ_ASSERT(mPrincipal);
  121. MOZ_ASSERT(!mScope.IsEmpty());
  122. // Some job types may have an empty script spec
  123. }
  124. ServiceWorkerJob::~ServiceWorkerJob()
  125. {
  126. AssertIsOnMainThread();
  127. // Jobs must finish or never be started. Destroying an actively running
  128. // job is an error.
  129. MOZ_ASSERT(mState != State::Started);
  130. MOZ_ASSERT_IF(mState == State::Finished, mResultCallbacksInvoked);
  131. }
  132. void
  133. ServiceWorkerJob::InvokeResultCallbacks(ErrorResult& aRv)
  134. {
  135. AssertIsOnMainThread();
  136. MOZ_DIAGNOSTIC_ASSERT(mState == State::Started);
  137. MOZ_DIAGNOSTIC_ASSERT(!mResultCallbacksInvoked);
  138. mResultCallbacksInvoked = true;
  139. nsTArray<RefPtr<Callback>> callbackList;
  140. callbackList.SwapElements(mResultCallbackList);
  141. for (RefPtr<Callback>& callback : callbackList) {
  142. // The callback might consume an exception on the ErrorResult, so we need
  143. // to clone in order to maintain the error for the next callback.
  144. ErrorResult rv;
  145. aRv.CloneTo(rv);
  146. callback->JobFinished(this, rv);
  147. // The callback might not consume the error.
  148. rv.SuppressException();
  149. }
  150. }
  151. void
  152. ServiceWorkerJob::InvokeResultCallbacks(nsresult aRv)
  153. {
  154. ErrorResult converted(aRv);
  155. InvokeResultCallbacks(converted);
  156. }
  157. void
  158. ServiceWorkerJob::Finish(ErrorResult& aRv)
  159. {
  160. AssertIsOnMainThread();
  161. // Avoid double-completion because it can result on operating on cleaned
  162. // up data. This should not happen, though, so also assert to try to
  163. // narrow down the causes.
  164. MOZ_DIAGNOSTIC_ASSERT(mState == State::Started);
  165. if (mState != State::Started) {
  166. return;
  167. }
  168. // Ensure that we only surface SecurityErr, TypeErr or InvalidStateErr to script.
  169. if (aRv.Failed() && !aRv.ErrorCodeIs(NS_ERROR_DOM_SECURITY_ERR) &&
  170. !aRv.ErrorCodeIs(NS_ERROR_DOM_TYPE_ERR) &&
  171. !aRv.ErrorCodeIs(NS_ERROR_DOM_INVALID_STATE_ERR)) {
  172. // Remove the old error code so we can replace it with a TypeError.
  173. aRv.SuppressException();
  174. NS_ConvertUTF8toUTF16 scriptSpec(mScriptSpec);
  175. NS_ConvertUTF8toUTF16 scope(mScope);
  176. // Throw the type error with a generic error message.
  177. aRv.ThrowTypeError<MSG_SW_INSTALL_ERROR>(scriptSpec, scope);
  178. }
  179. // The final callback may drop the last ref to this object.
  180. RefPtr<ServiceWorkerJob> kungFuDeathGrip = this;
  181. if (!mResultCallbacksInvoked) {
  182. InvokeResultCallbacks(aRv);
  183. }
  184. mState = State::Finished;
  185. MOZ_DIAGNOSTIC_ASSERT(mFinalCallback);
  186. if (mFinalCallback) {
  187. mFinalCallback->JobFinished(this, aRv);
  188. mFinalCallback = nullptr;
  189. }
  190. // The callback might not consume the error.
  191. aRv.SuppressException();
  192. // Async release this object to ensure that our caller methods complete
  193. // as well.
  194. NS_ReleaseOnMainThread(kungFuDeathGrip.forget(), true /* always proxy */);
  195. }
  196. void
  197. ServiceWorkerJob::Finish(nsresult aRv)
  198. {
  199. ErrorResult converted(aRv);
  200. Finish(converted);
  201. }
  202. } // namespace workers
  203. } // namespace dom
  204. } // namespace mozilla