Bug 1519100 - Implement module promises in html r=emilio,jonco

Differential Revision: https://phabricator.services.mozilla.com/D95885
This commit is contained in:
yulia 2020-12-02 12:40:08 +00:00
Родитель ecd3ed9ff7
Коммит 21104289bb
5 изменённых файлов: 87 добавлений и 41 удалений

Просмотреть файл

@ -173,8 +173,8 @@ nsresult nsJSUtils::ModuleInstantiate(JSContext* aCx,
return NS_OK;
}
nsresult nsJSUtils::ModuleEvaluate(JSContext* aCx,
JS::Handle<JSObject*> aModule) {
JSObject* nsJSUtils::ModuleEvaluate(JSContext* aCx,
JS::Handle<JSObject*> aModule) {
AUTO_PROFILER_LABEL("nsJSUtils::ModuleEvaluate", JS);
MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
@ -182,13 +182,9 @@ nsresult nsJSUtils::ModuleEvaluate(JSContext* aCx,
MOZ_ASSERT(CycleCollectedJSContext::Get() &&
CycleCollectedJSContext::Get()->MicroTaskLevel());
NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), NS_OK);
NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), nullptr);
if (!JS::ModuleEvaluate(aCx, aModule)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
return JS::ModuleEvaluate(aCx, aModule);
}
static bool AddScopeChainItem(JSContext* aCx, nsINode* aNode,

Просмотреть файл

@ -80,7 +80,18 @@ class nsJSUtils {
static nsresult ModuleInstantiate(JSContext* aCx,
JS::Handle<JSObject*> aModule);
static nsresult ModuleEvaluate(JSContext* aCx, JS::Handle<JSObject*> aModule);
/*
* Wrapper for JSAPI ModuleEvaluate function.
*
* @param JSContext aCx
* The JSContext where this is executed.
* @param JS::Handle<JSObject*> aModule
* The module to be evaluated.
* @returns JS::MutableHandle<JSObject*> aEvaluationPromise
* The evaluaation promise returned from evaluating the module.
*/
static JSObject* ModuleEvaluate(JSContext* aCx,
JS::Handle<JSObject*> aModule);
// Returns false if an exception got thrown on aCx. Passing a null
// aElement is allowed; that wil produce an empty aScopeChain.

Просмотреть файл

@ -16,6 +16,7 @@
#include "jsfriendapi.h"
#include "js/Array.h" // JS::GetArrayLength
#include "js/CompilationAndEvaluation.h"
#include "js/ContextOptions.h" // JS::ContextOptionsRef
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/MemoryFunctions.h"
#include "js/Modules.h" // JS::FinishDynamicModuleImport, JS::{G,S}etModuleResolveHook, JS::Get{ModulePrivate,ModuleScript,RequestedModule{s,Specifier,SourcePos}}, JS::SetModule{DynamicImport,Metadata}Hook
@ -219,7 +220,7 @@ ScriptLoader::~ScriptLoader() {
for (ScriptLoadRequest* req = mDynamicImportRequests.getFirst(); req;
req = req->getNext()) {
FinishDynamicImport(req->AsModuleRequest(), NS_ERROR_ABORT);
FinishDynamicImportAndReject(req->AsModuleRequest(), NS_ERROR_ABORT);
}
for (ScriptLoadRequest* req =
@ -1052,31 +1053,31 @@ void ScriptLoader::StartDynamicImport(ModuleLoadRequest* aRequest) {
nsresult rv = StartLoad(aRequest);
if (NS_FAILED(rv)) {
FinishDynamicImport(aRequest, rv);
FinishDynamicImportAndReject(aRequest, rv);
}
}
void ScriptLoader::FinishDynamicImport(ModuleLoadRequest* aRequest,
nsresult aResult) {
void ScriptLoader::FinishDynamicImportAndReject(ModuleLoadRequest* aRequest,
nsresult aResult) {
AutoJSAPI jsapi;
MOZ_ASSERT(NS_FAILED(aResult));
MOZ_ALWAYS_TRUE(jsapi.Init(aRequest->mDynamicPromise));
FinishDynamicImport(jsapi.cx(), aRequest, aResult);
FinishDynamicImport(jsapi.cx(), aRequest, aResult, nullptr);
}
void ScriptLoader::FinishDynamicImport(JSContext* aCx,
ModuleLoadRequest* aRequest,
nsresult aResult) {
void ScriptLoader::FinishDynamicImport(
JSContext* aCx, ModuleLoadRequest* aRequest, nsresult aResult,
JS::Handle<JSObject*> aEvaluationPromise) {
// If aResult is a failed result, we don't have an EvaluationPromise. If it
// succeeded, evaluationPromise may still be null, but in this case it will
// be handled by rejecting the dynamic module import promise in the JSAPI.
MOZ_ASSERT_IF(NS_FAILED(aResult), !aEvaluationPromise);
LOG(("ScriptLoadRequest (%p): Finish dynamic import %x %d", aRequest,
unsigned(aResult), JS_IsExceptionPending(aCx)));
// Complete the dynamic import, report failures indicated by aResult or as a
// pending exception on the context.
JS::DynamicImportStatus status =
(NS_FAILED(aResult) || JS_IsExceptionPending(aCx))
? JS::DynamicImportStatus::Failed
: JS::DynamicImportStatus::Ok;
if (NS_FAILED(aResult) &&
aResult != NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
MOZ_ASSERT(!JS_IsExceptionPending(aCx));
@ -1089,8 +1090,8 @@ void ScriptLoader::FinishDynamicImport(JSContext* aCx,
JS::Rooted<JSString*> specifier(aCx, aRequest->mDynamicSpecifier);
JS::Rooted<JSObject*> promise(aCx, aRequest->mDynamicPromise);
JS::FinishDynamicModuleImport(aCx, status, referencingScript, specifier,
promise);
JS::FinishDynamicModuleImport(aCx, aEvaluationPromise, referencingScript,
specifier, promise);
// FinishDynamicModuleImport clears any pending exception.
MOZ_ASSERT(!JS_IsExceptionPending(aCx));
@ -2642,7 +2643,7 @@ void ScriptLoader::ProcessDynamicImport(ModuleLoadRequest* aRequest) {
}
if (NS_FAILED(rv)) {
FinishDynamicImport(aRequest, rv);
FinishDynamicImportAndReject(aRequest, rv);
}
}
@ -2941,7 +2942,7 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
// For a dynamic import, the promise is rejected. Otherwise an error is
// either reported by AutoEntryScript.
if (request->IsDynamicImport()) {
FinishDynamicImport(cx, request, NS_OK);
FinishDynamicImport(cx, request, NS_OK, nullptr);
}
return NS_OK;
}
@ -2954,18 +2955,20 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
TRACE_FOR_TEST(aRequest->GetScriptElement(),
"scriptloader_evaluate_module");
rv = nsJSUtils::ModuleEvaluate(cx, module);
MOZ_ASSERT(NS_FAILED(rv) == aes.HasException());
if (NS_FAILED(rv)) {
LOG(("ScriptLoadRequest (%p): evaluation failed", aRequest));
// For a dynamic import, the promise is rejected. Otherwise an error is
// either reported by AutoEntryScript.
rv = NS_OK;
}
JS::Rooted<JSObject*> aEvaluationPromise(
cx, nsJSUtils::ModuleEvaluate(cx, module));
if (request->IsDynamicImport()) {
FinishDynamicImport(cx, request, rv);
FinishDynamicImport(cx, request, rv, aEvaluationPromise);
} else {
// If this is not a dynamic import, and if the promise is rejected, the
// value is unwrapped from the promise value.
if (!JS::ThrowOnModuleEvaluationFailure(cx, aEvaluationPromise)) {
LOG(("ScriptLoadRequest (%p): evaluation failed", aRequest));
// For a dynamic import, the promise is rejected. Otherwise an error
// is either reported by AutoEntryScript.
rv = NS_OK;
}
}
TRACE_FOR_TEST_NONE(aRequest->GetScriptElement(),
@ -3780,7 +3783,7 @@ void ScriptLoader::HandleLoadError(ScriptLoadRequest* aRequest,
// FinishDynamicImport must happen exactly once for each dynamic import
// request. If the load is aborted we do it when we remove the request
// from mDynamicImportRequests.
FinishDynamicImport(modReq, aResult);
FinishDynamicImportAndReject(modReq, aResult);
}
} else {
MOZ_ASSERT(!modReq->IsTopLevel());
@ -4028,7 +4031,7 @@ void ScriptLoader::ParsingComplete(bool aTerminated) {
// FinishDynamicImport must happen exactly once for each dynamic import
// request. If the load is aborted we do it when we remove the request
// from mDynamicImportRequests.
FinishDynamicImport(req->AsModuleRequest(), NS_ERROR_ABORT);
FinishDynamicImportAndReject(req->AsModuleRequest(), NS_ERROR_ABORT);
}
mDynamicImportRequests.Clear();

Просмотреть файл

@ -411,9 +411,40 @@ class ScriptLoader final : public nsISupports {
JS::MutableHandle<JSObject*> aModuleOut);
void StartDynamicImport(ModuleLoadRequest* aRequest);
void FinishDynamicImport(ModuleLoadRequest* aRequest, nsresult aResult);
/**
* Shorthand Wrapper for JSAPI FinishDynamicImport function for the reject
* case where we do not have `aEvaluationPromise`. As there is no evaluation
* Promise, JS::FinishDynamicImport will always reject.
*
* @param aRequest
* The module load request for the dynamic module.
* @param aResult
* The result of running ModuleEvaluate -- If this is successful, then
* we can await the associated EvaluationPromise.
*/
void FinishDynamicImportAndReject(ModuleLoadRequest* aRequest,
nsresult aResult);
/**
* Wrapper for JSAPI FinishDynamicImport function. Takes an optional argument
* `aEvaluationPromise` which, if null, exits early.
*
* @param aCX
* The JSContext for the module.
* @param aRequest
* The module load request for the dynamic module.
* @param aResult
* The result of running ModuleEvaluate -- If this is successful, then
* we can await the associated EvaluationPromise.
* @param aEvaluationPromise
* The evaluation promise returned from evaluating the module. If this
* is null, JS::FinishDynamicImport will reject the dynamic import
* module promise.
*/
void FinishDynamicImport(JSContext* aCx, ModuleLoadRequest* aRequest,
nsresult aResult);
nsresult aResult,
JS::Handle<JSObject*> aEvaluationPromise);
/*
* Get the currently active script. This is used as the initiating script when

Просмотреть файл

@ -405,7 +405,12 @@ void ExecutionRunnable::RunOnWorkletThread() {
// https://html.spec.whatwg.org/multipage/webappapis.html#run-a-module-script
// without /rethrow errors/ and so unhandled exceptions do not cause the
// promise to be rejected.
JS::ModuleEvaluate(cx, module);
JS::Rooted<JSObject*> evaluationPromise(cx, JS::ModuleEvaluate(cx, module));
if (!JS::ThrowOnModuleEvaluationFailure(cx, evaluationPromise)) {
mResult = NS_ERROR_DOM_UNKNOWN_ERR;
return;
}
// All done.
mResult = NS_OK;