зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1491403 - Part 3: Propagate the user input event handling state to the promise resolve handlers in case the promise creator requests it r=smaug,arai,baku
Depends on D7004 Differential Revision: https://phabricator.services.mozilla.com/D7005 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
bfde189b6d
Коммит
19e88f0bf4
|
@ -11,9 +11,11 @@
|
|||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
#include "mozilla/OwningNonNull.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/ResultExtensions.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/DOMException.h"
|
||||
|
@ -89,24 +91,39 @@ Promise::~Promise()
|
|||
|
||||
// static
|
||||
already_AddRefed<Promise>
|
||||
Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv)
|
||||
Promise::Create(nsIGlobalObject* aGlobal,
|
||||
ErrorResult& aRv,
|
||||
PropagateUserInteraction aPropagateUserInteraction)
|
||||
{
|
||||
if (!aGlobal) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<Promise> p = new Promise(aGlobal);
|
||||
p->CreateWrapper(nullptr, aRv);
|
||||
p->CreateWrapper(nullptr, aRv, aPropagateUserInteraction);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
Promise::MaybePropagateUserInputEventHandling()
|
||||
{
|
||||
JS::PromiseUserInputEventHandlingState state =
|
||||
EventStateManager::IsHandlingUserInput() ?
|
||||
JS::PromiseUserInputEventHandlingState::HadUserInteractionAtCreation :
|
||||
JS::PromiseUserInputEventHandlingState::DidntHaveUserInteractionAtCreation;
|
||||
JS::Rooted<JSObject*> p(RootingCx(), mPromiseObj);
|
||||
return JS::SetPromiseUserInputEventHandlingState(p, state);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<Promise>
|
||||
Promise::Resolve(nsIGlobalObject* aGlobal, JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue, ErrorResult& aRv)
|
||||
JS::Handle<JS::Value> aValue,
|
||||
ErrorResult& aRv,
|
||||
PropagateUserInteraction aPropagateUserInteraction)
|
||||
{
|
||||
JSAutoRealm ar(aCx, aGlobal->GetGlobalJSObject());
|
||||
JS::Rooted<JSObject*> p(aCx,
|
||||
|
@ -116,7 +133,7 @@ Promise::Resolve(nsIGlobalObject* aGlobal, JSContext* aCx,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return CreateFromExisting(aGlobal, p);
|
||||
return CreateFromExisting(aGlobal, p, aPropagateUserInteraction);
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -132,13 +149,18 @@ Promise::Reject(nsIGlobalObject* aGlobal, JSContext* aCx,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return CreateFromExisting(aGlobal, p);
|
||||
// This promise will never be resolved, so we pass
|
||||
// eDontPropagateUserInteraction for aPropagateUserInteraction
|
||||
// unconditionally.
|
||||
return CreateFromExisting(aGlobal, p, eDontPropagateUserInteraction);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<Promise>
|
||||
Promise::All(JSContext* aCx,
|
||||
const nsTArray<RefPtr<Promise>>& aPromiseList, ErrorResult& aRv)
|
||||
const nsTArray<RefPtr<Promise>>& aPromiseList,
|
||||
ErrorResult& aRv,
|
||||
PropagateUserInteraction aPropagateUserInteraction)
|
||||
{
|
||||
JS::Rooted<JSObject*> globalObj(aCx, JS::CurrentGlobalOrNull(aCx));
|
||||
if (!globalObj) {
|
||||
|
@ -174,7 +196,7 @@ Promise::All(JSContext* aCx,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return CreateFromExisting(global, result);
|
||||
return CreateFromExisting(global, result, aPropagateUserInteraction);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -271,7 +293,9 @@ Promise::ThenWithoutCycleCollection(
|
|||
}
|
||||
|
||||
void
|
||||
Promise::CreateWrapper(JS::Handle<JSObject*> aDesiredProto, ErrorResult& aRv)
|
||||
Promise::CreateWrapper(JS::Handle<JSObject*> aDesiredProto,
|
||||
ErrorResult& aRv,
|
||||
PropagateUserInteraction aPropagateUserInteraction)
|
||||
{
|
||||
AutoJSAPI jsapi;
|
||||
if (!jsapi.Init(mGlobal)) {
|
||||
|
@ -285,6 +309,9 @@ Promise::CreateWrapper(JS::Handle<JSObject*> aDesiredProto, ErrorResult& aRv)
|
|||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
if (aPropagateUserInteraction == ePropagateUserInteraction) {
|
||||
Unused << MaybePropagateUserInputEventHandling();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -491,12 +518,17 @@ Promise::HandleException(JSContext* aCx)
|
|||
// static
|
||||
already_AddRefed<Promise>
|
||||
Promise::CreateFromExisting(nsIGlobalObject* aGlobal,
|
||||
JS::Handle<JSObject*> aPromiseObj)
|
||||
JS::Handle<JSObject*> aPromiseObj,
|
||||
PropagateUserInteraction aPropagateUserInteraction)
|
||||
{
|
||||
MOZ_ASSERT(js::GetObjectCompartment(aGlobal->GetGlobalJSObject()) ==
|
||||
js::GetObjectCompartment(aPromiseObj));
|
||||
RefPtr<Promise> p = new Promise(aGlobal);
|
||||
p->mPromiseObj = aPromiseObj;
|
||||
if (aPropagateUserInteraction == ePropagateUserInteraction &&
|
||||
!p->MaybePropagateUserInputEventHandling()) {
|
||||
return nullptr;
|
||||
}
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
|
|
|
@ -51,12 +51,24 @@ public:
|
|||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Promise)
|
||||
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(Promise)
|
||||
|
||||
enum PropagateUserInteraction
|
||||
{
|
||||
eDontPropagateUserInteraction,
|
||||
ePropagateUserInteraction
|
||||
};
|
||||
|
||||
// Promise creation tries to create a JS reflector for the Promise, so is
|
||||
// fallible. Furthermore, we don't want to do JS-wrapping on a 0-refcount
|
||||
// object, so we addref before doing that and return the addrefed pointer
|
||||
// here.
|
||||
// Pass ePropagateUserInteraction for aPropagateUserInteraction if you want
|
||||
// the promise resolve handler to be called as if we were handling user
|
||||
// input events in case we are currently handling user input events.
|
||||
static already_AddRefed<Promise>
|
||||
Create(nsIGlobalObject* aGlobal, ErrorResult& aRv);
|
||||
Create(nsIGlobalObject* aGlobal,
|
||||
ErrorResult& aRv,
|
||||
PropagateUserInteraction aPropagateUserInteraction =
|
||||
eDontPropagateUserInteraction);
|
||||
|
||||
// Reports a rejected Promise by sending an error report.
|
||||
static void ReportRejectedPromise(JSContext* aCx, JS::HandleObject aPromise);
|
||||
|
@ -116,9 +128,15 @@ public:
|
|||
// Do the equivalent of Promise.resolve in the compartment of aGlobal. The
|
||||
// compartment of aCx is ignored. Errors are reported on the ErrorResult; if
|
||||
// aRv comes back !Failed(), this function MUST return a non-null value.
|
||||
// Pass ePropagateUserInteraction for aPropagateUserInteraction if you want
|
||||
// the promise resolve handler to be called as if we were handling user
|
||||
// input events in case we are currently handling user input events.
|
||||
static already_AddRefed<Promise>
|
||||
Resolve(nsIGlobalObject* aGlobal, JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue, ErrorResult& aRv);
|
||||
JS::Handle<JS::Value> aValue,
|
||||
ErrorResult& aRv,
|
||||
PropagateUserInteraction aPropagateUserInteraction =
|
||||
eDontPropagateUserInteraction);
|
||||
|
||||
// Do the equivalent of Promise.reject in the compartment of aGlobal. The
|
||||
// compartment of aCx is ignored. Errors are reported on the ErrorResult; if
|
||||
|
@ -130,9 +148,14 @@ public:
|
|||
// Do the equivalent of Promise.all in the current compartment of aCx. Errors
|
||||
// are reported on the ErrorResult; if aRv comes back !Failed(), this function
|
||||
// MUST return a non-null value.
|
||||
// Pass ePropagateUserInteraction for aPropagateUserInteraction if you want
|
||||
// the promise resolve handler to be called as if we were handling user
|
||||
// input events in case we are currently handling user input events.
|
||||
static already_AddRefed<Promise>
|
||||
All(JSContext* aCx, const nsTArray<RefPtr<Promise>>& aPromiseList,
|
||||
ErrorResult& aRv);
|
||||
ErrorResult& aRv,
|
||||
PropagateUserInteraction aPropagateUserInteraction =
|
||||
eDontPropagateUserInteraction);
|
||||
|
||||
void
|
||||
Then(JSContext* aCx,
|
||||
|
@ -193,9 +216,14 @@ public:
|
|||
|
||||
// Create a dom::Promise from a given SpiderMonkey Promise object.
|
||||
// aPromiseObj MUST be in the compartment of aGlobal's global JS object.
|
||||
// Pass ePropagateUserInteraction for aPropagateUserInteraction if you want
|
||||
// the promise resolve handler to be called as if we were handling user
|
||||
// input events in case we are currently handling user input events.
|
||||
static already_AddRefed<Promise>
|
||||
CreateFromExisting(nsIGlobalObject* aGlobal,
|
||||
JS::Handle<JSObject*> aPromiseObj);
|
||||
JS::Handle<JSObject*> aPromiseObj,
|
||||
PropagateUserInteraction aPropagateUserInteraction =
|
||||
eDontPropagateUserInteraction);
|
||||
|
||||
enum class PromiseState {
|
||||
Pending,
|
||||
|
@ -217,7 +245,13 @@ protected:
|
|||
|
||||
// Do JS-wrapping after Promise creation. Passing null for aDesiredProto will
|
||||
// use the default prototype for the sort of Promise we have.
|
||||
void CreateWrapper(JS::Handle<JSObject*> aDesiredProto, ErrorResult& aRv);
|
||||
// Pass ePropagateUserInteraction for aPropagateUserInteraction if you want
|
||||
// the promise resolve handler to be called as if we were handling user
|
||||
// input events in case we are currently handling user input events.
|
||||
void CreateWrapper(JS::Handle<JSObject*> aDesiredProto,
|
||||
ErrorResult& aRv,
|
||||
PropagateUserInteraction aPropagateUserInteraction =
|
||||
eDontPropagateUserInteraction);
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
|
@ -238,6 +272,8 @@ private:
|
|||
|
||||
void HandleException(JSContext* aCx);
|
||||
|
||||
bool MaybePropagateUserInputEventHandling();
|
||||
|
||||
RefPtr<nsIGlobalObject> mGlobal;
|
||||
|
||||
JS::Heap<JSObject*> mPromiseObj;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/CycleCollectedJSRuntime.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
|
@ -20,7 +21,6 @@
|
|||
#include "mozilla/dom/DOMJSClass.h"
|
||||
#include "mozilla/dom/DOMException.h"
|
||||
#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/PromiseBinding.h"
|
||||
#include "mozilla/dom/PromiseDebugging.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
|
@ -35,6 +35,7 @@
|
|||
#include "nsDOMJSUtils.h"
|
||||
#include "nsDOMMutationObserver.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsStringBuffer.h"
|
||||
|
||||
|
@ -203,15 +204,24 @@ CycleCollectedJSContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
|
|||
class PromiseJobRunnable final : public MicroTaskRunnable
|
||||
{
|
||||
public:
|
||||
PromiseJobRunnable(JS::HandleObject aCallback,
|
||||
PromiseJobRunnable(JS::HandleObject aPromise,
|
||||
JS::HandleObject aCallback,
|
||||
JS::HandleObject aCallbackGlobal,
|
||||
JS::HandleObject aAllocationSite,
|
||||
nsIGlobalObject* aIncumbentGlobal)
|
||||
:mCallback(
|
||||
: mCallback(
|
||||
new PromiseJobCallback(aCallback, aCallbackGlobal, aAllocationSite,
|
||||
aIncumbentGlobal))
|
||||
, mPropagateUserInputEventHandling(false)
|
||||
{
|
||||
MOZ_ASSERT(js::IsFunctionObject(aCallback));
|
||||
|
||||
if (aPromise) {
|
||||
JS::PromiseUserInputEventHandlingState state =
|
||||
JS::GetPromiseUserInputEventHandlingState(aPromise);
|
||||
mPropagateUserInputEventHandling =
|
||||
state == JS::PromiseUserInputEventHandlingState::HadUserInteractionAtCreation;
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~PromiseJobRunnable()
|
||||
|
@ -224,6 +234,16 @@ protected:
|
|||
JSObject* callback = mCallback->CallbackPreserveColor();
|
||||
nsIGlobalObject* global = callback ? xpc::NativeGlobal(callback) : nullptr;
|
||||
if (global && !global->IsDying()) {
|
||||
// Propagate the user input event handling bit if needed.
|
||||
nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(global);
|
||||
nsCOMPtr<nsIDocument> doc;
|
||||
if (win) {
|
||||
doc = win->GetExtantDoc();
|
||||
}
|
||||
AutoHandlingUserInputStatePusher userInpStatePusher(mPropagateUserInputEventHandling,
|
||||
nullptr,
|
||||
doc);
|
||||
|
||||
mCallback->Call("promise callback");
|
||||
aAso.CheckForInterrupt();
|
||||
}
|
||||
|
@ -244,6 +264,7 @@ protected:
|
|||
|
||||
private:
|
||||
RefPtr<PromiseJobCallback> mCallback;
|
||||
bool mPropagateUserInputEventHandling;
|
||||
};
|
||||
|
||||
/* static */
|
||||
|
@ -275,9 +296,10 @@ CycleCollectedJSContext::EnqueuePromiseJobCallback(JSContext* aCx,
|
|||
global = xpc::NativeGlobal(aIncumbentGlobal);
|
||||
}
|
||||
JS::RootedObject jobGlobal(aCx, JS::CurrentGlobalOrNull(aCx));
|
||||
RefPtr<MicroTaskRunnable> runnable = new PromiseJobRunnable(aJob, jobGlobal,
|
||||
aAllocationSite,
|
||||
global);
|
||||
RefPtr<PromiseJobRunnable> runnable = new PromiseJobRunnable(aPromise, aJob,
|
||||
jobGlobal,
|
||||
aAllocationSite,
|
||||
global);
|
||||
self->DispatchToMicroTask(runnable.forget());
|
||||
return true;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче