dom/workers/WorkerRunnable.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/workers/WorkerRunnable.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,525 @@
     1.4 +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "WorkerRunnable.h"
    1.10 +
    1.11 +#include "nsIEventTarget.h"
    1.12 +#include "nsIRunnable.h"
    1.13 +#include "nsThreadUtils.h"
    1.14 +
    1.15 +#include "mozilla/DebugOnly.h"
    1.16 +
    1.17 +#include "js/RootingAPI.h"
    1.18 +#include "js/Value.h"
    1.19 +
    1.20 +#include "WorkerPrivate.h"
    1.21 +
    1.22 +USING_WORKERS_NAMESPACE
    1.23 +
    1.24 +namespace {
    1.25 +
    1.26 +const nsIID kWorkerRunnableIID = {
    1.27 +  0x320cc0b5, 0xef12, 0x4084, { 0x88, 0x6e, 0xca, 0x6a, 0x81, 0xe4, 0x1d, 0x68 }
    1.28 +};
    1.29 +
    1.30 +void
    1.31 +MaybeReportMainThreadException(JSContext* aCx, bool aResult)
    1.32 +{
    1.33 +  AssertIsOnMainThread();
    1.34 +
    1.35 +  if (aCx && !aResult) {
    1.36 +    JS_ReportPendingException(aCx);
    1.37 +  }
    1.38 +}
    1.39 +
    1.40 +} // anonymous namespace
    1.41 +
    1.42 +#ifdef DEBUG
    1.43 +WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate,
    1.44 +                               TargetAndBusyBehavior aBehavior)
    1.45 +: mWorkerPrivate(aWorkerPrivate), mBehavior(aBehavior), mCanceled(0),
    1.46 +  mCallingCancelWithinRun(false)
    1.47 +{
    1.48 +  MOZ_ASSERT(aWorkerPrivate);
    1.49 +}
    1.50 +#endif
    1.51 +
    1.52 +bool
    1.53 +WorkerRunnable::PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1.54 +{
    1.55 +#ifdef DEBUG
    1.56 +  MOZ_ASSERT(aWorkerPrivate);
    1.57 +
    1.58 +  switch (mBehavior) {
    1.59 +    case ParentThreadUnchangedBusyCount:
    1.60 +      aWorkerPrivate->AssertIsOnWorkerThread();
    1.61 +      break;
    1.62 +
    1.63 +    case WorkerThreadModifyBusyCount:
    1.64 +      aWorkerPrivate->AssertIsOnParentThread();
    1.65 +      MOZ_ASSERT(aCx);
    1.66 +      break;
    1.67 +
    1.68 +    case WorkerThreadUnchangedBusyCount:
    1.69 +      aWorkerPrivate->AssertIsOnParentThread();
    1.70 +      break;
    1.71 +
    1.72 +    default:
    1.73 +      MOZ_ASSUME_UNREACHABLE("Unknown behavior!");
    1.74 +  }
    1.75 +#endif
    1.76 +
    1.77 +  if (mBehavior == WorkerThreadModifyBusyCount) {
    1.78 +    return aWorkerPrivate->ModifyBusyCount(aCx, true);
    1.79 +  }
    1.80 +
    1.81 +  return true;
    1.82 +}
    1.83 +
    1.84 +bool
    1.85 +WorkerRunnable::Dispatch(JSContext* aCx)
    1.86 +{
    1.87 +  bool ok;
    1.88 +
    1.89 +  if (!aCx) {
    1.90 +    ok = PreDispatch(nullptr, mWorkerPrivate);
    1.91 +    if (ok) {
    1.92 +      ok = DispatchInternal();
    1.93 +    }
    1.94 +    PostDispatch(nullptr, mWorkerPrivate, ok);
    1.95 +    return ok;
    1.96 +  }
    1.97 +
    1.98 +  JSAutoRequest ar(aCx);
    1.99 +
   1.100 +  JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
   1.101 +
   1.102 +  Maybe<JSAutoCompartment> ac;
   1.103 +  if (global) {
   1.104 +    ac.construct(aCx, global);
   1.105 +  }
   1.106 +
   1.107 +  ok = PreDispatch(aCx, mWorkerPrivate);
   1.108 +
   1.109 +  if (ok && !DispatchInternal()) {
   1.110 +    ok = false;
   1.111 +  }
   1.112 +
   1.113 +  PostDispatch(aCx, mWorkerPrivate, ok);
   1.114 +
   1.115 +  return ok;
   1.116 +}
   1.117 +
   1.118 +bool
   1.119 +WorkerRunnable::DispatchInternal()
   1.120 +{
   1.121 +  if (mBehavior == WorkerThreadModifyBusyCount ||
   1.122 +      mBehavior == WorkerThreadUnchangedBusyCount) {
   1.123 +    return NS_SUCCEEDED(mWorkerPrivate->Dispatch(this));
   1.124 +  }
   1.125 +
   1.126 +  MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
   1.127 +
   1.128 +  if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
   1.129 +    return NS_SUCCEEDED(parent->Dispatch(this));
   1.130 +  }
   1.131 +
   1.132 +  nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
   1.133 +  MOZ_ASSERT(mainThread);
   1.134 +
   1.135 +  return NS_SUCCEEDED(mainThread->Dispatch(this, NS_DISPATCH_NORMAL));
   1.136 +}
   1.137 +
   1.138 +void
   1.139 +WorkerRunnable::PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
   1.140 +                             bool aDispatchResult)
   1.141 +{
   1.142 +  MOZ_ASSERT(aWorkerPrivate);
   1.143 +
   1.144 +#ifdef DEBUG
   1.145 +  switch (mBehavior) {
   1.146 +    case ParentThreadUnchangedBusyCount:
   1.147 +      aWorkerPrivate->AssertIsOnWorkerThread();
   1.148 +      break;
   1.149 +
   1.150 +    case WorkerThreadModifyBusyCount:
   1.151 +      aWorkerPrivate->AssertIsOnParentThread();
   1.152 +      MOZ_ASSERT(aCx);
   1.153 +      break;
   1.154 +
   1.155 +    case WorkerThreadUnchangedBusyCount:
   1.156 +      aWorkerPrivate->AssertIsOnParentThread();
   1.157 +      break;
   1.158 +
   1.159 +    default:
   1.160 +      MOZ_ASSUME_UNREACHABLE("Unknown behavior!");
   1.161 +  }
   1.162 +#endif
   1.163 +
   1.164 +  if (!aDispatchResult) {
   1.165 +    if (mBehavior == WorkerThreadModifyBusyCount) {
   1.166 +      aWorkerPrivate->ModifyBusyCount(aCx, false);
   1.167 +    }
   1.168 +    if (aCx) {
   1.169 +      JS_ReportPendingException(aCx);
   1.170 +    }
   1.171 +  }
   1.172 +}
   1.173 +
   1.174 +void
   1.175 +WorkerRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
   1.176 +                        bool aRunResult)
   1.177 +{
   1.178 +  MOZ_ASSERT(aCx);
   1.179 +  MOZ_ASSERT(aWorkerPrivate);
   1.180 +
   1.181 +#ifdef DEBUG
   1.182 +  switch (mBehavior) {
   1.183 +    case ParentThreadUnchangedBusyCount:
   1.184 +      aWorkerPrivate->AssertIsOnParentThread();
   1.185 +      break;
   1.186 +
   1.187 +    case WorkerThreadModifyBusyCount:
   1.188 +      aWorkerPrivate->AssertIsOnWorkerThread();
   1.189 +      break;
   1.190 +
   1.191 +    case WorkerThreadUnchangedBusyCount:
   1.192 +      aWorkerPrivate->AssertIsOnWorkerThread();
   1.193 +      break;
   1.194 +
   1.195 +    default:
   1.196 +      MOZ_ASSUME_UNREACHABLE("Unknown behavior!");
   1.197 +  }
   1.198 +#endif
   1.199 +
   1.200 +  if (mBehavior == WorkerThreadModifyBusyCount) {
   1.201 +    if (!aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false)) {
   1.202 +      aRunResult = false;
   1.203 +    }
   1.204 +  }
   1.205 +
   1.206 +  if (!aRunResult) {
   1.207 +    JS_ReportPendingException(aCx);
   1.208 +  }
   1.209 +}
   1.210 +
   1.211 +// static
   1.212 +WorkerRunnable*
   1.213 +WorkerRunnable::FromRunnable(nsIRunnable* aRunnable)
   1.214 +{
   1.215 +  MOZ_ASSERT(aRunnable);
   1.216 +
   1.217 +  WorkerRunnable* runnable;
   1.218 +  nsresult rv = aRunnable->QueryInterface(kWorkerRunnableIID,
   1.219 +                                          reinterpret_cast<void**>(&runnable));
   1.220 +  if (NS_FAILED(rv)) {
   1.221 +    return nullptr;
   1.222 +  }
   1.223 +
   1.224 +  MOZ_ASSERT(runnable);
   1.225 +  return runnable;
   1.226 +}
   1.227 +
   1.228 +NS_IMPL_ADDREF(WorkerRunnable)
   1.229 +NS_IMPL_RELEASE(WorkerRunnable)
   1.230 +
   1.231 +NS_INTERFACE_MAP_BEGIN(WorkerRunnable)
   1.232 +  NS_INTERFACE_MAP_ENTRY(nsIRunnable)
   1.233 +  NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
   1.234 +  NS_INTERFACE_MAP_ENTRY(nsISupports)
   1.235 +  // kWorkerRunnableIID is special in that it does not AddRef its result.
   1.236 +  if (aIID.Equals(kWorkerRunnableIID)) {
   1.237 +    *aInstancePtr = this;
   1.238 +    return NS_OK;
   1.239 +  }
   1.240 +  else
   1.241 +NS_INTERFACE_MAP_END
   1.242 +
   1.243 +NS_IMETHODIMP
   1.244 +WorkerRunnable::Run()
   1.245 +{
   1.246 +  bool targetIsWorkerThread = mBehavior == WorkerThreadModifyBusyCount ||
   1.247 +                              mBehavior == WorkerThreadUnchangedBusyCount;
   1.248 +
   1.249 +#ifdef DEBUG
   1.250 +  MOZ_ASSERT_IF(mCallingCancelWithinRun, targetIsWorkerThread);
   1.251 +  if (targetIsWorkerThread) {
   1.252 +    mWorkerPrivate->AssertIsOnWorkerThread();
   1.253 +  }
   1.254 +  else {
   1.255 +    MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
   1.256 +    mWorkerPrivate->AssertIsOnParentThread();
   1.257 +  }
   1.258 +#endif
   1.259 +
   1.260 +  if (IsCanceled() && !mCallingCancelWithinRun) {
   1.261 +    return NS_OK;
   1.262 +  }
   1.263 +
   1.264 +  JSContext* cx;
   1.265 +  nsRefPtr<WorkerPrivate> kungFuDeathGrip;
   1.266 +  nsCxPusher pusher;
   1.267 +
   1.268 +  if (targetIsWorkerThread) {
   1.269 +    if (mWorkerPrivate->AllPendingRunnablesShouldBeCanceled() &&
   1.270 +        !IsCanceled() &&
   1.271 +        !mCallingCancelWithinRun) {
   1.272 +
   1.273 +      // Prevent recursion.
   1.274 +      mCallingCancelWithinRun = true;
   1.275 +
   1.276 +      Cancel();
   1.277 +
   1.278 +      MOZ_ASSERT(mCallingCancelWithinRun);
   1.279 +      mCallingCancelWithinRun = false;
   1.280 +
   1.281 +      MOZ_ASSERT(IsCanceled(), "Subclass Cancel() didn't set IsCanceled()!");
   1.282 +
   1.283 +      return NS_OK;
   1.284 +    }
   1.285 +
   1.286 +    cx = mWorkerPrivate->GetJSContext();
   1.287 +    MOZ_ASSERT(cx);
   1.288 +  }
   1.289 +  else {
   1.290 +    cx = mWorkerPrivate->ParentJSContext();
   1.291 +    MOZ_ASSERT(cx);
   1.292 +
   1.293 +    kungFuDeathGrip = mWorkerPrivate;
   1.294 +
   1.295 +    if (!mWorkerPrivate->GetParent()) {
   1.296 +      AssertIsOnMainThread();
   1.297 +      pusher.Push(cx);
   1.298 +    }
   1.299 +  }
   1.300 +
   1.301 +  JSAutoRequest ar(cx);
   1.302 +
   1.303 +  JS::Rooted<JSObject*> targetCompartmentObject(cx);
   1.304 +  if (targetIsWorkerThread) {
   1.305 +    targetCompartmentObject = JS::CurrentGlobalOrNull(cx);
   1.306 +  } else {
   1.307 +    targetCompartmentObject = mWorkerPrivate->GetWrapper();
   1.308 +  }
   1.309 +
   1.310 +  Maybe<JSAutoCompartment> ac;
   1.311 +  if (targetCompartmentObject) {
   1.312 +    ac.construct(cx, targetCompartmentObject);
   1.313 +  }
   1.314 +
   1.315 +  bool result = WorkerRun(cx, mWorkerPrivate);
   1.316 +
   1.317 +  // In the case of CompileScriptRunnnable, WorkerRun above can cause us to
   1.318 +  // lazily create a global, in which case we need to be in its compartment
   1.319 +  // when calling PostRun() below. Maybe<> this time...
   1.320 +  if (targetIsWorkerThread &&
   1.321 +      ac.empty() &&
   1.322 +      js::DefaultObjectForContextOrNull(cx)) {
   1.323 +    ac.construct(cx, js::DefaultObjectForContextOrNull(cx));
   1.324 +  }
   1.325 +
   1.326 +  PostRun(cx, mWorkerPrivate, result);
   1.327 +
   1.328 +  return result ? NS_OK : NS_ERROR_FAILURE;
   1.329 +}
   1.330 +
   1.331 +NS_IMETHODIMP
   1.332 +WorkerRunnable::Cancel()
   1.333 +{
   1.334 +  uint32_t canceledCount = ++mCanceled;
   1.335 +
   1.336 +  MOZ_ASSERT(canceledCount, "Cancel() overflow!");
   1.337 +
   1.338 +  // The docs say that Cancel() should not be called more than once and that we
   1.339 +  // should throw NS_ERROR_UNEXPECTED if it is.
   1.340 +  return (canceledCount == 1) ? NS_OK : NS_ERROR_UNEXPECTED;
   1.341 +}
   1.342 +
   1.343 +WorkerSyncRunnable::WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
   1.344 +                                       nsIEventTarget* aSyncLoopTarget)
   1.345 +: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
   1.346 +  mSyncLoopTarget(aSyncLoopTarget)
   1.347 +{
   1.348 +#ifdef DEBUG
   1.349 +  if (mSyncLoopTarget) {
   1.350 +    mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
   1.351 +  }
   1.352 +#endif
   1.353 +}
   1.354 +
   1.355 +WorkerSyncRunnable::WorkerSyncRunnable(
   1.356 +                               WorkerPrivate* aWorkerPrivate,
   1.357 +                               already_AddRefed<nsIEventTarget>&& aSyncLoopTarget)
   1.358 +: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
   1.359 +  mSyncLoopTarget(aSyncLoopTarget)
   1.360 +{
   1.361 +#ifdef DEBUG
   1.362 +  if (mSyncLoopTarget) {
   1.363 +    mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
   1.364 +  }
   1.365 +#endif
   1.366 +}
   1.367 +
   1.368 +WorkerSyncRunnable::~WorkerSyncRunnable()
   1.369 +{
   1.370 +}
   1.371 +
   1.372 +bool
   1.373 +WorkerSyncRunnable::DispatchInternal()
   1.374 +{
   1.375 +  if (mSyncLoopTarget) {
   1.376 +    return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(this, NS_DISPATCH_NORMAL));
   1.377 +  }
   1.378 +
   1.379 +  return WorkerRunnable::DispatchInternal();
   1.380 +}
   1.381 +
   1.382 +void
   1.383 +MainThreadWorkerSyncRunnable::PostDispatch(JSContext* aCx,
   1.384 +                                           WorkerPrivate* aWorkerPrivate,
   1.385 +                                           bool aDispatchResult)
   1.386 +{
   1.387 +  MaybeReportMainThreadException(aCx, aDispatchResult);
   1.388 +}
   1.389 +
   1.390 +StopSyncLoopRunnable::StopSyncLoopRunnable(
   1.391 +                               WorkerPrivate* aWorkerPrivate,
   1.392 +                               already_AddRefed<nsIEventTarget>&& aSyncLoopTarget,
   1.393 +                               bool aResult)
   1.394 +: WorkerSyncRunnable(aWorkerPrivate, Move(aSyncLoopTarget)), mResult(aResult)
   1.395 +{
   1.396 +#ifdef DEBUG
   1.397 +  mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
   1.398 +#endif
   1.399 +}
   1.400 +
   1.401 +NS_IMETHODIMP
   1.402 +StopSyncLoopRunnable::Cancel()
   1.403 +{
   1.404 +  nsresult rv = Run();
   1.405 +  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Run() failed");
   1.406 +
   1.407 +  nsresult rv2 = WorkerSyncRunnable::Cancel();
   1.408 +  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv2), "Cancel() failed");
   1.409 +
   1.410 +  return NS_FAILED(rv) ? rv : rv2;
   1.411 +}
   1.412 +
   1.413 +bool
   1.414 +StopSyncLoopRunnable::WorkerRun(JSContext* aCx,
   1.415 +                                WorkerPrivate* aWorkerPrivate)
   1.416 +{
   1.417 +  aWorkerPrivate->AssertIsOnWorkerThread();
   1.418 +  MOZ_ASSERT(mSyncLoopTarget);
   1.419 +
   1.420 +  nsCOMPtr<nsIEventTarget> syncLoopTarget;
   1.421 +  mSyncLoopTarget.swap(syncLoopTarget);
   1.422 +
   1.423 +  if (!mResult) {
   1.424 +    MaybeSetException(aCx);
   1.425 +  }
   1.426 +
   1.427 +  aWorkerPrivate->StopSyncLoop(syncLoopTarget, mResult);
   1.428 +  return true;
   1.429 +}
   1.430 +
   1.431 +bool
   1.432 +StopSyncLoopRunnable::DispatchInternal()
   1.433 +{
   1.434 +  MOZ_ASSERT(mSyncLoopTarget);
   1.435 +
   1.436 +  return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(this, NS_DISPATCH_NORMAL));
   1.437 +}
   1.438 +
   1.439 +void
   1.440 +MainThreadStopSyncLoopRunnable::PostDispatch(JSContext* aCx,
   1.441 +                                             WorkerPrivate* aWorkerPrivate,
   1.442 +                                             bool aDispatchResult)
   1.443 +{
   1.444 +  MaybeReportMainThreadException(aCx, aDispatchResult);
   1.445 +}
   1.446 +
   1.447 +#ifdef DEBUG
   1.448 +WorkerControlRunnable::WorkerControlRunnable(WorkerPrivate* aWorkerPrivate,
   1.449 +                                             TargetAndBusyBehavior aBehavior)
   1.450 +: WorkerRunnable(aWorkerPrivate, aBehavior)
   1.451 +{
   1.452 +  MOZ_ASSERT(aWorkerPrivate);
   1.453 +  MOZ_ASSERT(aBehavior == ParentThreadUnchangedBusyCount ||
   1.454 +             aBehavior == WorkerThreadUnchangedBusyCount,
   1.455 +             "WorkerControlRunnables should not modify the busy count");
   1.456 +}
   1.457 +#endif
   1.458 +
   1.459 +bool
   1.460 +WorkerControlRunnable::DispatchInternal()
   1.461 +{
   1.462 +  if (mBehavior == WorkerThreadUnchangedBusyCount) {
   1.463 +    return NS_SUCCEEDED(mWorkerPrivate->DispatchControlRunnable(this));
   1.464 +  }
   1.465 +
   1.466 +  if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
   1.467 +    return NS_SUCCEEDED(parent->DispatchControlRunnable(this));
   1.468 +  }
   1.469 +
   1.470 +  nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
   1.471 +  MOZ_ASSERT(mainThread);
   1.472 +
   1.473 +  return NS_SUCCEEDED(mainThread->Dispatch(this, NS_DISPATCH_NORMAL));
   1.474 +}
   1.475 +
   1.476 +void
   1.477 +MainThreadWorkerControlRunnable::PostDispatch(JSContext* aCx,
   1.478 +                                              WorkerPrivate* aWorkerPrivate,
   1.479 +                                              bool aDispatchResult)
   1.480 +{
   1.481 +  AssertIsOnMainThread();
   1.482 +
   1.483 +  if (aCx && !aDispatchResult) {
   1.484 +    JS_ReportPendingException(aCx);
   1.485 +  }
   1.486 +}
   1.487 +
   1.488 +NS_IMPL_ISUPPORTS_INHERITED0(WorkerControlRunnable, WorkerRunnable)
   1.489 +
   1.490 +bool
   1.491 +WorkerSameThreadRunnable::PreDispatch(JSContext* aCx,
   1.492 +                                      WorkerPrivate* aWorkerPrivate)
   1.493 +{
   1.494 +  aWorkerPrivate->AssertIsOnWorkerThread();
   1.495 +  return true;
   1.496 +}
   1.497 +
   1.498 +void
   1.499 +WorkerSameThreadRunnable::PostDispatch(JSContext* aCx,
   1.500 +                                       WorkerPrivate* aWorkerPrivate,
   1.501 +                                       bool aDispatchResult)
   1.502 +{
   1.503 +  aWorkerPrivate->AssertIsOnWorkerThread();
   1.504 +  if (aDispatchResult) {
   1.505 +    DebugOnly<bool> willIncrement = aWorkerPrivate->ModifyBusyCountFromWorker(aCx, true);
   1.506 +    // Should never fail since if this thread is still running, so should the
   1.507 +    // parent and it should be able to process a control runnable.
   1.508 +    MOZ_ASSERT(willIncrement);
   1.509 +  }
   1.510 +}
   1.511 +
   1.512 +void
   1.513 +WorkerSameThreadRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
   1.514 +                                  bool aRunResult)
   1.515 +{
   1.516 +  MOZ_ASSERT(aCx);
   1.517 +  MOZ_ASSERT(aWorkerPrivate);
   1.518 +
   1.519 +  aWorkerPrivate->AssertIsOnWorkerThread();
   1.520 +
   1.521 +  DebugOnly<bool> willDecrement = aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false);
   1.522 +  MOZ_ASSERT(willDecrement);
   1.523 +
   1.524 +  if (!aRunResult) {
   1.525 +    JS_ReportPendingException(aCx);
   1.526 +  }
   1.527 +}
   1.528 +

mercurial