Lines 29-35
a/Source/WebCore/bindings/js/ScriptModuleLoader.cpp_sec1
|
29 |
#include "CachedModuleScriptLoader.h" |
29 |
#include "CachedModuleScriptLoader.h" |
30 |
#include "CachedScript.h" |
30 |
#include "CachedScript.h" |
31 |
#include "CachedScriptFetcher.h" |
31 |
#include "CachedScriptFetcher.h" |
32 |
#include "Document.h" |
|
|
33 |
#include "Frame.h" |
32 |
#include "Frame.h" |
34 |
#include "JSDOMBinding.h" |
33 |
#include "JSDOMBinding.h" |
35 |
#include "JSDOMPromiseDeferred.h" |
34 |
#include "JSDOMPromiseDeferred.h" |
Lines 52-59
a/Source/WebCore/bindings/js/ScriptModuleLoader.cpp_sec2
|
52 |
|
51 |
|
53 |
namespace WebCore { |
52 |
namespace WebCore { |
54 |
|
53 |
|
55 |
ScriptModuleLoader::ScriptModuleLoader(Document& document) |
54 |
ScriptModuleLoader::ScriptModuleLoader(ScriptExecutionContext& context, OwnerType ownerType) |
56 |
: m_document(document) |
55 |
: m_context(context) |
|
|
56 |
, m_ownerType(ownerType) |
57 |
{ |
57 |
{ |
58 |
} |
58 |
} |
59 |
|
59 |
|
Lines 68-74
static bool isRootModule(JSC::JSValue importerModuleKey)
a/Source/WebCore/bindings/js/ScriptModuleLoader.cpp_sec3
|
68 |
return importerModuleKey.isSymbol() || importerModuleKey.isUndefined(); |
68 |
return importerModuleKey.isSymbol() || importerModuleKey.isUndefined(); |
69 |
} |
69 |
} |
70 |
|
70 |
|
71 |
static Expected<URL, String> resolveModuleSpecifier(Document& document, const String& specifier, const URL& baseURL) |
71 |
static Expected<URL, String> resolveModuleSpecifier(ScriptExecutionContext& context, ScriptModuleLoader::OwnerType ownerType, const String& specifier, const URL& baseURL) |
72 |
{ |
72 |
{ |
73 |
// https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier |
73 |
// https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier |
74 |
|
74 |
|
Lines 79-85
static Expected<URL, String> resolveModuleSpecifier(Document& document, const St
a/Source/WebCore/bindings/js/ScriptModuleLoader.cpp_sec4
|
79 |
if (!specifier.startsWith('/') && !specifier.startsWith("./") && !specifier.startsWith("../")) |
79 |
if (!specifier.startsWith('/') && !specifier.startsWith("./") && !specifier.startsWith("../")) |
80 |
return makeUnexpected(makeString("Module specifier, '"_s, specifier, "' does not start with \"/\", \"./\", or \"../\". Referenced from "_s, baseURL.string())); |
80 |
return makeUnexpected(makeString("Module specifier, '"_s, specifier, "' does not start with \"/\", \"./\", or \"../\". Referenced from "_s, baseURL.string())); |
81 |
|
81 |
|
82 |
auto result = document.completeURL(specifier, baseURL); |
82 |
URL result; |
|
|
83 |
if (ownerType == ScriptModuleLoader::OwnerType::Document) |
84 |
result = downcast<Document>(context).completeURL(specifier, baseURL); |
85 |
else |
86 |
result = URL(baseURL, specifier); |
87 |
|
83 |
if (!result.isValid()) |
88 |
if (!result.isValid()) |
84 |
return makeUnexpected(makeString("Module name, '"_s, result.string(), "' does not resolve to a valid URL."_s)); |
89 |
return makeUnexpected(makeString("Module name, '"_s, result.string(), "' does not resolve to a valid URL."_s)); |
85 |
return result; |
90 |
return result; |
Lines 107-113
JSC::Identifier ScriptModuleLoader::resolve(JSC::JSGlobalObject* jsGlobalObject,
a/Source/WebCore/bindings/js/ScriptModuleLoader.cpp_sec5
|
107 |
URL baseURL = responseURLFromRequestURL(*jsGlobalObject, importerModuleKey); |
112 |
URL baseURL = responseURLFromRequestURL(*jsGlobalObject, importerModuleKey); |
108 |
RETURN_IF_EXCEPTION(scope, { }); |
113 |
RETURN_IF_EXCEPTION(scope, { }); |
109 |
|
114 |
|
110 |
auto result = resolveModuleSpecifier(m_document, specifier, baseURL); |
115 |
auto result = resolveModuleSpecifier(m_context, m_ownerType, specifier, baseURL); |
111 |
if (!result) { |
116 |
if (!result) { |
112 |
JSC::throwTypeError(jsGlobalObject, scope, result.error()); |
117 |
JSC::throwTypeError(jsGlobalObject, scope, result.error()); |
113 |
return { }; |
118 |
return { }; |
Lines 161-173
JSC::JSInternalPromise* ScriptModuleLoader::fetch(JSC::JSGlobalObject* jsGlobalO
a/Source/WebCore/bindings/js/ScriptModuleLoader.cpp_sec6
|
161 |
if (auto* scriptFetchParameters = JSC::jsDynamicCast<JSC::JSScriptFetchParameters*>(vm, parameters)) |
166 |
if (auto* scriptFetchParameters = JSC::jsDynamicCast<JSC::JSScriptFetchParameters*>(vm, parameters)) |
162 |
topLevelFetchParameters = static_cast<ModuleFetchParameters*>(&scriptFetchParameters->parameters()); |
167 |
topLevelFetchParameters = static_cast<ModuleFetchParameters*>(&scriptFetchParameters->parameters()); |
163 |
|
168 |
|
164 |
auto loader = CachedModuleScriptLoader::create(*this, deferred.get(), *static_cast<CachedScriptFetcher*>(JSC::jsCast<JSC::JSScriptFetcher*>(scriptFetcher)->fetcher()), WTFMove(topLevelFetchParameters)); |
169 |
if (m_ownerType == OwnerType::Document) { |
165 |
m_loaders.add(loader.copyRef()); |
170 |
auto loader = CachedModuleScriptLoader::create(*this, deferred.get(), *static_cast<CachedScriptFetcher*>(JSC::jsCast<JSC::JSScriptFetcher*>(scriptFetcher)->fetcher()), WTFMove(topLevelFetchParameters)); |
166 |
if (!loader->load(m_document, completedURL)) { |
171 |
m_loaders.add(loader.copyRef()); |
167 |
loader->clearClient(); |
172 |
if (!loader->load(downcast<Document>(m_context), WTFMove(completedURL))) { |
168 |
m_loaders.remove(WTFMove(loader)); |
173 |
loader->clearClient(); |
169 |
rejectToPropagateNetworkError(deferred.get(), ModuleFetchFailureKind::WasErrored, "Importing a module script failed."_s); |
174 |
m_loaders.remove(WTFMove(loader)); |
170 |
return jsPromise; |
175 |
rejectToPropagateNetworkError(deferred.get(), ModuleFetchFailureKind::WasErrored, "Importing a module script failed."_s); |
|
|
176 |
return jsPromise; |
177 |
} |
178 |
} else { |
179 |
auto loader = WorkerModuleScriptLoader::create(*this, deferred.get(), *static_cast<CachedScriptFetcher*>(JSC::jsCast<JSC::JSScriptFetcher*>(scriptFetcher)->fetcher()), WTFMove(topLevelFetchParameters)); |
180 |
m_loaders.add(loader.copyRef()); |
181 |
loader->load(m_context, WTFMove(completedURL)); |
171 |
} |
182 |
} |
172 |
|
183 |
|
173 |
return jsPromise; |
184 |
return jsPromise; |
Lines 176-182
JSC::JSInternalPromise* ScriptModuleLoader::fetch(JSC::JSGlobalObject* jsGlobalO
a/Source/WebCore/bindings/js/ScriptModuleLoader.cpp_sec7
|
176 |
URL ScriptModuleLoader::moduleURL(JSC::JSGlobalObject& jsGlobalObject, JSC::JSValue moduleKeyValue) |
187 |
URL ScriptModuleLoader::moduleURL(JSC::JSGlobalObject& jsGlobalObject, JSC::JSValue moduleKeyValue) |
177 |
{ |
188 |
{ |
178 |
if (moduleKeyValue.isSymbol()) |
189 |
if (moduleKeyValue.isSymbol()) |
179 |
return m_document.url(); |
190 |
return m_context.url(); |
180 |
|
191 |
|
181 |
ASSERT(moduleKeyValue.isString()); |
192 |
ASSERT(moduleKeyValue.isString()); |
182 |
return URL(URL(), asString(moduleKeyValue)->value(&jsGlobalObject)); |
193 |
return URL(URL(), asString(moduleKeyValue)->value(&jsGlobalObject)); |
Lines 187-194
URL ScriptModuleLoader::responseURLFromRequestURL(JSC::JSGlobalObject& jsGlobalO
a/Source/WebCore/bindings/js/ScriptModuleLoader.cpp_sec8
|
187 |
JSC::VM& vm = jsGlobalObject.vm(); |
198 |
JSC::VM& vm = jsGlobalObject.vm(); |
188 |
auto scope = DECLARE_THROW_SCOPE(vm); |
199 |
auto scope = DECLARE_THROW_SCOPE(vm); |
189 |
|
200 |
|
190 |
if (isRootModule(moduleKeyValue)) |
201 |
if (isRootModule(moduleKeyValue)) { |
191 |
return m_document.baseURL(); |
202 |
if (m_ownerType == OwnerType::Document) |
|
|
203 |
return downcast<Document>(m_context).baseURL(); |
204 |
return m_context.url(); |
205 |
} |
192 |
|
206 |
|
193 |
ASSERT(!isRootModule(moduleKeyValue)); |
207 |
ASSERT(!isRootModule(moduleKeyValue)); |
194 |
ASSERT(moduleKeyValue.isString()); |
208 |
ASSERT(moduleKeyValue.isString()); |
Lines 219-226
JSC::JSValue ScriptModuleLoader::evaluate(JSC::JSGlobalObject* jsGlobalObject, J
a/Source/WebCore/bindings/js/ScriptModuleLoader.cpp_sec9
|
219 |
if (!sourceURL.isValid()) |
233 |
if (!sourceURL.isValid()) |
220 |
return JSC::throwTypeError(jsGlobalObject, scope, "Module key is an invalid URL."_s); |
234 |
return JSC::throwTypeError(jsGlobalObject, scope, "Module key is an invalid URL."_s); |
221 |
|
235 |
|
222 |
if (auto* frame = m_document.frame()) |
236 |
if (m_ownerType == OwnerType::Document) { |
223 |
return frame->script().evaluateModule(sourceURL, *moduleRecord); |
237 |
if (auto* frame = downcast<Document>(m_context).frame()) |
|
|
238 |
return frame->script().evaluateModule(sourceURL, *moduleRecord); |
239 |
} else { |
240 |
ASSERT(is<WorkerOrWorkletGlobalScope>(m_context)); |
241 |
if (auto* script = downcast<WorkerOrWorkletGlobalScope>(m_context).script()) |
242 |
return script->evaluateModule(*moduleRecord); |
243 |
} |
224 |
return JSC::jsUndefined(); |
244 |
return JSC::jsUndefined(); |
225 |
} |
245 |
} |
226 |
|
246 |
|
Lines 248-270
JSC::JSInternalPromise* ScriptModuleLoader::importModule(JSC::JSGlobalObject* js
a/Source/WebCore/bindings/js/ScriptModuleLoader.cpp_sec10
|
248 |
URL baseURL; |
268 |
URL baseURL; |
249 |
RefPtr<JSC::ScriptFetcher> scriptFetcher; |
269 |
RefPtr<JSC::ScriptFetcher> scriptFetcher; |
250 |
if (sourceOrigin.isNull()) { |
270 |
if (sourceOrigin.isNull()) { |
251 |
baseURL = m_document.baseURL(); |
271 |
String charset; |
252 |
scriptFetcher = CachedScriptFetcher::create(m_document.charset()); |
272 |
if (m_ownerType == OwnerType::Document) { |
|
|
273 |
baseURL = downcast<Document>(m_context).baseURL(); |
274 |
charset = downcast<Document>(m_context).charset(); |
275 |
} else { |
276 |
baseURL = m_context.url(); |
277 |
charset = "utf-8"_s; |
278 |
} |
279 |
scriptFetcher = CachedScriptFetcher::create(WTFMove(charset)); |
253 |
} else { |
280 |
} else { |
254 |
baseURL = URL(URL(), sourceOrigin.string()); |
281 |
baseURL = URL(URL(), sourceOrigin.string()); |
255 |
if (!baseURL.isValid()) |
282 |
if (!baseURL.isValid()) |
256 |
return rejectPromise(globalObject, TypeError, "Importer module key is not a Symbol or a String."_s); |
283 |
return rejectPromise(globalObject, TypeError, "Importer module key is not a Symbol or a String."_s); |
257 |
|
284 |
|
|
|
285 |
String charset; |
286 |
if (m_ownerType == OwnerType::Document) |
287 |
charset = downcast<Document>(m_context).charset(); |
288 |
else |
289 |
charset = "utf-8"_s; |
290 |
|
258 |
if (sourceOrigin.fetcher()) |
291 |
if (sourceOrigin.fetcher()) |
259 |
scriptFetcher = sourceOrigin.fetcher(); |
292 |
scriptFetcher = sourceOrigin.fetcher(); |
260 |
else |
293 |
else |
261 |
scriptFetcher = CachedScriptFetcher::create(m_document.charset()); |
294 |
scriptFetcher = CachedScriptFetcher::create(WTFMove(charset)); |
262 |
} |
295 |
} |
263 |
ASSERT(baseURL.isValid()); |
296 |
ASSERT(baseURL.isValid()); |
264 |
ASSERT(scriptFetcher); |
297 |
ASSERT(scriptFetcher); |
265 |
|
298 |
|
266 |
auto specifier = moduleName->value(jsGlobalObject); |
299 |
auto specifier = moduleName->value(jsGlobalObject); |
267 |
auto result = resolveModuleSpecifier(m_document, specifier, baseURL); |
300 |
auto result = resolveModuleSpecifier(m_context, m_ownerType, specifier, baseURL); |
268 |
if (!result) |
301 |
if (!result) |
269 |
return rejectPromise(globalObject, TypeError, result.error()); |
302 |
return rejectPromise(globalObject, TypeError, result.error()); |
270 |
|
303 |
|
Lines 288-349
JSC::JSObject* ScriptModuleLoader::createImportMetaProperties(JSC::JSGlobalObjec
a/Source/WebCore/bindings/js/ScriptModuleLoader.cpp_sec11
|
288 |
return metaProperties; |
321 |
return metaProperties; |
289 |
} |
322 |
} |
290 |
|
323 |
|
291 |
void ScriptModuleLoader::notifyFinished(CachedModuleScriptLoader& loader, RefPtr<DeferredPromise> promise) |
324 |
void ScriptModuleLoader::notifyFinished(ModuleScriptLoader& moduleScriptLoader, URL&& sourceURL, Ref<DeferredPromise> promise) |
292 |
{ |
325 |
{ |
293 |
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-single-module-script |
326 |
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-single-module-script |
294 |
|
327 |
|
295 |
URL sourceURL = loader.sourceURL(); |
328 |
if (!m_loaders.remove(&moduleScriptLoader)) |
296 |
if (!m_loaders.remove(&loader)) |
|
|
297 |
return; |
329 |
return; |
298 |
loader.clearClient(); |
330 |
moduleScriptLoader.clearClient(); |
299 |
|
331 |
|
300 |
auto& cachedScript = *loader.cachedScript(); |
332 |
if (m_ownerType == OwnerType::Document) { |
|
|
333 |
auto& loader = static_cast<CachedModuleScriptLoader&>(moduleScriptLoader); |
334 |
auto& cachedScript = *loader.cachedScript(); |
301 |
|
335 |
|
302 |
if (cachedScript.resourceError().isAccessControl()) { |
336 |
if (cachedScript.resourceError().isAccessControl()) { |
303 |
promise->reject(TypeError, "Cross-origin script load denied by Cross-Origin Resource Sharing policy."_s); |
337 |
promise->reject(TypeError, "Cross-origin script load denied by Cross-Origin Resource Sharing policy."_s); |
304 |
return; |
338 |
return; |
305 |
} |
339 |
} |
306 |
|
340 |
|
307 |
if (cachedScript.errorOccurred()) { |
341 |
if (cachedScript.errorOccurred()) { |
308 |
rejectToPropagateNetworkError(*promise, ModuleFetchFailureKind::WasErrored, "Importing a module script failed."_s); |
342 |
rejectToPropagateNetworkError(promise.get(), ModuleFetchFailureKind::WasErrored, "Importing a module script failed."_s); |
309 |
return; |
343 |
return; |
310 |
} |
344 |
} |
311 |
|
345 |
|
312 |
if (cachedScript.wasCanceled()) { |
346 |
if (cachedScript.wasCanceled()) { |
313 |
rejectToPropagateNetworkError(*promise, ModuleFetchFailureKind::WasCanceled, "Importing a module script is canceled."_s); |
347 |
rejectToPropagateNetworkError(promise.get(), ModuleFetchFailureKind::WasCanceled, "Importing a module script is canceled."_s); |
314 |
return; |
348 |
return; |
315 |
} |
349 |
} |
316 |
|
350 |
|
317 |
if (!MIMETypeRegistry::isSupportedJavaScriptMIMEType(cachedScript.response().mimeType())) { |
351 |
if (!MIMETypeRegistry::isSupportedJavaScriptMIMEType(cachedScript.response().mimeType())) { |
318 |
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-single-module-script |
352 |
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-single-module-script |
319 |
// The result of extracting a MIME type from response's header list (ignoring parameters) is not a JavaScript MIME type. |
353 |
// The result of extracting a MIME type from response's header list (ignoring parameters) is not a JavaScript MIME type. |
320 |
// For historical reasons, fetching a classic script does not include MIME type checking. In contrast, module scripts will fail to load if they are not of a correct MIME type. |
354 |
// For historical reasons, fetching a classic script does not include MIME type checking. In contrast, module scripts will fail to load if they are not of a correct MIME type. |
321 |
promise->reject(TypeError, makeString("'", cachedScript.response().mimeType(), "' is not a valid JavaScript MIME type.")); |
355 |
promise->reject(TypeError, makeString("'", cachedScript.response().mimeType(), "' is not a valid JavaScript MIME type.")); |
322 |
return; |
356 |
return; |
323 |
} |
357 |
} |
324 |
|
358 |
|
325 |
if (auto* parameters = loader.parameters()) { |
359 |
if (auto* parameters = loader.parameters()) { |
326 |
if (!matchIntegrityMetadata(cachedScript, parameters->integrity())) { |
360 |
if (!matchIntegrityMetadata(cachedScript, parameters->integrity())) { |
327 |
promise->reject(TypeError, makeString("Cannot load script ", integrityMismatchDescription(cachedScript, parameters->integrity()))); |
361 |
promise->reject(TypeError, makeString("Cannot load script ", integrityMismatchDescription(cachedScript, parameters->integrity()))); |
|
|
362 |
return; |
363 |
} |
364 |
} |
365 |
|
366 |
URL responseURL = cachedScript.response().url(); |
367 |
// If we do not have redirection, we must reserve the source URL's fragment explicitly here since ResourceResponse::url() is the one when we first cache it to MemoryCache. |
368 |
// FIXME: We should track fragments through redirections. |
369 |
// https://bugs.webkit.org/show_bug.cgi?id=158420 |
370 |
// https://bugs.webkit.org/show_bug.cgi?id=210490 |
371 |
if (!cachedScript.hasRedirections() && cachedScript.response().source() != ResourceResponse::Source::ServiceWorker) { |
372 |
if (sourceURL.hasFragmentIdentifier()) |
373 |
responseURL.setFragmentIdentifier(sourceURL.fragmentIdentifier()); |
374 |
} |
375 |
|
376 |
m_requestURLToResponseURLMap.add(sourceURL.string(), WTFMove(responseURL)); |
377 |
promise->resolveWithCallback([&] (JSDOMGlobalObject& jsGlobalObject) { |
378 |
return JSC::JSSourceCode::create(jsGlobalObject.vm(), |
379 |
JSC::SourceCode { ScriptSourceCode { &cachedScript, JSC::SourceProviderSourceType::Module, loader.scriptFetcher() }.jsSourceCode() }); |
380 |
}); |
381 |
} else { |
382 |
auto& loader = static_cast<WorkerModuleScriptLoader&>(moduleScriptLoader); |
383 |
auto& workerScriptLoader = loader.scriptLoader(); |
384 |
|
385 |
if (workerScriptLoader.failed()) { |
386 |
if (workerScriptLoader.error().isAccessControl()) { |
387 |
promise->reject(TypeError, "Cross-origin script load denied by Cross-Origin Resource Sharing policy."_s); |
388 |
return; |
389 |
} |
390 |
|
391 |
if (workerScriptLoader.error().isCancellation()) { |
392 |
rejectToPropagateNetworkError(promise.get(), ModuleFetchFailureKind::WasCanceled, "Importing a module script is canceled."_s); |
393 |
return; |
394 |
} |
395 |
|
396 |
rejectToPropagateNetworkError(promise.get(), ModuleFetchFailureKind::WasErrored, "Importing a module script failed."_s); |
328 |
return; |
397 |
return; |
329 |
} |
398 |
} |
330 |
} |
|
|
331 |
|
399 |
|
332 |
URL responseURL = cachedScript.response().url(); |
400 |
if (!MIMETypeRegistry::isSupportedJavaScriptMIMEType(workerScriptLoader.responseMIMEType())) { |
333 |
// If we do not have redirection, we must reserve the source URL's fragment explicitly here since ResourceResponse::url() is the one when we first cache it to MemoryCache. |
401 |
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-single-module-script |
334 |
// FIXME: We should track fragments through redirections. |
402 |
// The result of extracting a MIME type from response's header list (ignoring parameters) is not a JavaScript MIME type. |
335 |
// https://bugs.webkit.org/show_bug.cgi?id=158420 |
403 |
// For historical reasons, fetching a classic script does not include MIME type checking. In contrast, module scripts will fail to load if they are not of a correct MIME type. |
336 |
// https://bugs.webkit.org/show_bug.cgi?id=210490 |
404 |
promise->reject(TypeError, makeString("'", workerScriptLoader.responseMIMEType(), "' is not a valid JavaScript MIME type.")); |
337 |
if (!cachedScript.hasRedirections() && cachedScript.response().source() != ResourceResponse::Source::ServiceWorker) { |
405 |
return; |
338 |
if (sourceURL.hasFragmentIdentifier()) |
406 |
} |
339 |
responseURL.setFragmentIdentifier(sourceURL.fragmentIdentifier()); |
|
|
340 |
} |
341 |
|
407 |
|
342 |
m_requestURLToResponseURLMap.add(sourceURL.string(), WTFMove(responseURL)); |
408 |
URL responseURL = workerScriptLoader.responseURL(); |
343 |
promise->resolveWithCallback([&] (JSDOMGlobalObject& jsGlobalObject) { |
409 |
|
344 |
return JSC::JSSourceCode::create(jsGlobalObject.vm(), |
410 |
m_requestURLToResponseURLMap.add(sourceURL.string(), responseURL); |
345 |
JSC::SourceCode { ScriptSourceCode { &cachedScript, JSC::SourceProviderSourceType::Module, loader.scriptFetcher() }.jsSourceCode() }); |
411 |
promise->resolveWithCallback([&] (JSDOMGlobalObject& jsGlobalObject) { |
346 |
}); |
412 |
return JSC::JSSourceCode::create(jsGlobalObject.vm(), |
|
|
413 |
JSC::SourceCode { ScriptSourceCode { workerScriptLoader.script(), WTFMove(responseURL), { }, JSC::SourceProviderSourceType::Module, loader.scriptFetcher() }.jsSourceCode() }); |
414 |
}); |
415 |
} |
347 |
} |
416 |
} |
348 |
|
417 |
|
349 |
} |
418 |
} |