mirror of
https://github.com/Basilisk-Development-Team/UXP-mirror.git
synced 2026-11-12 20:32:03 -05:00
Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c284255192 | ||
|
|
868cbda8d0 | ||
|
|
62898913ed | ||
|
|
b9e4eb22b1 | ||
|
|
845bff9d31 | ||
|
|
883f60cf12 | ||
|
|
eaa73cedc8 | ||
|
|
a2478fe2cb | ||
|
|
480b203dea | ||
|
|
5c7eb114dd | ||
|
|
5b3cc8dab5 | ||
|
|
b8eb00f26c | ||
|
|
97bc446a95 | ||
|
|
d415454413 | ||
|
|
5b9c897904 | ||
|
|
63181f6d11 | ||
|
|
38892f7308 |
No files matched your search
@@ -294,13 +294,26 @@ HTMLScriptElement::FreezeExecutionAttrs(nsIDocument* aOwnerDoc)
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mIsModule && !mAsync && !mDefer && !mExternal);
|
||||
MOZ_ASSERT((mKind != ScriptKind::Module) &&
|
||||
(mKind != ScriptKind::ImportMap) &&
|
||||
!mAsync && !mDefer && !mExternal);
|
||||
|
||||
// Determine whether this is a classic script or a module script.
|
||||
// Determine whether this is a(n) classic/module/importmap script.
|
||||
nsAutoString type;
|
||||
GetScriptType(type);
|
||||
mIsModule = aOwnerDoc->ModuleScriptsEnabled() &&
|
||||
!type.IsEmpty() && type.LowerCaseEqualsASCII("module");
|
||||
if (!type.IsEmpty()) {
|
||||
if (aOwnerDoc->ModuleScriptsEnabled() &&
|
||||
type.LowerCaseEqualsASCII("module")) {
|
||||
mKind = ScriptKind::Module;
|
||||
}
|
||||
|
||||
// https://wicg.github.io/import-maps/#integration-prepare-a-script
|
||||
// If the script block's type string is an ASCII case-insensitive match
|
||||
// for the string "importmap", the script's type is "importmap".
|
||||
if (type.LowerCaseEqualsASCII("importmap")) {
|
||||
mKind = ScriptKind::ImportMap;
|
||||
}
|
||||
}
|
||||
|
||||
// variation of this code in nsSVGScriptElement - check if changes
|
||||
// need to be transfered when modifying. Note that we don't use GetSrc here
|
||||
@@ -320,7 +333,7 @@ HTMLScriptElement::FreezeExecutionAttrs(nsIDocument* aOwnerDoc)
|
||||
|
||||
bool defer, async;
|
||||
GetAsync(&async);
|
||||
mAsync = (mExternal || mIsModule) && async;
|
||||
mAsync = (mExternal || mKind == ScriptKind::Module) && async;
|
||||
GetDefer(&defer);
|
||||
mDefer = mExternal && !async && defer;
|
||||
|
||||
@@ -346,5 +359,12 @@ HTMLScriptElement::HasScriptContent()
|
||||
nsContentUtils::HasNonEmptyTextContent(this);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/scripting.html#dom-script-supports
|
||||
/* static */ bool
|
||||
HTMLScriptElement::Supports(const GlobalObject& aGlobal, const nsAString& aType)
|
||||
{
|
||||
return aType.EqualsLiteral("classic") || aType.EqualsLiteral("module") || aType.EqualsLiteral("importmap");
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@@ -115,6 +115,9 @@ public:
|
||||
bool NoModule();
|
||||
void SetNoModule(bool aValue, ErrorResult& rv);
|
||||
|
||||
[[nodiscard]] static bool Supports(const GlobalObject& aGlobal,
|
||||
const nsAString& aType);
|
||||
|
||||
protected:
|
||||
virtual ~HTMLScriptElement();
|
||||
|
||||
|
||||
@@ -323,3 +323,17 @@ ReloadFloodingPrevented=Call to Reload ignored due to excessive calls within a s
|
||||
DOMQuadBoundsAttrWarning=DOMQuad.bounds is deprecated in favor of DOMQuad.getBounds()
|
||||
UnsupportedEntryTypesIgnored=Ignoring unsupported entryTypes: %S.
|
||||
AllEntryTypesIgnored=No valid entryTypes; aborting registration.
|
||||
ModuleResolveFailureNoWarn=Error resolving module specifier “%S”.
|
||||
ModuleResolveFailureWarnRelative=Error resolving module specifier “%S”. Relative module specifiers must start with “./”, “../” or “/”.
|
||||
ImportMapResolveInvalidBareSpecifierWarnRelative=Error resolving module specifier “%S”. Relative module specifiers must start with “./”, “../” or “/”.
|
||||
ImportMapResolutionBlockedByNullEntry=Resolution of specifier “%S” was blocked by a null entry.
|
||||
ImportMapResolutionBlockedByAfterPrefix=Resolution of specifier “%S” was blocked since the after-prefix portion could not be URL-parsed relative to the URL mapped to by the prefix.
|
||||
ImportMapResolutionBlockedByBacktrackingPrefix=Resolution of specifier “%S” was blocked due to it backtracking above its prefix.
|
||||
ImportMapInvalidTopLevelKey=An invalid top-level key “%S” was present in the import map.
|
||||
ImportMapEmptySpecifierKeys=Specifier keys cannot be the empty string.
|
||||
ImportMapAddressesNotStrings=Addresses need to be strings.
|
||||
ImportMapInvalidAddress=Address for “%S” was invalid.
|
||||
ImportMapAddressNotEndsWithSlash=An invalid address was given for the specifier key “%S”; since the specifier ended with a slash, the address needs to as well.
|
||||
ImportMapScopePrefixNotParseable=The scope prefix URL “%S” was not parseable.
|
||||
ImportMapIntegrityKeyNotParseable=Import map integrity key %S was not parseable as a URL.
|
||||
ImportMapIntegrityValueNotAString=Import map integrity value for %S needs to be a string.
|
||||
+27
-1
@@ -7,6 +7,7 @@
|
||||
#include "MediaInfo.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "gfxASurface.h"
|
||||
#include "mozilla/layers/SharedRGBImage.h"
|
||||
#include "YCbCrUtils.h"
|
||||
#include "mozilla/layers/ImageBridgeChild.h"
|
||||
@@ -319,13 +320,38 @@ VideoData::CreateAndCopyData(const VideoInfo& aInfo,
|
||||
// Currently our decoder only knows how to output to ImageFormat::PLANAR_YCBCR
|
||||
// format.
|
||||
#if XP_WIN
|
||||
PlanarYCbCrData data = ConstructPlanarYCbCrData(aInfo, aBuffer, aPicture);
|
||||
if (data.mYUVColorSpace == YUVColorSpace::IDENTITY) {
|
||||
// Hardware acceleration in Windows does not play well with GBR-planar, so
|
||||
// convert from YUV to BGR format on the software side. See Issue #3211
|
||||
RefPtr<layers::SharedRGBImage> videoImage =
|
||||
aContainer->CreateSharedRGBImage();
|
||||
v->mImage = videoImage;
|
||||
|
||||
if (!v->mImage) {
|
||||
return nullptr;
|
||||
}
|
||||
SurfaceFormat format = SurfaceFormat::B8G8R8X8;
|
||||
if (!videoImage->Allocate(IntSize(aBuffer.mPlanes[0].mWidth,
|
||||
aBuffer.mPlanes[0].mHeight),
|
||||
format)) {
|
||||
return nullptr;
|
||||
}
|
||||
uint8_t* rgb_buffer = videoImage->GetBuffer();
|
||||
IntSize size = videoImage->GetSize();
|
||||
|
||||
ConvertYCbCrToRGB(data, format, size, rgb_buffer,
|
||||
gfxASurface::FormatStrideForWidth(format, size.width));
|
||||
|
||||
return v.forget();
|
||||
}
|
||||
|
||||
// We disable this code path on Windows 7 due to intermittent crashes with old drivers.
|
||||
// See Mozilla bug 1405110.
|
||||
if (IsWin8OrLater() && !XRE_IsParentProcess() &&
|
||||
aAllocator && aAllocator->GetCompositorBackendType()
|
||||
== layers::LayersBackend::LAYERS_D3D11) {
|
||||
RefPtr<layers::D3D11YCbCrImage> d3d11Image = new layers::D3D11YCbCrImage();
|
||||
PlanarYCbCrData data = ConstructPlanarYCbCrData(aInfo, aBuffer, aPicture);
|
||||
if (d3d11Image->SetData(layers::ImageBridgeChild::GetSingleton()
|
||||
? layers::ImageBridgeChild::GetSingleton().get()
|
||||
: aAllocator,
|
||||
|
||||
@@ -0,0 +1,803 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "ImportMap.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "ModuleScript.h"
|
||||
#include "ScriptLoader.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIScriptElement.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
using mozilla::Err;
|
||||
using mozilla::LazyLogModule;
|
||||
using mozilla::MakeUnique;
|
||||
using mozilla::UniquePtr;
|
||||
using mozilla::WrapNotNull;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
LazyLogModule ImportMap::gImportMapLog("ImportMap");
|
||||
|
||||
#undef LOG
|
||||
#define LOG(args) \
|
||||
MOZ_LOG(ImportMap::gImportMapLog, mozilla::LogLevel::Debug, args)
|
||||
|
||||
#define LOG_ENABLED() \
|
||||
MOZ_LOG_TEST(ImportMap::gImportMapLog, mozilla::LogLevel::Debug)
|
||||
|
||||
void ReportWarningHelper::Report(const char* aMessageName,
|
||||
const nsTArray<nsString>& aParams) const {
|
||||
mLoader->ReportWarningToConsole(mRequest, aMessageName, aParams);
|
||||
}
|
||||
|
||||
// Bug 1979050: reading a top-level key with JS_GetProperty walks the prototype
|
||||
// chain, so a polluted Object.prototype could inject import map entries. Only
|
||||
// own properties count, both for the presence check and for the value read.
|
||||
static bool
|
||||
GetOwnProperty(JSContext* aCx, JS::Handle<JSObject*> aObject, const char* aName,
|
||||
JS::MutableHandle<JS::Value> aResult)
|
||||
{
|
||||
bool hasOwn = false;
|
||||
if (!JS_HasOwnProperty(aCx, aObject, aName, &hasOwn)) {
|
||||
return false;
|
||||
}
|
||||
if (!hasOwn) {
|
||||
aResult.setUndefined();
|
||||
return true;
|
||||
}
|
||||
return JS_GetProperty(aCx, aObject, aName, aResult);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#resolving-a-url-like-module-specifier
|
||||
static ResolveResult
|
||||
ResolveURLLikeModuleSpecifier(const nsAString& aSpecifier, nsIURI* aBaseURL)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv;
|
||||
|
||||
// Step 1. If specifier starts with "/", "./", or "../", then:
|
||||
if (StringBeginsWith(aSpecifier, NS_LITERAL_STRING("/")) ||
|
||||
StringBeginsWith(aSpecifier, NS_LITERAL_STRING("./")) ||
|
||||
StringBeginsWith(aSpecifier, NS_LITERAL_STRING("../"))) {
|
||||
// Step 1.1. Let url be the result of parsing specifier with baseURL as the
|
||||
// base URL.
|
||||
rv = NS_NewURI(getter_AddRefs(uri), aSpecifier, nullptr, aBaseURL);
|
||||
// Step 1.2. If url is failure, then return null.
|
||||
if (NS_FAILED(rv)) {
|
||||
return Err(ResolveError::Failure);
|
||||
}
|
||||
|
||||
// Step 1.3. Return url.
|
||||
return WrapNotNull(uri);
|
||||
}
|
||||
|
||||
// Step 2. Let url be the result of parsing specifier (with no base URL).
|
||||
rv = NS_NewURI(getter_AddRefs(uri), aSpecifier);
|
||||
// Step 3. If url is failure, then return null.
|
||||
if (NS_FAILED(rv)) {
|
||||
return Err(ResolveError::FailureMayBeBare);
|
||||
}
|
||||
|
||||
// Step 4. Return url.
|
||||
return WrapNotNull(uri);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#normalizing-a-specifier-key
|
||||
static void
|
||||
NormalizeSpecifierKey(const nsAString& aSpecifierKey, nsIURI* aBaseURL,
|
||||
const ReportWarningHelper& aWarning, nsAString& aRetVal)
|
||||
{
|
||||
// Step 1. If specifierKey is the empty string, then:
|
||||
if (aSpecifierKey.IsEmpty()) {
|
||||
// Step 1.1. Report a warning to the console that specifier keys cannot be
|
||||
// the empty string.
|
||||
aWarning.Report("ImportMapEmptySpecifierKeys");
|
||||
|
||||
// Step 1.2. Return null.
|
||||
aRetVal = EmptyString();
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2. Let url be the result of resolving a URL-like module specifier,
|
||||
// given specifierKey and baseURL.
|
||||
auto parseResult = ResolveURLLikeModuleSpecifier(aSpecifierKey, aBaseURL);
|
||||
|
||||
// Step 3. If url is not null, then return the serialization of url.
|
||||
if (parseResult.isOk()) {
|
||||
nsCOMPtr<nsIURI> url = parseResult.unwrap();
|
||||
aRetVal = NS_ConvertUTF8toUTF16(url->GetSpecOrDefault());
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 4. Return specifierKey.
|
||||
aRetVal = aSpecifierKey;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#sorting-and-normalizing-a-module-specifier-map
|
||||
static UniquePtr<SpecifierMap>
|
||||
SortAndNormalizeSpecifierMap(JSContext* aCx, JS::HandleObject aOriginalMap, nsIURI* aBaseURL,
|
||||
const ReportWarningHelper& aWarning)
|
||||
{
|
||||
// Step 1. Let normalized be an empty ordered map.
|
||||
UniquePtr<SpecifierMap> normalized = MakeUnique<SpecifierMap>();
|
||||
|
||||
JS::Rooted<JS::IdVector> specifierKeys(aCx, JS::IdVector(aCx));
|
||||
if (!JS_Enumerate(aCx, aOriginalMap, &specifierKeys)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 2. For each specifierKey → value of originalMap,
|
||||
for (size_t i = 0; i < specifierKeys.length(); i++) {
|
||||
const JS::RootedId specifierId(aCx, specifierKeys[i]);
|
||||
nsAutoJSString specifierKey;
|
||||
NS_ENSURE_TRUE(specifierKey.init(aCx, specifierId), nullptr);
|
||||
|
||||
// Step 2.1. Let normalizedSpecifierKey be the result of normalizing a
|
||||
// specifier key given specifierKey and baseURL.
|
||||
nsString normalizedSpecifierKey;
|
||||
NormalizeSpecifierKey(specifierKey, aBaseURL, aWarning,
|
||||
normalizedSpecifierKey);
|
||||
|
||||
// Step 2.2. If normalizedSpecifierKey is null, then continue.
|
||||
if (normalizedSpecifierKey.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Impl note: JS_Enumerate only returns own properties, so reading the value
|
||||
// by id is already immune to prototype pollution (bug 1979050).
|
||||
JS::RootedValue idVal(aCx);
|
||||
NS_ENSURE_TRUE(JS_GetPropertyById(aCx, aOriginalMap, specifierId, &idVal),
|
||||
nullptr);
|
||||
// Step 2.3. If value is not a string, then:
|
||||
if (!idVal.isString()) {
|
||||
// Step 2.3.1. The user agent may report a warning to the console
|
||||
// indicating that addresses need to be strings.
|
||||
aWarning.Report("ImportMapAddressesNotStrings");
|
||||
|
||||
// Step 2.3.2. Set normalized[normalizedSpecifierKey] to null.
|
||||
normalized->insert_or_assign(normalizedSpecifierKey, nullptr);
|
||||
|
||||
// Step 2.3.3. Continue.
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoJSString value;
|
||||
NS_ENSURE_TRUE(value.init(aCx, idVal), nullptr);
|
||||
|
||||
// Step 2.4. Let addressURL be the result of resolving a URL-like module
|
||||
// specifier given value and baseURL.
|
||||
auto parseResult = ResolveURLLikeModuleSpecifier(value, aBaseURL);
|
||||
|
||||
// Step 2.5. If addressURL is null, then:
|
||||
if (parseResult.isErr()) {
|
||||
// Step 2.5.1. The user agent may report a warning to the console
|
||||
// indicating that the address was invalid.
|
||||
AutoTArray<nsString, 1> params;
|
||||
params.AppendElement(value);
|
||||
aWarning.Report("ImportMapInvalidAddress", params);
|
||||
|
||||
// Step 2.5.2. Set normalized[normalizedSpecifierKey] to null.
|
||||
normalized->insert_or_assign(normalizedSpecifierKey, nullptr);
|
||||
|
||||
// Step 2.5.3. Continue.
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> addressURL = parseResult.unwrap();
|
||||
nsCString address = addressURL->GetSpecOrDefault();
|
||||
// Step 2.6. If specifierKey ends with U+002F (/), and the serialization
|
||||
// of addressURL does not end with U+002F (/), then:
|
||||
if (StringEndsWith(specifierKey, NS_LITERAL_STRING("/")) &&
|
||||
!StringEndsWith(address, NS_LITERAL_CSTRING("/"))) {
|
||||
// Step 2.6.1. The user agent may report a warning to the console
|
||||
// indicating that an invalid address was given for the specifier key
|
||||
// specifierKey; since specifierKey ends with a slash, the address needs
|
||||
// to as well.
|
||||
AutoTArray<nsString, 2> params;
|
||||
params.AppendElement(specifierKey);
|
||||
params.AppendElement(NS_ConvertUTF8toUTF16(address));
|
||||
aWarning.Report("ImportMapAddressNotEndsWithSlash", params);
|
||||
|
||||
// Step 2.6.2. Set normalized[normalizedSpecifierKey] to null.
|
||||
normalized->insert_or_assign(normalizedSpecifierKey, nullptr);
|
||||
|
||||
// Step 2.6.3. Continue.
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG(("ImportMap::SortAndNormalizeSpecifierMap {%s, %s}",
|
||||
NS_ConvertUTF16toUTF8(normalizedSpecifierKey).get(),
|
||||
addressURL->GetSpecOrDefault().get()));
|
||||
|
||||
// Step 2.7. Set normalized[normalizedSpecifierKey] to addressURL.
|
||||
normalized->insert_or_assign(normalizedSpecifierKey, addressURL);
|
||||
}
|
||||
|
||||
// Step 3: Return the result of sorting normalized, with an entry a being
|
||||
// less than an entry b if b's key is code unit less than a's key.
|
||||
//
|
||||
// Impl note: The sorting is done when inserting the entry.
|
||||
return normalized;
|
||||
}
|
||||
|
||||
// Check if it's a map defined in
|
||||
// https://infra.spec.whatwg.org/#ordered-map
|
||||
//
|
||||
// If it is, *aIsMap will be set to true.
|
||||
static bool
|
||||
IsMapObject(JSContext* aCx, JS::HandleValue aMapVal, bool* aIsMap)
|
||||
{
|
||||
MOZ_ASSERT(aIsMap);
|
||||
|
||||
*aIsMap = false;
|
||||
if (!aMapVal.isObject()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isArray;
|
||||
if (!JS_IsArrayObject(aCx, aMapVal, &isArray)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*aIsMap = !isArray;
|
||||
return true;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#sorting-and-normalizing-scopes
|
||||
static UniquePtr<ScopeMap>
|
||||
SortAndNormalizeScopes(JSContext* aCx, JS::HandleObject aOriginalMap, nsIURI* aBaseURL,
|
||||
const ReportWarningHelper& aWarning)
|
||||
{
|
||||
JS::Rooted<JS::IdVector> scopeKeys(aCx, JS::IdVector(aCx));
|
||||
if (!JS_Enumerate(aCx, aOriginalMap, &scopeKeys)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 1. Let normalized be an empty map.
|
||||
UniquePtr<ScopeMap> normalized = MakeUnique<ScopeMap>();
|
||||
|
||||
// Step 2. For each scopePrefix → potentialSpecifierMap of originalMap,
|
||||
for (size_t i = 0; i < scopeKeys.length(); i++) {
|
||||
const JS::RootedId scopeKey(aCx, scopeKeys[i]);
|
||||
nsAutoJSString scopePrefix;
|
||||
NS_ENSURE_TRUE(scopePrefix.init(aCx, scopeKey), nullptr);
|
||||
|
||||
// Step 2.1. If potentialSpecifierMap is not an ordered map, then throw a
|
||||
// TypeError indicating that the value of the scope with prefix scopePrefix
|
||||
// needs to be a JSON object.
|
||||
JS::RootedValue mapVal(aCx);
|
||||
NS_ENSURE_TRUE(JS_GetPropertyById(aCx, aOriginalMap, scopeKey, &mapVal),
|
||||
nullptr);
|
||||
|
||||
bool isMap;
|
||||
if (!IsMapObject(aCx, mapVal, &isMap)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!isMap) {
|
||||
const char16_t* scope = scopePrefix.get();
|
||||
JS_ReportErrorNumberUC(aCx, js::GetErrorMessage, nullptr,
|
||||
JSMSG_IMPORT_MAPS_SCOPE_VALUE_NOT_A_MAP, scope);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 2.2. Let scopePrefixURL be the result of URL parsing scopePrefix
|
||||
// with baseURL.
|
||||
nsCOMPtr<nsIURI> scopePrefixURL;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(scopePrefixURL), scopePrefix,
|
||||
nullptr, aBaseURL);
|
||||
|
||||
// Step 2.3. If scopePrefixURL is failure, then:
|
||||
if (NS_FAILED(rv)) {
|
||||
// Step 2.3.1. The user agent may report a warning to the console that
|
||||
// the scope prefix URL was not parseable.
|
||||
AutoTArray<nsString, 1> params;
|
||||
params.AppendElement(scopePrefix);
|
||||
aWarning.Report("ImportMapScopePrefixNotParseable", params);
|
||||
|
||||
// Step 2.3.2. Continue.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Step 2.4. Let normalizedScopePrefix be the serialization of
|
||||
// scopePrefixURL.
|
||||
nsCString normalizedScopePrefix = scopePrefixURL->GetSpecOrDefault();
|
||||
|
||||
// Step 2.5. Set normalized[normalizedScopePrefix] to the result of sorting
|
||||
// and normalizing a specifier map given potentialSpecifierMap and baseURL.
|
||||
JS::RootedObject potentialSpecifierMap(aCx, &mapVal.toObject());
|
||||
UniquePtr<SpecifierMap> specifierMap = SortAndNormalizeSpecifierMap(
|
||||
aCx, potentialSpecifierMap, aBaseURL, aWarning);
|
||||
if (!specifierMap) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
normalized->insert_or_assign(normalizedScopePrefix,
|
||||
std::move(specifierMap));
|
||||
}
|
||||
|
||||
// Step 3. Return the result of sorting in descending order normalized, with
|
||||
// an entry a being less than an entry b if a's key is code unit less than b's
|
||||
// key.
|
||||
//
|
||||
// Impl note: The sorting is done when inserting the entry.
|
||||
return normalized;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#normalize-a-module-integrity-map
|
||||
static UniquePtr<IntegrityMap>
|
||||
NormalizeModuleIntegrityMap(JSContext* aCx, JS::HandleObject aOriginalMap,
|
||||
nsIURI* aBaseURL,
|
||||
const ReportWarningHelper& aWarning)
|
||||
{
|
||||
UniquePtr<IntegrityMap> normalized = MakeUnique<IntegrityMap>();
|
||||
|
||||
JS::Rooted<JS::IdVector> keys(aCx, JS::IdVector(aCx));
|
||||
if (!JS_Enumerate(aCx, aOriginalMap, &keys)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < keys.length(); i++) {
|
||||
const JS::RootedId keyId(aCx, keys[i]);
|
||||
nsAutoJSString key;
|
||||
NS_ENSURE_TRUE(key.init(aCx, keyId), nullptr);
|
||||
|
||||
// Step 2.1. Let resolvedURL be the result of resolving a URL-like module
|
||||
// specifier given key and baseURL.
|
||||
auto parseResult = ResolveURLLikeModuleSpecifier(key, aBaseURL);
|
||||
|
||||
// Step 2.2. If resolvedURL is null, warn and continue.
|
||||
if (parseResult.isErr()) {
|
||||
AutoTArray<nsString, 1> params;
|
||||
params.AppendElement(key);
|
||||
aWarning.Report("ImportMapIntegrityKeyNotParseable", params);
|
||||
continue;
|
||||
}
|
||||
|
||||
JS::RootedValue idVal(aCx);
|
||||
NS_ENSURE_TRUE(JS_GetPropertyById(aCx, aOriginalMap, keyId, &idVal), nullptr);
|
||||
|
||||
// Step 2.3. If value is not a string, warn and continue.
|
||||
if (!idVal.isString()) {
|
||||
AutoTArray<nsString, 1> params;
|
||||
params.AppendElement(key);
|
||||
aWarning.Report("ImportMapIntegrityValueNotAString", params);
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoJSString value;
|
||||
NS_ENSURE_TRUE(value.init(aCx, idVal), nullptr);
|
||||
|
||||
// Step 2.4. Set normalized[resolvedURL] to value.
|
||||
normalized->insert_or_assign(parseResult.unwrap()->GetSpecOrDefault(),
|
||||
nsString(value));
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#parse-an-import-map-string
|
||||
// static
|
||||
UniquePtr<ImportMap>
|
||||
ImportMap::ParseString(JSContext* aCx, JS::SourceBufferHolder& aInput, nsIURI* aBaseURL,
|
||||
const ReportWarningHelper& aWarning)
|
||||
{
|
||||
// Step 1. Let parsed be the result of parsing JSON into Infra values given
|
||||
// input.
|
||||
JS::Rooted<JS::Value> parsedVal(aCx);
|
||||
if (!JS_ParseJSON(aCx, aInput.get(), aInput.length(), &parsedVal)) {
|
||||
// If JS_ParseJSON fail it will throw SyntaxError.
|
||||
NS_WARNING("Parsing Import map string failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 2. If parsed is not an ordered map, then throw a TypeError indicating
|
||||
// that the top-level value needs to be a JSON object.
|
||||
bool isMap;
|
||||
if (!IsMapObject(aCx, parsedVal, &isMap)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!isMap) {
|
||||
JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
|
||||
JSMSG_IMPORT_MAPS_NOT_A_MAP);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::RootedObject parsedObj(aCx, &parsedVal.toObject());
|
||||
JS::RootedValue importsVal(aCx);
|
||||
if (!GetOwnProperty(aCx, parsedObj, "imports", &importsVal)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 3. Let sortedAndNormalizedImports be an empty ordered map.
|
||||
//
|
||||
// Impl note: If parsed["imports"] doesn't exist, we will allocate
|
||||
// sortedAndNormalizedImports to an empty map in Step 8 below.
|
||||
UniquePtr<SpecifierMap> sortedAndNormalizedImports = nullptr;
|
||||
|
||||
// Step 4. If parsed["imports"] exists, then:
|
||||
if (!importsVal.isUndefined()) {
|
||||
// Step 4.1. If parsed["imports"] is not an ordered map, then throw a
|
||||
// TypeError indicating that the "imports" top-level key needs to be a JSON
|
||||
// object.
|
||||
bool isMap;
|
||||
if (!IsMapObject(aCx, importsVal, &isMap)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!isMap) {
|
||||
JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
|
||||
JSMSG_IMPORT_MAPS_IMPORTS_NOT_A_MAP);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 4.2. Set sortedAndNormalizedImports to the result of sorting and
|
||||
// normalizing a module specifier map given parsed["imports"] and baseURL.
|
||||
JS::RootedObject importsObj(aCx, &importsVal.toObject());
|
||||
sortedAndNormalizedImports =
|
||||
SortAndNormalizeSpecifierMap(aCx, importsObj, aBaseURL, aWarning);
|
||||
if (!sortedAndNormalizedImports) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
JS::RootedValue scopesVal(aCx);
|
||||
if (!GetOwnProperty(aCx, parsedObj, "scopes", &scopesVal)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 5. Let sortedAndNormalizedScopes be an empty ordered map.
|
||||
//
|
||||
// Impl note: If parsed["scopes"] doesn't exist, we will allocate
|
||||
// sortedAndNormalizedScopes to an empty map in Step 8 below.
|
||||
UniquePtr<ScopeMap> sortedAndNormalizedScopes = nullptr;
|
||||
|
||||
// Step 6. If parsed["scopes"] exists, then:
|
||||
if (!scopesVal.isUndefined()) {
|
||||
// Step 6.1. If parsed["scopes"] is not an ordered map, then throw a
|
||||
// TypeError indicating that the "scopes" top-level key needs to be a JSON
|
||||
// object.
|
||||
bool isMap;
|
||||
if (!IsMapObject(aCx, scopesVal, &isMap)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!isMap) {
|
||||
JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
|
||||
JSMSG_IMPORT_MAPS_SCOPES_NOT_A_MAP);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 6.2. Set sortedAndNormalizedScopes to the result of sorting and
|
||||
// normalizing scopes given parsed["scopes"] and baseURL.
|
||||
JS::RootedObject scopesObj(aCx, &scopesVal.toObject());
|
||||
sortedAndNormalizedScopes =
|
||||
SortAndNormalizeScopes(aCx, scopesObj, aBaseURL, aWarning);
|
||||
if (!sortedAndNormalizedScopes) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
JS::RootedValue integrityVal(aCx);
|
||||
if (!GetOwnProperty(aCx, parsedObj, "integrity", &integrityVal)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Impl note: If parsed["integrity"] doesn't exist, we will allocate
|
||||
// sortedAndNormalizedIntegrity to an empty map below.
|
||||
UniquePtr<IntegrityMap> sortedAndNormalizedIntegrity = nullptr;
|
||||
|
||||
if (!integrityVal.isUndefined()) {
|
||||
bool isMap;
|
||||
if (!IsMapObject(aCx, integrityVal, &isMap)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!isMap) {
|
||||
JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
|
||||
JSMSG_IMPORT_MAPS_INTEGRITY_NOT_A_MAP);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::RootedObject integrityObj(aCx, &integrityVal.toObject());
|
||||
sortedAndNormalizedIntegrity = NormalizeModuleIntegrityMap(
|
||||
aCx, integrityObj, aBaseURL, aWarning);
|
||||
if (!sortedAndNormalizedIntegrity) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 7. If parsed's keys contains any items besides "imports" or
|
||||
// "scopes", then the user agent should report a warning to the console
|
||||
// indicating that an invalid top-level key was present in the import map.
|
||||
JS::Rooted<JS::IdVector> keys(aCx, JS::IdVector(aCx));
|
||||
if (!JS_Enumerate(aCx, parsedObj, &keys)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < keys.length(); i++) {
|
||||
const JS::RootedId key(aCx, keys[i]);
|
||||
nsAutoJSString val;
|
||||
NS_ENSURE_TRUE(val.init(aCx, key), nullptr);
|
||||
if (val.EqualsLiteral("imports") || val.EqualsLiteral("scopes") ||
|
||||
val.EqualsLiteral("integrity")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AutoTArray<nsString, 1> params;
|
||||
params.AppendElement(val);
|
||||
aWarning.Report("ImportMapInvalidTopLevelKey", params);
|
||||
}
|
||||
|
||||
// Impl note: Create empty maps for sortedAndNormalizedImports and
|
||||
// sortedAndNormalizedScopes if they aren't allocated.
|
||||
if (!sortedAndNormalizedImports) {
|
||||
sortedAndNormalizedImports = MakeUnique<SpecifierMap>();
|
||||
}
|
||||
if (!sortedAndNormalizedScopes) {
|
||||
sortedAndNormalizedScopes = MakeUnique<ScopeMap>();
|
||||
}
|
||||
if (!sortedAndNormalizedIntegrity) {
|
||||
sortedAndNormalizedIntegrity = MakeUnique<IntegrityMap>();
|
||||
}
|
||||
|
||||
// Step 8. Return an import map whose imports are
|
||||
// sortedAndNormalizedImports and whose scopes scopes are
|
||||
// sortedAndNormalizedScopes.
|
||||
return MakeUnique<ImportMap>(std::move(sortedAndNormalizedImports),
|
||||
std::move(sortedAndNormalizedScopes),
|
||||
std::move(sortedAndNormalizedIntegrity));
|
||||
}
|
||||
|
||||
const nsString* ImportMap::GetIntegrity(nsIURI* aURL) const {
|
||||
if (!mIntegrity || !aURL) {
|
||||
return nullptr;
|
||||
}
|
||||
auto it = mIntegrity->find(aURL->GetSpecOrDefault());
|
||||
if (it == mIntegrity->end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#is-special
|
||||
static bool
|
||||
IsSpecialScheme(nsIURI* aURI)
|
||||
{
|
||||
nsAutoCString scheme;
|
||||
aURI->GetScheme(scheme);
|
||||
return scheme.EqualsLiteral("ftp") || scheme.EqualsLiteral("file") ||
|
||||
scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https") ||
|
||||
scheme.EqualsLiteral("ws") || scheme.EqualsLiteral("wss");
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#resolving-an-imports-match
|
||||
static mozilla::Result<nsCOMPtr<nsIURI>, ResolveError>
|
||||
ResolveImportsMatch(nsString& aNormalizedSpecifier, nsIURI* aAsURL,
|
||||
const SpecifierMap* aSpecifierMap)
|
||||
{
|
||||
// Step 1. For each specifierKey → resolutionResult of specifierMap,
|
||||
for (auto&& [specifierKey, resolutionResult] : *aSpecifierMap) {
|
||||
nsAutoString specifier{aNormalizedSpecifier};
|
||||
nsCString asURL = aAsURL ? aAsURL->GetSpecOrDefault() : EmptyCString();
|
||||
|
||||
// Step 1.1. If specifierKey is normalizedSpecifier, then:
|
||||
if (specifierKey.Equals(aNormalizedSpecifier)) {
|
||||
// Step 1.1.1. If resolutionResult is null, then throw a TypeError
|
||||
// indicating that resolution of specifierKey was blocked by a null entry.
|
||||
// This will terminate the entire resolve a module specifier algorithm,
|
||||
// without any further fallbacks.
|
||||
if (!resolutionResult) {
|
||||
LOG(
|
||||
("ImportMap::ResolveImportsMatch normalizedSpecifier: %s, "
|
||||
"specifierKey: %s, but resolution is null.",
|
||||
NS_ConvertUTF16toUTF8(aNormalizedSpecifier).get(),
|
||||
NS_ConvertUTF16toUTF8(specifierKey).get()));
|
||||
return Err(ResolveError::BlockedByNullEntry);
|
||||
}
|
||||
|
||||
// Step 1.1.2. Assert: resolutionResult is a URL.
|
||||
MOZ_ASSERT(resolutionResult);
|
||||
|
||||
// Step 1.1.3. Return resolutionResult.
|
||||
return resolutionResult;
|
||||
}
|
||||
|
||||
// Step 1.2. If all of the following are true:
|
||||
// specifierKey ends with U+002F (/),
|
||||
// specifierKey is a code unit prefix of normalizedSpecifier, and
|
||||
// either asURL is null, or asURL is special
|
||||
if (StringEndsWith(specifierKey, NS_LITERAL_STRING("/")) &&
|
||||
StringBeginsWith(aNormalizedSpecifier, specifierKey) &&
|
||||
(!aAsURL || IsSpecialScheme(aAsURL))) {
|
||||
// Step 1.2.1. If resolutionResult is null, then throw a TypeError
|
||||
// indicating that resolution of specifierKey was blocked by a null entry.
|
||||
// This will terminate the entire resolve a module specifier algorithm,
|
||||
// without any further fallbacks.
|
||||
if (!resolutionResult) {
|
||||
LOG(
|
||||
("ImportMap::ResolveImportsMatch normalizedSpecifier: %s, "
|
||||
"specifierKey: %s, but resolution is null.",
|
||||
NS_ConvertUTF16toUTF8(aNormalizedSpecifier).get(),
|
||||
NS_ConvertUTF16toUTF8(specifierKey).get()));
|
||||
return Err(ResolveError::BlockedByNullEntry);
|
||||
}
|
||||
|
||||
// Step 1.2.2. Assert: resolutionResult is a URL.
|
||||
MOZ_ASSERT(resolutionResult);
|
||||
|
||||
// Step 1.2.3. Let afterPrefix be the portion of normalizedSpecifier after
|
||||
// the initial specifierKey prefix.
|
||||
nsAutoString afterPrefix(
|
||||
Substring(aNormalizedSpecifier, specifierKey.Length()));
|
||||
|
||||
// Step 1.2.4. Assert: resolutionResult, serialized, ends with U+002F (/),
|
||||
// as enforced during parsing
|
||||
MOZ_ASSERT(StringEndsWith(resolutionResult->GetSpecOrDefault(),
|
||||
NS_LITERAL_CSTRING("/")));
|
||||
|
||||
// Step 1.2.5. Let url be the result of URL parsing afterPrefix with
|
||||
// resolutionResult.
|
||||
nsCOMPtr<nsIURI> url;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(url), afterPrefix, nullptr,
|
||||
resolutionResult);
|
||||
|
||||
// Step 1.2.6. If url is failure, then throw a TypeError indicating that
|
||||
// resolution of normalizedSpecifier was blocked since the afterPrefix
|
||||
// portion could not be URL-parsed relative to the resolutionResult mapped
|
||||
// to by the specifierKey prefix.
|
||||
//
|
||||
// This will terminate the entire resolve a module specifier algorithm,
|
||||
// without any further fallbacks.
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(
|
||||
("ImportMap::ResolveImportsMatch normalizedSpecifier: %s, "
|
||||
"specifierKey: %s, resolutionResult: %s, afterPrefix: %s, "
|
||||
"but URL is not parsable.",
|
||||
NS_ConvertUTF16toUTF8(aNormalizedSpecifier).get(),
|
||||
NS_ConvertUTF16toUTF8(specifierKey).get(),
|
||||
resolutionResult->GetSpecOrDefault().get(),
|
||||
NS_ConvertUTF16toUTF8(afterPrefix).get()));
|
||||
return Err(ResolveError::BlockedByAfterPrefix);
|
||||
}
|
||||
|
||||
// Step 1.2.7. Assert: url is a URL.
|
||||
MOZ_ASSERT(url);
|
||||
|
||||
// Step 1.2.8. If the serialization of resolutionResult is not a code unit
|
||||
// prefix of the serialization of url, then throw a TypeError indicating
|
||||
// that resolution of normalizedSpecifier was blocked due to it
|
||||
// backtracking above its prefix specifierKey.
|
||||
//
|
||||
// This will terminate the entire resolve a module specifier algorithm,
|
||||
// without any further fallbacks.
|
||||
if (!StringBeginsWith(url->GetSpecOrDefault(),
|
||||
resolutionResult->GetSpecOrDefault())) {
|
||||
LOG(
|
||||
("ImportMap::ResolveImportsMatch normalizedSpecifier: %s, "
|
||||
"specifierKey: %s, "
|
||||
"url %s does not start with resolutionResult %s.",
|
||||
NS_ConvertUTF16toUTF8(aNormalizedSpecifier).get(),
|
||||
NS_ConvertUTF16toUTF8(specifierKey).get(),
|
||||
url->GetSpecOrDefault().get(),
|
||||
resolutionResult->GetSpecOrDefault().get()));
|
||||
return Err(ResolveError::BlockedByBacktrackingPrefix);
|
||||
}
|
||||
|
||||
// Step 1.2.9. Return url.
|
||||
return std::move(url);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2. Return null.
|
||||
return nsCOMPtr<nsIURI>(nullptr);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier
|
||||
// static
|
||||
ResolveResult
|
||||
ImportMap::ResolveModuleSpecifier(ImportMap* aImportMap, ScriptLoader* aLoader,
|
||||
LoadedScript* aScript, const nsAString& aSpecifier)
|
||||
{
|
||||
LOG(("ImportMap::ResolveModuleSpecifier specifier: %s",
|
||||
NS_ConvertUTF16toUTF8(aSpecifier).get()));
|
||||
nsCOMPtr<nsIURI> baseURL;
|
||||
if (aScript) {
|
||||
baseURL = aScript->BaseURL();
|
||||
} else {
|
||||
baseURL = aLoader->GetBaseURI();
|
||||
}
|
||||
|
||||
// Step 7. Let asURL be the result of resolving a URL-like module specifier
|
||||
// given specifier and baseURL.
|
||||
//
|
||||
// Impl note: Step 6 is done below if aImportMap exists.
|
||||
auto parseResult = ResolveURLLikeModuleSpecifier(aSpecifier, baseURL);
|
||||
nsCOMPtr<nsIURI> asURL;
|
||||
if (parseResult.isOk()) {
|
||||
asURL = parseResult.unwrap();
|
||||
}
|
||||
|
||||
if (aImportMap) {
|
||||
// Step 6. Let baseURLString be baseURL, serialized.
|
||||
nsCString baseURLString = baseURL->GetSpecOrDefault();
|
||||
|
||||
// Step 8. Let normalizedSpecifier be the serialization of asURL, if asURL
|
||||
// is non-null; otherwise, specifier.
|
||||
nsAutoString normalizedSpecifier =
|
||||
asURL ? NS_ConvertUTF8toUTF16(asURL->GetSpecOrDefault())
|
||||
: nsAutoString{aSpecifier};
|
||||
|
||||
// Step 9. For each scopePrefix → scopeImports of importMap's scopes,
|
||||
for (auto&& [scopePrefix, scopeImports] : *aImportMap->mScopes) {
|
||||
// Step 9.1. If scopePrefix is baseURLString, or if scopePrefix ends with
|
||||
// U+002F (/) and scopePrefix is a code unit prefix of baseURLString,
|
||||
// then:
|
||||
if (scopePrefix.Equals(baseURLString) ||
|
||||
(StringEndsWith(scopePrefix, NS_LITERAL_CSTRING("/")) &&
|
||||
StringBeginsWith(baseURLString, scopePrefix))) {
|
||||
// Step 9.1.1. Let scopeImportsMatch be the result of resolving an
|
||||
// imports match given normalizedSpecifier, asURL, and scopeImports.
|
||||
auto result =
|
||||
ResolveImportsMatch(normalizedSpecifier, asURL, scopeImports.get());
|
||||
if (result.isErr()) {
|
||||
return result.propagateErr();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> scopeImportsMatch = result.unwrap();
|
||||
// Step 9.1.2. If scopeImportsMatch is not null, then return
|
||||
// scopeImportsMatch.
|
||||
if (scopeImportsMatch) {
|
||||
LOG((
|
||||
"ImportMap::ResolveModuleSpecifier returns scopeImportsMatch: %s",
|
||||
scopeImportsMatch->GetSpecOrDefault().get()));
|
||||
return WrapNotNull(scopeImportsMatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 10. Let topLevelImportsMatch be the result of resolving an imports
|
||||
// match given normalizedSpecifier, asURL, and importMap's imports.
|
||||
auto result = ResolveImportsMatch(normalizedSpecifier, asURL,
|
||||
aImportMap->mImports.get());
|
||||
if (result.isErr()) {
|
||||
return result.propagateErr();
|
||||
}
|
||||
nsCOMPtr<nsIURI> topLevelImportsMatch = result.unwrap();
|
||||
|
||||
// Step 11. If topLevelImportsMatch is not null, then return
|
||||
// topLevelImportsMatch.
|
||||
if (topLevelImportsMatch) {
|
||||
LOG(("ImportMap::ResolveModuleSpecifier returns topLevelImportsMatch: %s",
|
||||
topLevelImportsMatch->GetSpecOrDefault().get()));
|
||||
return WrapNotNull(topLevelImportsMatch);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 12. At this point, the specifier was able to be turned in to a URL,
|
||||
// but it wasn't remapped to anything by importMap. If asURL is not null, then
|
||||
// return asURL.
|
||||
if (asURL) {
|
||||
LOG(("ImportMap::ResolveModuleSpecifier returns asURL: %s",
|
||||
asURL->GetSpecOrDefault().get()));
|
||||
return WrapNotNull(asURL);
|
||||
}
|
||||
|
||||
// Step 13. Throw a TypeError indicating that specifier was a bare specifier,
|
||||
// but was not remapped to anything by importMap.
|
||||
if (parseResult.unwrapErr() != ResolveError::FailureMayBeBare) {
|
||||
// We may have failed to parse a non-bare specifier for another reason.
|
||||
return Err(ResolveError::Failure);
|
||||
}
|
||||
|
||||
return Err(ResolveError::InvalidBareSpecifier);
|
||||
}
|
||||
|
||||
#undef LOG
|
||||
#undef LOG_ENABLED
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@@ -0,0 +1,115 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 dom_script_ImportMap_h
|
||||
#define dom_script_ImportMap_h
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
#include "js/SourceBufferHolder.h"
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsStringFwd.h"
|
||||
#include "nsTArray.h"
|
||||
#include "ResolveResult.h"
|
||||
|
||||
struct JSContext;
|
||||
class nsIScriptElement;
|
||||
class nsIURI;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class LoadedScript;
|
||||
class ScriptLoadRequest;
|
||||
class ScriptLoader;
|
||||
|
||||
/**
|
||||
* A helper class to report warning to ScriptLoader.
|
||||
*/
|
||||
class ReportWarningHelper {
|
||||
public:
|
||||
ReportWarningHelper(ScriptLoader* aLoader, ScriptLoadRequest* aRequest)
|
||||
: mLoader(aLoader), mRequest(aRequest) {}
|
||||
|
||||
void Report(const char* aMessageName,
|
||||
const nsTArray<nsString>& aParams = nsTArray<nsString>()) const;
|
||||
|
||||
private:
|
||||
RefPtr<ScriptLoader> mLoader;
|
||||
ScriptLoadRequest* mRequest;
|
||||
};
|
||||
|
||||
// Specifier map from import maps.
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#specifier-map
|
||||
using SpecifierMap =
|
||||
std::map<nsString, nsCOMPtr<nsIURI>, std::greater<nsString>>;
|
||||
|
||||
// Scope map from import maps.
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#import-map-scopes
|
||||
using ScopeMap = std::map<nsCString, mozilla::UniquePtr<SpecifierMap>,
|
||||
std::greater<nsCString>>;
|
||||
|
||||
// Integrity map from import maps.
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#module-integrity-map
|
||||
using IntegrityMap = std::map<nsCString, nsString>;
|
||||
|
||||
/**
|
||||
* Implementation of Import maps.
|
||||
* https://html.spec.whatwg.org/multipage/webappapis.html#import-maps
|
||||
*/
|
||||
class ImportMap {
|
||||
public:
|
||||
ImportMap(mozilla::UniquePtr<SpecifierMap> aImports,
|
||||
mozilla::UniquePtr<ScopeMap> aScopes,
|
||||
mozilla::UniquePtr<IntegrityMap> aIntegrity)
|
||||
: mImports(std::move(aImports)),
|
||||
mScopes(std::move(aScopes)),
|
||||
mIntegrity(std::move(aIntegrity)) {}
|
||||
|
||||
/**
|
||||
* Parse the JSON string from the Import map script.
|
||||
* This function will throw TypeError if there's any invalid key or value in
|
||||
* the JSON text according to the spec.
|
||||
*
|
||||
* See https://html.spec.whatwg.org/multipage/webappapis.html#parse-an-import-map-string
|
||||
*/
|
||||
static mozilla::UniquePtr<ImportMap> ParseString(
|
||||
JSContext* aCx, JS::SourceBufferHolder& aInput, nsIURI* aBaseURL,
|
||||
const ReportWarningHelper& aWarning);
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-integrity-metadata
|
||||
const nsString* GetIntegrity(nsIURI* aURL) const;
|
||||
|
||||
// Implements
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier
|
||||
static ResolveResult ResolveModuleSpecifier(ImportMap* aImportMap,
|
||||
ScriptLoader* aLoader,
|
||||
LoadedScript* aScript,
|
||||
const nsAString& aSpecifier);
|
||||
|
||||
// Logging
|
||||
static mozilla::LazyLogModule gImportMapLog;
|
||||
|
||||
private:
|
||||
/**
|
||||
* https://html.spec.whatwg.org/multipage/webappapis.html#import-map
|
||||
*
|
||||
* A import map is a struct with two items:
|
||||
* 1. imports, a specifier map, and
|
||||
* 2. scopes, an ordered map of URLs to specifier maps.
|
||||
*/
|
||||
mozilla::UniquePtr<SpecifierMap> mImports;
|
||||
mozilla::UniquePtr<ScopeMap> mScopes;
|
||||
mozilla::UniquePtr<IntegrityMap> mIntegrity;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // dom_script_ImportMap_h
|
||||
@@ -57,7 +57,8 @@ static VisitedURLSet* NewVisitedSetForTopLevelImport(nsIURI* aURI) {
|
||||
/* static */ ModuleLoadRequest* ModuleLoadRequest::CreateStaticImport(
|
||||
nsIURI* aURI, ModuleLoadRequest* aParent) {
|
||||
auto request =
|
||||
new ModuleLoadRequest(aURI, aParent->mFetchOptions, SRIMetadata(),
|
||||
new ModuleLoadRequest(aURI, aParent->mFetchOptions,
|
||||
aParent->mLoader->ComputeModuleIntegrity(aURI),
|
||||
aParent->mURI, false, /* is top level */
|
||||
false, /* is dynamic import */
|
||||
aParent->mLoader, aParent->mVisitedSet);
|
||||
@@ -77,7 +78,7 @@ static VisitedURLSet* NewVisitedSetForTopLevelImport(nsIURI* aURI) {
|
||||
MOZ_ASSERT(aPromise);
|
||||
|
||||
auto request = new ModuleLoadRequest(
|
||||
aURI, aFetchOptions, SRIMetadata(), aBaseURL,
|
||||
aURI, aFetchOptions, aLoader->ComputeModuleIntegrity(aURI), aBaseURL,
|
||||
true, /* is top level */
|
||||
true, /* is dynamic import */
|
||||
aLoader, NewVisitedSetForTopLevelImport(aURI));
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 dom_script_ResolveResult_h
|
||||
#define dom_script_ResolveResult_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/NotNull.h"
|
||||
#include "mozilla/Result.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIURI.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
enum class ResolveError : uint8_t
|
||||
{
|
||||
Failure,
|
||||
FailureMayBeBare,
|
||||
BlockedByNullEntry,
|
||||
BlockedByAfterPrefix,
|
||||
BlockedByBacktrackingPrefix,
|
||||
InvalidBareSpecifier
|
||||
};
|
||||
|
||||
struct ResolveErrorInfo
|
||||
{
|
||||
static const char* GetString(ResolveError aError) {
|
||||
switch (aError) {
|
||||
case ResolveError::Failure:
|
||||
return "ModuleResolveFailureNoWarn";
|
||||
case ResolveError::FailureMayBeBare:
|
||||
return "ModuleResolveFailureWarnRelative";
|
||||
case ResolveError::BlockedByNullEntry:
|
||||
return "ImportMapResolutionBlockedByNullEntry";
|
||||
case ResolveError::BlockedByAfterPrefix:
|
||||
return "ImportMapResolutionBlockedByAfterPrefix";
|
||||
case ResolveError::BlockedByBacktrackingPrefix:
|
||||
return "ImportMapResolutionBlockedByBacktrackingPrefix";
|
||||
case ResolveError::InvalidBareSpecifier:
|
||||
return "ImportMapResolveInvalidBareSpecifierWarnRelative";
|
||||
default:
|
||||
MOZ_CRASH("Unexpected ResolveError value");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* ResolveResult is used to store the result of 'resolving a module specifier',
|
||||
* which could be an URI on success or a ResolveError on failure.
|
||||
*/
|
||||
using ResolveResult =
|
||||
mozilla::Result<mozilla::NotNull<nsCOMPtr<nsIURI>>, ResolveError>;
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // dom_script_ResolveResult_h
|
||||
@@ -0,0 +1,25 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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_dom_ScriptKind_h
|
||||
#define mozilla_dom_ScriptKind_h
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Per-request data structure
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
enum class ScriptKind {
|
||||
Classic,
|
||||
Module,
|
||||
ImportMap
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
+292
-68
@@ -60,6 +60,8 @@
|
||||
#include "nsIScriptError.h"
|
||||
|
||||
using JS::SourceBufferHolder;
|
||||
using mozilla::Err;
|
||||
using mozilla::WrapNotNull;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@@ -326,6 +328,7 @@ ScriptLoader::ScriptLoader(nsIDocument *aDocument)
|
||||
mDeferEnabled(false),
|
||||
mDocumentParsingDone(false),
|
||||
mBlockingDOMContentLoaded(false),
|
||||
mImportMapsAllowed(true),
|
||||
mReporter(new ConsoleReportCollector())
|
||||
{
|
||||
EnsureModuleHooksInitialized();
|
||||
@@ -670,15 +673,35 @@ ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest)
|
||||
}
|
||||
|
||||
static bool
|
||||
CreateTypeError(JSContext* aCx, ModuleScript* aScript, const nsString& aMessage,
|
||||
CreateTypeError(JSContext* aCx, LoadedScript* aScript, const nsString& aMessage,
|
||||
JS::MutableHandle<JS::Value> aError)
|
||||
{
|
||||
JS::Rooted<JSObject*> module(aCx, aScript->ModuleRecord());
|
||||
JS::Rooted<JSScript*> script(aCx, JS::GetModuleScript(aCx, module));
|
||||
JS::Rooted<JSString*> filename(aCx);
|
||||
filename = JS_NewStringCopyZ(aCx, JS_GetScriptFilename(script));
|
||||
if (aScript && aScript->IsModuleScript()) {
|
||||
JS::Rooted<JSObject*> module(aCx,
|
||||
aScript->AsModuleScript()->ModuleRecord());
|
||||
if (module) {
|
||||
JS::Rooted<JSScript*> script(aCx, JS::GetModuleScript(aCx, module));
|
||||
if (script) {
|
||||
filename = JS_NewStringCopyZ(aCx, JS_GetScriptFilename(script));
|
||||
if (!filename) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!filename) {
|
||||
return false;
|
||||
// No module record (classic script, dynamic import with no referencing
|
||||
// script, or a module that never got that far). Fall back to the base URL.
|
||||
nsAutoCString url;
|
||||
if (aScript && aScript->BaseURL()) {
|
||||
aScript->BaseURL()->GetAsciiSpec(url);
|
||||
}
|
||||
filename = JS_NewStringCopyZ(aCx, url.get());
|
||||
if (!filename) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
JS::Rooted<JSString*> message(aCx, JS_NewUCStringCopyZ(aCx, aMessage.get()));
|
||||
@@ -691,63 +714,30 @@ CreateTypeError(JSContext* aCx, ModuleScript* aScript, const nsString& aMessage,
|
||||
}
|
||||
|
||||
static nsresult
|
||||
HandleResolveFailure(JSContext* aCx, ModuleScript* aScript,
|
||||
const nsAString& aSpecifier)
|
||||
HandleResolveFailure(JSContext* aCx, LoadedScript* aScript,
|
||||
const nsAString& aSpecifier,
|
||||
JS::MutableHandle<JS::Value> aErrorOut)
|
||||
{
|
||||
// TODO: How can we get the line number of the failed import?
|
||||
|
||||
nsAutoString message(NS_LITERAL_STRING("Error resolving module specifier: "));
|
||||
message.Append(aSpecifier);
|
||||
|
||||
JS::Rooted<JS::Value> error(aCx);
|
||||
if (!CreateTypeError(aCx, aScript, message, &error)) {
|
||||
if (!CreateTypeError(aCx, aScript, message, aErrorOut)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
aScript->SetParseError(error);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static already_AddRefed<nsIURI>
|
||||
ResolveModuleSpecifier(ScriptLoader* aLoader, LoadedScript* aScript, const nsAString& aSpecifier)
|
||||
ResolveResult
|
||||
ResolveModuleSpecifier(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 "../"
|
||||
//
|
||||
// Bareword module specifiers are currently disallowed as these may be given
|
||||
// special meanings in the future.
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri), aSpecifier);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return uri.forget();
|
||||
}
|
||||
|
||||
if (rv != NS_ERROR_MALFORMED_URI) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!StringBeginsWith(aSpecifier, NS_LITERAL_STRING("/")) &&
|
||||
!StringBeginsWith(aSpecifier, NS_LITERAL_STRING("./")) &&
|
||||
!StringBeginsWith(aSpecifier, NS_LITERAL_STRING("../"))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
// ImportMap::ResolveModuleSpecifier implements the whole algorithm, including
|
||||
// the aImportMap == nullptr case, so there is nothing left to do here.
|
||||
return ImportMap::ResolveModuleSpecifier(aLoader->GetImportMap(), aLoader,
|
||||
aScript, aSpecifier);
|
||||
}
|
||||
|
||||
static nsresult
|
||||
@@ -780,15 +770,18 @@ ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI>* aUrlsOu
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Let url be the result of resolving a module specifier given module script and requested.
|
||||
nsCOMPtr<nsIURI> uri =
|
||||
ResolveModuleSpecifier(aRequest->mLoader, ms, specifier);
|
||||
if (!uri) {
|
||||
nsresult rv = HandleResolveFailure(cx, ms, specifier);
|
||||
// Let url be the result of resolving a module specifier given module script
|
||||
// and requested.
|
||||
auto result = ResolveModuleSpecifier(aRequest->mLoader, ms, specifier);
|
||||
if (result.isErr()) {
|
||||
JS::Rooted<JS::Value> error(cx);
|
||||
nsresult rv = HandleResolveFailure(cx, ms, specifier, &error);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
ms->SetParseError(error);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri = result.unwrap();
|
||||
if (aUrlsOut) {
|
||||
aUrlsOut->AppendElement(uri.forget());
|
||||
}
|
||||
@@ -953,17 +946,28 @@ HostResolveImportedModule(JSContext* aCx,
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(loader, script, string);
|
||||
auto result = ResolveModuleSpecifier(loader, script, string);
|
||||
|
||||
// This cannot fail because resolving a module specifier must have been
|
||||
// previously successful with these same two arguments.
|
||||
MOZ_ASSERT(uri, "Failed to resolve previously-resolved module specifier");
|
||||
if (result.isErr()) {
|
||||
JS_ReportErrorASCII(aCx, "Failed to resolve previously-resolved module specifier");
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri = result.unwrap();
|
||||
MOZ_ASSERT(uri);
|
||||
|
||||
// Let resolved module script be moduleMap[url]. (This entry must exist for us
|
||||
// to have gotten to this point.)
|
||||
|
||||
ModuleScript* ms = loader->GetFetchedModule(uri);
|
||||
MOZ_ASSERT(ms, "Resolved module not found in module map");
|
||||
// This is now a potential result with import maps.
|
||||
// So instead of asserting report the error to JS and return.
|
||||
if (!ms) {
|
||||
JS_ReportErrorASCII(aCx, "Resolved module not found in module map");
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!ms->HasParseError());
|
||||
MOZ_ASSERT(ms->ModuleRecord());
|
||||
@@ -1016,13 +1020,22 @@ bool HostImportModuleDynamically(JSContext* aCx,
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(loader, script, string);
|
||||
if (!uri) {
|
||||
JS_ReportErrorNumberUC(aCx, js::GetErrorMessage, nullptr,
|
||||
JSMSG_BAD_MODULE_SPECIFIER, string.get());
|
||||
// Import maps should be disabled after this point.
|
||||
// At least until multiple import maps support is included.
|
||||
loader->DisallowImportMaps();
|
||||
|
||||
auto result = ResolveModuleSpecifier(loader, script, string);
|
||||
if (result.isErr()) {
|
||||
JS::Rooted<JS::Value> error(aCx);
|
||||
if (NS_FAILED(HandleResolveFailure(aCx, script, string, &error))) {
|
||||
return false;
|
||||
}
|
||||
JS_SetPendingException(aCx, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri = result.unwrap();
|
||||
|
||||
// Create a new top-level load request.
|
||||
ScriptFetchOptions* options;
|
||||
nsIURI* baseURL;
|
||||
@@ -1500,11 +1513,12 @@ ScriptLoader::CreateLoadRequest(ScriptKind aKind,
|
||||
ScriptFetchOptions* fetchOptions =
|
||||
new ScriptFetchOptions(aCORSMode, aReferrerPolicy, aElement, aTriggeringPrincipal);
|
||||
|
||||
if (aKind == ScriptKind::Classic) {
|
||||
if (aKind == ScriptKind::Classic || aKind == ScriptKind::ImportMap) {
|
||||
return new ScriptLoadRequest(aKind, aURI, fetchOptions, aIntegrity, referrer);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aKind == ScriptKind::Module);
|
||||
|
||||
return ModuleLoadRequest::CreateTopLevel(aURI, fetchOptions, aIntegrity, referrer, this);
|
||||
}
|
||||
|
||||
@@ -1526,9 +1540,14 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
||||
nsAutoString type;
|
||||
bool hasType = aElement->GetScriptType(type);
|
||||
|
||||
ScriptKind scriptKind = aElement->GetScriptIsModule() ?
|
||||
ScriptKind::Module :
|
||||
ScriptKind::Classic;
|
||||
ScriptKind scriptKind;
|
||||
if (aElement->GetScriptIsModule()) {
|
||||
scriptKind = ScriptKind::Module;
|
||||
} else if (aElement->GetScriptIsImportMap()) {
|
||||
scriptKind = ScriptKind::ImportMap;
|
||||
} else {
|
||||
scriptKind = ScriptKind::Classic;
|
||||
}
|
||||
|
||||
// Step 13. Check that the script is not an eventhandler
|
||||
if (IsScriptEventHandler(scriptKind, scriptContent)) {
|
||||
@@ -1569,12 +1588,31 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/scripting.html#prepare-the-script-element
|
||||
// Step 33.1: external import maps are not supported.
|
||||
if (scriptKind == ScriptKind::ImportMap && aElement->GetScriptExternal()) {
|
||||
NS_DispatchToCurrentThread(
|
||||
NewRunnableMethod(aElement, &nsIScriptElement::FireErrorEvent));
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/scripting.html#prepare-the-script-element
|
||||
// If el's type is "importmap": if el's relevant global object's import maps
|
||||
// allowed is false, queue an element task to fire `error` at el, and return.
|
||||
if (scriptKind == ScriptKind::ImportMap && !mImportMapsAllowed) {
|
||||
NS_WARNING("ScriptLoader: import maps allowed is false.");
|
||||
NS_DispatchToCurrentThread(
|
||||
NewRunnableMethod(aElement, &nsIScriptElement::FireErrorEvent));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 15. and later in the HTML5 spec
|
||||
nsresult rv = NS_OK;
|
||||
RefPtr<ScriptLoadRequest> request;
|
||||
mozilla::net::ReferrerPolicy referrerPolicy = GetReferrerPolicy(aElement);
|
||||
if (aElement->GetScriptExternal()) {
|
||||
// external script
|
||||
|
||||
nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI();
|
||||
if (!scriptURI) {
|
||||
// Asynchronously report the failure to create a URI object
|
||||
@@ -1584,6 +1622,14 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-module-script-tree
|
||||
// Step 1. Disallow further import maps given settings object.
|
||||
// Must happen for every module script element, not only when a
|
||||
// speculative preload happens to be reused.
|
||||
if (scriptKind == ScriptKind::Module) {
|
||||
DisallowImportMaps();
|
||||
}
|
||||
|
||||
// Double-check that the preload matches what we're asked to load now.
|
||||
CORSMode ourCORSMode = aElement->GetCORSMode();
|
||||
nsTArray<PreloadInfo>::index_type i =
|
||||
@@ -1611,6 +1657,31 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
||||
request->Cancel();
|
||||
return false;
|
||||
}
|
||||
// A speculative preload is performed before the tree ops of its flush
|
||||
// batch run (nsHtml5TreeOpExecutor::RunFlushLoop), and PreloadURI has no
|
||||
// element, so a module preload never carries import-map integrity.
|
||||
// mIntegrity is const and the channel is already open, so the preload
|
||||
// has to be discarded rather than patched.
|
||||
if (scriptKind == ScriptKind::Module &&
|
||||
!scriptContent->HasAttr(kNameSpaceID_None, nsGkAtoms::integrity)) {
|
||||
SRIMetadata fromMap = ComputeModuleIntegrity(scriptURI);
|
||||
if (!fromMap.IsEmpty() &&
|
||||
!fromMap.GetIntegrityString().Equals(
|
||||
request->mIntegrity.GetIntegrityString())) {
|
||||
// Drop the stale preload. The module map entry has to go too,
|
||||
// otherwise the replacement request's StartLoad() will see
|
||||
// ModuleMapContainsURL() == true and just wait for this
|
||||
// unverified fetch instead of starting its own.
|
||||
request->Cancel();
|
||||
RefPtr<GenericPromise::Private> waiters;
|
||||
bool ownsFetch = mFetchingModules.Get(scriptURI, getter_AddRefs(waiters)) && !waiters;
|
||||
if (ownsFetch) {
|
||||
mFetchingModules.Remove(scriptURI);
|
||||
}
|
||||
mFetchedModules.Remove(scriptURI);
|
||||
request = nullptr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Drop the preload
|
||||
request = nullptr;
|
||||
@@ -1628,7 +1699,9 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
||||
// No usable preload found.
|
||||
|
||||
SRIMetadata sriMetadata;
|
||||
{
|
||||
if (scriptContent->HasAttr(kNameSpaceID_None, nsGkAtoms::integrity)) {
|
||||
// The element's own integrity attribute wins — even when it is present
|
||||
// but empty, in which case no metadata is used at all.
|
||||
nsAutoString integrity;
|
||||
scriptContent->GetAttr(kNameSpaceID_None, nsGkAtoms::integrity,
|
||||
integrity);
|
||||
@@ -1643,6 +1716,11 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
||||
SRICheck::IntegrityMetadata(integrity, sourceUri, mReporter,
|
||||
&sriMetadata);
|
||||
}
|
||||
} else if (scriptKind == ScriptKind::Module) {
|
||||
// https://html.spec.whatwg.org/multipage/scripting.html#prepare-the-script-element
|
||||
// Step 33.1.11: no integrity attribute, so resolve the metadata from
|
||||
// the document's import map for the resolved URL.
|
||||
sriMetadata = ComputeModuleIntegrity(scriptURI);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = aElement->GetScriptURITriggeringPrincipal();
|
||||
@@ -1787,6 +1865,10 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
||||
request->mBaseURL = mDocument->GetDocBaseURI();
|
||||
|
||||
if (request->IsModuleRequest()) {
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-an-inline-module-script-graph
|
||||
// Step 1. Disallow further import maps given settings object.
|
||||
DisallowImportMaps();
|
||||
|
||||
ModuleLoadRequest* modReq = request->AsModuleRequest();
|
||||
|
||||
if (aElement->GetScriptAsync()) {
|
||||
@@ -1802,6 +1884,39 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (request->IsImportMapRequest()) {
|
||||
// https://html.spec.whatwg.org/multipage/scripting.html#prepare-the-script-element
|
||||
// Step 31.2 type is "importmap":
|
||||
// Impl note: Step 1 is done above before creating a ScriptLoadRequest.
|
||||
MOZ_ASSERT(mImportMapsAllowed);
|
||||
|
||||
// Step 2. Set el's relevant global object's import maps allowed to false.
|
||||
DisallowImportMaps();
|
||||
|
||||
// Step 3. Let result be the result of creating an import map parse result
|
||||
// given source text and base URL.
|
||||
UniquePtr<ImportMap> importMap = ParseImportMap(request);
|
||||
if (!importMap) {
|
||||
// If parsing import maps fails, the exception will have been reported by
|
||||
// ImportMap::ParseString, and the registration of the import map bails
|
||||
// out early. Note the script's result is an import map parse result,
|
||||
// which is never null, so no error event is fired here.
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-element
|
||||
// The spec defines 'register an import map' should be done in
|
||||
// 'execute the script element', because inside 'execute the script element'
|
||||
// it will perform a 'preparation-time document check'.
|
||||
// However, as import maps can currently only be inline scripts, the
|
||||
// 'preparation-time document check' will never fail for import maps.
|
||||
// So we simply call 'register an import map' here.
|
||||
MOZ_ASSERT(aElement->GetScriptIsImportMap());
|
||||
RegisterImportMap(std::move(importMap));
|
||||
return false;
|
||||
}
|
||||
|
||||
request->mProgress = ScriptLoadRequest::Progress::Ready;
|
||||
if (aElement->GetParserCreated() == FROM_PARSER_XSLT &&
|
||||
(!ReadyToExecuteParserBlockingScripts() || !mXSLTRequests.isEmpty())) {
|
||||
@@ -1948,6 +2063,28 @@ OffThreadScriptLoaderCallback(void *aToken, void *aCallbackData)
|
||||
NS_DispatchToMainThread(aRunnable);
|
||||
}
|
||||
|
||||
void
|
||||
ScriptLoader::ReportWarningToConsole(ScriptLoadRequest* aRequest,
|
||||
const char* aMessageName,
|
||||
const nsTArray<nsString>& aParams) const
|
||||
{
|
||||
nsIScriptElement* element = aRequest->Element();
|
||||
uint32_t lineNo = element ? element->GetScriptLineNumber() : 0;
|
||||
uint32_t columnNo = element ? element->GetScriptColumnNumber() : 0;
|
||||
|
||||
nsTArray<const char16_t*> stringParams;
|
||||
for (const auto& param : aParams) {
|
||||
stringParams.AppendElement(param.get());
|
||||
}
|
||||
|
||||
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
|
||||
NS_LITERAL_CSTRING("Script Loader"), mDocument,
|
||||
nsContentUtils::eDOM_PROPERTIES, aMessageName,
|
||||
stringParams.Elements(),
|
||||
stringParams.Length(), aRequest->mBaseURL,
|
||||
EmptyString(), lineNo, columnNo);
|
||||
}
|
||||
|
||||
nsresult
|
||||
ScriptLoader::AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest)
|
||||
{
|
||||
@@ -2740,7 +2877,15 @@ void
|
||||
ScriptLoader::HandleLoadError(ScriptLoadRequest *aRequest, nsresult aResult) {
|
||||
if (aRequest->IsModuleRequest() && !aRequest->mIsInline) {
|
||||
auto request = aRequest->AsModuleRequest();
|
||||
SetModuleFetchFinishedAndResumeWaitingRequests(request, aResult);
|
||||
|
||||
// A preload dropped in ProcessScriptElement has already had its URL
|
||||
// removed from the module map, so its late completion must not touch the
|
||||
// entry that the replacement request now owns: SetModuleFetchFinished...
|
||||
// would trip its MOZ_ALWAYS_TRUE on mFetchingModules and then park a
|
||||
// nullptr in mFetchedModules over the real module.
|
||||
if (IsFetchingModule(request)) {
|
||||
SetModuleFetchFinishedAndResumeWaitingRequests(request, aResult);
|
||||
}
|
||||
}
|
||||
|
||||
if (aRequest->mInDeferList) {
|
||||
@@ -2790,8 +2935,15 @@ ScriptLoader::HandleLoadError(ScriptLoadRequest *aRequest, nsresult aResult) {
|
||||
FinishDynamicImport(modReq, aResult);
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(!modReq->IsTopLevel());
|
||||
// A top-level request can reach here: it is a preload that
|
||||
// ProcessScriptElement dropped in favour of a replacement request
|
||||
// because the import map supplied integrity metadata. Dropping it
|
||||
// cleared mIsPreload and took it out of mPreloads, but it was never in
|
||||
// any of the request lists either, so it is indistinguishable from a
|
||||
// child import except by IsTopLevel(). Its error is reported through
|
||||
// the replacement request, so just cancel and forget it.
|
||||
MOZ_ASSERT(!modReq->isInList());
|
||||
MOZ_ASSERT_IF(modReq->IsTopLevel(), modReq->IsCanceled());
|
||||
modReq->Cancel();
|
||||
// The error is handled for the top level module.
|
||||
}
|
||||
@@ -3160,5 +3312,77 @@ ScriptLoader::MaybeRemovedDeferRequests()
|
||||
return false;
|
||||
}
|
||||
|
||||
UniquePtr<ImportMap>
|
||||
ScriptLoader::ParseImportMap(ScriptLoadRequest* aRequest)
|
||||
{
|
||||
nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
|
||||
if (!globalObject) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (!jsapi.Init(globalObject)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Import map scripts are always inline, so the source text is on the element.
|
||||
nsAutoString inlineData;
|
||||
SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData);
|
||||
ReportWarningHelper warning{this, aRequest};
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/scripting.html#prepare-a-script
|
||||
// https://wicg.github.io/import-maps/#integration-prepare-a-script
|
||||
// Insert the following case to prepare a script step 25.2:
|
||||
// (Impl Note: the latest html spec is step 27.2)
|
||||
// Switch on the script's type:
|
||||
// "importmap"
|
||||
// Step 1. Let import map parse result be the result of create an import map
|
||||
// parse result, given source text, base URL and settings object.
|
||||
//
|
||||
// Impl note: According to the spec, ImportMap::ParseString will throw a
|
||||
// TypeError if there's any invalid key/value in the text. After the parsing
|
||||
// is done, we should report the error if there's any, this is done in
|
||||
// ~AutoJSAPI.
|
||||
//
|
||||
// See https://wicg.github.io/import-maps/#register-an-import-map, step 7.
|
||||
return ImportMap::ParseString(jsapi.cx(), srcBuf, aRequest->mBaseURL, warning);
|
||||
}
|
||||
|
||||
void
|
||||
ScriptLoader::RegisterImportMap(UniquePtr<ImportMap> aImportMap)
|
||||
{
|
||||
// Check for aImportMap is done in ScriptLoader.
|
||||
MOZ_ASSERT(aImportMap);
|
||||
|
||||
// Step 8. Set element's node document's import map to import map parse
|
||||
// result's import map.
|
||||
mImportMap = std::move(aImportMap);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-integrity-metadata
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#get-the-descendant-script-fetch-options
|
||||
SRIMetadata
|
||||
ScriptLoader::ComputeModuleIntegrity(nsIURI* aURI)
|
||||
{
|
||||
SRIMetadata integrity;
|
||||
ImportMap* importMap = GetImportMap();
|
||||
if (!importMap || !aURI) {
|
||||
return integrity;
|
||||
}
|
||||
|
||||
// Step 2. If map's integrity[url] does not exist, return the empty string.
|
||||
const nsString* metadata = importMap->GetIntegrity(aURI);
|
||||
if (!metadata || metadata->IsEmpty()) {
|
||||
return integrity;
|
||||
}
|
||||
|
||||
nsAutoCString sourceUri;
|
||||
if (mDocument && mDocument->GetDocumentURI()) {
|
||||
mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
|
||||
}
|
||||
SRICheck::IntegrityMetadata(*metadata, sourceUri, mReporter, &integrity);
|
||||
return integrity;
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
@@ -6,6 +6,7 @@
|
||||
#ifndef mozilla_dom_ScriptLoader_h
|
||||
#define mozilla_dom_ScriptLoader_h
|
||||
|
||||
#include "ImportMap.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsIUnicodeDecoder.h"
|
||||
@@ -18,11 +19,14 @@
|
||||
#include "nsIIncrementalStreamLoader.h"
|
||||
#include "nsURIHashKey.h"
|
||||
#include "mozilla/CORSMode.h"
|
||||
#include "mozilla/dom/ScriptKind.h"
|
||||
#include "mozilla/dom/SRIMetadata.h"
|
||||
#include "mozilla/dom/SRICheck.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/net/ReferrerPolicy.h"
|
||||
#include "mozilla/Vector.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "ResolveResult.h"
|
||||
|
||||
class nsIURI;
|
||||
|
||||
@@ -39,15 +43,6 @@ class ModuleLoadRequest;
|
||||
class ModuleScript;
|
||||
class ScriptLoadRequestList;
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Per-request data structure
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
enum class ScriptKind {
|
||||
Classic,
|
||||
Module
|
||||
};
|
||||
|
||||
/*
|
||||
* Some options used when fetching script resources. This only loosely
|
||||
* corresponds to HTML's "script fetch options".
|
||||
@@ -103,6 +98,11 @@ public:
|
||||
return mKind == ScriptKind::Module;
|
||||
}
|
||||
|
||||
bool IsImportMapRequest() const
|
||||
{
|
||||
return mKind == ScriptKind::ImportMap;
|
||||
}
|
||||
|
||||
ModuleLoadRequest* AsModuleRequest();
|
||||
|
||||
void FireScriptAvailable(nsresult aResult)
|
||||
@@ -562,6 +562,11 @@ public:
|
||||
return mPendingChildLoaders.AppendElement(aChild) != nullptr;
|
||||
}
|
||||
|
||||
nsIURI* GetBaseURI() const {
|
||||
MOZ_ASSERT(mDocument);
|
||||
return mDocument->GetBaseURI().take();
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the map of loaded modules. Called when a Document object is reused
|
||||
* for a different global.
|
||||
@@ -598,6 +603,33 @@ public:
|
||||
|
||||
nsIDocument* GetDocument() const { return mDocument; }
|
||||
|
||||
// Process <script type="importmap">
|
||||
mozilla::UniquePtr<ImportMap> ParseImportMap(ScriptLoadRequest* aRequest);
|
||||
|
||||
// Implements
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#register-an-import-map
|
||||
void RegisterImportMap(mozilla::UniquePtr<ImportMap> aImportMap);
|
||||
|
||||
ImportMap* GetImportMap() { return mImportMap.get(); }
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-integrity-metadata
|
||||
SRIMetadata ComputeModuleIntegrity(nsIURI* aURI);
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#concept-import-maps-allowed
|
||||
bool IsImportMapAllowed() const { return mImportMapsAllowed; }
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#disallow-further-import-maps
|
||||
void DisallowImportMaps() { mImportMapsAllowed = false; }
|
||||
|
||||
void ReportWarningToConsole(ScriptLoadRequest* aRequest, const char* aMessageName,
|
||||
const nsTArray<nsString>& aParams = nsTArray<nsString>()) const;
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
mozilla::UniquePtr<ImportMap> mImportMap;
|
||||
bool mImportMapsAllowed;
|
||||
|
||||
private:
|
||||
virtual ~ScriptLoader();
|
||||
|
||||
|
||||
@@ -12,13 +12,17 @@ XPIDL_MODULE = 'dom'
|
||||
EXPORTS += ['nsIScriptElement.h']
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
"ImportMap.h",
|
||||
'ModuleScript.h',
|
||||
'ResolveResult.h',
|
||||
'ScriptElement.h',
|
||||
'ScriptKind.h',
|
||||
'ScriptLoader.h',
|
||||
'ScriptSettings.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
"ImportMap.cpp",
|
||||
'ModuleLoadRequest.cpp',
|
||||
'ModuleScript.cpp',
|
||||
'ScriptElement.cpp',
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "nsIDOMHTMLScriptElement.h"
|
||||
#include "mozilla/CORSMode.h"
|
||||
#include "mozilla/net/ReferrerPolicy.h"
|
||||
#include "mozilla/dom/ScriptKind.h"
|
||||
|
||||
#define NS_ISCRIPTELEMENT_IID \
|
||||
{ 0xe60fca9b, 0x1b96, 0x4e4e, \
|
||||
@@ -39,10 +40,10 @@ public:
|
||||
mForceAsync(aFromParser == mozilla::dom::NOT_FROM_PARSER ||
|
||||
aFromParser == mozilla::dom::FROM_PARSER_FRAGMENT),
|
||||
mFrozen(false),
|
||||
mIsModule(false),
|
||||
mDefer(false),
|
||||
mAsync(false),
|
||||
mExternal(false),
|
||||
mKind(mozilla::dom::ScriptKind::Classic),
|
||||
mParserCreated(aFromParser == mozilla::dom::FROM_PARSER_FRAGMENT ?
|
||||
mozilla::dom::NOT_FROM_PARSER : aFromParser),
|
||||
// Fragment parser-created scripts (if executable)
|
||||
@@ -85,6 +86,7 @@ public:
|
||||
* Freezes the return values of the following methods so that subsequent
|
||||
* modifications to the attributes don't change execution behavior:
|
||||
* - GetScriptIsModule()
|
||||
* - GetScriptIsImportMap()
|
||||
* - GetScriptDeferred()
|
||||
* - GetScriptAsync()
|
||||
* - GetScriptURI()
|
||||
@@ -99,7 +101,16 @@ public:
|
||||
bool GetScriptIsModule()
|
||||
{
|
||||
NS_PRECONDITION(mFrozen, "Execution attributes not yet frozen: Not ready for this call!");
|
||||
return mIsModule;
|
||||
return mKind == mozilla::dom::ScriptKind::Module;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the script an import map. Currently only supported by HTML scripts.
|
||||
*/
|
||||
bool GetScriptIsImportMap()
|
||||
{
|
||||
NS_PRECONDITION(mFrozen, "Not ready for this call yet!");
|
||||
return mKind == mozilla::dom::ScriptKind::ImportMap;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -338,11 +349,6 @@ protected:
|
||||
*/
|
||||
bool mFrozen;
|
||||
|
||||
/**
|
||||
* The effective moduleness.
|
||||
*/
|
||||
bool mIsModule;
|
||||
|
||||
/**
|
||||
* The effective deferredness.
|
||||
*/
|
||||
@@ -359,6 +365,11 @@ protected:
|
||||
*/
|
||||
bool mExternal;
|
||||
|
||||
/**
|
||||
* The effective script kind.
|
||||
*/
|
||||
mozilla::dom::ScriptKind mKind;
|
||||
|
||||
/**
|
||||
* Whether this element was parser-created.
|
||||
*/
|
||||
|
||||
@@ -32,6 +32,8 @@ interface HTMLScriptElement : HTMLElement {
|
||||
attribute DOMString nonce;
|
||||
[CEReactions, SetterThrows, Pure]
|
||||
attribute DOMString integrity;
|
||||
|
||||
static boolean supports(DOMString type);
|
||||
};
|
||||
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
|
||||
|
||||
@@ -301,6 +301,7 @@ LoadContextOptions(const char* aPrefName, void* /* aClosure */)
|
||||
.setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
|
||||
.setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
|
||||
.setStreams(GetWorkerPref<bool>(NS_LITERAL_CSTRING("streams")))
|
||||
.setWeakRefs(GetWorkerPref<bool>(NS_LITERAL_CSTRING("weakrefs")))
|
||||
.setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")))
|
||||
.setArrayProtoValues(GetWorkerPref<bool>(
|
||||
NS_LITERAL_CSTRING("array_prototype_values")));
|
||||
|
||||
@@ -35,6 +35,7 @@ WORKER_SIMPLE_PREF("dom.serviceWorkers.openWindow.enabled", OpenWindowEnabled, O
|
||||
WORKER_SIMPLE_PREF("dom.storageManager.enabled", StorageManagerEnabled, STORAGEMANAGER_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.streams.enabled", StreamsEnabled, STREAMS_ENABLED)
|
||||
WORKER_SIMPLE_PREF("javascript.options.weakrefs", WeakRefsEnabled, WEAKREFS_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED)
|
||||
WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK)
|
||||
|
||||
@@ -20,10 +20,8 @@ function BigInt_toLocaleString() {
|
||||
if (locales === undefined && options === undefined) {
|
||||
// This cache only optimizes when no explicit locales and options
|
||||
// arguments were supplied.
|
||||
if (!IsRuntimeDefaultLocale(numberFormatCache.runtimeDefaultLocale)) {
|
||||
if (numberFormatCache.numberFormat === undefined)
|
||||
numberFormatCache.numberFormat = intl_NumberFormat(locales, options);
|
||||
numberFormatCache.runtimeDefaultLocale = RuntimeDefaultLocale();
|
||||
}
|
||||
numberFormat = numberFormatCache.numberFormat;
|
||||
} else {
|
||||
numberFormat = intl_NumberFormat(locales, options);
|
||||
|
||||
@@ -66,10 +66,28 @@ struct WeakCell {
|
||||
WeakCellKind kind;
|
||||
WeakRef<JSObject*> object;
|
||||
WeakRef<JS::Symbol*> symbol;
|
||||
bool enabled;
|
||||
|
||||
WeakCell() : kind(WeakCellKind::Empty), object(nullptr), symbol(nullptr) {}
|
||||
explicit WeakCell(JSObject* obj) : kind(WeakCellKind::Object), object(obj), symbol(nullptr) {}
|
||||
explicit WeakCell(JS::Symbol* sym) : kind(WeakCellKind::Symbol), object(nullptr), symbol(sym) {}
|
||||
WeakCell()
|
||||
: kind(WeakCellKind::Empty)
|
||||
, object(nullptr)
|
||||
, symbol(nullptr)
|
||||
, enabled(true)
|
||||
{}
|
||||
|
||||
explicit WeakCell(JSObject* obj, bool enabled)
|
||||
: kind(WeakCellKind::Object)
|
||||
, object(obj)
|
||||
, symbol(nullptr)
|
||||
, enabled(enabled)
|
||||
{}
|
||||
|
||||
explicit WeakCell(JS::Symbol* sym, bool enabled)
|
||||
: kind(WeakCellKind::Symbol)
|
||||
, object(nullptr)
|
||||
, symbol(sym)
|
||||
, enabled(enabled)
|
||||
{}
|
||||
|
||||
void clear() {
|
||||
kind = WeakCellKind::Empty;
|
||||
@@ -101,7 +119,9 @@ struct WeakCell {
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (IsInsideNursery(target)) {
|
||||
if (!enabled) {
|
||||
// Do nothing
|
||||
} else if (IsInsideNursery(target)) {
|
||||
TraceManuallyBarrieredEdge(trc, object.unsafeGet(), name);
|
||||
} else {
|
||||
if (trc->isMarkingTracer() && !target->asTenured().zone()->isCollecting())
|
||||
@@ -110,7 +130,7 @@ struct WeakCell {
|
||||
}
|
||||
} else if (kind == WeakCellKind::Symbol) {
|
||||
JS::Symbol* target = symbol.unbarrieredGet();
|
||||
if (target) {
|
||||
if (enabled && target) {
|
||||
if (trc->isMarkingTracer() && !target->asTenured().zone()->isCollecting())
|
||||
return;
|
||||
TraceWeakEdge(trc, &symbol, name);
|
||||
@@ -124,14 +144,16 @@ struct WeakCell {
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (IsInsideNursery(target)) {
|
||||
if (!enabled) {
|
||||
// Do nothing
|
||||
} else if (IsInsideNursery(target)) {
|
||||
TraceManuallyBarrieredEdge(trc, object.unsafeGet(), name);
|
||||
} else if (target->asTenured().zone()->isCollecting()) {
|
||||
TraceWeakEdge(trc, &object, name);
|
||||
}
|
||||
} else if (kind == WeakCellKind::Symbol) {
|
||||
JS::Symbol* target = symbol.unbarrieredGet();
|
||||
if (target && target->asTenured().zone()->isCollecting())
|
||||
if (enabled && target && target->asTenured().zone()->isCollecting())
|
||||
TraceWeakEdge(trc, &symbol, name);
|
||||
}
|
||||
}
|
||||
@@ -143,22 +165,24 @@ struct FinalizationRecord {
|
||||
WeakCell unregisterToken;
|
||||
bool active;
|
||||
bool queued;
|
||||
bool enabled;
|
||||
|
||||
FinalizationRecord(HandleValue targetValue, HandleValue heldValue,
|
||||
HandleValue unregisterTokenValue)
|
||||
HandleValue unregisterTokenValue, bool enabled)
|
||||
: heldValue(heldValue),
|
||||
active(true),
|
||||
queued(false)
|
||||
queued(false),
|
||||
enabled(enabled)
|
||||
{
|
||||
if (targetValue.isObject())
|
||||
target = WeakCell(NormalizeWeakObject(&targetValue.toObject()));
|
||||
target = WeakCell(NormalizeWeakObject(&targetValue.toObject()), enabled);
|
||||
else
|
||||
target = WeakCell(targetValue.toSymbol());
|
||||
target = WeakCell(targetValue.toSymbol(), enabled);
|
||||
|
||||
if (unregisterTokenValue.isObject())
|
||||
unregisterToken = WeakCell(NormalizeWeakObject(&unregisterTokenValue.toObject()));
|
||||
unregisterToken = WeakCell(NormalizeWeakObject(&unregisterTokenValue.toObject()), enabled);
|
||||
else if (unregisterTokenValue.isSymbol())
|
||||
unregisterToken = WeakCell(unregisterTokenValue.toSymbol());
|
||||
unregisterToken = WeakCell(unregisterTokenValue.toSymbol(), enabled);
|
||||
}
|
||||
|
||||
void trace(JSTracer* trc) {
|
||||
@@ -217,13 +241,15 @@ struct FinalizationRegistryObject::Data {
|
||||
FinalizationRecordVector records;
|
||||
FinalizationRecordVector cleanupQueue;
|
||||
bool queuedForCleanup;
|
||||
bool enabled;
|
||||
|
||||
explicit Data(JSObject* cleanupCallback)
|
||||
explicit Data(JSObject* cleanupCallback, bool enabled)
|
||||
: cleanupCallback(cleanupCallback),
|
||||
cleanupJob(nullptr),
|
||||
records(SystemAllocPolicy()),
|
||||
cleanupQueue(SystemAllocPolicy()),
|
||||
queuedForCleanup(false)
|
||||
queuedForCleanup(false),
|
||||
enabled(enabled)
|
||||
{}
|
||||
|
||||
~Data() {
|
||||
@@ -232,11 +258,13 @@ struct FinalizationRegistryObject::Data {
|
||||
}
|
||||
|
||||
void trace(JSTracer* trc) {
|
||||
JS::TraceEdge(trc, &cleanupCallback, "FinalizationRegistry cleanup callback");
|
||||
if (cleanupJob.unbarrieredGet())
|
||||
JS::TraceEdge(trc, &cleanupJob, "FinalizationRegistry cleanup job");
|
||||
for (size_t i = 0; i < records.length(); i++)
|
||||
records[i]->trace(trc);
|
||||
if (enabled) {
|
||||
JS::TraceEdge(trc, &cleanupCallback, "FinalizationRegistry cleanup callback");
|
||||
if (cleanupJob.unbarrieredGet())
|
||||
JS::TraceEdge(trc, &cleanupJob, "FinalizationRegistry cleanup job");
|
||||
for (size_t i = 0; i < records.length(); i++)
|
||||
records[i]->trace(trc);
|
||||
}
|
||||
}
|
||||
|
||||
bool appendRecord(FinalizationRecord* record) {
|
||||
@@ -317,7 +345,7 @@ FinalizationRegistry_register_impl(JSContext* cx, const CallArgs& args)
|
||||
unregisterToken.setUndefined();
|
||||
}
|
||||
|
||||
FinalizationRecord* record = cx->new_<FinalizationRecord>(target, heldValue, unregisterToken);
|
||||
FinalizationRecord* record = cx->new_<FinalizationRecord>(target, heldValue, unregisterToken, cx->options().weakRefs());
|
||||
if (!record)
|
||||
return false;
|
||||
|
||||
@@ -401,7 +429,7 @@ FinalizationRegistryObject::create(JSContext* cx, HandleObject cleanupCallback,
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
Data* data = cx->new_<Data>(cleanupCallback);
|
||||
Data* data = cx->new_<Data>(cleanupCallback, cx->options().weakRefs());
|
||||
if (!data)
|
||||
return nullptr;
|
||||
|
||||
|
||||
@@ -1155,8 +1155,6 @@ ModuleObject::execute(JSContext* cx, HandleModuleObject self, MutableHandleValue
|
||||
evalPromise = &promiseObj->as<PromiseObject>();
|
||||
self->setEvaluationPromise(evalPromise);
|
||||
}
|
||||
} else {
|
||||
self->setReservedSlot(ScriptSlot, UndefinedValue());
|
||||
}
|
||||
|
||||
RootedModuleEnvironmentObject scope(cx, self->environment());
|
||||
@@ -1184,9 +1182,10 @@ ModuleObject::execute(JSContext* cx, HandleModuleObject self, MutableHandleValue
|
||||
RootedValue undefinedValue(cx, UndefinedValue());
|
||||
if (!PromiseObject::resolve(cx, evalPromise, undefinedValue))
|
||||
return false;
|
||||
self->setReservedSlot(ScriptSlot, UndefinedValue());
|
||||
rval.setObject(*evalPromise);
|
||||
}
|
||||
// Module finished and will not be resumed; release the script.
|
||||
self->setReservedSlot(ScriptSlot, UndefinedValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -146,9 +146,9 @@ WeakRefObject::create(JSContext* cx, HandleValue target, HandleObject proto /* =
|
||||
|
||||
Referent* data;
|
||||
if (target.isObject())
|
||||
data = cx->new_<Referent>(&target.toObject());
|
||||
data = cx->new_<Referent>(&target.toObject(), cx->options().weakRefs());
|
||||
else
|
||||
data = cx->new_<Referent>(target.toSymbol());
|
||||
data = cx->new_<Referent>(target.toSymbol(), cx->options().weakRefs());
|
||||
|
||||
if (!data)
|
||||
return nullptr;
|
||||
@@ -216,7 +216,9 @@ WeakRefObject::trace(JSTracer* trc, JSObject* obj)
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (IsInsideNursery(target)) {
|
||||
if (!data->enabled) {
|
||||
// Pref disabled, don't do anything.
|
||||
} else if (IsInsideNursery(target)) {
|
||||
// Weak edges must be tenured; trace strongly while referent is in the nursery.
|
||||
TraceManuallyBarrieredEdge(trc, data->objectTarget.unsafeGet(),
|
||||
"WeakRef nursery referent");
|
||||
@@ -226,7 +228,7 @@ WeakRefObject::trace(JSTracer* trc, JSObject* obj)
|
||||
} else {
|
||||
MOZ_ASSERT(data->isSymbol());
|
||||
JS::Symbol* target = data->symbolTarget.unbarrieredGet();
|
||||
if (target)
|
||||
if (data->enabled && target)
|
||||
TraceWeakEdge(trc, &data->symbolTarget, "WeakRef symbol referent");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,17 +22,27 @@ class WeakRefObject : public NativeObject
|
||||
Symbol
|
||||
};
|
||||
|
||||
explicit Referent(JSObject* obj)
|
||||
: kind(Kind::Object), objectTarget(obj), symbolTarget(nullptr) {}
|
||||
explicit Referent(JS::Symbol* sym)
|
||||
: kind(Kind::Symbol), objectTarget(nullptr), symbolTarget(sym) {}
|
||||
|
||||
explicit Referent(JSObject* obj, bool enabled)
|
||||
: kind(Kind::Object)
|
||||
, objectTarget(obj)
|
||||
, symbolTarget(nullptr)
|
||||
, enabled(enabled)
|
||||
{}
|
||||
|
||||
explicit Referent(JS::Symbol* sym, bool enabled)
|
||||
: kind(Kind::Symbol)
|
||||
, objectTarget(nullptr)
|
||||
, symbolTarget(sym)
|
||||
, enabled(enabled)
|
||||
{}
|
||||
|
||||
bool isObject() const { return kind == Kind::Object; }
|
||||
bool isSymbol() const { return kind == Kind::Symbol; }
|
||||
|
||||
Kind kind;
|
||||
WeakRef<JSObject*> objectTarget;
|
||||
WeakRef<JS::Symbol*> symbolTarget;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static const Class class_;
|
||||
|
||||
@@ -612,6 +612,13 @@ MSG_DEF(JSMSG_NO_DYNAMIC_IMPORT, 0, JSEXN_SYNTAXERR, "dynamic module imp
|
||||
MSG_DEF(JSMSG_DYNAMIC_IMPORT_FAILED, 0, JSEXN_TYPEERR, "error loading dynamically imported module")
|
||||
MSG_DEF(JSMSG_BAD_MODULE_SPECIFIER, 1, JSEXN_TYPEERR, "error resolving module specifier '{0}'")
|
||||
|
||||
// Import maps
|
||||
MSG_DEF(JSMSG_IMPORT_MAPS_NOT_A_MAP, 0, JSEXN_TYPEERR, "the top-level value needs to be a JSON object")
|
||||
MSG_DEF(JSMSG_IMPORT_MAPS_IMPORTS_NOT_A_MAP, 0, JSEXN_TYPEERR, "the imports top-level key needs to be a JSON object")
|
||||
MSG_DEF(JSMSG_IMPORT_MAPS_SCOPES_NOT_A_MAP, 0, JSEXN_TYPEERR, "the scopes top-level key needs to be a JSON object")
|
||||
MSG_DEF(JSMSG_IMPORT_MAPS_SCOPE_VALUE_NOT_A_MAP, 1, JSEXN_TYPEERR, "the value of the scope with prefix '{0}' needs to be a JSON object")
|
||||
MSG_DEF(JSMSG_IMPORT_MAPS_INTEGRITY_NOT_A_MAP, 0, JSEXN_TYPEERR, "The value of the \"integrity\" top-level key in an import map needs to be a JSON object")
|
||||
|
||||
// Promise
|
||||
MSG_DEF(JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF, 0, JSEXN_TYPEERR, "A promise cannot be resolved with itself.")
|
||||
MSG_DEF(JSMSG_PROMISE_CAPABILITY_HAS_SOMETHING_ALREADY, 0, JSEXN_TYPEERR, "GetCapabilitiesExecutor function already invoked with non-undefined values.")
|
||||
|
||||
+1
-1
@@ -4821,7 +4821,7 @@ JS::GetModuleScript(JSContext* cx, JS::HandleObject moduleArg)
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, moduleArg);
|
||||
return moduleArg->as<ModuleObject>().script();
|
||||
return moduleArg->as<ModuleObject>().maybeScript();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject*)
|
||||
|
||||
+6
-3
@@ -999,7 +999,8 @@ class JS_PUBLIC_API(ContextOptions) {
|
||||
strictMode_(false),
|
||||
extraWarnings_(false),
|
||||
arrayProtoValues_(true),
|
||||
streams_(false)
|
||||
streams_(false),
|
||||
weakRefs_(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1139,12 +1140,13 @@ class JS_PUBLIC_API(ContextOptions) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool weakRefs() const { return true; }
|
||||
bool weakRefs() const { return weakRefs_; }
|
||||
ContextOptions& setWeakRefs(bool flag) {
|
||||
(void) flag;
|
||||
weakRefs_ = flag;
|
||||
return *this;
|
||||
}
|
||||
ContextOptions& toggleWeakRefs() {
|
||||
weakRefs_ = !weakRefs_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -1165,6 +1167,7 @@ class JS_PUBLIC_API(ContextOptions) {
|
||||
bool extraWarnings_ : 1;
|
||||
bool arrayProtoValues_ : 1;
|
||||
bool streams_ : 1;
|
||||
bool weakRefs_ : 1;
|
||||
};
|
||||
|
||||
JS_PUBLIC_API(ContextOptions&)
|
||||
|
||||
@@ -925,6 +925,14 @@ js::IsWrappedArrayConstructor(JSContext* cx, const Value& v, bool* result)
|
||||
static bool
|
||||
IsArraySpecies(JSContext* cx, HandleObject origArray)
|
||||
{
|
||||
// Cache the common native-array case. Keep the existing lookup for
|
||||
// unboxed arrays, proxies, and arrays with nonstandard constructors.
|
||||
if (origArray->is<ArrayObject>() &&
|
||||
cx->compartment()->arraySpeciesLookup.tryOptimizeArray(cx, &origArray->as<ArrayObject>()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedValue ctor(cx);
|
||||
if (!GetPropertyPure(cx, origArray, NameToId(cx->names().constructor), ctor.address()))
|
||||
return false;
|
||||
@@ -3846,3 +3854,154 @@ js::ArrayInfo(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
js::ArraySpeciesLookup::initialize(JSContext* cx)
|
||||
{
|
||||
MOZ_ASSERT(state_ == State::Uninitialized);
|
||||
|
||||
// Get the canonical Array.prototype.
|
||||
NativeObject* arrayProto = cx->global()->maybeGetArrayPrototype();
|
||||
|
||||
// Leave the cache uninitialized if the Array class itself is not yet
|
||||
// initialized.
|
||||
if (!arrayProto)
|
||||
return;
|
||||
|
||||
// Get the canonical Array constructor.
|
||||
const Value& arrayCtorValue = cx->global()->getConstructor(JSProto_Array);
|
||||
MOZ_ASSERT(arrayCtorValue.isObject(),
|
||||
"The Array constructor is initialized iff Array.prototype is initialized");
|
||||
JSFunction* arrayCtor = &arrayCtorValue.toObject().as<JSFunction>();
|
||||
|
||||
// Shortcut returns below means Array[@@species] will never be
|
||||
// optimizable, set to disabled now, and clear it later when we succeed.
|
||||
state_ = State::Disabled;
|
||||
|
||||
// Look up Array.prototype.constructor and ensure it's a data property.
|
||||
Shape* ctorShape = arrayProto->lookup(cx, NameToId(cx->names().constructor));
|
||||
if (!ctorShape || !ctorShape->hasSlot() || !ctorShape->hasDefaultGetter())
|
||||
return;
|
||||
|
||||
// Get the referred value, and ensure it holds the canonical Array
|
||||
// constructor.
|
||||
JSFunction* ctorFun;
|
||||
if (!IsFunctionObject(arrayProto->getSlot(ctorShape->slot()), &ctorFun))
|
||||
return;
|
||||
if (ctorFun != arrayCtor)
|
||||
return;
|
||||
|
||||
// Look up the '@@species' value on Array
|
||||
Shape* speciesShape = arrayCtor->lookup(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().species));
|
||||
if (!speciesShape || !speciesShape->hasGetterValue())
|
||||
return;
|
||||
|
||||
// Get the referred value, ensure it holds the canonical Array[@@species]
|
||||
// function.
|
||||
JSFunction* speciesFun;
|
||||
if (!IsFunctionObject(speciesShape->getterValue(), &speciesFun))
|
||||
return;
|
||||
if (!IsSelfHostedFunctionWithName(speciesFun, cx->names().ArraySpecies))
|
||||
return;
|
||||
|
||||
// Store raw pointers below. This is okay to do here, because all objects
|
||||
// are in the tenured heap.
|
||||
MOZ_ASSERT(!IsInsideNursery(arrayProto));
|
||||
MOZ_ASSERT(!IsInsideNursery(arrayCtor));
|
||||
MOZ_ASSERT(!IsInsideNursery(arrayCtor->lastProperty()));
|
||||
MOZ_ASSERT(!IsInsideNursery(speciesShape));
|
||||
MOZ_ASSERT(!IsInsideNursery(speciesFun));
|
||||
MOZ_ASSERT(!IsInsideNursery(arrayProto->lastProperty()));
|
||||
|
||||
state_ = State::Initialized;
|
||||
arrayProto_ = arrayProto;
|
||||
arrayConstructor_ = arrayCtor;
|
||||
arrayConstructorShape_ = arrayCtor->lastProperty();
|
||||
#ifdef DEBUG
|
||||
arraySpeciesShape_ = speciesShape;
|
||||
canonicalSpeciesFunc_ = speciesFun;
|
||||
#endif
|
||||
arrayProtoShape_ = arrayProto->lastProperty();
|
||||
arrayProtoConstructorSlot_ = ctorShape->slot();
|
||||
}
|
||||
|
||||
void
|
||||
js::ArraySpeciesLookup::reset()
|
||||
{
|
||||
state_ = State::Uninitialized;
|
||||
arrayProto_ = nullptr;
|
||||
arrayConstructor_ = nullptr;
|
||||
arrayConstructorShape_ = nullptr;
|
||||
#ifdef DEBUG
|
||||
arraySpeciesShape_ = nullptr;
|
||||
canonicalSpeciesFunc_ = nullptr;
|
||||
#endif
|
||||
arrayProtoShape_ = nullptr;
|
||||
arrayProtoConstructorSlot_ = -1;
|
||||
}
|
||||
|
||||
bool
|
||||
js::ArraySpeciesLookup::isArrayStateStillSane()
|
||||
{
|
||||
MOZ_ASSERT(state_ == State::Initialized);
|
||||
|
||||
// Ensure that Array.prototype still has the expected shape.
|
||||
if (arrayProto_->lastProperty() != arrayProtoShape_)
|
||||
return false;
|
||||
|
||||
// Ensure that Array.prototype.constructor contains the canonical Array
|
||||
// constructor function.
|
||||
if (arrayProto_->getSlot(arrayProtoConstructorSlot_) != ObjectValue(*arrayConstructor_))
|
||||
return false;
|
||||
|
||||
// Ensure that Array still has the expected shape.
|
||||
if (arrayConstructor_->lastProperty() != arrayConstructorShape_)
|
||||
return false;
|
||||
|
||||
// Ensure the species getter contains the canonical @@species function.
|
||||
// Note: This is currently guaranteed to be always true, because modifying
|
||||
// the getter property implies a new shape is generated. If this ever
|
||||
// changes, convert this assertion into an if-statement.
|
||||
MOZ_ASSERT(arraySpeciesShape_->getterObject() == canonicalSpeciesFunc_);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::ArraySpeciesLookup::tryOptimizeArray(JSContext* cx, ArrayObject* array)
|
||||
{
|
||||
// Arrays with only their non-deletable "length" property cannot shadow
|
||||
// Array.prototype.constructor. For arrays with extra properties, check
|
||||
// for an own constructor without invoking getters. Unrelated properties
|
||||
// can still use the cached prototype and species checks below.
|
||||
Shape* shape = array->lastProperty();
|
||||
if (shape->previous() && !shape->previous()->isEmptyShape()) {
|
||||
if (array->lookupPure(cx->names().constructor))
|
||||
return false;
|
||||
} else {
|
||||
MOZ_ASSERT(JSID_IS_ATOM(shape->propidRaw(), cx->names().length));
|
||||
}
|
||||
|
||||
if (state_ == State::Uninitialized) {
|
||||
// If the cache is not initialized, initialize it.
|
||||
initialize(cx);
|
||||
} else if (state_ == State::Initialized && !isArrayStateStillSane()) {
|
||||
// Otherwise, if the array state is no longer sane, reinitialize.
|
||||
reset();
|
||||
initialize(cx);
|
||||
}
|
||||
|
||||
// If the cache is disabled or still uninitialized, don't bother trying to
|
||||
// optimize.
|
||||
if (state_ != State::Initialized)
|
||||
return false;
|
||||
|
||||
// By the time we get here, we should have a sane array state.
|
||||
MOZ_ASSERT(isArrayStateStillSane());
|
||||
|
||||
// Ensure |array|'s prototype is the actual Array.prototype.
|
||||
if (array->staticPrototype() != arrayProto_)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -219,6 +219,85 @@ array_construct(JSContext* cx, unsigned argc, Value* vp);
|
||||
extern bool
|
||||
IsWrappedArrayConstructor(JSContext* cx, const Value& v, bool* result);
|
||||
|
||||
class MOZ_NON_TEMPORARY_CLASS ArraySpeciesLookup final
|
||||
{
|
||||
/*
|
||||
* An ArraySpeciesLookup holds the following:
|
||||
*
|
||||
* Array.prototype (arrayProto_)
|
||||
* To ensure that the incoming array has the standard proto.
|
||||
*
|
||||
* Array.prototype's shape (arrayProtoShape_)
|
||||
* To ensure that Array.prototype has not been modified.
|
||||
*
|
||||
* Array (arrayConstructor_)
|
||||
* Array's shape (arrayConstructorShape_)
|
||||
* To ensure that Array has not been modified.
|
||||
*
|
||||
* Array.prototype's slot number for constructor (arrayProtoConstructorSlot_)
|
||||
* To quickly retrieve and ensure that the Array constructor
|
||||
* stored in the slot has not changed.
|
||||
*
|
||||
* Array's shape for the @@species getter. (arraySpeciesShape_)
|
||||
* Array's canonical value for @@species (canonicalSpeciesFunc_)
|
||||
* To quickly retrieve and ensure that the @@species getter for Array
|
||||
* has not changed.
|
||||
*/
|
||||
|
||||
// Pointer to canonical Array.prototype and Array.
|
||||
NativeObject* arrayProto_;
|
||||
NativeObject* arrayConstructor_;
|
||||
|
||||
// Shape of matching Array, and slot containing the @@species
|
||||
// property, and the canonical value.
|
||||
Shape* arrayConstructorShape_;
|
||||
#ifdef DEBUG
|
||||
Shape* arraySpeciesShape_;
|
||||
JSFunction* canonicalSpeciesFunc_;
|
||||
#endif
|
||||
|
||||
// Shape of matching Array.prototype object, and slot containing the
|
||||
// constructor for it.
|
||||
Shape* arrayProtoShape_;
|
||||
uint32_t arrayProtoConstructorSlot_;
|
||||
|
||||
enum class State : uint8_t {
|
||||
// Flags marking the lazy initialization of the above fields.
|
||||
Uninitialized,
|
||||
Initialized,
|
||||
|
||||
// The disabled flag is set when we don't want to try optimizing
|
||||
// anymore because core objects were changed.
|
||||
Disabled
|
||||
};
|
||||
|
||||
State state_;
|
||||
|
||||
// Initialize the internal fields.
|
||||
void initialize(JSContext* cx);
|
||||
|
||||
// Reset the cache.
|
||||
void reset();
|
||||
|
||||
// Check if the global array-related objects have not been messed with
|
||||
// in a way that would disable this cache.
|
||||
bool isArrayStateStillSane();
|
||||
|
||||
public:
|
||||
ArraySpeciesLookup() {
|
||||
reset();
|
||||
}
|
||||
|
||||
// Try to optimize the @@species lookup for an array.
|
||||
bool tryOptimizeArray(JSContext* cx, ArrayObject* array);
|
||||
|
||||
// Purge the cache and all info associated with it.
|
||||
void purge() {
|
||||
if (state_ == State::Initialized)
|
||||
reset();
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* jsarray_h */
|
||||
@@ -62,6 +62,7 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
|
||||
allocationMetadataBuilder(nullptr),
|
||||
lastAnimationTime(0),
|
||||
regExps(zone),
|
||||
arraySpeciesLookup(),
|
||||
globalWriteBarriered(0),
|
||||
detachedTypedObjects(0),
|
||||
objectMetadataState(ImmediateMetadata()),
|
||||
@@ -963,6 +964,7 @@ JSCompartment::purge()
|
||||
{
|
||||
dtoaCache.purge();
|
||||
promiseLookup.purge();
|
||||
arraySpeciesLookup.purge();
|
||||
lastCachedNativeIterator = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -447,6 +447,8 @@ struct JSCompartment
|
||||
|
||||
js::RegExpCompartment regExps;
|
||||
|
||||
js::ArraySpeciesLookup arraySpeciesLookup;
|
||||
|
||||
/*
|
||||
* For generational GC, record whether a write barrier has added this
|
||||
* compartment's global to the store buffer since the last minor GC.
|
||||
|
||||
@@ -1442,6 +1442,7 @@ ReloadPrefsCallback(const char* pref, void* data)
|
||||
bool extraWarnings = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict");
|
||||
|
||||
bool streams = Preferences::GetBool(JS_OPTIONS_DOT_STR "streams");
|
||||
bool weakRefs = Preferences::GetBool(JS_OPTIONS_DOT_STR "weakrefs");
|
||||
|
||||
bool unboxedObjects = Preferences::GetBool(JS_OPTIONS_DOT_STR "unboxed_objects");
|
||||
|
||||
@@ -1468,7 +1469,8 @@ ReloadPrefsCallback(const char* pref, void* data)
|
||||
.setWerror(werror)
|
||||
.setExtraWarnings(extraWarnings)
|
||||
.setArrayProtoValues(arrayProtoValues)
|
||||
.setStreams(streams);
|
||||
.setStreams(streams)
|
||||
.setWeakRefs(weakRefs);
|
||||
|
||||
JS_SetParallelParsingEnabled(cx, parallelParsing);
|
||||
JS_SetOffthreadIonCompilationEnabled(cx, offthreadIonCompilation);
|
||||
|
||||
@@ -220,6 +220,12 @@ public:
|
||||
return mImpl.unwrapErr();
|
||||
}
|
||||
|
||||
|
||||
GenericErrorResult<E> propagateErr() const {
|
||||
MOZ_ASSERT(isErr());
|
||||
return GenericErrorResult<E>(mImpl.unwrapErr());
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a function V -> W over this result's success variant. If this result is
|
||||
* an error, do not invoke the function and return a copy of the error.
|
||||
@@ -276,6 +282,13 @@ MakeGenericErrorResult(E&& aErrorValue)
|
||||
return GenericErrorResult<E>(aErrorValue);
|
||||
}
|
||||
|
||||
|
||||
template <typename E>
|
||||
inline GenericErrorResult<E> Err(E&& aErrorValue)
|
||||
{
|
||||
return GenericErrorResult<E>(aErrorValue);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
/**
|
||||
|
||||
@@ -1314,6 +1314,9 @@ pref("javascript.options.dynamicImport", true);
|
||||
// Streams API
|
||||
pref("javascript.options.streams", true);
|
||||
|
||||
// WeakRefs API
|
||||
pref("javascript.options.weakrefs", true);
|
||||
|
||||
// advanced prefs
|
||||
pref("advanced.mailftp", false);
|
||||
pref("image.animation_mode", "normal");
|
||||
|
||||
Reference in new issue
Block a user