Bug 1342012 - Allow dynamic import in cases where there's no referencing script or module r=smaug

This commit is contained in:
Jon Coppeard 2018-12-06 16:52:18 -05:00
parent 045c231468
commit e8462c961e
12 changed files with 144 additions and 104 deletions

View File

@ -24,13 +24,11 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_CLASS(LoadedScript)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(LoadedScript)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoader)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(LoadedScript)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoader)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@ -40,13 +38,11 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(LoadedScript)
NS_IMPL_CYCLE_COLLECTING_RELEASE(LoadedScript)
LoadedScript::LoadedScript(ScriptKind aKind, ScriptLoader* aLoader,
ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL)
LoadedScript::LoadedScript(ScriptKind aKind, ScriptFetchOptions* aFetchOptions,
nsIURI* aBaseURL)
: mKind(aKind),
mLoader(aLoader),
mFetchOptions(aFetchOptions),
mBaseURL(aBaseURL) {
MOZ_ASSERT(mLoader);
MOZ_ASSERT(mFetchOptions);
MOZ_ASSERT(mBaseURL);
}
@ -84,10 +80,9 @@ void HostFinalizeTopLevelScript(JSFreeOp* aFop, const JS::Value& aPrivate) {
// ClassicScript
//////////////////////////////////////////////////////////////
ClassicScript::ClassicScript(ScriptLoader* aLoader,
ScriptFetchOptions* aFetchOptions,
ClassicScript::ClassicScript(ScriptFetchOptions* aFetchOptions,
nsIURI* aBaseURL)
: LoadedScript(ScriptKind::eClassic, aLoader, aFetchOptions, aBaseURL) {}
: LoadedScript(ScriptKind::eClassic, aFetchOptions, aBaseURL) {}
//////////////////////////////////////////////////////////////
// ModuleScript
@ -116,9 +111,8 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_ADDREF_INHERITED(ModuleScript, LoadedScript)
NS_IMPL_RELEASE_INHERITED(ModuleScript, LoadedScript)
ModuleScript::ModuleScript(ScriptLoader* aLoader,
ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL)
: LoadedScript(ScriptKind::eModule, aLoader, aFetchOptions, aBaseURL),
ModuleScript::ModuleScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL)
: LoadedScript(ScriptKind::eModule, aFetchOptions, aBaseURL),
mSourceElementAssociated(false) {
MOZ_ASSERT(!ModuleRecord());
MOZ_ASSERT(!HasParseError());

View File

@ -26,13 +26,12 @@ class ModuleScript;
class LoadedScript : public nsISupports {
ScriptKind mKind;
RefPtr<ScriptLoader> mLoader;
RefPtr<ScriptFetchOptions> mFetchOptions;
nsCOMPtr<nsIURI> mBaseURL;
protected:
LoadedScript(ScriptKind aKind, ScriptLoader* aLoader,
ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL);
LoadedScript(ScriptKind aKind, ScriptFetchOptions* aFetchOptions,
nsIURI* aBaseURL);
virtual ~LoadedScript();
@ -45,7 +44,6 @@ class LoadedScript : public nsISupports {
inline ClassicScript* AsClassicScript();
inline ModuleScript* AsModuleScript();
ScriptLoader* Loader() const { return mLoader; }
ScriptFetchOptions* FetchOptions() const { return mFetchOptions; }
nsIURI* BaseURL() const { return mBaseURL; }
@ -56,8 +54,7 @@ class ClassicScript final : public LoadedScript {
~ClassicScript() = default;
public:
ClassicScript(ScriptLoader* aLoader, ScriptFetchOptions* aFetchOptions,
nsIURI* aBaseURL);
ClassicScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL);
};
// A single module script. May be used to satisfy multiple load requests.
@ -75,8 +72,7 @@ class ModuleScript final : public LoadedScript {
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ModuleScript,
LoadedScript)
ModuleScript(ScriptLoader* aLoader, ScriptFetchOptions* aFetchOptions,
nsIURI* aBaseURL);
ModuleScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL);
void SetModuleRecord(JS::Handle<JSObject*> aModuleRecord);
void SetParseError(const JS::Value& aError);

View File

@ -74,17 +74,16 @@ static VisitedURLSet* NewVisitedSetForTopLevelImport(nsIURI* aURI) {
}
/* static */ ModuleLoadRequest* ModuleLoadRequest::CreateDynamicImport(
nsIURI* aURI, LoadedScript* aScript,
JS::Handle<JS::Value> aReferencingPrivate, JS::Handle<JSString*> aSpecifier,
JS::Handle<JSObject*> aPromise) {
nsIURI* aURI, ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL,
ScriptLoader* aLoader, JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier, JS::Handle<JSObject*> aPromise) {
MOZ_ASSERT(aSpecifier);
MOZ_ASSERT(aPromise);
auto request = new ModuleLoadRequest(
aURI, aScript->FetchOptions(), SRIMetadata(), aScript->BaseURL(),
true, /* is top level */
aURI, aFetchOptions, SRIMetadata(), aBaseURL, true, /* is top level */
true, /* is dynamic import */
aScript->Loader(), NewVisitedSetForTopLevelImport(aURI));
aLoader, NewVisitedSetForTopLevelImport(aURI));
request->mIsInline = false;
request->mScriptMode = ScriptMode::eAsync;

View File

@ -59,8 +59,8 @@ class ModuleLoadRequest final : public ScriptLoadRequest {
// Create a module load request for dynamic module import.
static ModuleLoadRequest* CreateDynamicImport(
nsIURI* aURI, LoadedScript* aScript,
JS::Handle<JS::Value> aReferencingPrivate,
nsIURI* aURI, ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL,
ScriptLoader* aLoader, JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier, JS::Handle<JSObject*> aPromise);
bool IsTopLevel() const override { return mIsTopLevel; }

View File

@ -17,6 +17,7 @@
#include "js/CompilationAndEvaluation.h"
#include "js/MemoryFunctions.h"
#include "js/OffThreadScriptCompilation.h"
#include "js/Realm.h"
#include "js/SourceText.h"
#include "js/Utility.h"
#include "xpcpublic.h"
@ -29,6 +30,7 @@
#include "mozilla/dom/SRILogHelper.h"
#include "nsGkAtoms.h"
#include "nsNetUtil.h"
#include "nsGlobalWindowInner.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptContext.h"
#include "nsIScriptSecurityManager.h"
@ -475,7 +477,7 @@ nsresult ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) {
MOZ_ASSERT(NS_SUCCEEDED(rv) == (module != nullptr));
RefPtr<ModuleScript> moduleScript =
new ModuleScript(this, aRequest->mFetchOptions, aRequest->mBaseURL);
new ModuleScript(aRequest->mFetchOptions, aRequest->mBaseURL);
aRequest->mModuleScript = moduleScript;
if (!module) {
@ -545,7 +547,7 @@ static nsresult HandleResolveFailure(JSContext* aCx, ModuleScript* aScript,
}
static already_AddRefed<nsIURI> ResolveModuleSpecifier(
LoadedScript* aScript, const nsAString& aSpecifier) {
ScriptLoader* aLoader, LoadedScript* aScript, const nsAString& aSpecifier) {
// The following module specifiers are allowed by the spec:
// - a valid absolute URL
// - a valid relative URL that starts with "/", "./" or "../"
@ -569,7 +571,15 @@ static already_AddRefed<nsIURI> ResolveModuleSpecifier(
return nullptr;
}
rv = NS_NewURI(getter_AddRefs(uri), aSpecifier, nullptr, aScript->BaseURL());
// Get the document's base URL if we don't have a referencing script here.
nsCOMPtr<nsIURI> baseURL;
if (aScript) {
baseURL = aScript->BaseURL();
} else {
baseURL = aLoader->GetDocument()->GetDocBaseURI();
}
rv = NS_NewURI(getter_AddRefs(uri), aSpecifier, nullptr, baseURL);
if (NS_SUCCEEDED(rv)) {
return uri.forget();
}
@ -613,7 +623,8 @@ static nsresult ResolveRequestedModules(ModuleLoadRequest* aRequest,
// Let url be the result of resolving a module specifier given module script
// and requested.
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(ms, specifier);
nsCOMPtr<nsIURI> uri =
ResolveModuleSpecifier(aRequest->mLoader, ms, specifier);
if (!uri) {
uint32_t lineNumber = 0;
uint32_t columnNumber = 0;
@ -725,32 +736,81 @@ RefPtr<GenericPromise> ScriptLoader::StartFetchingModuleAndDependencies(
return ready;
}
// 8.1.3.8.1 HostResolveImportedModule(referencingModule, specifier)
JSObject* HostResolveImportedModule(JSContext* aCx,
JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier) {
// Let referencing module script be referencingModule.[[HostDefined]].
if (aReferencingPrivate.isUndefined()) {
JS_ReportErrorNumberUC(aCx, js::GetErrorMessage, nullptr,
JSMSG_IMPORT_SCRIPT_NOT_FOUND);
static ScriptLoader* GetCurrentScriptLoader(JSContext* aCx) {
JSObject* object = JS::CurrentGlobalOrNull(aCx);
if (!object) {
return nullptr;
}
RefPtr<LoadedScript> script =
static_cast<LoadedScript*>(aReferencingPrivate.toPrivate());
nsIGlobalObject* global = xpc::NativeGlobal(object);
if (!global) {
return nullptr;
}
nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(global);
nsGlobalWindowInner* innerWindow = nsGlobalWindowInner::Cast(win);
if (!innerWindow) {
return nullptr;
}
nsIDocument* document = innerWindow->GetDocument();
if (!document) {
return nullptr;
}
ScriptLoader* loader = document->ScriptLoader();
if (!loader) {
return nullptr;
}
return loader;
}
static LoadedScript* GetLoadedScriptOrNull(
JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate) {
if (aReferencingPrivate.isUndefined()) {
return nullptr;
}
auto script = static_cast<LoadedScript*>(aReferencingPrivate.toPrivate());
MOZ_ASSERT_IF(
script->IsModuleScript(),
JS::GetModulePrivate(script->AsModuleScript()->ModuleRecord()) ==
aReferencingPrivate);
return script;
}
// 8.1.3.8.1 HostResolveImportedModule(referencingModule, specifier)
JSObject* HostResolveImportedModule(JSContext* aCx,
JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier) {
JS::Rooted<JSObject*> module(aCx);
ScriptLoader::ResolveImportedModule(aCx, aReferencingPrivate, aSpecifier,
&module);
return module;
}
/* static */ void ScriptLoader::ResolveImportedModule(
JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier, JS::MutableHandle<JSObject*> aModuleOut) {
MOZ_ASSERT(!aModuleOut);
RefPtr<LoadedScript> script(GetLoadedScriptOrNull(aCx, aReferencingPrivate));
// Let url be the result of resolving a module specifier given referencing
// module script and specifier.
nsAutoJSString string;
if (!string.init(aCx, aSpecifier)) {
return nullptr;
return;
}
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(script, string);
RefPtr<ScriptLoader> loader = GetCurrentScriptLoader(aCx);
if (!loader) {
return;
}
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(loader, script, string);
// This cannot fail because resolving a module specifier must have been
// previously successful with these same two arguments.
@ -758,13 +818,12 @@ JSObject* HostResolveImportedModule(JSContext* aCx,
// Let resolved module script be moduleMap[url]. (This entry must exist for us
// to have gotten to this point.)
ModuleScript* ms = script->Loader()->GetFetchedModule(uri);
ModuleScript* ms = loader->GetFetchedModule(uri);
MOZ_ASSERT(ms, "Resolved module not found in module map");
MOZ_ASSERT(!ms->HasParseError());
MOZ_ASSERT(ms->ModuleRecord());
return ms->ModuleRecord();
aModuleOut.set(ms->ModuleRecord());
}
bool HostPopulateImportMeta(JSContext* aCx,
@ -794,17 +853,7 @@ bool HostImportModuleDynamically(JSContext* aCx,
JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier,
JS::Handle<JSObject*> aPromise) {
if (aReferencingPrivate.isUndefined()) {
JS_ReportErrorNumberUC(aCx, js::GetErrorMessage, nullptr,
JSMSG_IMPORT_SCRIPT_NOT_FOUND);
return false;
}
auto script = static_cast<LoadedScript*>(aReferencingPrivate.toPrivate());
MOZ_ASSERT_IF(
script->IsModuleScript(),
JS::GetModulePrivate(script->AsModuleScript()->ModuleRecord()) ==
aReferencingPrivate);
RefPtr<LoadedScript> script(GetLoadedScriptOrNull(aCx, aReferencingPrivate));
// Attempt to resolve the module specifier.
nsAutoJSString string;
@ -812,7 +861,12 @@ bool HostImportModuleDynamically(JSContext* aCx,
return false;
}
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(script, string);
RefPtr<ScriptLoader> loader = GetCurrentScriptLoader(aCx);
if (!loader) {
return false;
}
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(loader, script, string);
if (!uri) {
JS_ReportErrorNumberUC(aCx, js::GetErrorMessage, nullptr,
JSMSG_BAD_MODULE_SPECIFIER, string.get());
@ -820,10 +874,27 @@ bool HostImportModuleDynamically(JSContext* aCx,
}
// Create a new top-level load request.
RefPtr<ModuleLoadRequest> request = ModuleLoadRequest::CreateDynamicImport(
uri, script, aReferencingPrivate, aSpecifier, aPromise);
ScriptFetchOptions* options;
nsIURI* baseURL;
if (script) {
options = script->FetchOptions();
baseURL = script->BaseURL();
} else {
// We don't have a referencing script so fall back on using
// options from the document. This can happen when the user
// triggers an inline event handler, as there is no active script
// there.
nsIDocument* document = loader->GetDocument();
options = new ScriptFetchOptions(mozilla::CORS_NONE,
document->GetReferrerPolicy(), nullptr,
document->NodePrincipal());
baseURL = document->GetDocBaseURI();
}
script->Loader()->StartDynamicImport(request);
RefPtr<ModuleLoadRequest> request = ModuleLoadRequest::CreateDynamicImport(
uri, options, baseURL, loader, aReferencingPrivate, aSpecifier, aPromise);
loader->StartDynamicImport(request);
return true;
}
@ -848,7 +919,7 @@ void ScriptLoader::FinishDynamicImport(ModuleLoadRequest* aRequest,
void ScriptLoader::FinishDynamicImport(JSContext* aCx,
ModuleLoadRequest* aRequest,
nsresult aResult) {
LOG(("ScriptLoadRequest (%p): Finish dynamic import %d %d", aRequest,
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
@ -2534,7 +2605,7 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
// Create a ClassicScript object and associate it with the
// JSScript.
RefPtr<ClassicScript> classicScript = new ClassicScript(
this, aRequest->mFetchOptions, aRequest->mBaseURL);
aRequest->mFetchOptions, aRequest->mBaseURL);
classicScript->AssociateWithScript(script);
rv = exec.ExecScript();

View File

@ -311,6 +311,23 @@ class ScriptLoader final : public nsISupports {
*/
void Destroy() { GiveUpBytecodeEncoding(); }
/**
* Implement the HostResolveImportedModule abstract operation.
*
* Resolve a module specifier string and look this up in the module
* map, returning the result. This is only called for previously
* loaded modules and always succeeds.
*
* @param aReferencingPrivate A JS::Value which is either undefined
* or contains a LoadedScript private pointer.
* @param aSpecifier The module specifier.
* @param aModuleOut This is set to the module found.
*/
static void ResolveImportedModule(JSContext* aCx,
JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier,
JS::MutableHandle<JSObject*> aModuleOut);
void StartDynamicImport(ModuleLoadRequest* aRequest);
void FinishDynamicImport(ModuleLoadRequest* aRequest, nsresult aResult);
void FinishDynamicImport(JSContext* aCx, ModuleLoadRequest* aRequest,
@ -322,6 +339,8 @@ class ScriptLoader final : public nsISupports {
*/
static LoadedScript* GetActiveScript(JSContext* aCx);
nsIDocument* GetDocument() const { return mDocument; }
private:
virtual ~ScriptLoader();
@ -578,6 +597,7 @@ class ScriptLoader final : public nsISupports {
nsCOMPtr<nsIConsoleReportCollector> mReporter;
// Logging
public:
static LazyLogModule gCspPRLog;
static LazyLogModule gScriptLoaderLog;
};

View File

@ -1,5 +0,0 @@
[inline-event-handler.html]
expected: TIMEOUT
[dynamic import should work when triggered from inline event handlers]
expected: TIMEOUT

View File

@ -1,7 +0,0 @@
[string-compilation-base-url-inline-classic.html]
[reflected inline event handlers should successfully import]
expected: FAIL
[inline event handlers triggered via UA code should successfully import]
expected: FAIL

View File

@ -1,7 +0,0 @@
[string-compilation-base-url-inline-module.html]
[reflected inline event handlers should successfully import]
expected: FAIL
[inline event handlers triggered via UA code should successfully import]
expected: FAIL

View File

@ -1,7 +0,0 @@
[string-compilation-classic.html]
[reflected inline event handlers should successfully import]
expected: FAIL
[inline event handlers triggered via UA code should successfully import]
expected: FAIL

View File

@ -1,7 +0,0 @@
[string-compilation-module.html]
[reflected inline event handlers should successfully import]
expected: FAIL
[inline event handlers triggered via UA code should successfully import]
expected: FAIL

View File

@ -1,7 +0,0 @@
[string-compilation-of-promise-result.html]
[Evaled the script via eval, successful import]
expected: FAIL
[Evaled the script via Function, successful import]
expected: FAIL