From 70aa4c4f6287f4dbaab53725e050dc11802e6f99 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Thu, 26 Apr 2018 09:18:01 -0700 Subject: [PATCH] Bug 1457187 P1 Add a DOMMozPromiseRequestHolder helper class to auto-disconnect MozPromise Thenables when the global dies. r=baku --- dom/base/DOMMozPromiseRequestHolder.h | 125 ++++++++++++++++++++++++++ dom/base/moz.build | 1 + 2 files changed, 126 insertions(+) create mode 100644 dom/base/DOMMozPromiseRequestHolder.h diff --git a/dom/base/DOMMozPromiseRequestHolder.h b/dom/base/DOMMozPromiseRequestHolder.h new file mode 100644 index 000000000000..d293d98f3b59 --- /dev/null +++ b/dom/base/DOMMozPromiseRequestHolder.h @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_DOMMozPromiseRequestHolder_h +#define mozilla_dom_DOMMozPromiseRequestHolder_h + +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/MozPromise.h" + +namespace mozilla { +namespace dom { + +/** + * This is a helper class that can be used when MozPromises are + * being consumed by binding layer code. It effectively creates + * a MozPromiseRequestHolder that auto-disconnects when the binding's + * global is disconnected. + * + * It can be used like this: + * + * RefPtr + * SomeAsyncAPI(Args& aArgs, ErrorResult& aRv) + * { + * nsIGlobalObject* global = GetParentObject(); + * if (!global) { + * aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + * return nullptr; + * } + * + * RefPtr outer = Promise::Create(global, aRv); + * if (aRv.Failed()) { + * return nullptr; + * } + * + * RefPtr holder = + * new DOMMozPromiseRequestHolder(global); + * + * DoAsyncStuff()->Then( + * global->EventTargetFor(TaskCategory::Other), __func__, + * [holder, outer] (const Result& aResult) { + * holder->Complete(); + * outer->MaybeResolve(aResult); + * }, [holder, outer] (nsresult aRv) { + * holder->Complete(); + * outer->MaybeReject(aRv); + * })->Track(*holder); + * + * return outer.forget(); + * } + * + * NOTE: Currently this helper class extends DETH. This is only + * so that it can bind to the global and receive the + * DisconnectFromOwner() method call. In this future the + * binding code should be factored out so DETH is not + * needed here. See bug 1456893. + */ +template +class DOMMozPromiseRequestHolder final : public DOMEventTargetHelper +{ + MozPromiseRequestHolder mHolder; + + ~DOMMozPromiseRequestHolder() = default; + + void + DisconnectFromOwner() override + { + mHolder.DisconnectIfExists(); + DOMEventTargetHelper::DisconnectFromOwner(); + } + + JSObject* + WrapObject(JSContext* aCx, JS::Handle aGivenProto) override + { + // We are extending DETH to get notified when the global goes + // away, but this object should never actually be exposed to + // script. + MOZ_CRASH("illegal method"); + } + +public: + explicit DOMMozPromiseRequestHolder(nsIGlobalObject* aGlobal) + : DOMEventTargetHelper(aGlobal) + { + MOZ_DIAGNOSTIC_ASSERT(aGlobal); + } + + operator MozPromiseRequestHolder& () + { + return mHolder; + } + + operator const MozPromiseRequestHolder& () const + { + return mHolder; + } + + void + Complete() + { + mHolder.Complete(); + DisconnectFromOwner(); + } + + void + DisconnectIfExists() + { + mHolder.DisconnectIfExists(); + } + + bool + Exists() const + { + return mHolder.Exists(); + } + + NS_INLINE_DECL_REFCOUNTING_INHERITED(DOMMozPromiseRequestHolder, DOMEventTargetHelper) +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_DOMMozPromiseRequestHolder_h diff --git a/dom/base/moz.build b/dom/base/moz.build index c9e13f32df0c..b7c11f8eb8a0 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -169,6 +169,7 @@ EXPORTS.mozilla.dom += [ 'DOMImplementation.h', 'DOMIntersectionObserver.h', 'DOMMatrix.h', + 'DOMMozPromiseRequestHolder.h', 'DOMParser.h', 'DOMPoint.h', 'DOMPrefs.h',