Bug 1789543 - Part 3: Trigger missing chrome/resource URL error handling with original JSM URL when fallback to ESMified URL also fails. r=yulia

Depends on D156763

Differential Revision: https://phabricator.services.mozilla.com/D156764
This commit is contained in:
Tooru Fujisawa 2022-09-12 01:04:37 +00:00
Родитель ce2d3ed35b
Коммит e8ade43d3f
5 изменённых файлов: 87 добавлений и 19 удалений

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

@ -10,6 +10,8 @@
#include "js/loader/LoadContextBase.h" #include "js/loader/LoadContextBase.h"
#include "js/loader/ModuleLoaderBase.h" #include "js/loader/ModuleLoaderBase.h"
#include "SkipCheckForBrokenURLOrZeroSized.h"
class mozJSModuleLoader; class mozJSModuleLoader;
namespace mozilla { namespace mozilla {
@ -103,6 +105,8 @@ class ComponentLoadContext : public JS::loader::LoadContextBase {
// before being passed to the module loader. // before being passed to the module loader.
nsresult mRv; nsresult mRv;
SkipCheckForBrokenURLOrZeroSized mSkipCheck;
// The exception thrown during compiling a module script. These fields are // The exception thrown during compiling a module script. These fields are
// used temporarily before being passed to the module loader. // used temporarily before being passed to the module loader.
JS::PersistentRooted<JS::Value> mExceptionValue; JS::PersistentRooted<JS::Value> mExceptionValue;

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

@ -0,0 +1,22 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_loader_SkipCheckForBrokenURLOrZeroSized_h
#define mozilla_loader_SkipCheckForBrokenURLOrZeroSized_h
#include <stdint.h> // uint8_t
namespace mozilla {
namespace loader {
// Represents the `aSkipCheckForBrokenURLOrZeroSized` parameter for
// `NS_NewChannel` function.
enum class SkipCheckForBrokenURLOrZeroSized : uint8_t { No, Yes };
} // namespace loader
} // namespace mozilla
#endif // mozilla_loader_SkipCheckForBrokenURLOrZeroSized_h

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

@ -45,6 +45,7 @@ EXPORTS.mozilla.loader += [
"AutoMemMap.h", "AutoMemMap.h",
"ComponentModuleLoader.h", "ComponentModuleLoader.h",
"ScriptCacheActors.h", "ScriptCacheActors.h",
"SkipCheckForBrokenURLOrZeroSized.h",
] ]
EXTRA_JS_MODULES += [ EXTRA_JS_MODULES += [

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

@ -289,10 +289,25 @@ mozJSModuleLoader::mozJSModuleLoader()
class MOZ_STACK_CLASS ModuleLoaderInfo { class MOZ_STACK_CLASS ModuleLoaderInfo {
public: public:
explicit ModuleLoaderInfo(const nsACString& aLocation) explicit ModuleLoaderInfo(const nsACString& aLocation,
: mLocation(&aLocation), mIsModule(false) {} SkipCheckForBrokenURLOrZeroSized aSkipCheck =
SkipCheckForBrokenURLOrZeroSized::No)
: mLocation(&aLocation), mIsModule(false), mSkipCheck(aSkipCheck) {}
explicit ModuleLoaderInfo(JS::loader::ModuleLoadRequest* aRequest) explicit ModuleLoaderInfo(JS::loader::ModuleLoadRequest* aRequest)
: mLocation(nullptr), mURI(aRequest->mURI), mIsModule(true) {} : mLocation(nullptr),
mURI(aRequest->mURI),
mIsModule(true),
mSkipCheck(aRequest->GetComponentLoadContext()->mSkipCheck) {}
SkipCheckForBrokenURLOrZeroSized getSkipCheckForBrokenURLOrZeroSized() const {
return mSkipCheck;
}
void resetChannelWithCheckForBrokenURLOrZeroSized() {
MOZ_ASSERT(mSkipCheck == SkipCheckForBrokenURLOrZeroSized::Yes);
mSkipCheck = SkipCheckForBrokenURLOrZeroSized::No;
mScriptChannel = nullptr;
}
nsIIOService* IOService() { nsIIOService* IOService() {
MOZ_ASSERT(mIOService); MOZ_ASSERT(mIOService);
@ -325,14 +340,7 @@ class MOZ_STACK_CLASS ModuleLoaderInfo {
nsresult EnsureScriptChannel() { nsresult EnsureScriptChannel() {
BEGIN_ENSURE(ScriptChannel, IOService, URI); BEGIN_ENSURE(ScriptChannel, IOService, URI);
// The JSM may already be ESM-ified, and in that case the load is expected // Skip check for missing URL when handling JSM-to-ESM fallback.
// to fail. Suppress the error message, the crash, and also the telemetry
// event for the failure.
//
// If this load fails, it will be redirected to `.sys.mjs` URL
// in TryFallbackToImportESModule, and the check is performed there.
bool skipCheckForBrokenURLOrZeroSized = !IsModule();
return NS_NewChannel( return NS_NewChannel(
getter_AddRefs(mScriptChannel), mURI, getter_AddRefs(mScriptChannel), mURI,
nsContentUtils::GetSystemPrincipal(), nsContentUtils::GetSystemPrincipal(),
@ -342,7 +350,7 @@ class MOZ_STACK_CLASS ModuleLoaderInfo {
/* aPerformanceStorage = */ nullptr, /* aPerformanceStorage = */ nullptr,
/* aLoadGroup = */ nullptr, /* aCallbacks = */ nullptr, /* aLoadGroup = */ nullptr, /* aCallbacks = */ nullptr,
nsIRequest::LOAD_NORMAL, mIOService, /* aSandboxFlags = */ 0, nsIRequest::LOAD_NORMAL, mIOService, /* aSandboxFlags = */ 0,
skipCheckForBrokenURLOrZeroSized); mSkipCheck == SkipCheckForBrokenURLOrZeroSized::Yes);
} }
nsIURI* ResolvedURI() { nsIURI* ResolvedURI() {
@ -374,6 +382,7 @@ class MOZ_STACK_CLASS ModuleLoaderInfo {
nsCOMPtr<nsIChannel> mScriptChannel; nsCOMPtr<nsIChannel> mScriptChannel;
nsCOMPtr<nsIURI> mResolvedURI; nsCOMPtr<nsIURI> mResolvedURI;
const bool mIsModule; const bool mIsModule;
SkipCheckForBrokenURLOrZeroSized mSkipCheck;
}; };
template <typename... Args> template <typename... Args>
@ -1394,7 +1403,14 @@ nsresult mozJSModuleLoader::Import(JSContext* aCx, const nsACString& aLocation,
MarkerInnerWindowIdFromJSContext(aCx)), MarkerInnerWindowIdFromJSContext(aCx)),
aLocation); aLocation);
ModuleLoaderInfo info(aLocation); // The JSM may already be ESM-ified, and in that case the load is expected
// to fail. Suppress the error message, the crash, and also the telemetry
// event for the failure.
//
// If this load fails, it will be redirected to `.sys.mjs` URL
// in TryFallbackToImportESModule, and if the redirect also fails,
// the load is performed again below, with the check enabled.
ModuleLoaderInfo info(aLocation, SkipCheckForBrokenURLOrZeroSized::Yes);
nsresult rv; nsresult rv;
ModuleEntry* mod; ModuleEntry* mod;
@ -1467,8 +1483,23 @@ nsresult mozJSModuleLoader::Import(JSContext* aCx, const nsACString& aLocation,
} }
if (rv == NS_ERROR_FILE_NOT_FOUND) { if (rv == NS_ERROR_FILE_NOT_FOUND) {
return TryFallbackToImportESModule(aCx, aLocation, aModuleGlobal, rv = TryFallbackToImportESModule(aCx, aLocation, aModuleGlobal,
aModuleExports, aIgnoreExports); aModuleExports, aIgnoreExports);
if (rv == NS_ERROR_FILE_NOT_FOUND) {
// Both JSM and ESM are not found, with the check inside necko
// skipped (See EnsureScriptChannel and mSkipCheck).
//
// Perform the load again with the check enabled, so that
// logging, crash-on-autonation, and telemetry event happen.
if (NS_SUCCEEDED(info.EnsureURI()) &&
!LocationIsRealFile(info.URI())) {
info.resetChannelWithCheckForBrokenURLOrZeroSized();
(void)ReadScript(info);
}
}
return rv;
} }
// Something failed, but we don't know what it is, guess. // Something failed, but we don't know what it is, guess.
@ -1526,7 +1557,10 @@ nsresult mozJSModuleLoader::TryFallbackToImportESModule(
} }
JS::RootedObject moduleNamespace(aCx); JS::RootedObject moduleNamespace(aCx);
nsresult rv = ImportESModule(aCx, mjsLocation, &moduleNamespace); // The fallback can fail if the URL was not for ESMified JSM. Suppress the
// error message, the crash, and also the telemetry event for the failure.
nsresult rv = ImportESModule(aCx, mjsLocation, &moduleNamespace,
SkipCheckForBrokenURLOrZeroSized::Yes);
if (rv == NS_ERROR_FILE_NOT_FOUND) { if (rv == NS_ERROR_FILE_NOT_FOUND) {
// The error for ESModule shouldn't be exposed if the file does not exist. // The error for ESModule shouldn't be exposed if the file does not exist.
if (JS_IsExceptionPending(aCx)) { if (JS_IsExceptionPending(aCx)) {
@ -1605,7 +1639,9 @@ nsresult mozJSModuleLoader::TryCachedFallbackToImportESModule(
nsresult mozJSModuleLoader::ImportESModule( nsresult mozJSModuleLoader::ImportESModule(
JSContext* aCx, const nsACString& aLocation, JSContext* aCx, const nsACString& aLocation,
JS::MutableHandleObject aModuleNamespace) { JS::MutableHandleObject aModuleNamespace,
SkipCheckForBrokenURLOrZeroSized
aSkipCheck /* = SkipCheckForBrokenURLOrZeroSized::No */) {
using namespace JS::loader; using namespace JS::loader;
MOZ_ASSERT(mModuleLoader); MOZ_ASSERT(mModuleLoader);
@ -1643,6 +1679,7 @@ nsresult mozJSModuleLoader::ImportESModule(
CORS_NONE, dom::ReferrerPolicy::No_referrer, principal); CORS_NONE, dom::ReferrerPolicy::No_referrer, principal);
RefPtr<ComponentLoadContext> context = new ComponentLoadContext(); RefPtr<ComponentLoadContext> context = new ComponentLoadContext();
context->mSkipCheck = aSkipCheck;
RefPtr<VisitedURLSet> visitedSet = RefPtr<VisitedURLSet> visitedSet =
ModuleLoadRequest::NewVisitedSetForTopLevelImport(uri); ModuleLoadRequest::NewVisitedSetForTopLevelImport(uri);

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

@ -18,6 +18,7 @@
#include "nsClassHashtable.h" #include "nsClassHashtable.h"
#include "jsapi.h" #include "jsapi.h"
#include "js/experimental/JSStencil.h" #include "js/experimental/JSStencil.h"
#include "SkipCheckForBrokenURLOrZeroSized.h"
#include "xpcpublic.h" #include "xpcpublic.h"
@ -75,8 +76,11 @@ class mozJSModuleLoader final : public nsIMemoryReporter {
bool aIgnoreExports = false); bool aIgnoreExports = false);
// Load an ES6 module and all its dependencies. // Load an ES6 module and all its dependencies.
nsresult ImportESModule(JSContext* aCx, const nsACString& aResourceURI, nsresult ImportESModule(
JS::MutableHandleObject aModuleNamespace); JSContext* aCx, const nsACString& aResourceURI,
JS::MutableHandleObject aModuleNamespace,
mozilla::loader::SkipCheckForBrokenURLOrZeroSized aSkipCheck =
mozilla::loader::SkipCheckForBrokenURLOrZeroSized::No);
// Fallback from Import to ImportESModule. // Fallback from Import to ImportESModule.
nsresult TryFallbackToImportESModule(JSContext* aCx, nsresult TryFallbackToImportESModule(JSContext* aCx,