Bug 1575744 - P4. Add MozPromise::FromDomPromise. r=bholley

Similar to MozPromise::FromGeckoResult.

Allows to create a MozPromise that will be resolved/rejected when the JS promise does the same.
It would be nice to be able to chain the two promise types, but it would be an additional effort.
MozPromise::FromDomPromise is limited to primitive types only and the reject value type must be nsresult.

Differential Revision: https://phabricator.services.mozilla.com/D46017

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jean-Yves Avenard 2019-09-20 04:09:46 +00:00
parent 89412cad70
commit 912b0df630
7 changed files with 153 additions and 51 deletions

View File

@ -0,0 +1,54 @@
/* -*- 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/. */
#include "PromiseNativeHandler.h"
#include "mozilla/dom/Promise.h"
#include "nsISupportsImpl.h"
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS0(DomPromiseListener)
DomPromiseListener::DomPromiseListener(dom::Promise* aDOMPromise) {
aDOMPromise->AppendNativeHandler(this);
}
DomPromiseListener::DomPromiseListener(dom::Promise* aDOMPromise,
CallbackType&& aResolve,
CallbackType&& aReject)
: mResolve(Some(std::move(aResolve))), mReject(Some(std::move(aReject))) {
aDOMPromise->AppendNativeHandler(this);
}
void DomPromiseListener::ResolvedCallback(JSContext* aCx,
JS::Handle<JS::Value> aValue) {
if (mResolve) {
(*mResolve)(aCx, aValue);
}
// Let's clear the lambdas in case we have a cycle to ourselves.
mResolve.reset();
mReject.reset();
}
void DomPromiseListener::RejectedCallback(JSContext* aCx,
JS::Handle<JS::Value> aValue) {
if (mReject) {
(*mReject)(aCx, aValue);
}
// Let's clear the lambdas in case we have a cycle to ourselves.
mResolve.reset();
mReject.reset();
}
void DomPromiseListener::SetResolvers(CallbackType&& aResolve,
CallbackType&& aReject) {
mResolve = Some(std::move(aResolve));
mReject = Some(std::move(aReject));
}
} // namespace dom
} // namespace mozilla

View File

@ -7,12 +7,15 @@
#ifndef mozilla_dom_PromiseNativeHandler_h
#define mozilla_dom_PromiseNativeHandler_h
#include "nsISupports.h"
#include <functional>
#include "js/TypeDecls.h"
#include "nsISupports.h"
namespace mozilla {
namespace dom {
class Promise;
/*
* PromiseNativeHandler allows C++ to react to a Promise being
* rejected/resolved. A PromiseNativeHandler can be appended to a Promise using
@ -20,7 +23,7 @@ namespace dom {
*/
class PromiseNativeHandler : public nsISupports {
protected:
virtual ~PromiseNativeHandler() {}
virtual ~PromiseNativeHandler() = default;
public:
MOZ_CAN_RUN_SCRIPT
@ -32,6 +35,27 @@ class PromiseNativeHandler : public nsISupports {
JS::Handle<JS::Value> aValue) = 0;
};
// This class is used to set C++ callbacks once a dom Promise a resolved or
// rejected.
class DomPromiseListener final : public PromiseNativeHandler {
NS_DECL_ISUPPORTS
public:
using CallbackType = std::function<void(JSContext*, JS::Handle<JS::Value>)>;
explicit DomPromiseListener(Promise* aDOMPromise);
DomPromiseListener(Promise* aDOMPromise, CallbackType&& aResolve,
CallbackType&& aReject);
void SetResolvers(CallbackType&& aResolve, CallbackType&& aReject);
void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
private:
~DomPromiseListener() = default;
Maybe<CallbackType> mResolve;
Maybe<CallbackType> mReject;
};
} // namespace dom
} // namespace mozilla

View File

@ -18,6 +18,7 @@ EXPORTS.mozilla.dom += [
UNIFIED_SOURCES += [
'Promise.cpp',
'PromiseDebugging.cpp',
'PromiseNativeHandler.cpp',
]
LOCAL_INCLUDES += [

View File

@ -10,9 +10,10 @@
#include <inttypes.h>
#include "DocumentChannelParent.h"
#include "mozilla/dom/nsCSPContext.h"
#include "mozilla/MozPromiseInlines.h" // For MozPromise::FromDomPromise
#include "mozilla/ScopeExit.h"
#include "mozilla/Sprintf.h"
#include "mozilla/dom/nsCSPContext.h"
#include "nsHttp.h"
#include "nsHttpChannel.h"
@ -124,7 +125,6 @@
#include "../../cache2/CacheFileUtils.h"
#include "../../cache2/CacheHashUtils.h"
#include "nsINetworkLinkService.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ServiceWorkerUtils.h"
#include "mozilla/net/AsyncUrlChannelClassifier.h"
@ -7241,51 +7241,6 @@ nsHttpChannel::GetRequestMethod(nsACString& aMethod) {
return HttpBaseChannel::GetRequestMethod(aMethod);
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsIRequestObserver
//-----------------------------------------------------------------------------
// This class is used to convert from a DOM promise to a MozPromise.
class DomPromiseListener final : dom::PromiseNativeHandler {
NS_DECL_ISUPPORTS
static RefPtr<nsHttpChannel::ContentProcessIdPromise> Create(
dom::Promise* aDOMPromise) {
MOZ_ASSERT(aDOMPromise);
RefPtr<DomPromiseListener> handler = new DomPromiseListener();
RefPtr<nsHttpChannel::ContentProcessIdPromise> promise =
handler->mPromiseHolder.Ensure(__func__);
aDOMPromise->AppendNativeHandler(handler);
return promise;
}
virtual void ResolvedCallback(JSContext* aCx,
JS::Handle<JS::Value> aValue) override {
uint64_t cpId;
if (!JS::ToUint64(aCx, aValue, &cpId)) {
mPromiseHolder.Reject(NS_ERROR_FAILURE, __func__);
return;
}
mPromiseHolder.Resolve(cpId, __func__);
}
virtual void RejectedCallback(JSContext* aCx,
JS::Handle<JS::Value> aValue) override {
if (!aValue.isInt32()) {
mPromiseHolder.Reject(NS_ERROR_DOM_NOT_NUMBER_ERR, __func__);
return;
}
mPromiseHolder.Reject((nsresult)aValue.toInt32(), __func__);
}
private:
DomPromiseListener() = default;
~DomPromiseListener() = default;
MozPromiseHolder<nsHttpChannel::ContentProcessIdPromise> mPromiseHolder;
};
NS_IMPL_ISUPPORTS0(DomPromiseListener)
//-----------------------------------------------------------------------------
// nsHttpChannel::nsIProcessSwitchRequestor
//-----------------------------------------------------------------------------
@ -7315,7 +7270,7 @@ NS_IMETHODIMP nsHttpChannel::SwitchProcessTo(
}
mRedirectContentProcessIdPromise =
DomPromiseListener::Create(aContentProcessIdPromise);
ContentProcessIdPromise::FromDomPromise(aContentProcessIdPromise);
mCrossProcessRedirectIdentifier = aIdentifier;
return NS_OK;
}
@ -7618,6 +7573,10 @@ nsresult nsHttpChannel::ProcessCrossOriginResourcePolicyHeader() {
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsIRequestObserver
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsHttpChannel::OnStartRequest(nsIRequest* request) {
nsresult rv;

View File

@ -9,8 +9,8 @@
# include "mozilla/Logging.h"
# include "mozilla/Maybe.h"
# include "mozilla/Mutex.h"
# include "mozilla/Monitor.h"
# include "mozilla/Mutex.h"
# include "mozilla/RefPtr.h"
# include "mozilla/Tuple.h"
# include "mozilla/TypeTraits.h"
@ -38,6 +38,10 @@
namespace mozilla {
namespace dom {
class Promise;
}
extern LazyLogModule gMozPromiseLog;
# define PROMISE_LOG(x, ...) \
@ -954,6 +958,12 @@ class MozPromise : public MozPromiseBase {
}
# endif
// Creates a C++ MozPromise from its JS counterpart, dom::Promise.
// FromDomPromise currently only supports primitive types (int8/16/32, float,
// double) And the reject value type must be a nsresult.
// To use, please include MozPromiseInlines.h
static RefPtr<MozPromise> FromDomPromise(dom::Promise* aDOMPromise);
// Note we expose the function AssertIsDead() instead of IsDead() since
// checking IsDead() is a data race in the situation where the request is not
// dead. Therefore we enforce the form |Assert(IsDead())| by exposing

View File

@ -0,0 +1,53 @@
/* -*- 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/. */
#if !defined(MozPromiseInlines_h_)
# define MozPromiseInlines_h_
# include "mozilla/MozPromise.h"
# include "mozilla/dom/PrimitiveConversions.h"
# include "mozilla/dom/PromiseNativeHandler.h"
namespace mozilla {
// Creates a C++ MozPromise from its JS counterpart, dom::Promise.
// FromDomPromise currently only supports primitive types (int8/16/32, float,
// double) And the reject value type must be a nsresult.
template <typename ResolveValueT, typename RejectValueT, bool IsExclusive>
RefPtr<MozPromise<ResolveValueT, RejectValueT, IsExclusive>>
MozPromise<ResolveValueT, RejectValueT, IsExclusive>::FromDomPromise(
dom::Promise* aDOMPromise) {
static_assert(IsSame<RejectValueType, nsresult>::value,
"Reject type must be nsresult");
RefPtr<Private> p = new Private(__func__);
RefPtr<dom::DomPromiseListener> listener = new dom::DomPromiseListener(
aDOMPromise,
[p](JSContext* aCx, JS::Handle<JS::Value> aValue) {
ResolveValueT value;
bool ok = dom::ValueToPrimitive<ResolveValueT,
dom::ConversionBehavior::eDefault>(
aCx, aValue, &value);
if (!ok) {
p->Reject(NS_ERROR_FAILURE, __func__);
return;
}
p->Resolve(value, __func__);
},
[p](JSContext* aCx, JS::Handle<JS::Value> aValue) {
if (!aValue.isInt32()) {
p->Reject(NS_ERROR_DOM_NOT_NUMBER_ERR, __func__);
return;
}
nsresult rv = nsresult(aValue.toInt32());
MOZ_ASSERT(NS_SUCCEEDED(rv));
p->Reject(rv, __func__);
});
return p;
}
} // namespace mozilla
#endif

View File

@ -53,6 +53,7 @@ EXPORTS.mozilla += [
'MainThreadIdlePeriod.h',
'Monitor.h',
'MozPromise.h',
'MozPromiseInlines.h',
'Mutex.h',
'PerformanceCounter.h',
'Queue.h',