Bug 1775424 - Part 1: Impl import.meta.resolve() on browser. r=jonco,yulia

Define 'resolve()' function on import.meta object on browser.

Add mochitests for import.meta.resolve, as some of the wpt tests for
import.meta.resolve cannot be run due to some bugs in wpt testing framework.
See https://bugzilla.mozilla.org/show_bug.cgi?id=1785806

Differential Revision: https://phabricator.services.mozilla.com/D154105
This commit is contained in:
Yoshi Cheng-Hao Huang 2022-08-29 18:12:09 +00:00
Родитель e7701511cf
Коммит a9970aa2ff
8 изменённых файлов: 220 добавлений и 22 удалений

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

@ -27,6 +27,7 @@ support-files =
[test_scriptNotParsedAsModule.html]
[test_typeAttrCaseInsensitive.html]
[test_moduleNotFound.html]
[test_import_meta_resolve.html]
[test_importNotFound.html]
[test_syntaxError.html]
[test_syntaxErrorAsync.html]

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

@ -17,6 +17,7 @@ prefs =
[test_dynamic_import_reject_importMap.html]
[test_externalImportMap.html]
[test_import_meta_resolve_importMap.html]
[test_inline_module_reject_importMap.html]
[test_load_importMap_with_base.html]
[test_load_importMap_with_base2.html]

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

@ -0,0 +1,49 @@
<!DOCTYPE html>
<head>
<meta charset=utf-8>
<title>Test import.meta.resolve with import maps</title>
</head>
<body onload='testLoaded()'>
<script type="importmap">
{
"imports": {
"simple": "./module_simpleExport.js"
}
}
</script>
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script>
var wasRun = false;
var hasThrown = false;
window.onerror = handleError;
function handleError(msg, url, line, col, error) {
ok(error instanceof TypeError, "Thrown error should be TypeError.");
hasThrown = true;
}
</script>
<script type="module">
ok(import.meta.resolve("simple") ==
"chrome://mochitests/content/chrome/dom/base/test/jsmodules/importmaps/module_simpleExport.js",
"calling import.meta.resolve with a specifier from import map.");
wasRun = true;
</script>
<script type="module">
// should throw a TypeError
import.meta.resolve("fail");
</script>
<script>
SimpleTest.waitForExplicitFinish();
function testLoaded() {
ok(wasRun, "Check inline module has run.");
ok(hasThrown, "Check inline module has thrown.");
SimpleTest.finish();
}
</script>
</body>

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

@ -0,0 +1,65 @@
<!DOCTYPE html>
<head>
<meta charset=utf-8>
<title>Test import.meta.resolve</title>
</head>
<body onload='testLoaded()'>
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script>
SimpleTest.waitForExplicitFinish();
function assertThrowsTypeError(fn, msg) {
let hasThrown = false;
try {
fn();
} catch (error) {
hasThrown = true;
ok(error instanceof TypeError, "Thrown error should be TypeError.");
}
ok(hasThrown, msg);
}
function testLoaded() {
SimpleTest.finish();
}
</script>
<script type="module">
is(typeof import.meta.resolve, "function", "resolve should be a function.");
is(import.meta.resolve.name, "resolve", "resolve.name should be 'resolve'.");
is(import.meta.resolve.length, 1, "resolve.length should be 1.");
is(Object.getPrototypeOf(import.meta.resolve), Function.prototype,
"prototype of resolve should be Function.prototype.");
</script>
<script type="module">
is(import.meta.resolve("http://example.com/"), "http://example.com/",
"resolve specifiers with absolute path.");
</script>
<script type="module">
is(import.meta.resolve("./x"), (new URL("./x", import.meta.url)).href,
"resolve specifiers with relative path.");
</script>
<script type="module">
assertThrowsTypeError(() => new import.meta.resolve("./x"),
"import.meta.resolve is not a constructor.");
</script>
<script type="module">
// Fails to resolve the specifier should throw a TypeError.
assertThrowsTypeError(() => import.meta.resolve("failed"),
"import.meta.resolve should throw if fails to resolve");
</script>
<script type="module">
for (const name of Reflect.ownKeys(import.meta)) {
const desc = Object.getOwnPropertyDescriptor(import.meta, name);
is(desc.writable, true, name + ".writable should be true.");
is(desc.enumerable, true, name + ".enumerable should be true.");
is(desc.configurable, true, name + ".configurable should be true.");
}
</script>
</body>

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

@ -141,6 +141,75 @@ JSObject* ModuleLoaderBase::HostResolveImportedModule(
return module;
}
// static
bool ModuleLoaderBase::ImportMetaResolve(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
RootedValue modulePrivate(
cx, js::GetFunctionNativeReserved(&args.callee(), ModulePrivateSlot));
// https://html.spec.whatwg.org/#hostgetimportmetaproperties
// Step 4.1. Set specifier to ? ToString(specifier).
//
// https://tc39.es/ecma262/#sec-tostring
RootedValue v(cx, args.get(ImportMetaResolveSpecifierArg));
RootedString specifier(cx, JS::ToString(cx, v));
if (!specifier) {
return false;
}
// Step 4.2, 4.3 are implemented in ImportMetaResolveImpl.
RootedString url(cx, ImportMetaResolveImpl(cx, modulePrivate, specifier));
if (!url) {
return false;
}
// Step 4.4. Return the serialization of url.
args.rval().setString(url);
return true;
}
// static
JSString* ModuleLoaderBase::ImportMetaResolveImpl(
JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier) {
RefPtr<ModuleScript> script =
static_cast<ModuleScript*>(aReferencingPrivate.toPrivate());
MOZ_ASSERT(script->IsModuleScript());
MOZ_ASSERT(JS::GetModulePrivate(script->ModuleRecord()) ==
aReferencingPrivate);
RefPtr<ModuleLoaderBase> loader = GetCurrentModuleLoader(aCx);
if (!loader) {
return nullptr;
}
nsAutoJSString specifier;
if (!specifier.init(aCx, aSpecifier)) {
return nullptr;
}
auto result = loader->ResolveModuleSpecifier(script, specifier);
if (result.isErr()) {
JS::Rooted<JS::Value> error(aCx);
nsresult rv = HandleResolveFailure(aCx, script, specifier,
result.unwrapErr(), 0, 0, &error);
if (NS_FAILED(rv)) {
JS_ReportOutOfMemory(aCx);
return nullptr;
}
JS_SetPendingException(aCx, error);
return nullptr;
}
nsCOMPtr<nsIURI> uri = result.unwrap();
nsAutoCString url;
MOZ_ALWAYS_SUCCEEDS(uri->GetAsciiSpec(url));
return JS_NewStringCopyZ(aCx, url.get());
}
// static
bool ModuleLoaderBase::HostPopulateImportMeta(
JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
@ -161,8 +230,28 @@ bool ModuleLoaderBase::HostPopulateImportMeta(
return false;
}
return JS_DefineProperty(aCx, aMetaObject, "url", urlString,
JSPROP_ENUMERATE);
// https://html.spec.whatwg.org/#import-meta-url
if (!JS_DefineProperty(aCx, aMetaObject, "url", urlString,
JSPROP_ENUMERATE)) {
return false;
}
// https://html.spec.whatwg.org/#import-meta-resolve
// Define 'resolve' function on the import.meta object.
JSFunction* resolveFunc = js::DefineFunctionWithReserved(
aCx, aMetaObject, "resolve", ImportMetaResolve, ImportMetaResolveNumArgs,
JSPROP_ENUMERATE);
if (!resolveFunc) {
return false;
}
// Store the 'active script' of the meta object into the function slot.
// https://html.spec.whatwg.org/#active-script
RootedObject resolveFuncObj(aCx, JS_GetFunctionObject(resolveFunc));
js::SetFunctionNativeReserved(resolveFuncObj, ModulePrivateSlot,
aReferencingPrivate);
return true;
}
// static

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

@ -296,6 +296,10 @@ class ModuleLoaderBase : public nsISupports {
static bool HostPopulateImportMeta(JSContext* aCx,
JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSObject*> aMetaObject);
static bool ImportMetaResolve(JSContext* cx, unsigned argc, Value* vp);
static JSString* ImportMetaResolveImpl(
JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier);
static bool HostImportModuleDynamically(
JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSObject*> aModuleRequest, JS::Handle<JSObject*> aPromise);
@ -378,6 +382,14 @@ class ModuleLoaderBase : public nsISupports {
nsresult CreateModuleScript(ModuleLoadRequest* aRequest);
// The slot stored in ImportMetaResolve function.
enum { ModulePrivateSlot = 0, SlotCount };
// The number of args in ImportMetaResolve.
static const uint32_t ImportMetaResolveNumArgs = 1;
// The index of the 'specifier' argument in ImportMetaResolve.
static const uint32_t ImportMetaResolveSpecifierArg = 0;
public:
static mozilla::LazyLogModule gCspPRLog;
static mozilla::LazyLogModule gModuleLoaderBaseLog;

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

@ -1,12 +1,2 @@
[import-meta-resolve-importmap.html]
[import.meta.resolve() given an import mapped bare specifier]
expected: FAIL
[import.meta.resolve() given an import mapped URL-like specifier]
expected: FAIL
[Testing the ToString() step of import.meta.resolve() via import maps]
expected: FAIL
[import(import.meta.resolve(x)) can be different from import(x)]
expected: FAIL
prefs: [dom.importMaps.enabled:true]

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

@ -1,9 +0,0 @@
[import-meta-resolve-multiple-scripts.html]
[import.meta.resolve resolves URLs relative to the import.meta.url, not relative to the active script when it is called: another global's inline script]
expected: FAIL
[import.meta.resolve still works if its global has been destroyed (by detaching the iframe)]
expected: FAIL
[import.meta.resolve resolves URLs relative to the import.meta.url, not relative to the active script when it is called: another module script]
expected: FAIL