mirror of
https://github.com/Basilisk-Development-Team/UXP-mirror.git
synced 2026-11-04 08:52:01 -05:00
Compare commits
40
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6b9d9cbea | ||
|
|
b863bd2edc | ||
|
|
a8a397fbce | ||
|
|
a89597b5db | ||
|
|
074d194776 | ||
|
|
76d62cfc38 | ||
|
|
eefaee9064 | ||
|
|
1864bbec2a | ||
|
|
5771487106 | ||
|
|
887fface56 | ||
|
|
4782e8dfa2 | ||
|
|
bd061ac043 | ||
|
|
31a64578e2 | ||
|
|
85dc118aac | ||
|
|
2f3cf3c8cf | ||
|
|
241f06bc53 | ||
|
|
ecb827c5c4 | ||
|
|
fa5aba4c0f | ||
|
|
b6d909ad17 | ||
|
|
6bc5748b00 | ||
|
|
3abb8e28ad | ||
|
|
18df01eb54 | ||
|
|
8aa9355535 | ||
|
|
511ac54cab | ||
|
|
6d2e301d90 | ||
|
|
2d8013526a | ||
|
|
d86f49ba59 | ||
|
|
870fd86e13 | ||
|
|
13f505539c | ||
|
|
5262e01a4e | ||
|
|
4aeaea9195 | ||
|
|
5ef801fdc5 | ||
|
|
0ba1d49ae5 | ||
|
|
e8a6ee355f | ||
|
|
4694c60964 | ||
|
|
74ebcea409 | ||
|
|
c732425e1a | ||
|
|
51468e998c | ||
|
|
77746f1d90 | ||
|
|
cb0208163f |
No files matched your search
@@ -161,6 +161,8 @@ def old_configure_options(*options):
|
||||
'--enable-b2g-camera',
|
||||
'--enable-b2g-ril',
|
||||
'--enable-bundled-fonts',
|
||||
'--enable-chrome-compression',
|
||||
'--enable-chrome-format',
|
||||
'--enable-clang-plugin',
|
||||
'--enable-content-sandbox',
|
||||
'--enable-cookies',
|
||||
@@ -262,6 +264,7 @@ def old_configure_options(*options):
|
||||
'--with-app-name',
|
||||
'--with-arch',
|
||||
'--with-branding',
|
||||
'--with-chrome-name',
|
||||
'--with-crashreporter-enable-percent',
|
||||
'--with-cross-lib',
|
||||
'--with-debug-label',
|
||||
|
||||
@@ -86,6 +86,7 @@ included_inclnames_to_ignore = set([
|
||||
'unicode/udat.h', # ICU
|
||||
'unicode/udatpg.h', # ICU
|
||||
'unicode/uenum.h', # ICU
|
||||
'unicode/uniset.h', # ICU
|
||||
'unicode/unorm.h', # ICU
|
||||
'unicode/unum.h', # ICU
|
||||
'unicode/unumsys.h', # ICU
|
||||
|
||||
+1
-1
@@ -216,7 +216,7 @@ endif # NO_PROFILE_GUIDED_OPTIMIZE
|
||||
|
||||
MAKE_JARS_FLAGS = \
|
||||
-t $(topsrcdir) \
|
||||
-f $(MOZ_JAR_MAKER_FILE_FORMAT) \
|
||||
-f $(MOZ_CHROME_FILE_FORMAT) \
|
||||
$(NULL)
|
||||
|
||||
ifdef USE_EXTENSION_MANIFEST
|
||||
|
||||
@@ -526,7 +526,6 @@ hardware/lights.h
|
||||
hardware/power.h
|
||||
hardware_legacy/power.h
|
||||
hardware_legacy/uevent.h
|
||||
hardware_legacy/vibrator.h
|
||||
#endif
|
||||
HIToolbox/HIToolbox.h
|
||||
hlink.h
|
||||
|
||||
@@ -22,7 +22,7 @@ REF_MANIFEST = $(if $(USE_EXTENSION_MANIFEST),chrome.manifest,test.manifest)
|
||||
|
||||
check-%::
|
||||
if test -d $(FINAL_TARGET); then rm -rf $(FINAL_TARGET); fi;
|
||||
$(MAKE) realchrome MOZ_JAR_MAKER_FILE_FORMAT=$*
|
||||
$(MAKE) realchrome MOZ_CHROME_FILE_FORMAT=$*
|
||||
@echo 'Comparing manifests...'
|
||||
@if ! sort $(MY_MANIFEST) | diff --text -U 0 $(srcdir)/../$(REF_MANIFEST).$* - ; then \
|
||||
echo 'TEST-UNEXPECTED-FAIL | config/tests/$(REF_MANIFEST).$* | differing content in manifest!' ; \
|
||||
|
||||
@@ -94,7 +94,7 @@ exports.menuitems = [
|
||||
},
|
||||
key: {
|
||||
id: "browserToolbox",
|
||||
modifiers: "accel,alt,shift",
|
||||
modifiers: isMac ? "accel,alt" : "accel,shift",
|
||||
keytext: true
|
||||
}
|
||||
},
|
||||
|
||||
+1
-192
@@ -97,11 +97,6 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
static bool sVibratorEnabled = false;
|
||||
static uint32_t sMaxVibrateMS = 0;
|
||||
static uint32_t sMaxVibrateListLen = 0;
|
||||
static const char* kVibrationPermissionType = "vibration";
|
||||
|
||||
static void
|
||||
AddPermission(nsIPrincipal* aPrincipal, const char* aType, uint32_t aPermission,
|
||||
uint32_t aExpireType, int64_t aExpireTime)
|
||||
@@ -152,12 +147,7 @@ GetPermission(nsIPrincipal* aPrincipal, const char* aType)
|
||||
void
|
||||
Navigator::Init()
|
||||
{
|
||||
Preferences::AddBoolVarCache(&sVibratorEnabled,
|
||||
"dom.vibrator.enabled", true);
|
||||
Preferences::AddUintVarCache(&sMaxVibrateMS,
|
||||
"dom.vibrator.max_vibrate_ms", 10000);
|
||||
Preferences::AddUintVarCache(&sMaxVibrateListLen,
|
||||
"dom.vibrator.max_vibrate_list_len", 128);
|
||||
// Add any Preferences::Add*VarCache(&sPref, "pref", default) here if needed.
|
||||
}
|
||||
|
||||
Navigator::Navigator(nsPIDOMWindowInner* aWindow)
|
||||
@@ -705,82 +695,6 @@ Navigator::RefreshMIMEArray()
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class VibrateWindowListener : public nsIDOMEventListener
|
||||
{
|
||||
public:
|
||||
VibrateWindowListener(nsPIDOMWindowInner* aWindow, nsIDocument* aDocument)
|
||||
{
|
||||
mWindow = do_GetWeakReference(aWindow);
|
||||
mDocument = do_GetWeakReference(aDocument);
|
||||
|
||||
NS_NAMED_LITERAL_STRING(visibilitychange, "visibilitychange");
|
||||
aDocument->AddSystemEventListener(visibilitychange,
|
||||
this, /* listener */
|
||||
true, /* use capture */
|
||||
false /* wants untrusted */);
|
||||
}
|
||||
|
||||
void RemoveListener();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
private:
|
||||
virtual ~VibrateWindowListener()
|
||||
{
|
||||
}
|
||||
|
||||
nsWeakPtr mWindow;
|
||||
nsWeakPtr mDocument;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(VibrateWindowListener, nsIDOMEventListener)
|
||||
|
||||
StaticRefPtr<VibrateWindowListener> gVibrateWindowListener;
|
||||
|
||||
static bool
|
||||
MayVibrate(nsIDocument* doc) {
|
||||
// Hidden documents cannot start or stop a vibration.
|
||||
return (doc && !doc->Hidden());
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
VibrateWindowListener::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
nsCOMPtr<nsIDocument> doc =
|
||||
do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
|
||||
|
||||
if (!MayVibrate(doc)) {
|
||||
// It's important that we call CancelVibrate(), not Vibrate() with an
|
||||
// empty list, because Vibrate() will fail if we're no longer focused, but
|
||||
// CancelVibrate() will succeed, so long as nobody else has started a new
|
||||
// vibration pattern.
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
|
||||
hal::CancelVibrate(window);
|
||||
RemoveListener();
|
||||
gVibrateWindowListener = nullptr;
|
||||
// Careful: The line above might have deleted |this|!
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
VibrateWindowListener::RemoveListener()
|
||||
{
|
||||
nsCOMPtr<EventTarget> target = do_QueryReferent(mDocument);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
NS_NAMED_LITERAL_STRING(visibilitychange, "visibilitychange");
|
||||
target->RemoveSystemEventListener(visibilitychange, this,
|
||||
true /* use capture */);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void
|
||||
Navigator::AddIdleObserver(MozIdleObserver& aIdleObserver, ErrorResult& aRv)
|
||||
{
|
||||
@@ -809,111 +723,6 @@ Navigator::RemoveIdleObserver(MozIdleObserver& aIdleObserver, ErrorResult& aRv)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Navigator::SetVibrationPermission(bool aPermitted, bool aPersistent)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsTArray<uint32_t> pattern;
|
||||
pattern.SwapElements(mRequestedVibrationPattern);
|
||||
|
||||
if (!mWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
|
||||
|
||||
if (!MayVibrate(doc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aPermitted) {
|
||||
// Add a listener to cancel the vibration if the document becomes hidden,
|
||||
// and remove the old visibility listener, if there was one.
|
||||
if (!gVibrateWindowListener) {
|
||||
// If gVibrateWindowListener is null, this is the first time we've vibrated,
|
||||
// and we need to register a listener to clear gVibrateWindowListener on
|
||||
// shutdown.
|
||||
ClearOnShutdown(&gVibrateWindowListener);
|
||||
} else {
|
||||
gVibrateWindowListener->RemoveListener();
|
||||
}
|
||||
gVibrateWindowListener = new VibrateWindowListener(mWindow, doc);
|
||||
hal::Vibrate(pattern, mWindow);
|
||||
}
|
||||
|
||||
if (aPersistent) {
|
||||
AddPermission(doc->NodePrincipal(), kVibrationPermissionType,
|
||||
aPermitted ? nsIPermissionManager::ALLOW_ACTION :
|
||||
nsIPermissionManager::DENY_ACTION,
|
||||
nsIPermissionManager::EXPIRE_SESSION, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Navigator::Vibrate(uint32_t aDuration)
|
||||
{
|
||||
AutoTArray<uint32_t, 1> pattern;
|
||||
pattern.AppendElement(aDuration);
|
||||
return Vibrate(pattern);
|
||||
}
|
||||
|
||||
bool
|
||||
Navigator::Vibrate(const nsTArray<uint32_t>& aPattern)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mWindow) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
|
||||
|
||||
if (!MayVibrate(doc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsTArray<uint32_t> pattern(aPattern);
|
||||
|
||||
if (pattern.Length() > sMaxVibrateListLen) {
|
||||
pattern.SetLength(sMaxVibrateListLen);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < pattern.Length(); ++i) {
|
||||
pattern[i] = std::min(sMaxVibrateMS, pattern[i]);
|
||||
}
|
||||
|
||||
// The spec says we check sVibratorEnabled after we've done the sanity
|
||||
// checking on the pattern.
|
||||
if (!sVibratorEnabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
mRequestedVibrationPattern.SwapElements(pattern);
|
||||
uint32_t permission = GetPermission(mWindow, kVibrationPermissionType);
|
||||
|
||||
if (permission == nsIPermissionManager::ALLOW_ACTION ||
|
||||
mRequestedVibrationPattern.IsEmpty() ||
|
||||
(mRequestedVibrationPattern.Length() == 1 &&
|
||||
mRequestedVibrationPattern[0] == 0)) {
|
||||
// Always allow cancelling vibration and respect session permissions.
|
||||
SetVibrationPermission(true /* permitted */, false /* persistent */);
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (!obs || permission == nsIPermissionManager::DENY_ACTION) {
|
||||
// Abort without observer service or on denied session permission.
|
||||
SetVibrationPermission(false /* permitted */, false /* persistent */);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Request user permission.
|
||||
obs->NotifyObservers(ToSupports(this), "Vibration:Request", nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
// Pointer Events interface
|
||||
//*****************************************************************************
|
||||
|
||||
@@ -149,9 +149,6 @@ public:
|
||||
// NavigatorBinding::ClearCachedUserAgentValue(this);
|
||||
void ClearUserAgentCache();
|
||||
|
||||
bool Vibrate(uint32_t aDuration);
|
||||
bool Vibrate(const nsTArray<uint32_t>& aDuration);
|
||||
void SetVibrationPermission(bool aPermitted, bool aPersistent);
|
||||
uint32_t MaxTouchPoints();
|
||||
void GetAppCodeName(nsString& aAppCodeName, ErrorResult& aRv)
|
||||
{
|
||||
|
||||
@@ -8527,25 +8527,6 @@ nsContentUtils::PushEnabled(JSContext* aCx, JSObject* aObj)
|
||||
return workerPrivate->PushEnabled();
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
nsContentUtils::StreamsEnabled(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return Preferences::GetBool("dom.streams.enabled", false);
|
||||
}
|
||||
|
||||
using namespace workers;
|
||||
|
||||
// Otherwise, check the pref via the WorkerPrivate
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
|
||||
if (!workerPrivate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return workerPrivate->StreamsEnabled();
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
nsContentUtils::IsNonSubresourceRequest(nsIChannel* aChannel)
|
||||
|
||||
@@ -2622,8 +2622,6 @@ public:
|
||||
static mozilla::net::ReferrerPolicy GetReferrerPolicyFromHeader(const nsAString& aHeader);
|
||||
|
||||
static bool PushEnabled(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
static bool StreamsEnabled(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
static bool IsNonSubresourceRequest(nsIChannel* aChannel);
|
||||
|
||||
|
||||
@@ -1384,6 +1384,8 @@ nsTreeSanitizer::SanitizeChildren(nsINode* aRoot)
|
||||
nsAutoString styleText;
|
||||
nsContentUtils::GetNodeTextContent(node, false, styleText);
|
||||
|
||||
RemoveAllAttributesFromDescendants(elt);
|
||||
|
||||
nsAutoString sanitizedStyle;
|
||||
nsCOMPtr<nsIURI> baseURI = node->GetBaseURI();
|
||||
if (SanitizeStyleSheet(styleText,
|
||||
@@ -1479,6 +1481,17 @@ nsTreeSanitizer::RemoveAllAttributes(nsIContent* aElement)
|
||||
}
|
||||
}
|
||||
|
||||
void nsTreeSanitizer::RemoveAllAttributesFromDescendants(mozilla::dom::Element* aElement) {
|
||||
nsIContent* node = aElement->GetFirstChild();
|
||||
while (node) {
|
||||
if (node->IsElement()) {
|
||||
mozilla::dom::Element* elt = node->AsElement();
|
||||
RemoveAllAttributes(elt);
|
||||
}
|
||||
node = node->GetNextNode(aElement);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsTreeSanitizer::InitializeStatics()
|
||||
{
|
||||
|
||||
@@ -183,6 +183,12 @@ class MOZ_STACK_CLASS nsTreeSanitizer {
|
||||
*/
|
||||
void RemoveAllAttributes(nsIContent* aElement);
|
||||
|
||||
/**
|
||||
* Removes all attributes from the descendants of an element but not from
|
||||
* the element itself.
|
||||
*/
|
||||
void RemoveAllAttributesFromDescendants(mozilla::dom::Element* aElement);
|
||||
|
||||
/**
|
||||
* The whitelist of HTML elements.
|
||||
*/
|
||||
|
||||
@@ -714,7 +714,6 @@ DOMInterfaces = {
|
||||
'headers': 'headers_',
|
||||
'referrerPolicy': 'referrerPolicy_'
|
||||
},
|
||||
'implicitJSContext': [ 'arrayBuffer', 'blob', 'formData', 'json', 'text' ],
|
||||
},
|
||||
|
||||
'ResizeObservation': {
|
||||
@@ -734,8 +733,6 @@ DOMInterfaces = {
|
||||
|
||||
'Response': {
|
||||
'binaryNames': { 'headers': 'headers_' },
|
||||
'implicitJSContext': [ 'arrayBuffer', 'blob', 'formData', 'json', 'text',
|
||||
'clone', 'cloneUnfiltered' ],
|
||||
},
|
||||
|
||||
'RGBColor': {
|
||||
|
||||
+74
-90
@@ -1156,10 +1156,7 @@ class CGHeaders(CGWrapper):
|
||||
# just include their header if we need to have functions
|
||||
# taking references to them declared in that header.
|
||||
headerSet = declareIncludes
|
||||
if unrolled.isReadableStream():
|
||||
headerSet.add("mozilla/dom/ReadableStream.h")
|
||||
else:
|
||||
headerSet.add("mozilla/dom/TypedArray.h")
|
||||
headerSet.add("mozilla/dom/TypedArray.h")
|
||||
else:
|
||||
try:
|
||||
typeDesc = config.getDescriptor(unrolled.inner.identifier.name)
|
||||
@@ -1374,10 +1371,7 @@ def UnionTypes(unionTypes, config):
|
||||
elif f.isInterface():
|
||||
if f.isSpiderMonkeyInterface():
|
||||
headers.add("jsfriendapi.h")
|
||||
if f.isReadableStream():
|
||||
headers.add("mozilla/dom/ReadableStream.h")
|
||||
else:
|
||||
headers.add("mozilla/dom/TypedArray.h")
|
||||
headers.add("mozilla/dom/TypedArray.h")
|
||||
else:
|
||||
try:
|
||||
typeDesc = config.getDescriptor(f.inner.identifier.name)
|
||||
@@ -1463,10 +1457,7 @@ def UnionConversions(unionTypes, config):
|
||||
elif f.isInterface():
|
||||
if f.isSpiderMonkeyInterface():
|
||||
headers.add("jsfriendapi.h")
|
||||
if f.isReadableStream():
|
||||
headers.add("mozilla/dom/ReadableStream.h")
|
||||
else:
|
||||
headers.add("mozilla/dom/TypedArray.h")
|
||||
headers.add("mozilla/dom/TypedArray.h")
|
||||
elif f.inner.isExternal():
|
||||
try:
|
||||
typeDesc = config.getDescriptor(f.inner.identifier.name)
|
||||
@@ -5602,8 +5593,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
||||
if type.isSpiderMonkeyInterface():
|
||||
assert not isEnforceRange and not isClamp
|
||||
name = type.unroll().name # unroll() because it may be nullable
|
||||
interfaceType = CGGeneric(name)
|
||||
declType = interfaceType
|
||||
arrayType = CGGeneric(name)
|
||||
declType = arrayType
|
||||
if type.nullable():
|
||||
declType = CGTemplatedType("Nullable", declType)
|
||||
objRef = "${declName}.SetValue()"
|
||||
@@ -5627,23 +5618,22 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
||||
# This is a bit annoying. In a union we don't want to have a
|
||||
# holder, since unions don't support that. But if we're optional we
|
||||
# want to have a holder, so that the callee doesn't see
|
||||
# Optional<RootedSpiderMonkeyInterface<InterfaceType>>. So do a
|
||||
# holder if we're optional and use a RootedSpiderMonkeyInterface
|
||||
# otherwise.
|
||||
# Optional<RootedTypedArray<ArrayType> >. So do a holder if we're
|
||||
# optional and use a RootedTypedArray otherwise.
|
||||
if isOptional:
|
||||
holderType = CGTemplatedType("SpiderMonkeyInterfaceRooter", interfaceType)
|
||||
# If our SpiderMonkey interface is nullable, this will set the
|
||||
# Nullable to be not-null, but that's ok because we make an
|
||||
# explicit SetNull() call on it as needed if our JS value is
|
||||
# actually null. XXXbz Because "Maybe" takes const refs for
|
||||
# constructor arguments, we can't pass a reference here; have
|
||||
# to pass a pointer.
|
||||
holderType = CGTemplatedType("TypedArrayRooter", arrayType)
|
||||
# If our typed array is nullable, this will set the Nullable to
|
||||
# be not-null, but that's ok because we make an explicit
|
||||
# SetNull() call on it as needed if our JS value is actually
|
||||
# null. XXXbz Because "Maybe" takes const refs for constructor
|
||||
# arguments, we can't pass a reference here; have to pass a
|
||||
# pointer.
|
||||
holderArgs = "cx, &%s" % objRef
|
||||
declArgs = None
|
||||
else:
|
||||
holderType = None
|
||||
holderArgs = None
|
||||
declType = CGTemplatedType("RootedSpiderMonkeyInterface", declType)
|
||||
declType = CGTemplatedType("RootedTypedArray", declType)
|
||||
declArgs = "cx"
|
||||
else:
|
||||
holderType = None
|
||||
@@ -6392,7 +6382,7 @@ def getMaybeWrapValueFuncForType(type):
|
||||
if type.nullable():
|
||||
return "MaybeWrapObjectOrNullValue"
|
||||
return "MaybeWrapObjectValue"
|
||||
# SpiderMonkey interfaces are never DOM objects. Neither are sequences or
|
||||
# Spidermonkey interfaces are never DOM objects. Neither are sequences or
|
||||
# dictionaries, since those are always plain JS objects.
|
||||
if type.isSpiderMonkeyInterface() or type.isDictionary() or type.isSequence():
|
||||
if type.nullable():
|
||||
@@ -6412,8 +6402,7 @@ recordWrapLevel = 0
|
||||
|
||||
|
||||
def getWrapTemplateForType(type, descriptorProvider, result, successCode,
|
||||
returnsNewObject, exceptionCode,
|
||||
spiderMonkeyInterfacesAreStructs,
|
||||
returnsNewObject, exceptionCode, typedArraysAreStructs,
|
||||
isConstructorRetval=False):
|
||||
"""
|
||||
Reflect a C++ value stored in "result", of IDL type "type" into JS. The
|
||||
@@ -6423,9 +6412,8 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
|
||||
doing a 'break' if the entire conversion template is inside a block that
|
||||
the 'break' will exit).
|
||||
|
||||
If spiderMonkeyInterfacesAreStructs is true, then if the type is a
|
||||
SpiderMonkey interface, "result" is one of the
|
||||
dom::SpiderMonkeyInterfaceObjectStorage subclasses, not a JSObject*.
|
||||
If typedArraysAreStructs is true, then if the type is a typed array,
|
||||
"result" is one of the dom::TypedArray subclasses, not a JSObject*.
|
||||
|
||||
The resulting string should be used with string.Template. It
|
||||
needs the following keys when substituting:
|
||||
@@ -6520,7 +6508,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
|
||||
recTemplate, recInfall = getWrapTemplateForType(type.inner, descriptorProvider,
|
||||
"%s.Value()" % result, successCode,
|
||||
returnsNewObject, exceptionCode,
|
||||
spiderMonkeyInterfacesAreStructs)
|
||||
typedArraysAreStructs)
|
||||
code = fill(
|
||||
"""
|
||||
|
||||
@@ -6552,7 +6540,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
|
||||
'returnsNewObject': returnsNewObject,
|
||||
'exceptionCode': exceptionCode,
|
||||
'obj': "returnArray",
|
||||
'spiderMonkeyInterfacesAreStructs': spiderMonkeyInterfacesAreStructs
|
||||
'typedArraysAreStructs': typedArraysAreStructs
|
||||
})
|
||||
sequenceWrapLevel -= 1
|
||||
code = fill(
|
||||
@@ -6606,7 +6594,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
|
||||
'returnsNewObject': returnsNewObject,
|
||||
'exceptionCode': exceptionCode,
|
||||
'obj': "returnObj",
|
||||
'spiderMonkeyInterfacesAreStructs': spiderMonkeyInterfacesAreStructs
|
||||
'typedArraysAreStructs': typedArraysAreStructs
|
||||
})
|
||||
recordWrapLevel -= 1
|
||||
if type.keyType.isByteString():
|
||||
@@ -6772,7 +6760,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
|
||||
return (head + _setValue(result, wrapAsType=type), False)
|
||||
|
||||
if (type.isObject() or (type.isSpiderMonkeyInterface() and
|
||||
not spiderMonkeyInterfacesAreStructs)):
|
||||
not typedArraysAreStructs)):
|
||||
# See comments in GetOrCreateDOMReflector explaining why we need
|
||||
# to wrap here.
|
||||
if type.nullable():
|
||||
@@ -6791,21 +6779,21 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
|
||||
|
||||
if not (type.isUnion() or type.isPrimitive() or type.isDictionary() or
|
||||
type.isDate() or
|
||||
(type.isSpiderMonkeyInterface() and spiderMonkeyInterfacesAreStructs)):
|
||||
(type.isSpiderMonkeyInterface() and typedArraysAreStructs)):
|
||||
raise TypeError("Need to learn to wrap %s" % type)
|
||||
|
||||
if type.nullable():
|
||||
recTemplate, recInfal = getWrapTemplateForType(type.inner, descriptorProvider,
|
||||
"%s.Value()" % result, successCode,
|
||||
returnsNewObject, exceptionCode,
|
||||
spiderMonkeyInterfacesAreStructs)
|
||||
typedArraysAreStructs)
|
||||
return ("if (%s.IsNull()) {\n" % result +
|
||||
indent(setNull()) +
|
||||
"}\n" +
|
||||
recTemplate, recInfal)
|
||||
|
||||
if type.isSpiderMonkeyInterface():
|
||||
assert spiderMonkeyInterfacesAreStructs
|
||||
assert typedArraysAreStructs
|
||||
# See comments in GetOrCreateDOMReflector explaining why we need
|
||||
# to wrap here.
|
||||
# NB: setObject(..., some-object-type) calls JS_WrapValue(), so is fallible
|
||||
@@ -6887,7 +6875,7 @@ def wrapForType(type, descriptorProvider, templateValues):
|
||||
templateValues.get('successCode', None),
|
||||
templateValues.get('returnsNewObject', False),
|
||||
templateValues.get('exceptionCode', "return false;\n"),
|
||||
templateValues.get('spiderMonkeyInterfacesAreStructs', False),
|
||||
templateValues.get('typedArraysAreStructs', False),
|
||||
isConstructorRetval=templateValues.get('isConstructorRetval', False))[0]
|
||||
|
||||
defaultValues = {'obj': 'obj'}
|
||||
@@ -8227,10 +8215,10 @@ class CGMethodCall(CGThing):
|
||||
# "object" arg.
|
||||
|
||||
# First grab all the overloads that have a non-callback interface
|
||||
# (which includes SpiderMonkey interfaces) at the distinguishing
|
||||
# index. We can also include the ones that have an "object" here,
|
||||
# since if those are present no other object-typed argument will
|
||||
# be.
|
||||
# (which includes typed arrays and arraybuffers) at the
|
||||
# distinguishing index. We can also include the ones that have an
|
||||
# "object" here, since if those are present no other object-typed
|
||||
# argument will be.
|
||||
objectSigs = [
|
||||
s for s in possibleSignatures
|
||||
if (distinguishingType(s).isObject() or
|
||||
@@ -8261,17 +8249,16 @@ class CGMethodCall(CGThing):
|
||||
# There might be more than one thing in objectSigs; we need to check
|
||||
# which ones we unwrap to.
|
||||
if len(objectSigs) > 0:
|
||||
# Here it's enough to guard on our argument being an object.
|
||||
# The code for unwrapping non-callback interfaces, spiderMonkey
|
||||
# interfaces, sequences, and Dates will just bail out and move
|
||||
# on to the next overload if the object fails to unwrap
|
||||
# correctly, while "object" accepts any object anyway. We
|
||||
# could even not do the isObject() check up front here, but in
|
||||
# cases where we have multiple object overloads it makes sense
|
||||
# to do it only once instead of for each overload. That will
|
||||
# also allow the unwrapping test to skip having to do codegen
|
||||
# for the null-or-undefined case, which we already handled
|
||||
# above.
|
||||
# Here it's enough to guard on our argument being an object. The
|
||||
# code for unwrapping non-callback interfaces, typed arrays,
|
||||
# sequences, and Dates will just bail out and move on to
|
||||
# the next overload if the object fails to unwrap correctly,
|
||||
# while "object" accepts any object anyway. We could even not
|
||||
# do the isObject() check up front here, but in cases where we
|
||||
# have multiple object overloads it makes sense to do it only
|
||||
# once instead of for each overload. That will also allow the
|
||||
# unwrapping test to skip having to do codegen for the
|
||||
# null-or-undefined case, which we already handled above.
|
||||
caseBody.append(CGGeneric("if (%s.isObject()) {\n" %
|
||||
distinguishingArg))
|
||||
for sig in objectSigs:
|
||||
@@ -10284,7 +10271,7 @@ class CGUnionStruct(CGThing):
|
||||
"jsvalHandle": "rval",
|
||||
"obj": "scopeObj",
|
||||
"result": val,
|
||||
"spiderMonkeyInterfacesAreStructs": True
|
||||
"typedArraysAreStructs": True
|
||||
})
|
||||
return CGGeneric(wrapCode)
|
||||
|
||||
@@ -13264,7 +13251,7 @@ class CGDictionary(CGThing):
|
||||
# 'obj' can just be allowed to be the string "obj", since that
|
||||
# will be our dictionary object, which is presumably itself in
|
||||
# the right scope.
|
||||
'spiderMonkeyInterfacesAreStructs': True
|
||||
'typedArraysAreStructs': True
|
||||
})
|
||||
conversion = CGGeneric(innerTemplate)
|
||||
conversion = CGWrapper(conversion,
|
||||
@@ -13724,7 +13711,7 @@ class ForwardDeclarationBuilder:
|
||||
except NoSuchDescriptorError:
|
||||
pass
|
||||
|
||||
# Note: SpiderMonkey interfaces are typedefs, so can't be
|
||||
# Note: Spidermonkey interfaces are typedefs, so can't be
|
||||
# forward-declared
|
||||
elif t.isPromise():
|
||||
self.addInMozillaDom("Promise")
|
||||
@@ -14039,7 +14026,7 @@ class CGBindingRoot(CGThing):
|
||||
return {desc.getDescriptor(desc.interface.parent.identifier.name)}
|
||||
for x in dependencySortObjects(jsImplemented, getParentDescriptor,
|
||||
lambda d: d.interface.identifier.name):
|
||||
cgthings.append(CGCallbackInterface(x, spiderMonkeyInterfacesAreStructs=True))
|
||||
cgthings.append(CGCallbackInterface(x, typedArraysAreStructs=True))
|
||||
cgthings.append(CGJSImplClass(x))
|
||||
|
||||
# And make sure we have the right number of newlines at the end
|
||||
@@ -14101,13 +14088,14 @@ class CGBindingRoot(CGThing):
|
||||
class CGNativeMember(ClassMethod):
|
||||
def __init__(self, descriptorProvider, member, name, signature, extendedAttrs,
|
||||
breakAfter=True, passJSBitsAsNeeded=True, visibility="public",
|
||||
spiderMonkeyInterfacesAreStructs=True,
|
||||
variadicIsSequence=False, resultNotAddRefed=False,
|
||||
virtual=False, override=False):
|
||||
typedArraysAreStructs=True, variadicIsSequence=False,
|
||||
resultNotAddRefed=False,
|
||||
virtual=False,
|
||||
override=False):
|
||||
"""
|
||||
If spiderMonkeyInterfacesAreStructs is false, SpiderMonkey interfaces
|
||||
will be passed as JS::Handle<JSObject*>. If it's true they will be
|
||||
passed as one of the dom::SpiderMonkeyInterfaceObjectStorage subclasses.
|
||||
If typedArraysAreStructs is false, typed arrays will be passed as
|
||||
JS::Handle<JSObject*>. If it's true they will be passed as one of the
|
||||
dom::TypedArray subclasses.
|
||||
|
||||
If passJSBitsAsNeeded is false, we don't automatically pass in a
|
||||
JSContext* or a JSObject* based on the return and argument types. We
|
||||
@@ -14118,7 +14106,7 @@ class CGNativeMember(ClassMethod):
|
||||
self.extendedAttrs = extendedAttrs
|
||||
self.resultAlreadyAddRefed = not resultNotAddRefed
|
||||
self.passJSBitsAsNeeded = passJSBitsAsNeeded
|
||||
self.spiderMonkeyInterfacesAreStructs = spiderMonkeyInterfacesAreStructs
|
||||
self.typedArraysAreStructs = typedArraysAreStructs
|
||||
self.variadicIsSequence = variadicIsSequence
|
||||
breakAfterSelf = "\n" if breakAfter else ""
|
||||
ClassMethod.__init__(self, name,
|
||||
@@ -14425,7 +14413,7 @@ class CGNativeMember(ClassMethod):
|
||||
False, False)
|
||||
|
||||
if type.isSpiderMonkeyInterface():
|
||||
if not self.spiderMonkeyInterfacesAreStructs:
|
||||
if not self.typedArraysAreStructs:
|
||||
return "JS::Handle<JSObject*>", False, False
|
||||
|
||||
# Unroll for the name, in case we're nullable.
|
||||
@@ -15597,16 +15585,16 @@ class CGFastCallback(CGClass):
|
||||
|
||||
|
||||
class CGCallbackInterface(CGCallback):
|
||||
def __init__(self, descriptor, spiderMonkeyInterfacesAreStructs=False):
|
||||
def __init__(self, descriptor, typedArraysAreStructs=False):
|
||||
iface = descriptor.interface
|
||||
attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()]
|
||||
getters = [CallbackGetter(a, descriptor, spiderMonkeyInterfacesAreStructs)
|
||||
getters = [CallbackGetter(a, descriptor, typedArraysAreStructs)
|
||||
for a in attrs]
|
||||
setters = [CallbackSetter(a, descriptor, spiderMonkeyInterfacesAreStructs)
|
||||
setters = [CallbackSetter(a, descriptor, typedArraysAreStructs)
|
||||
for a in attrs if not a.readonly]
|
||||
methods = [m for m in iface.members
|
||||
if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()]
|
||||
methods = [CallbackOperation(m, sig, descriptor, spiderMonkeyInterfacesAreStructs)
|
||||
methods = [CallbackOperation(m, sig, descriptor, typedArraysAreStructs)
|
||||
for m in methods for sig in m.signatures()]
|
||||
if iface.isJSImplemented() and iface.ctor():
|
||||
sigs = descriptor.interface.ctor().signatures()
|
||||
@@ -15651,8 +15639,7 @@ class CallbackMember(CGNativeMember):
|
||||
# CallSetup already handled the unmark-gray bits for us. we don't have
|
||||
# anything better to use for 'obj', really...
|
||||
def __init__(self, sig, name, descriptorProvider, needThisHandling,
|
||||
rethrowContentException=False,
|
||||
spiderMonkeyInterfacesAreStructs=False,
|
||||
rethrowContentException=False, typedArraysAreStructs=False,
|
||||
wrapScope='CallbackKnownNotGray()'):
|
||||
"""
|
||||
needThisHandling is True if we need to be able to accept a specified
|
||||
@@ -15687,7 +15674,7 @@ class CallbackMember(CGNativeMember):
|
||||
extendedAttrs={},
|
||||
passJSBitsAsNeeded=False,
|
||||
visibility=visibility,
|
||||
spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
|
||||
typedArraysAreStructs=typedArraysAreStructs)
|
||||
# We have to do all the generation of our body now, because
|
||||
# the caller relies on us throwing if we can't manage it.
|
||||
self.exceptionCode = ("aRv.Throw(NS_ERROR_UNEXPECTED);\n"
|
||||
@@ -15804,7 +15791,7 @@ class CallbackMember(CGNativeMember):
|
||||
'obj': self.wrapScope,
|
||||
'returnsNewObject': False,
|
||||
'exceptionCode': self.exceptionCode,
|
||||
'spiderMonkeyInterfacesAreStructs': self.spiderMonkeyInterfacesAreStructs
|
||||
'typedArraysAreStructs': self.typedArraysAreStructs
|
||||
})
|
||||
except MethodNotNewObjectError as err:
|
||||
raise TypeError("%s being passed as an argument to %s but is not "
|
||||
@@ -15908,11 +15895,10 @@ class CallbackMember(CGNativeMember):
|
||||
|
||||
class CallbackMethod(CallbackMember):
|
||||
def __init__(self, sig, name, descriptorProvider, needThisHandling,
|
||||
rethrowContentException=False,
|
||||
spiderMonkeyInterfacesAreStructs=False):
|
||||
rethrowContentException=False, typedArraysAreStructs=False):
|
||||
CallbackMember.__init__(self, sig, name, descriptorProvider,
|
||||
needThisHandling, rethrowContentException,
|
||||
spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
|
||||
typedArraysAreStructs=typedArraysAreStructs)
|
||||
|
||||
def getRvalDecl(self):
|
||||
return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
|
||||
@@ -15971,12 +15957,12 @@ class CallbackOperationBase(CallbackMethod):
|
||||
"""
|
||||
def __init__(self, signature, jsName, nativeName, descriptor,
|
||||
singleOperation, rethrowContentException=False,
|
||||
spiderMonkeyInterfacesAreStructs=False):
|
||||
typedArraysAreStructs=False):
|
||||
self.singleOperation = singleOperation
|
||||
self.methodName = descriptor.binaryNameFor(jsName)
|
||||
CallbackMethod.__init__(self, signature, nativeName, descriptor,
|
||||
singleOperation, rethrowContentException,
|
||||
spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
|
||||
typedArraysAreStructs=typedArraysAreStructs)
|
||||
|
||||
def getThisDecl(self):
|
||||
if not self.singleOperation:
|
||||
@@ -16027,8 +16013,7 @@ class CallbackOperation(CallbackOperationBase):
|
||||
"""
|
||||
Codegen actual WebIDL operations on callback interfaces.
|
||||
"""
|
||||
def __init__(self, method, signature, descriptor,
|
||||
spiderMonkeyInterfacesAreStructs):
|
||||
def __init__(self, method, signature, descriptor, typedArraysAreStructs):
|
||||
self.ensureASCIIName(method)
|
||||
self.method = method
|
||||
jsName = method.identifier.name
|
||||
@@ -16037,7 +16022,7 @@ class CallbackOperation(CallbackOperationBase):
|
||||
MakeNativeName(descriptor.binaryNameFor(jsName)),
|
||||
descriptor, descriptor.interface.isSingleOperationInterface(),
|
||||
rethrowContentException=descriptor.interface.isJSImplemented(),
|
||||
spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
|
||||
typedArraysAreStructs=typedArraysAreStructs)
|
||||
|
||||
def getPrettyName(self):
|
||||
return "%s.%s" % (self.descriptorProvider.interface.identifier.name,
|
||||
@@ -16048,14 +16033,13 @@ class CallbackAccessor(CallbackMember):
|
||||
"""
|
||||
Shared superclass for CallbackGetter and CallbackSetter.
|
||||
"""
|
||||
def __init__(self, attr, sig, name, descriptor,
|
||||
spiderMonkeyInterfacesAreStructs):
|
||||
def __init__(self, attr, sig, name, descriptor, typedArraysAreStructs):
|
||||
self.ensureASCIIName(attr)
|
||||
self.attrName = attr.identifier.name
|
||||
CallbackMember.__init__(self, sig, name, descriptor,
|
||||
needThisHandling=False,
|
||||
rethrowContentException=descriptor.interface.isJSImplemented(),
|
||||
spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
|
||||
typedArraysAreStructs=typedArraysAreStructs)
|
||||
|
||||
def getPrettyName(self):
|
||||
return "%s.%s" % (self.descriptorProvider.interface.identifier.name,
|
||||
@@ -16063,12 +16047,12 @@ class CallbackAccessor(CallbackMember):
|
||||
|
||||
|
||||
class CallbackGetter(CallbackAccessor):
|
||||
def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs):
|
||||
def __init__(self, attr, descriptor, typedArraysAreStructs):
|
||||
CallbackAccessor.__init__(self, attr,
|
||||
(attr.type, []),
|
||||
callbackGetterName(attr, descriptor),
|
||||
descriptor,
|
||||
spiderMonkeyInterfacesAreStructs)
|
||||
typedArraysAreStructs)
|
||||
|
||||
def getRvalDecl(self):
|
||||
return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
|
||||
@@ -16090,12 +16074,12 @@ class CallbackGetter(CallbackAccessor):
|
||||
|
||||
|
||||
class CallbackSetter(CallbackAccessor):
|
||||
def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs):
|
||||
def __init__(self, attr, descriptor, typedArraysAreStructs):
|
||||
CallbackAccessor.__init__(self, attr,
|
||||
(BuiltinTypes[IDLBuiltinType.Types.void],
|
||||
[FakeArgument(attr.type, attr)]),
|
||||
callbackSetterName(attr, descriptor),
|
||||
descriptor, spiderMonkeyInterfacesAreStructs)
|
||||
descriptor, typedArraysAreStructs)
|
||||
|
||||
def getRvalDecl(self):
|
||||
# We don't need an rval
|
||||
@@ -16130,7 +16114,7 @@ class CGJSImplInitOperation(CallbackOperationBase):
|
||||
"__init", "__Init", descriptor,
|
||||
singleOperation=False,
|
||||
rethrowContentException=True,
|
||||
spiderMonkeyInterfacesAreStructs=True)
|
||||
typedArraysAreStructs=True)
|
||||
|
||||
def getPrettyName(self):
|
||||
return "__init"
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_ReadableStream_h
|
||||
#define mozilla_dom_ReadableStream_h
|
||||
|
||||
#include "mozilla/dom/SpiderMonkeyInterface.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct ReadableStream : public SpiderMonkeyInterfaceObjectStorage
|
||||
{
|
||||
inline bool Init(JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(!inited());
|
||||
mImplObj = mWrappedObj = js::UnwrapReadableStream(obj);
|
||||
return inited();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_ReadableStream_h */
|
||||
@@ -1,144 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_SpiderMonkeyInterface_h
|
||||
#define mozilla_dom_SpiderMonkeyInterface_h
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "js/TracingAPI.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/*
|
||||
* Class that just handles the JSObject storage and tracing for spidermonkey
|
||||
* interfaces
|
||||
*/
|
||||
struct SpiderMonkeyInterfaceObjectStorage
|
||||
{
|
||||
protected:
|
||||
JSObject* mImplObj;
|
||||
JSObject* mWrappedObj;
|
||||
|
||||
SpiderMonkeyInterfaceObjectStorage()
|
||||
: mImplObj(nullptr),
|
||||
mWrappedObj(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
SpiderMonkeyInterfaceObjectStorage(SpiderMonkeyInterfaceObjectStorage&& aOther)
|
||||
: mImplObj(aOther.mImplObj),
|
||||
mWrappedObj(aOther.mWrappedObj)
|
||||
{
|
||||
aOther.mImplObj = nullptr;
|
||||
aOther.mWrappedObj = nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
inline void TraceSelf(JSTracer* trc)
|
||||
{
|
||||
JS::UnsafeTraceRoot(trc, &mImplObj, "SpiderMonkeyInterfaceObjectStorage.mImplObj");
|
||||
JS::UnsafeTraceRoot(trc, &mWrappedObj, "SpiderMonkeyInterfaceObjectStorage.mWrappedObj");
|
||||
}
|
||||
|
||||
inline bool inited() const
|
||||
{
|
||||
return !!mImplObj;
|
||||
}
|
||||
|
||||
inline bool WrapIntoNewCompartment(JSContext* cx)
|
||||
{
|
||||
return JS_WrapObject(cx,
|
||||
JS::MutableHandle<JSObject*>::fromMarkedLocation(&mWrappedObj));
|
||||
}
|
||||
|
||||
inline JSObject *Obj() const
|
||||
{
|
||||
MOZ_ASSERT(inited());
|
||||
return mWrappedObj;
|
||||
}
|
||||
|
||||
private:
|
||||
SpiderMonkeyInterfaceObjectStorage(const SpiderMonkeyInterfaceObjectStorage&) = delete;
|
||||
};
|
||||
|
||||
// A class for rooting an existing SpiderMonkey Interface struct
|
||||
template<typename InterfaceType>
|
||||
class MOZ_RAII SpiderMonkeyInterfaceRooter : private JS::CustomAutoRooter
|
||||
{
|
||||
public:
|
||||
template <typename CX>
|
||||
SpiderMonkeyInterfaceRooter(const CX& cx,
|
||||
InterfaceType* aInterface MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
|
||||
mInterface(aInterface)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void trace(JSTracer* trc) override
|
||||
{
|
||||
mInterface->TraceSelf(trc);
|
||||
}
|
||||
|
||||
private:
|
||||
SpiderMonkeyInterfaceObjectStorage* const mInterface;
|
||||
};
|
||||
|
||||
// And a specialization for dealing with nullable SpiderMonkey interfaces
|
||||
template<typename Inner> struct Nullable;
|
||||
template<typename InterfaceType>
|
||||
class MOZ_RAII SpiderMonkeyInterfaceRooter<Nullable<InterfaceType>> :
|
||||
private JS::CustomAutoRooter
|
||||
{
|
||||
public:
|
||||
template <typename CX>
|
||||
SpiderMonkeyInterfaceRooter(const CX& cx,
|
||||
Nullable<InterfaceType>* aInterface MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
|
||||
mInterface(aInterface)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void trace(JSTracer* trc) override
|
||||
{
|
||||
if (!mInterface->IsNull()) {
|
||||
mInterface->Value().TraceSelf(trc);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Nullable<InterfaceType>* const mInterface;
|
||||
};
|
||||
|
||||
// Class for easily setting up a rooted SpiderMonkey interface object on the
|
||||
// stack
|
||||
template<typename InterfaceType>
|
||||
class MOZ_RAII RootedSpiderMonkeyInterface final : public InterfaceType,
|
||||
private SpiderMonkeyInterfaceRooter<InterfaceType>
|
||||
{
|
||||
public:
|
||||
template <typename CX>
|
||||
explicit RootedSpiderMonkeyInterface(const CX& cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: InterfaceType(),
|
||||
SpiderMonkeyInterfaceRooter<InterfaceType>(cx, this
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename CX>
|
||||
RootedSpiderMonkeyInterface(const CX& cx, JSObject* obj MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: InterfaceType(obj),
|
||||
SpiderMonkeyInterfaceRooter<InterfaceType>(cx, this
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_SpiderMonkeyInterface_h */
|
||||
+127
-7
@@ -6,15 +6,51 @@
|
||||
#ifndef mozilla_dom_TypedArray_h
|
||||
#define mozilla_dom_TypedArray_h
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/TracingAPI.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/SpiderMonkeyInterface.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/*
|
||||
* Class that just handles the JSObject storage and tracing for typed arrays
|
||||
*/
|
||||
struct TypedArrayObjectStorage : AllTypedArraysBase {
|
||||
protected:
|
||||
JSObject* mTypedObj;
|
||||
JSObject* mWrappedObj;
|
||||
|
||||
TypedArrayObjectStorage()
|
||||
: mTypedObj(nullptr),
|
||||
mWrappedObj(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
TypedArrayObjectStorage(TypedArrayObjectStorage&& aOther)
|
||||
: mTypedObj(aOther.mTypedObj),
|
||||
mWrappedObj(aOther.mWrappedObj)
|
||||
{
|
||||
aOther.mTypedObj = nullptr;
|
||||
aOther.mWrappedObj = nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
inline void TraceSelf(JSTracer* trc)
|
||||
{
|
||||
JS::UnsafeTraceRoot(trc, &mTypedObj, "TypedArray.mTypedObj");
|
||||
JS::UnsafeTraceRoot(trc, &mWrappedObj, "TypedArray.mWrappedObj");
|
||||
}
|
||||
|
||||
private:
|
||||
TypedArrayObjectStorage(const TypedArrayObjectStorage&) = delete;
|
||||
};
|
||||
|
||||
/*
|
||||
* Various typed array classes for argument conversion. We have a base class
|
||||
* that has a way of initializing a TypedArray from an existing typed array, and
|
||||
@@ -24,9 +60,7 @@ namespace dom {
|
||||
template<typename T,
|
||||
JSObject* UnwrapArray(JSObject*),
|
||||
void GetLengthAndDataAndSharedness(JSObject*, uint32_t*, bool*, T**)>
|
||||
struct TypedArray_base : public SpiderMonkeyInterfaceObjectStorage,
|
||||
AllTypedArraysBase
|
||||
{
|
||||
struct TypedArray_base : public TypedArrayObjectStorage {
|
||||
typedef T element_type;
|
||||
|
||||
TypedArray_base()
|
||||
@@ -38,7 +72,7 @@ struct TypedArray_base : public SpiderMonkeyInterfaceObjectStorage,
|
||||
}
|
||||
|
||||
TypedArray_base(TypedArray_base&& aOther)
|
||||
: SpiderMonkeyInterfaceObjectStorage(Move(aOther)),
|
||||
: TypedArrayObjectStorage(Move(aOther)),
|
||||
mData(aOther.mData),
|
||||
mLength(aOther.mLength),
|
||||
mShared(aOther.mShared),
|
||||
@@ -60,10 +94,14 @@ public:
|
||||
inline bool Init(JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(!inited());
|
||||
mImplObj = mWrappedObj = UnwrapArray(obj);
|
||||
mTypedObj = mWrappedObj = UnwrapArray(obj);
|
||||
return inited();
|
||||
}
|
||||
|
||||
inline bool inited() const {
|
||||
return !!mTypedObj;
|
||||
}
|
||||
|
||||
// About shared memory:
|
||||
//
|
||||
// Any DOM TypedArray as well as any DOM ArrayBufferView that does
|
||||
@@ -135,11 +173,22 @@ public:
|
||||
return mLength;
|
||||
}
|
||||
|
||||
inline JSObject *Obj() const {
|
||||
MOZ_ASSERT(inited());
|
||||
return mWrappedObj;
|
||||
}
|
||||
|
||||
inline bool WrapIntoNewCompartment(JSContext* cx)
|
||||
{
|
||||
return JS_WrapObject(cx,
|
||||
JS::MutableHandle<JSObject*>::fromMarkedLocation(&mWrappedObj));
|
||||
}
|
||||
|
||||
inline void ComputeLengthAndData() const
|
||||
{
|
||||
MOZ_ASSERT(inited());
|
||||
MOZ_ASSERT(!mComputed);
|
||||
GetLengthAndDataAndSharedness(mImplObj, &mLength, &mShared, &mData);
|
||||
GetLengthAndDataAndSharedness(mTypedObj, &mLength, &mShared, &mData);
|
||||
mComputed = true;
|
||||
}
|
||||
|
||||
@@ -314,6 +363,77 @@ class TypedArrayCreator
|
||||
const ArrayType& mArray;
|
||||
};
|
||||
|
||||
// A class for rooting an existing TypedArray struct
|
||||
template<typename ArrayType>
|
||||
class MOZ_RAII TypedArrayRooter : private JS::CustomAutoRooter
|
||||
{
|
||||
public:
|
||||
template <typename CX>
|
||||
TypedArrayRooter(const CX& cx,
|
||||
ArrayType* aArray MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
|
||||
JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
|
||||
mArray(aArray)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void trace(JSTracer* trc) override
|
||||
{
|
||||
mArray->TraceSelf(trc);
|
||||
}
|
||||
|
||||
private:
|
||||
TypedArrayObjectStorage* const mArray;
|
||||
};
|
||||
|
||||
// And a specialization for dealing with nullable typed arrays
|
||||
template<typename Inner> struct Nullable;
|
||||
template<typename ArrayType>
|
||||
class MOZ_RAII TypedArrayRooter<Nullable<ArrayType> > :
|
||||
private JS::CustomAutoRooter
|
||||
{
|
||||
public:
|
||||
template <typename CX>
|
||||
TypedArrayRooter(const CX& cx,
|
||||
Nullable<ArrayType>* aArray MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
|
||||
JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
|
||||
mArray(aArray)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void trace(JSTracer* trc) override
|
||||
{
|
||||
if (!mArray->IsNull()) {
|
||||
mArray->Value().TraceSelf(trc);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Nullable<ArrayType>* const mArray;
|
||||
};
|
||||
|
||||
// Class for easily setting up a rooted typed array object on the stack
|
||||
template<typename ArrayType>
|
||||
class MOZ_RAII RootedTypedArray final : public ArrayType,
|
||||
private TypedArrayRooter<ArrayType>
|
||||
{
|
||||
public:
|
||||
template <typename CX>
|
||||
explicit RootedTypedArray(const CX& cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
|
||||
ArrayType(),
|
||||
TypedArrayRooter<ArrayType>(cx, this
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename CX>
|
||||
RootedTypedArray(const CX& cx, JSObject* obj MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
|
||||
ArrayType(obj),
|
||||
TypedArrayRooter<ArrayType>(cx, this
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
@@ -38,11 +38,9 @@ EXPORTS.mozilla.dom += [
|
||||
'NonRefcountedDOMObject.h',
|
||||
'Nullable.h',
|
||||
'PrimitiveConversions.h',
|
||||
'ReadableStream.h',
|
||||
'Record.h',
|
||||
'RootedDictionary.h',
|
||||
'SimpleGlobalObject.h',
|
||||
'SpiderMonkeyInterface.h',
|
||||
'StructuredClone.h',
|
||||
'ToJSValue.h',
|
||||
'TypedArray.h',
|
||||
|
||||
@@ -2048,9 +2048,6 @@ class IDLType(IDLObject):
|
||||
def isRecord(self):
|
||||
return False
|
||||
|
||||
def isReadableStream(self):
|
||||
return False
|
||||
|
||||
def isArrayBuffer(self):
|
||||
return False
|
||||
|
||||
@@ -2078,12 +2075,12 @@ class IDLType(IDLObject):
|
||||
|
||||
def isSpiderMonkeyInterface(self):
|
||||
""" Returns a boolean indicating whether this type is an 'interface'
|
||||
type that is implemented in SpiderMonkey. """
|
||||
type that is implemented in Spidermonkey. At the moment, this
|
||||
only returns true for the types from the TypedArray spec. """
|
||||
return self.isInterface() and (self.isArrayBuffer() or
|
||||
self.isArrayBufferView() or
|
||||
self.isSharedArrayBuffer() or
|
||||
self.isTypedArray() or
|
||||
self.isReadableStream())
|
||||
self.isTypedArray())
|
||||
|
||||
def isDictionary(self):
|
||||
return False
|
||||
@@ -2272,9 +2269,6 @@ class IDLNullableType(IDLParameterizedType):
|
||||
def isRecord(self):
|
||||
return self.inner.isRecord()
|
||||
|
||||
def isReadableStream(self):
|
||||
return self.inner.isReadableStream()
|
||||
|
||||
def isArrayBuffer(self):
|
||||
return self.inner.isArrayBuffer()
|
||||
|
||||
@@ -2634,9 +2628,6 @@ class IDLTypedefType(IDLType):
|
||||
def isRecord(self):
|
||||
return self.inner.isRecord()
|
||||
|
||||
def isReadableStream(self):
|
||||
return self.inner.isReadableStream()
|
||||
|
||||
def isDictionary(self):
|
||||
return self.inner.isDictionary()
|
||||
|
||||
@@ -2948,8 +2939,7 @@ class IDLBuiltinType(IDLType):
|
||||
'Int32Array',
|
||||
'Uint32Array',
|
||||
'Float32Array',
|
||||
'Float64Array',
|
||||
'ReadableStream',
|
||||
'Float64Array'
|
||||
)
|
||||
|
||||
TagLookup = {
|
||||
@@ -2984,8 +2974,7 @@ class IDLBuiltinType(IDLType):
|
||||
Types.Int32Array: IDLType.Tags.interface,
|
||||
Types.Uint32Array: IDLType.Tags.interface,
|
||||
Types.Float32Array: IDLType.Tags.interface,
|
||||
Types.Float64Array: IDLType.Tags.interface,
|
||||
Types.ReadableStream: IDLType.Tags.interface,
|
||||
Types.Float64Array: IDLType.Tags.interface
|
||||
}
|
||||
|
||||
def __init__(self, location, name, type):
|
||||
@@ -3032,9 +3021,6 @@ class IDLBuiltinType(IDLType):
|
||||
return (self._typeTag >= IDLBuiltinType.Types.Int8Array and
|
||||
self._typeTag <= IDLBuiltinType.Types.Float64Array)
|
||||
|
||||
def isReadableStream(self):
|
||||
return self._typeTag == IDLBuiltinType.Types.ReadableStream
|
||||
|
||||
def isInterface(self):
|
||||
# TypedArray things are interface types per the TypedArray spec,
|
||||
# but we handle them as builtins because SpiderMonkey implements
|
||||
@@ -3042,8 +3028,7 @@ class IDLBuiltinType(IDLType):
|
||||
return (self.isArrayBuffer() or
|
||||
self.isArrayBufferView() or
|
||||
self.isSharedArrayBuffer() or
|
||||
self.isTypedArray() or
|
||||
self.isReadableStream())
|
||||
self.isTypedArray())
|
||||
|
||||
def isNonCallbackInterface(self):
|
||||
# All the interfaces we can be are non-callback
|
||||
@@ -3113,7 +3098,6 @@ class IDLBuiltinType(IDLType):
|
||||
# that's not an ArrayBuffer or a callback interface
|
||||
(self.isArrayBuffer() and not other.isArrayBuffer()) or
|
||||
(self.isSharedArrayBuffer() and not other.isSharedArrayBuffer()) or
|
||||
(self.isReadableStream() and not other.isReadableStream()) or
|
||||
# ArrayBufferView is distinguishable from everything
|
||||
# that's not an ArrayBufferView or typed array.
|
||||
(self.isArrayBufferView() and not other.isArrayBufferView() and
|
||||
@@ -3223,10 +3207,7 @@ BuiltinTypes = {
|
||||
IDLBuiltinType.Types.Float32Array),
|
||||
IDLBuiltinType.Types.Float64Array:
|
||||
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float64Array",
|
||||
IDLBuiltinType.Types.Float64Array),
|
||||
IDLBuiltinType.Types.ReadableStream:
|
||||
IDLBuiltinType(BuiltinLocation("<builtin type>"), "ReadableStream",
|
||||
IDLBuiltinType.Types.ReadableStream),
|
||||
IDLBuiltinType.Types.Float64Array)
|
||||
}
|
||||
|
||||
|
||||
@@ -5255,8 +5236,7 @@ class Tokenizer(object):
|
||||
"maplike": "MAPLIKE",
|
||||
"setlike": "SETLIKE",
|
||||
"iterable": "ITERABLE",
|
||||
"namespace": "NAMESPACE",
|
||||
"ReadableStream": "READABLESTREAM",
|
||||
"namespace": "NAMESPACE"
|
||||
}
|
||||
|
||||
tokens.extend(keywords.values())
|
||||
@@ -6444,7 +6424,6 @@ class Parser(Tokenizer):
|
||||
NonAnyType : PrimitiveType Null
|
||||
| ARRAYBUFFER Null
|
||||
| SHAREDARRAYBUFFER Null
|
||||
| READABLESTREAM Null
|
||||
| OBJECT Null
|
||||
"""
|
||||
if p[1] == "object":
|
||||
@@ -6453,8 +6432,6 @@ class Parser(Tokenizer):
|
||||
type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer]
|
||||
elif p[1] == "SharedArrayBuffer":
|
||||
type = BuiltinTypes[IDLBuiltinType.Types.SharedArrayBuffer]
|
||||
elif p[1] == "ReadableStream":
|
||||
type = BuiltinTypes[IDLBuiltinType.Types.ReadableStream]
|
||||
else:
|
||||
type = BuiltinTypes[p[1]]
|
||||
|
||||
|
||||
@@ -5947,7 +5947,7 @@ void
|
||||
CanvasRenderingContext2D::PutImageData(ImageData& aImageData, double aDx,
|
||||
double aDy, ErrorResult& aError)
|
||||
{
|
||||
RootedSpiderMonkeyInterface<Uint8ClampedArray> arr(RootingCx());
|
||||
RootedTypedArray<Uint8ClampedArray> arr(RootingCx());
|
||||
DebugOnly<bool> inited = arr.Init(aImageData.GetDataObject());
|
||||
MOZ_ASSERT(inited);
|
||||
|
||||
@@ -5963,7 +5963,7 @@ CanvasRenderingContext2D::PutImageData(ImageData& aImageData, double aDx,
|
||||
double aDirtyHeight,
|
||||
ErrorResult& aError)
|
||||
{
|
||||
RootedSpiderMonkeyInterface<Uint8ClampedArray> arr(RootingCx());
|
||||
RootedTypedArray<Uint8ClampedArray> arr(RootingCx());
|
||||
DebugOnly<bool> inited = arr.Init(aImageData.GetDataObject());
|
||||
MOZ_ASSERT(inited);
|
||||
|
||||
|
||||
@@ -303,13 +303,13 @@ WebGLContext::FromDomElem(const char* funcName, TexImageTarget target, uint32_t
|
||||
uint32_t height, uint32_t depth, const dom::Element& elem,
|
||||
ErrorResult* const out_error)
|
||||
{
|
||||
if (elem.IsHTMLElement(nsGkAtoms::canvas)) {
|
||||
const dom::HTMLCanvasElement* canvas = static_cast<const dom::HTMLCanvasElement*>(&elem);
|
||||
if (canvas->IsWriteOnly()) {
|
||||
out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (elem.IsHTMLElement(nsGkAtoms::canvas)) {
|
||||
const dom::HTMLCanvasElement* canvas = static_cast<const dom::HTMLCanvasElement*>(&elem);
|
||||
if (canvas->IsWriteOnly()) {
|
||||
out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t flags = nsLayoutUtils::SFE_WANT_IMAGE_SURFACE |
|
||||
nsLayoutUtils::SFE_USE_ELEMENT_SIZE_IF_VECTOR;
|
||||
@@ -475,7 +475,7 @@ WebGLTexture::TexImage(const char* funcName, TexImageTarget target, GLint level,
|
||||
GLsizei depth, GLint border, const webgl::PackingInfo& pi,
|
||||
const TexImageSource& src)
|
||||
{
|
||||
dom::RootedSpiderMonkeyInterface<dom::Uint8ClampedArray> scopedArr(dom::RootingCx());
|
||||
dom::RootedTypedArray<dom::Uint8ClampedArray> scopedArr(dom::RootingCx());
|
||||
const auto blob = ValidateTexOrSubImage(mContext, funcName, target, width, height,
|
||||
depth, border, pi, src, &scopedArr);
|
||||
if (!blob)
|
||||
@@ -491,7 +491,7 @@ WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target, GLint lev
|
||||
const webgl::PackingInfo& pi, const TexImageSource& src)
|
||||
{
|
||||
const GLint border = 0;
|
||||
dom::RootedSpiderMonkeyInterface<dom::Uint8ClampedArray> scopedArr(dom::RootingCx());
|
||||
dom::RootedTypedArray<dom::Uint8ClampedArray> scopedArr(dom::RootingCx());
|
||||
const auto blob = ValidateTexOrSubImage(mContext, funcName, target, width, height,
|
||||
depth, border, pi, src, &scopedArr);
|
||||
if (!blob)
|
||||
|
||||
@@ -1434,7 +1434,7 @@ public:
|
||||
mDataIsJwk = false;
|
||||
|
||||
// Try ArrayBuffer
|
||||
RootedSpiderMonkeyInterface<ArrayBuffer> ab(aCx);
|
||||
RootedTypedArray<ArrayBuffer> ab(aCx);
|
||||
if (ab.Init(aKeyData)) {
|
||||
if (!mKeyData.Assign(ab)) {
|
||||
mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
|
||||
@@ -1443,7 +1443,7 @@ public:
|
||||
}
|
||||
|
||||
// Try ArrayBufferView
|
||||
RootedSpiderMonkeyInterface<ArrayBufferView> abv(aCx);
|
||||
RootedTypedArray<ArrayBufferView> abv(aCx);
|
||||
if (abv.Init(aKeyData)) {
|
||||
if (!mKeyData.Assign(abv)) {
|
||||
mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
|
||||
|
||||
+7
-239
@@ -5,7 +5,6 @@
|
||||
|
||||
#include "Fetch.h"
|
||||
#include "FetchConsumer.h"
|
||||
#include "FetchStream.h"
|
||||
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIGlobalObject.h"
|
||||
@@ -24,7 +23,6 @@
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/BodyUtil.h"
|
||||
#include "mozilla/dom/DOMError.h"
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
#include "mozilla/dom/Exceptions.h"
|
||||
#include "mozilla/dom/FetchDriver.h"
|
||||
@@ -38,7 +36,6 @@
|
||||
#include "mozilla/dom/Response.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/URLSearchParams.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/dom/workers/ServiceWorkerManager.h"
|
||||
|
||||
#include "FetchObserver.h"
|
||||
@@ -843,7 +840,7 @@ ExtractFromURLSearchParams(const URLSearchParams& aParams,
|
||||
} // namespace
|
||||
|
||||
nsresult
|
||||
ExtractByteStreamFromBody(const fetch::OwningBodyInit& aBodyInit,
|
||||
ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
|
||||
nsIInputStream** aStream,
|
||||
nsCString& aContentType,
|
||||
uint64_t& aContentLength)
|
||||
@@ -881,7 +878,7 @@ ExtractByteStreamFromBody(const fetch::OwningBodyInit& aBodyInit,
|
||||
}
|
||||
|
||||
nsresult
|
||||
ExtractByteStreamFromBody(const fetch::BodyInit& aBodyInit,
|
||||
ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
|
||||
nsIInputStream** aStream,
|
||||
nsCString& aContentType,
|
||||
uint64_t& aContentLength)
|
||||
@@ -919,54 +916,9 @@ ExtractByteStreamFromBody(const fetch::BodyInit& aBodyInit,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ExtractByteStreamFromBody(const fetch::ResponseBodyInit& aBodyInit,
|
||||
nsIInputStream** aStream,
|
||||
nsCString& aContentType,
|
||||
uint64_t& aContentLength)
|
||||
{
|
||||
MOZ_ASSERT(aStream);
|
||||
MOZ_ASSERT(!*aStream);
|
||||
|
||||
// ReadableStreams should be handled by
|
||||
// BodyExtractorReadableStream::GetAsStream.
|
||||
MOZ_ASSERT(!aBodyInit.IsReadableStream());
|
||||
|
||||
if (aBodyInit.IsArrayBuffer()) {
|
||||
const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer();
|
||||
return ExtractFromArrayBuffer(buf, aStream, aContentLength);
|
||||
}
|
||||
if (aBodyInit.IsArrayBufferView()) {
|
||||
const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView();
|
||||
return ExtractFromArrayBufferView(buf, aStream, aContentLength);
|
||||
}
|
||||
if (aBodyInit.IsBlob()) {
|
||||
const Blob& blob = aBodyInit.GetAsBlob();
|
||||
return ExtractFromBlob(blob, aStream, aContentType, aContentLength);
|
||||
}
|
||||
if (aBodyInit.IsFormData()) {
|
||||
FormData& form = aBodyInit.GetAsFormData();
|
||||
return ExtractFromFormData(form, aStream, aContentType, aContentLength);
|
||||
}
|
||||
if (aBodyInit.IsUSVString()) {
|
||||
nsAutoString str;
|
||||
str.Assign(aBodyInit.GetAsUSVString());
|
||||
return ExtractFromUSVString(str, aStream, aContentType, aContentLength);
|
||||
}
|
||||
if (aBodyInit.IsURLSearchParams()) {
|
||||
URLSearchParams& params = aBodyInit.GetAsURLSearchParams();
|
||||
return ExtractFromURLSearchParams(params, aStream, aContentType, aContentLength);
|
||||
}
|
||||
|
||||
NS_NOTREACHED("Should never reach here");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
template <class Derived>
|
||||
FetchBody<Derived>::FetchBody()
|
||||
: mWorkerPrivate(nullptr)
|
||||
, mReadableStreamBody(nullptr)
|
||||
, mBodyUsed(false)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
@@ -986,46 +938,9 @@ FetchBody<Derived>::~FetchBody()
|
||||
{
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
bool
|
||||
FetchBody<Derived>::BodyUsed() const
|
||||
{
|
||||
if (mBodyUsed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If this object is disturbed or locked, return false.
|
||||
if (mReadableStreamBody) {
|
||||
AutoJSAPI jsapi;
|
||||
// If we start tracking ownership, we need something like this.
|
||||
//if (!jsapi.Init(mOwner)) {
|
||||
// return true;
|
||||
//}
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
JS::Rooted<JSObject*> body(cx, mReadableStreamBody);
|
||||
if (JS::ReadableStreamIsDisturbed(body) ||
|
||||
JS::ReadableStreamIsLocked(body) ||
|
||||
!JS::ReadableStreamIsReadable(body)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template
|
||||
bool
|
||||
FetchBody<Request>::BodyUsed() const;
|
||||
|
||||
template
|
||||
bool
|
||||
FetchBody<Response>::BodyUsed() const;
|
||||
|
||||
template <class Derived>
|
||||
already_AddRefed<Promise>
|
||||
FetchBody<Derived>::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv)
|
||||
FetchBody<Derived>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<AbortSignal> signal = DerivedClass()->GetSignal();
|
||||
if (signal && signal->Aborted()) {
|
||||
@@ -1040,20 +955,9 @@ FetchBody<Derived>::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorRes
|
||||
|
||||
SetBodyUsed();
|
||||
|
||||
// If we already created a ReadableStreamBody we have to lock it now because
|
||||
// it may have been shared with other objects..
|
||||
if (mReadableStreamBody) {
|
||||
JS::Rooted<JSObject*> body(aCx, mReadableStreamBody);
|
||||
LockStream(aCx, body, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = DerivedClass()->GetParentObject();
|
||||
|
||||
RefPtr<Promise> promise =
|
||||
FetchBodyConsumer<Derived>::Create(global, this, signal, aType, aRv);
|
||||
FetchBodyConsumer<Derived>::Create(DerivedClass()->GetParentObject(),
|
||||
this, signal, aType, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1063,11 +967,11 @@ FetchBody<Derived>::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorRes
|
||||
|
||||
template
|
||||
already_AddRefed<Promise>
|
||||
FetchBody<Request>::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv);
|
||||
FetchBody<Request>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv);
|
||||
|
||||
template
|
||||
already_AddRefed<Promise>
|
||||
FetchBody<Response>::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv);
|
||||
FetchBody<Response>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv);
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
@@ -1097,141 +1001,5 @@ template
|
||||
void
|
||||
FetchBody<Response>::SetMimeType();
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
FetchBody<Derived>::SetReadableStreamBody(JSObject* aBody)
|
||||
{
|
||||
MOZ_ASSERT(!mReadableStreamBody);
|
||||
MOZ_ASSERT(aBody);
|
||||
mReadableStreamBody = aBody;
|
||||
}
|
||||
|
||||
template
|
||||
void
|
||||
FetchBody<Request>::SetReadableStreamBody(JSObject* aBody);
|
||||
|
||||
template
|
||||
void
|
||||
FetchBody<Response>::SetReadableStreamBody(JSObject* aBody);
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
FetchBody<Derived>::GetBody(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aBodyOut,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> inputStream;
|
||||
DerivedClass()->GetBody(getter_AddRefs(inputStream));
|
||||
|
||||
if (!inputStream) {
|
||||
aBodyOut.set(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mReadableStreamBody) {
|
||||
JS::Rooted<JSObject*> body(aCx,
|
||||
FetchStream::Create(aCx,
|
||||
DerivedClass()->GetParentObject(),
|
||||
inputStream,
|
||||
aRv));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(body);
|
||||
|
||||
// If the body has been already consumed, we close the stream.
|
||||
if (BodyUsed()) {
|
||||
LockStream(aCx, body, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mReadableStreamBody = body;
|
||||
}
|
||||
|
||||
aBodyOut.set(mReadableStreamBody);
|
||||
}
|
||||
|
||||
template
|
||||
void
|
||||
FetchBody<Request>::GetBody(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aMessage,
|
||||
ErrorResult& aRv);
|
||||
|
||||
template
|
||||
void
|
||||
FetchBody<Response>::GetBody(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aMessage,
|
||||
ErrorResult& aRv);
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
FetchBody<Derived>::LockStream(JSContext* aCx,
|
||||
JS::HandleObject aStream,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// XXXMC: TODO
|
||||
}
|
||||
|
||||
template
|
||||
void
|
||||
FetchBody<Request>::LockStream(JSContext* aCx,
|
||||
JS::HandleObject aStream,
|
||||
ErrorResult& aRv);
|
||||
|
||||
template
|
||||
void
|
||||
FetchBody<Response>::LockStream(JSContext* aCx,
|
||||
JS::HandleObject aStream,
|
||||
ErrorResult& aRv);
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
FetchBody<Derived>::MaybeTeeReadableStreamBody(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aBodyOut,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(!BodyUsed());
|
||||
|
||||
if (!mReadableStreamBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> stream(aCx, mReadableStreamBody);
|
||||
|
||||
// If this is a ReadableStream with an external source, this has been
|
||||
// generated by a Fetch. In this case, Fetch will be able to recreate it
|
||||
// again when GetBody() is called.
|
||||
if (JS::ReadableStreamGetMode(stream) == JS::ReadableStreamMode::ExternalSource) {
|
||||
aBodyOut.set(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> branch1(aCx);
|
||||
JS::Rooted<JSObject*> branch2(aCx);
|
||||
|
||||
if (!JS::ReadableStreamTee(aCx, stream, &branch1, &branch2)) {
|
||||
aRv.StealExceptionFromJSContext(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
mReadableStreamBody = branch1;
|
||||
aBodyOut.set(branch2);
|
||||
}
|
||||
|
||||
template
|
||||
void
|
||||
FetchBody<Request>::MaybeTeeReadableStreamBody(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aMessage,
|
||||
ErrorResult& aRv);
|
||||
|
||||
template
|
||||
void
|
||||
FetchBody<Response>::MaybeTeeReadableStreamBody(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aMessage,
|
||||
ErrorResult& aRv);
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
+207
-260
@@ -1,260 +1,207 @@
|
||||
/* -*- 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_Fetch_h
|
||||
#define mozilla_dom_Fetch_h
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIStreamLoader.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsError.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/RequestBinding.h"
|
||||
|
||||
class nsIGlobalObject;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
|
||||
class BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrReadableStreamOrUSVString;
|
||||
class BlobImpl;
|
||||
class InternalRequest;
|
||||
class OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
|
||||
struct ReadableStream;
|
||||
class RequestOrUSVString;
|
||||
|
||||
namespace workers {
|
||||
class WorkerPrivate;
|
||||
} // namespace workers
|
||||
|
||||
already_AddRefed<Promise>
|
||||
FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
|
||||
const RequestInit& aInit, ErrorResult& aRv);
|
||||
|
||||
nsresult
|
||||
UpdateRequestReferrer(nsIGlobalObject* aGlobal, InternalRequest* aRequest);
|
||||
|
||||
/* Deal with unwieldy long webIDL-generated type names */
|
||||
namespace fetch {
|
||||
typedef ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams BodyInit;
|
||||
typedef BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrReadableStreamOrUSVString ResponseBodyInit;
|
||||
typedef OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams OwningBodyInit;
|
||||
};
|
||||
|
||||
/*
|
||||
* Creates an nsIInputStream based on the fetch specifications 'extract a byte
|
||||
* stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract.
|
||||
* Stores content type in out param aContentType.
|
||||
*/
|
||||
nsresult
|
||||
ExtractByteStreamFromBody(const fetch::OwningBodyInit& aBodyInit,
|
||||
nsIInputStream** aStream,
|
||||
nsCString& aContentType,
|
||||
uint64_t& aContentLength);
|
||||
|
||||
/*
|
||||
* Non-owning version.
|
||||
*/
|
||||
nsresult
|
||||
ExtractByteStreamFromBody(const fetch::BodyInit& aBodyInit,
|
||||
nsIInputStream** aStream,
|
||||
nsCString& aContentType,
|
||||
uint64_t& aContentLength);
|
||||
|
||||
/*
|
||||
* Non-owning version. This method should go away when BodyInit will contain
|
||||
* ReadableStream.
|
||||
*/
|
||||
nsresult
|
||||
ExtractByteStreamFromBody(const fetch::ResponseBodyInit& aBodyInit,
|
||||
nsIInputStream** aStream,
|
||||
nsCString& aContentType,
|
||||
uint64_t& aContentLength);
|
||||
|
||||
template <class Derived> class FetchBodyConsumer;
|
||||
|
||||
enum FetchConsumeType
|
||||
{
|
||||
CONSUME_ARRAYBUFFER,
|
||||
CONSUME_BLOB,
|
||||
CONSUME_FORMDATA,
|
||||
CONSUME_JSON,
|
||||
CONSUME_TEXT,
|
||||
};
|
||||
|
||||
class FetchStreamHolder
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
|
||||
NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
|
||||
|
||||
virtual void NullifyStream() = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* FetchBody's body consumption uses nsIInputStreamPump to read from the
|
||||
* underlying stream to a block of memory, which is then adopted by
|
||||
* ContinueConsumeBody() and converted to the right type based on the JS
|
||||
* function called.
|
||||
*
|
||||
* Use of the nsIInputStreamPump complicates things on the worker thread.
|
||||
* The solution used here is similar to WebSockets.
|
||||
* The difference is that we are only interested in completion and not data
|
||||
* events, and nsIInputStreamPump can only deliver completion on the main thread.
|
||||
*
|
||||
* Before starting the pump on the main thread, we addref the FetchBody to keep
|
||||
* it alive. Then we add a feature, to track the status of the worker.
|
||||
*
|
||||
* ContinueConsumeBody() is the function that cleans things up in both success
|
||||
* and error conditions and so all callers call it with the appropriate status.
|
||||
*
|
||||
* Once the read is initiated on the main thread there are two possibilities.
|
||||
*
|
||||
* 1) Pump finishes before worker has finished Running.
|
||||
* In this case we adopt the data and dispatch a runnable to the worker,
|
||||
* which derefs FetchBody and removes the feature and resolves the Promise.
|
||||
*
|
||||
* 2) Pump still working while worker has stopped Running.
|
||||
* The feature is Notify()ed and ContinueConsumeBody() is called with
|
||||
* NS_BINDING_ABORTED. We first Cancel() the pump using a sync runnable to
|
||||
* ensure that mFetchBody remains alive (since mConsumeBodyPump is strongly
|
||||
* held by it) until pump->Cancel() is called. OnStreamComplete() will not
|
||||
* do anything if the error code is NS_BINDING_ABORTED, so we don't have to
|
||||
* worry about keeping anything alive.
|
||||
*
|
||||
* The pump is always released on the main thread.
|
||||
*/
|
||||
template <class Derived>
|
||||
class FetchBody : public FetchStreamHolder
|
||||
{
|
||||
public:
|
||||
friend class FetchBodyConsumer<Derived>;
|
||||
|
||||
bool
|
||||
BodyUsed() const;
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ArrayBuffer(JSContext* aCx, ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(aCx, CONSUME_ARRAYBUFFER, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Blob(JSContext* aCx, ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(aCx, CONSUME_BLOB, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
FormData(JSContext* aCx, ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(aCx, CONSUME_FORMDATA, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Json(JSContext* aCx, ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(aCx, CONSUME_JSON, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Text(JSContext* aCx, ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(aCx, CONSUME_TEXT, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
GetBody(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aBodyOut,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// If the body contains a ReadableStream body object, this method produces a
|
||||
// tee() of it.
|
||||
void
|
||||
MaybeTeeReadableStreamBody(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aBodyOut,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// Utility public methods accessed by various runnables.
|
||||
|
||||
void
|
||||
SetBodyUsed()
|
||||
{
|
||||
mBodyUsed = true;
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
MimeType() const
|
||||
{
|
||||
return mMimeType;
|
||||
}
|
||||
|
||||
void
|
||||
NullifyStream() override
|
||||
{
|
||||
mReadableStreamBody = nullptr;
|
||||
}
|
||||
|
||||
virtual AbortSignal*
|
||||
GetSignal() const = 0;
|
||||
|
||||
protected:
|
||||
FetchBody();
|
||||
|
||||
// Always set whenever the FetchBody is created on the worker thread.
|
||||
workers::WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
// This is the ReadableStream exposed to content. Its underlying source is a FetchStream object.
|
||||
JS::Heap<JSObject*> mReadableStreamBody;
|
||||
|
||||
virtual ~FetchBody();
|
||||
|
||||
void
|
||||
SetMimeType();
|
||||
|
||||
void
|
||||
SetReadableStreamBody(JSObject* aBody);
|
||||
|
||||
private:
|
||||
Derived*
|
||||
DerivedClass() const
|
||||
{
|
||||
return static_cast<Derived*>(const_cast<FetchBody*>(this));
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv);
|
||||
|
||||
void
|
||||
LockStream(JSContext* aCx, JS::HandleObject aStream, ErrorResult& aRv);
|
||||
|
||||
bool
|
||||
IsOnTargetThread()
|
||||
{
|
||||
return NS_IsMainThread() == !mWorkerPrivate;
|
||||
}
|
||||
|
||||
void
|
||||
AssertIsOnTargetThread()
|
||||
{
|
||||
MOZ_ASSERT(IsOnTargetThread());
|
||||
}
|
||||
|
||||
// Only ever set once, always on target thread.
|
||||
bool mBodyUsed;
|
||||
nsCString mMimeType;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_Fetch_h
|
||||
/* -*- 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_Fetch_h
|
||||
#define mozilla_dom_Fetch_h
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIStreamLoader.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsError.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/RequestBinding.h"
|
||||
|
||||
class nsIGlobalObject;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
|
||||
class BlobImpl;
|
||||
class InternalRequest;
|
||||
class OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
|
||||
class RequestOrUSVString;
|
||||
|
||||
namespace workers {
|
||||
class WorkerPrivate;
|
||||
} // namespace workers
|
||||
|
||||
already_AddRefed<Promise>
|
||||
FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
|
||||
const RequestInit& aInit, ErrorResult& aRv);
|
||||
|
||||
nsresult
|
||||
UpdateRequestReferrer(nsIGlobalObject* aGlobal, InternalRequest* aRequest);
|
||||
|
||||
/*
|
||||
* Creates an nsIInputStream based on the fetch specifications 'extract a byte
|
||||
* stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract.
|
||||
* Stores content type in out param aContentType.
|
||||
*/
|
||||
nsresult
|
||||
ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
|
||||
nsIInputStream** aStream,
|
||||
nsCString& aContentType,
|
||||
uint64_t& aContentLength);
|
||||
|
||||
/*
|
||||
* Non-owning version.
|
||||
*/
|
||||
nsresult
|
||||
ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
|
||||
nsIInputStream** aStream,
|
||||
nsCString& aContentType,
|
||||
uint64_t& aContentLength);
|
||||
|
||||
template <class Derived> class FetchBodyConsumer;
|
||||
|
||||
enum FetchConsumeType
|
||||
{
|
||||
CONSUME_ARRAYBUFFER,
|
||||
CONSUME_BLOB,
|
||||
CONSUME_FORMDATA,
|
||||
CONSUME_JSON,
|
||||
CONSUME_TEXT,
|
||||
};
|
||||
|
||||
/*
|
||||
* FetchBody's body consumption uses nsIInputStreamPump to read from the
|
||||
* underlying stream to a block of memory, which is then adopted by
|
||||
* ContinueConsumeBody() and converted to the right type based on the JS
|
||||
* function called.
|
||||
*
|
||||
* Use of the nsIInputStreamPump complicates things on the worker thread.
|
||||
* The solution used here is similar to WebSockets.
|
||||
* The difference is that we are only interested in completion and not data
|
||||
* events, and nsIInputStreamPump can only deliver completion on the main thread.
|
||||
*
|
||||
* Before starting the pump on the main thread, we addref the FetchBody to keep
|
||||
* it alive. Then we add a feature, to track the status of the worker.
|
||||
*
|
||||
* ContinueConsumeBody() is the function that cleans things up in both success
|
||||
* and error conditions and so all callers call it with the appropriate status.
|
||||
*
|
||||
* Once the read is initiated on the main thread there are two possibilities.
|
||||
*
|
||||
* 1) Pump finishes before worker has finished Running.
|
||||
* In this case we adopt the data and dispatch a runnable to the worker,
|
||||
* which derefs FetchBody and removes the feature and resolves the Promise.
|
||||
*
|
||||
* 2) Pump still working while worker has stopped Running.
|
||||
* The feature is Notify()ed and ContinueConsumeBody() is called with
|
||||
* NS_BINDING_ABORTED. We first Cancel() the pump using a sync runnable to
|
||||
* ensure that mFetchBody remains alive (since mConsumeBodyPump is strongly
|
||||
* held by it) until pump->Cancel() is called. OnStreamComplete() will not
|
||||
* do anything if the error code is NS_BINDING_ABORTED, so we don't have to
|
||||
* worry about keeping anything alive.
|
||||
*
|
||||
* The pump is always released on the main thread.
|
||||
*/
|
||||
template <class Derived>
|
||||
class FetchBody
|
||||
{
|
||||
public:
|
||||
friend class FetchBodyConsumer<Derived>;
|
||||
|
||||
NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
|
||||
NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
|
||||
|
||||
bool
|
||||
BodyUsed() const { return mBodyUsed; }
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ArrayBuffer(ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(CONSUME_ARRAYBUFFER, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Blob(ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(CONSUME_BLOB, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
FormData(ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(CONSUME_FORMDATA, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Json(ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(CONSUME_JSON, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Text(ErrorResult& aRv)
|
||||
{
|
||||
return ConsumeBody(CONSUME_TEXT, aRv);
|
||||
}
|
||||
|
||||
// Utility public methods accessed by various runnables.
|
||||
|
||||
void
|
||||
SetBodyUsed()
|
||||
{
|
||||
mBodyUsed = true;
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
MimeType() const
|
||||
{
|
||||
return mMimeType;
|
||||
}
|
||||
|
||||
virtual AbortSignal*
|
||||
GetSignal() const = 0;
|
||||
|
||||
protected:
|
||||
FetchBody();
|
||||
|
||||
// Always set whenever the FetchBody is created on the worker thread.
|
||||
workers::WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
virtual ~FetchBody();
|
||||
|
||||
void
|
||||
SetMimeType();
|
||||
private:
|
||||
Derived*
|
||||
DerivedClass() const
|
||||
{
|
||||
return static_cast<Derived*>(const_cast<FetchBody*>(this));
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ConsumeBody(FetchConsumeType aType, ErrorResult& aRv);
|
||||
|
||||
bool
|
||||
IsOnTargetThread()
|
||||
{
|
||||
return NS_IsMainThread() == !mWorkerPrivate;
|
||||
}
|
||||
|
||||
void
|
||||
AssertIsOnTargetThread()
|
||||
{
|
||||
MOZ_ASSERT(IsOnTargetThread());
|
||||
}
|
||||
|
||||
// Only ever set once, always on target thread.
|
||||
bool mBodyUsed;
|
||||
nsCString mMimeType;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_Fetch_h
|
||||
@@ -1,553 +0,0 @@
|
||||
/* -*- 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 "FetchStream.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsITransport.h"
|
||||
#include "nsIStreamTransportService.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
#include "Workers.h"
|
||||
|
||||
#include "mozilla/dom/DOMError.h"
|
||||
|
||||
#define FETCH_STREAM_FLAG 0
|
||||
|
||||
static NS_DEFINE_CID(kStreamTransportServiceCID,
|
||||
NS_STREAMTRANSPORTSERVICE_CID);
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
using namespace workers;
|
||||
|
||||
namespace {
|
||||
|
||||
class FetchStreamWorkerHolder final : public WorkerHolder
|
||||
{
|
||||
public:
|
||||
explicit FetchStreamWorkerHolder(FetchStream* aStream)
|
||||
: WorkerHolder()
|
||||
, mStream(aStream)
|
||||
, mWasNotified(false)
|
||||
{}
|
||||
|
||||
bool Notify(Status aStatus) override
|
||||
{
|
||||
if (!mWasNotified) {
|
||||
mWasNotified = true;
|
||||
mStream->Close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
WorkerPrivate* GetWorkerPrivate() const
|
||||
{
|
||||
return mWorkerPrivate;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<FetchStream> mStream;
|
||||
bool mWasNotified;
|
||||
};
|
||||
|
||||
class FetchStreamWorkerHolderShutdown final : public WorkerControlRunnable
|
||||
{
|
||||
public:
|
||||
FetchStreamWorkerHolderShutdown(WorkerPrivate* aWorkerPrivate,
|
||||
UniquePtr<WorkerHolder>&& aHolder,
|
||||
nsCOMPtr<nsIGlobalObject>&& aGlobal,
|
||||
RefPtr<FetchStreamHolder>&& aStreamHolder)
|
||||
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
||||
, mHolder(Move(aHolder))
|
||||
, mGlobal(Move(aGlobal))
|
||||
, mStreamHolder(Move(aStreamHolder))
|
||||
{}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
mHolder = nullptr;
|
||||
mGlobal = nullptr;
|
||||
|
||||
mStreamHolder->NullifyStream();
|
||||
mStreamHolder = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// This runnable starts from a JS Thread. We need to disable a couple of
|
||||
// assertions by overriding the following methods.
|
||||
|
||||
bool
|
||||
PreDispatch(WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
|
||||
{}
|
||||
|
||||
private:
|
||||
UniquePtr<WorkerHolder> mHolder;
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
RefPtr<FetchStreamHolder> mStreamHolder;
|
||||
};
|
||||
|
||||
} // anonymous
|
||||
|
||||
NS_IMPL_ISUPPORTS(FetchStream, nsIInputStreamCallback, nsIObserver,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
/* static */ JSObject*
|
||||
FetchStream::Create(JSContext* aCx, FetchStreamHolder* aStreamHolder,
|
||||
nsIGlobalObject* aGlobal, nsIInputStream* aInputStream,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aCx);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aInputStream);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aStreamHolder);
|
||||
|
||||
RefPtr<FetchStream> stream = new FetchStream(aGlobal, aStreamHolder, aInputStream);
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
if (NS_WARN_IF(!os)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
aRv = os->AddObserver(stream, DOM_WINDOW_DESTROYED_TOPIC, true);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} else {
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
UniquePtr<FetchStreamWorkerHolder> holder(
|
||||
new FetchStreamWorkerHolder(stream));
|
||||
if (NS_WARN_IF(!holder->HoldWorker(workerPrivate, Closing))) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Note, this will create a ref-cycle between the holder and the stream.
|
||||
// The cycle is broken when the stream is closed or the worker begins
|
||||
// shutting down.
|
||||
stream->mWorkerHolder = Move(holder);
|
||||
}
|
||||
|
||||
if (!JS::HasReadableStreamCallbacks(aCx)) {
|
||||
JS::SetReadableStreamCallbacks(aCx,
|
||||
&FetchStream::RequestDataCallback,
|
||||
&FetchStream::WriteIntoReadRequestCallback,
|
||||
&FetchStream::CancelCallback,
|
||||
&FetchStream::ClosedCallback,
|
||||
&FetchStream::ErroredCallback,
|
||||
&FetchStream::FinalizeCallback);
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> body(aCx,
|
||||
JS::NewReadableExternalSourceStreamObject(aCx, stream, FETCH_STREAM_FLAG));
|
||||
if (!body) {
|
||||
aRv.StealExceptionFromJSContext(aCx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
stream->mReadableStream = body;
|
||||
|
||||
// JS engine will call the finalize callback.
|
||||
NS_ADDREF(stream.get());
|
||||
return body;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
FetchStream::RequestDataCallback(JSContext* aCx,
|
||||
JS::HandleObject aStream,
|
||||
void* aUnderlyingSource,
|
||||
uint8_t aFlags,
|
||||
size_t aDesiredSize)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
|
||||
MOZ_DIAGNOSTIC_ASSERT(JS::ReadableStreamIsDisturbed(aStream));
|
||||
|
||||
RefPtr<FetchStream> stream = static_cast<FetchStream*>(aUnderlyingSource);
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(stream->mState == eWaiting ||
|
||||
stream->mState == eChecking);
|
||||
|
||||
if (stream->mState == eChecking) {
|
||||
// If we are looking for more data, there is nothing else we should do:
|
||||
// let's move this checking operation in a reading.
|
||||
MOZ_ASSERT(stream->mInputStream);
|
||||
stream->mState = eReading;
|
||||
return;
|
||||
}
|
||||
|
||||
stream->mState = eReading;
|
||||
|
||||
if (!stream->mInputStream) {
|
||||
// This is the first use of the stream. Let's convert the
|
||||
// mOriginalInputStream into an nsIAsyncInputStream.
|
||||
MOZ_ASSERT(stream->mOriginalInputStream);
|
||||
|
||||
bool nonBlocking = false;
|
||||
nsresult rv = stream->mOriginalInputStream->IsNonBlocking(&nonBlocking);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
stream->ErrorPropagation(aCx, aStream, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> asyncStream =
|
||||
do_QueryInterface(stream->mOriginalInputStream);
|
||||
if (!nonBlocking || !asyncStream) {
|
||||
nsCOMPtr<nsIStreamTransportService> sts =
|
||||
do_GetService(kStreamTransportServiceCID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
stream->ErrorPropagation(aCx, aStream, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITransport> transport;
|
||||
rv = sts->CreateInputTransport(stream->mOriginalInputStream,
|
||||
/* aStartOffset */ 0,
|
||||
/* aReadLimit */ -1,
|
||||
/* aCloseWhenDone */ true,
|
||||
getter_AddRefs(transport));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
stream->ErrorPropagation(aCx, aStream, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> wrapper;
|
||||
rv = transport->OpenInputStream(/* aFlags */ 0,
|
||||
/* aSegmentSize */ 0,
|
||||
/* aSegmentCount */ 0,
|
||||
getter_AddRefs(wrapper));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
stream->ErrorPropagation(aCx, aStream, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
asyncStream = do_QueryInterface(wrapper);
|
||||
}
|
||||
|
||||
stream->mInputStream = asyncStream;
|
||||
stream->mOriginalInputStream = nullptr;
|
||||
}
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(stream->mInputStream);
|
||||
MOZ_DIAGNOSTIC_ASSERT(!stream->mOriginalInputStream);
|
||||
|
||||
nsresult rv =
|
||||
stream->mInputStream->AsyncWait(stream, 0, 0, stream->mOwningEventTarget);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
stream->ErrorPropagation(aCx, aStream, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
// All good.
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
FetchStream::WriteIntoReadRequestCallback(JSContext* aCx,
|
||||
JS::HandleObject aStream,
|
||||
void* aUnderlyingSource,
|
||||
uint8_t aFlags, void* aBuffer,
|
||||
size_t aLength, size_t* aByteWritten)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aBuffer);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aByteWritten);
|
||||
|
||||
RefPtr<FetchStream> stream = static_cast<FetchStream*>(aUnderlyingSource);
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(stream->mInputStream);
|
||||
MOZ_DIAGNOSTIC_ASSERT(stream->mState == eWriting);
|
||||
stream->mState = eChecking;
|
||||
|
||||
uint32_t written;
|
||||
nsresult rv =
|
||||
stream->mInputStream->Read(static_cast<char*>(aBuffer), aLength, &written);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
stream->ErrorPropagation(aCx, aStream, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
*aByteWritten = written;
|
||||
|
||||
if (written == 0) {
|
||||
stream->CloseAndReleaseObjects(aCx, aStream);
|
||||
return;
|
||||
}
|
||||
|
||||
rv = stream->mInputStream->AsyncWait(stream, 0, 0, stream->mOwningEventTarget);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
stream->ErrorPropagation(aCx, aStream, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
// All good.
|
||||
}
|
||||
|
||||
/* static */ JS::Value
|
||||
FetchStream::CancelCallback(JSContext* aCx, JS::HandleObject aStream,
|
||||
void* aUnderlyingSource, uint8_t aFlags,
|
||||
JS::HandleValue aReason)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
|
||||
|
||||
RefPtr<FetchStream> stream = static_cast<FetchStream*>(aUnderlyingSource);
|
||||
|
||||
if (stream->mInputStream) {
|
||||
stream->mInputStream->CloseWithStatus(NS_BASE_STREAM_CLOSED);
|
||||
}
|
||||
|
||||
stream->ReleaseObjects();
|
||||
|
||||
return JS::UndefinedValue();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
FetchStream::ClosedCallback(JSContext* aCx, JS::HandleObject aStream,
|
||||
void* aUnderlyingSource, uint8_t aFlags)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
FetchStream::ErroredCallback(JSContext* aCx, JS::HandleObject aStream,
|
||||
void* aUnderlyingSource, uint8_t aFlags,
|
||||
JS::HandleValue aReason)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
|
||||
}
|
||||
|
||||
void
|
||||
FetchStream::FinalizeCallback(void* aUnderlyingSource, uint8_t aFlags)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
|
||||
|
||||
// This can be called in any thread.
|
||||
|
||||
RefPtr<FetchStream> stream =
|
||||
dont_AddRef(static_cast<FetchStream*>(aUnderlyingSource));
|
||||
|
||||
stream->ReleaseObjects();
|
||||
}
|
||||
|
||||
FetchStream::FetchStream(nsIGlobalObject* aGlobal,
|
||||
FetchStreamHolder* aStreamHolder,
|
||||
nsIInputStream* aInputStream)
|
||||
: mState(eWaiting)
|
||||
, mGlobal(aGlobal)
|
||||
, mStreamHolder(aStreamHolder)
|
||||
, mOriginalInputStream(aInputStream)
|
||||
, mOwningEventTarget(nullptr)
|
||||
, mReadableStream(nullptr)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aInputStream);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aStreamHolder);
|
||||
}
|
||||
|
||||
FetchStream::~FetchStream()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
FetchStream::ErrorPropagation(JSContext* aCx, JS::HandleObject aStream,
|
||||
nsresult aError)
|
||||
{
|
||||
// Nothing to do.
|
||||
if (mState == eClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's close the stream.
|
||||
if (aError == NS_BASE_STREAM_CLOSED) {
|
||||
CloseAndReleaseObjects(aCx, aStream);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal);
|
||||
|
||||
// Let's use a generic error.
|
||||
RefPtr<DOMError> error = new DOMError(window, NS_ERROR_DOM_TYPE_ERR);
|
||||
|
||||
JS::Rooted<JS::Value> errorValue(aCx);
|
||||
if (ToJSValue(aCx, error, &errorValue)) {
|
||||
JS::ReadableStreamError(aCx, aStream, errorValue);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FetchStream::OnInputStreamReady(nsIAsyncInputStream* aStream)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aStream);
|
||||
|
||||
// Already closed. We have nothing else to do here.
|
||||
if (mState == eClosed) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(mInputStream);
|
||||
MOZ_DIAGNOSTIC_ASSERT(mState == eReading || mState == eChecking);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(mGlobal))) {
|
||||
// Without JSContext we are not able to close the stream or to propagate the
|
||||
// error.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::Rooted<JSObject*> stream(cx, mReadableStream);
|
||||
|
||||
uint64_t size = 0;
|
||||
nsresult rv = mInputStream->Available(&size);
|
||||
if (NS_SUCCEEDED(rv) && size == 0) {
|
||||
// In theory this should not happen. If size is 0, the stream should be
|
||||
// considered closed.
|
||||
rv = NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
// No warning for stream closed.
|
||||
if (rv == NS_BASE_STREAM_CLOSED || NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ErrorPropagation(cx, stream, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// This extra checking is completed. Let's wait for the next read request.
|
||||
if (mState == eChecking) {
|
||||
mState = eWaiting;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mState = eWriting;
|
||||
JS::ReadableStreamUpdateDataAvailableFromSource(cx, stream, size);
|
||||
|
||||
// The WriteInto callback changes mState to eChecking.
|
||||
MOZ_DIAGNOSTIC_ASSERT(mState == eChecking);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
FetchStream::RetrieveInputStream(void* aUnderlyingReadableStreamSource,
|
||||
nsIInputStream** aInputStream)
|
||||
{
|
||||
MOZ_ASSERT(aUnderlyingReadableStreamSource);
|
||||
MOZ_ASSERT(aInputStream);
|
||||
|
||||
RefPtr<FetchStream> stream =
|
||||
static_cast<FetchStream*>(aUnderlyingReadableStreamSource);
|
||||
|
||||
// if mOriginalInputStream is null, the reading already started. We don't want
|
||||
// to expose the internal inputStream.
|
||||
if (NS_WARN_IF(!stream->mOriginalInputStream)) {
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream = stream->mOriginalInputStream;
|
||||
inputStream.forget(aInputStream);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
FetchStream::Close()
|
||||
{
|
||||
if (mState == eClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(mGlobal))) {
|
||||
ReleaseObjects();
|
||||
return;
|
||||
}
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::Rooted<JSObject*> stream(cx, mReadableStream);
|
||||
|
||||
CloseAndReleaseObjects(cx, stream);
|
||||
}
|
||||
|
||||
void
|
||||
FetchStream::CloseAndReleaseObjects(JSContext* aCx, JS::HandleObject aStream)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(mState != eClosed);
|
||||
|
||||
ReleaseObjects();
|
||||
|
||||
if (JS::ReadableStreamIsReadable(aStream)) {
|
||||
JS::ReadableStreamClose(aCx, aStream);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FetchStream::ReleaseObjects()
|
||||
{
|
||||
if (mState == eClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
mState = eClosed;
|
||||
|
||||
if (mWorkerHolder) {
|
||||
RefPtr<FetchStreamWorkerHolderShutdown> r =
|
||||
new FetchStreamWorkerHolderShutdown(
|
||||
static_cast<FetchStreamWorkerHolder*>(mWorkerHolder.get())->GetWorkerPrivate(),
|
||||
Move(mWorkerHolder), Move(mGlobal), Move(mStreamHolder));
|
||||
r->Dispatch();
|
||||
} else {
|
||||
RefPtr<FetchStream> self = this;
|
||||
RefPtr<Runnable> r = NS_NewRunnableFunction(
|
||||
[self] () {
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
if (os) {
|
||||
os->RemoveObserver(self, DOM_WINDOW_DESTROYED_TOPIC);
|
||||
}
|
||||
self->mGlobal = nullptr;
|
||||
|
||||
self->mStreamHolder->NullifyStream();
|
||||
self->mStreamHolder = nullptr;
|
||||
});
|
||||
|
||||
NS_DispatchToMainThread(r);
|
||||
}
|
||||
}
|
||||
|
||||
// nsIObserver
|
||||
// -----------
|
||||
|
||||
NS_IMETHODIMP
|
||||
FetchStream::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
MOZ_ASSERT(strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal);
|
||||
if (SameCOMIdentity(aSubject, window)) {
|
||||
Close();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
@@ -1,135 +0,0 @@
|
||||
/* -*- 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_FetchStream_h
|
||||
#define mozilla_dom_FetchStream_h
|
||||
|
||||
#include "Fetch.h"
|
||||
#include "jsapi.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsWeakReference.h"
|
||||
|
||||
class nsIGlobalObject;
|
||||
|
||||
class nsIInputStream;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace workers {
|
||||
class WorkerHolder;
|
||||
}
|
||||
|
||||
class FetchStreamHolder;
|
||||
|
||||
class FetchStream final : public nsIInputStreamCallback
|
||||
, public nsIObserver
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIINPUTSTREAMCALLBACK
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static JSObject*
|
||||
Create(JSContext* aCx, FetchStreamHolder* aStreamHolder,
|
||||
nsIGlobalObject* aGlobal, nsIInputStream* aInputStream,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
Close();
|
||||
|
||||
static nsresult
|
||||
RetrieveInputStream(void* aUnderlyingReadableStreamSource,
|
||||
nsIInputStream** aInputStream);
|
||||
private:
|
||||
FetchStream(nsIGlobalObject* aGlobal,
|
||||
FetchStreamHolder* aStreamHolder,
|
||||
nsIInputStream* aInputStream);
|
||||
~FetchStream();
|
||||
|
||||
static void
|
||||
RequestDataCallback(JSContext* aCx, JS::HandleObject aStream,
|
||||
void* aUnderlyingSource, uint8_t aFlags,
|
||||
size_t aDesiredSize);
|
||||
|
||||
static void
|
||||
WriteIntoReadRequestCallback(JSContext* aCx, JS::HandleObject aStream,
|
||||
void* aUnderlyingSource, uint8_t aFlags,
|
||||
void* aBuffer, size_t aLength,
|
||||
size_t* aByteWritten);
|
||||
|
||||
static JS::Value
|
||||
CancelCallback(JSContext* aCx, JS::HandleObject aStream,
|
||||
void* aUnderlyingSource, uint8_t aFlags,
|
||||
JS::HandleValue aReason);
|
||||
|
||||
static void
|
||||
ClosedCallback(JSContext* aCx, JS::HandleObject aStream,
|
||||
void* aUnderlyingSource, uint8_t aFlags);
|
||||
|
||||
static void
|
||||
ErroredCallback(JSContext* aCx, JS::HandleObject aStream,
|
||||
void* aUnderlyingSource, uint8_t aFlags,
|
||||
JS::HandleValue reason);
|
||||
|
||||
static void
|
||||
FinalizeCallback(void* aUnderlyingSource, uint8_t aFlags);
|
||||
|
||||
void
|
||||
ErrorPropagation(JSContext* aCx, JS::HandleObject aStream, nsresult aRv);
|
||||
|
||||
void
|
||||
CloseAndReleaseObjects(JSContext* aCx, JS::HandleObject aStream);
|
||||
|
||||
void
|
||||
ReleaseObjects();
|
||||
|
||||
// Common methods
|
||||
|
||||
enum State {
|
||||
// RequestDataCallback has not been called yet. We haven't started to read
|
||||
// data from the stream yet.
|
||||
eWaiting,
|
||||
|
||||
// We are reading data in a separate I/O thread.
|
||||
eReading,
|
||||
|
||||
// We are ready to write something in the JS Buffer.
|
||||
eWriting,
|
||||
|
||||
// After a writing, we want to check if the stream is closed. After the
|
||||
// check, we go back to eWaiting. If a reading request happens in the
|
||||
// meantime, we move to eReading state.
|
||||
eChecking,
|
||||
|
||||
// Operation completed.
|
||||
eClosed,
|
||||
};
|
||||
|
||||
// Touched only on the target thread.
|
||||
State mState;
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
RefPtr<FetchStreamHolder> mStreamHolder;
|
||||
nsCOMPtr<nsIEventTarget> mOwningEventTarget;
|
||||
|
||||
// This is the original inputStream received during the CTOR. It will be
|
||||
// converted into an nsIAsyncInputStream and stored into mInputStream at the
|
||||
// first use.
|
||||
nsCOMPtr<nsIInputStream> mOriginalInputStream;
|
||||
nsCOMPtr<nsIAsyncInputStream> mInputStream;
|
||||
|
||||
UniquePtr<workers::WorkerHolder> mWorkerHolder;
|
||||
|
||||
JS::Heap<JSObject*> mReadableStream;
|
||||
};
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
||||
#endif // mozilla_dom_FetchStream_h
|
||||
+1
-18
@@ -29,24 +29,7 @@ using namespace workers;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Request)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Request)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(Request)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Request)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mHeaders)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Request)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHeaders)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Request)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReadableStreamBody)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Request, mOwner, mHeaders)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Request)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
|
||||
@@ -128,8 +128,6 @@ public:
|
||||
void
|
||||
GetBody(nsIInputStream** aStream) { return mRequest->GetBody(aStream); }
|
||||
|
||||
using FetchBody::GetBody;
|
||||
|
||||
void
|
||||
SetBody(nsIInputStream* aStream) { return mRequest->SetBody(aStream); }
|
||||
|
||||
|
||||
+11
-110
@@ -12,14 +12,12 @@
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/FetchBinding.h"
|
||||
#include "mozilla/dom/ResponseBinding.h"
|
||||
#include "mozilla/dom/Headers.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/URL.h"
|
||||
|
||||
#include "nsDOMString.h"
|
||||
|
||||
#include "FetchStream.h"
|
||||
#include "InternalResponse.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
@@ -28,27 +26,7 @@ namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Response)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Response)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(Response)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Response)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mHeaders)
|
||||
|
||||
tmp->mReadableStreamBody = nullptr;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Response)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHeaders)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Response)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReadableStreamBody)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Response, mOwner, mHeaders)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Response)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
@@ -64,14 +42,10 @@ Response::Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse
|
||||
MOZ_ASSERT(aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Immutable ||
|
||||
aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Response);
|
||||
SetMimeType();
|
||||
|
||||
// Keep a firm grip!
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
Response::~Response()
|
||||
{
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<Response>
|
||||
@@ -131,7 +105,7 @@ Response::Redirect(const GlobalObject& aGlobal, const nsAString& aUrl,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Optional<Nullable<fetch::ResponseBodyInit>> body;
|
||||
Optional<Nullable<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>> body;
|
||||
ResponseInit init;
|
||||
init.mStatus = aStatus;
|
||||
RefPtr<Response> r = Response::Constructor(aGlobal, body, init, aRv);
|
||||
@@ -152,7 +126,7 @@ Response::Redirect(const GlobalObject& aGlobal, const nsAString& aUrl,
|
||||
|
||||
/*static*/ already_AddRefed<Response>
|
||||
Response::Constructor(const GlobalObject& aGlobal,
|
||||
const Optional<Nullable<fetch::ResponseBodyInit>>& aBody,
|
||||
const Optional<Nullable<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>>& aBody,
|
||||
const ResponseInit& aInit, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
@@ -227,56 +201,13 @@ Response::Constructor(const GlobalObject& aGlobal,
|
||||
nsCOMPtr<nsIInputStream> bodyStream;
|
||||
nsCString contentType;
|
||||
uint64_t bodySize = 0;
|
||||
|
||||
if (aBody.Value().IsReadableStream()) {
|
||||
const ReadableStream& readableStream =
|
||||
aBody.Value().GetAsReadableStream();
|
||||
|
||||
JS::Rooted<JSObject*> readableStreamObj(aGlobal.Context(),
|
||||
readableStream.Obj());
|
||||
|
||||
if (JS::ReadableStreamIsDisturbed(readableStreamObj) ||
|
||||
JS::ReadableStreamIsLocked(readableStreamObj) ||
|
||||
!JS::ReadableStreamIsReadable(readableStreamObj)) {
|
||||
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
r->SetReadableStreamBody(readableStreamObj);
|
||||
|
||||
// XXXMC: TODO
|
||||
MOZ_ASSERT(JS::ReadableStreamGetMode(readableStreamObj) !=
|
||||
JS::ReadableStreamMode::ExternalSource);
|
||||
|
||||
void* underlyingSource = nullptr;
|
||||
if (!JS::ReadableStreamGetExternalUnderlyingSource(aGlobal.Context(),
|
||||
readableStreamObj,
|
||||
&underlyingSource)) {
|
||||
aRv.StealExceptionFromJSContext(aGlobal.Context());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bodySize = InternalResponse::UNKNOWN_BODY_SIZE;
|
||||
|
||||
MOZ_ASSERT(underlyingSource);
|
||||
aRv = FetchStream::RetrieveInputStream(underlyingSource,
|
||||
getter_AddRefs(bodyStream));
|
||||
|
||||
// Releasing of the external source is needed in order to avoid an extra stream lock.
|
||||
JS::ReadableStreamReleaseExternalUnderlyingSource(readableStreamObj);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
aRv = ExtractByteStreamFromBody(aBody.Value().Value(),
|
||||
getter_AddRefs(bodyStream),
|
||||
contentType,
|
||||
bodySize);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
aRv = ExtractByteStreamFromBody(aBody.Value().Value(),
|
||||
getter_AddRefs(bodyStream),
|
||||
contentType,
|
||||
bodySize);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
internalResponse->SetBody(bodyStream, bodySize);
|
||||
|
||||
if (!contentType.IsVoid() &&
|
||||
@@ -297,7 +228,7 @@ Response::Constructor(const GlobalObject& aGlobal,
|
||||
}
|
||||
|
||||
already_AddRefed<Response>
|
||||
Response::Clone(JSContext* aCx, ErrorResult& aRv) const
|
||||
Response::Clone(ErrorResult& aRv) const
|
||||
{
|
||||
if (BodyUsed()) {
|
||||
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
|
||||
@@ -306,26 +237,11 @@ Response::Clone(JSContext* aCx, ErrorResult& aRv) const
|
||||
|
||||
RefPtr<InternalResponse> ir = mInternalResponse->Clone();
|
||||
RefPtr<Response> response = new Response(mOwner, ir, mSignal);
|
||||
|
||||
JS::Rooted<JSObject*> body(aCx);
|
||||
MaybeTeeReadableStreamBody(aCx, &body, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (body) {
|
||||
// Maybe we have a body, but we receive null from MaybeTeeReadableStreamBody
|
||||
// if this body is a native stream. In this case, the InternalResponse will
|
||||
// have a clone of the native body and the ReadableStream will be created
|
||||
// lazily if needed.
|
||||
response->SetReadableStreamBody(body);
|
||||
}
|
||||
|
||||
return response.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Response>
|
||||
Response::CloneUnfiltered(JSContext* aCx, ErrorResult& aRv) const
|
||||
Response::CloneUnfiltered(ErrorResult& aRv) const
|
||||
{
|
||||
if (BodyUsed()) {
|
||||
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
|
||||
@@ -335,21 +251,6 @@ Response::CloneUnfiltered(JSContext* aCx, ErrorResult& aRv) const
|
||||
RefPtr<InternalResponse> clone = mInternalResponse->Clone();
|
||||
RefPtr<InternalResponse> ir = clone->Unfiltered();
|
||||
RefPtr<Response> ref = new Response(mOwner, ir, mSignal);
|
||||
|
||||
JS::Rooted<JSObject*> body(aCx);
|
||||
MaybeTeeReadableStreamBody(aCx, &body, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (body) {
|
||||
// Maybe we have a body, but we receive null from MaybeTeeReadableStreamBody
|
||||
// if this body is a native stream. In this case, the InternalResponse will
|
||||
// have a clone of the native body and the ReadableStream will be created
|
||||
// lazily if needed.
|
||||
ref->SetReadableStreamBody(body);
|
||||
}
|
||||
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
|
||||
@@ -105,8 +105,6 @@ public:
|
||||
void
|
||||
GetBody(nsIInputStream** aStream) { return mInternalResponse->GetBody(aStream); }
|
||||
|
||||
using FetchBody::GetBody;
|
||||
|
||||
static already_AddRefed<Response>
|
||||
Error(const GlobalObject& aGlobal);
|
||||
|
||||
@@ -115,7 +113,7 @@ public:
|
||||
|
||||
static already_AddRefed<Response>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
const Optional<Nullable<fetch::ResponseBodyInit>>& aBody,
|
||||
const Optional<Nullable<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>>& aBody,
|
||||
const ResponseInit& aInit, ErrorResult& rv);
|
||||
|
||||
nsIGlobalObject* GetParentObject() const
|
||||
@@ -124,10 +122,10 @@ public:
|
||||
}
|
||||
|
||||
already_AddRefed<Response>
|
||||
Clone(JSContext* aCx, ErrorResult& aRv) const;
|
||||
Clone(ErrorResult& aRv) const;
|
||||
|
||||
already_AddRefed<Response>
|
||||
CloneUnfiltered(JSContext* aCx, ErrorResult& aRv) const;
|
||||
CloneUnfiltered(ErrorResult& aRv) const;
|
||||
|
||||
void
|
||||
SetBody(nsIInputStream* aBody, int64_t aBodySize);
|
||||
|
||||
@@ -29,7 +29,6 @@ SOURCES += [
|
||||
'ChannelInfo.cpp',
|
||||
'FetchDriver.cpp',
|
||||
'FetchObserver.cpp',
|
||||
'FetchStream.cpp',
|
||||
'FetchUtil.cpp',
|
||||
'Headers.cpp',
|
||||
'InternalHeaders.cpp',
|
||||
|
||||
@@ -296,7 +296,7 @@ TCPSocketParent::RecvData(const SendableData& aData,
|
||||
const nsTArray<uint8_t>& buffer = aData.get_ArrayOfuint8_t();
|
||||
bool ok = IPC::DeserializeArrayBuffer(autoCx, buffer, &val);
|
||||
NS_ENSURE_TRUE(ok, true);
|
||||
RootedSpiderMonkeyInterface<ArrayBuffer> data(autoCx);
|
||||
RootedTypedArray<ArrayBuffer> data(autoCx);
|
||||
data.Init(&val.toObject());
|
||||
Optional<uint32_t> byteLength(buffer.Length());
|
||||
mSocket->SendWithTrackingNumber(autoCx, data, 0, byteLength, aTrackingNumber, rv);
|
||||
|
||||
+37
-30
@@ -599,38 +599,31 @@ ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest)
|
||||
return rv;
|
||||
}
|
||||
|
||||
static bool
|
||||
CreateTypeError(JSContext* aCx, ModuleScript* 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 (!filename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JSString*> message(aCx, JS_NewUCStringCopyZ(aCx, aMessage.get()));
|
||||
if (!message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, 0, 0, nullptr,
|
||||
message, aError);
|
||||
}
|
||||
|
||||
static nsresult
|
||||
HandleResolveFailure(JSContext* aCx, ModuleScript* aScript,
|
||||
const nsAString& aSpecifier)
|
||||
const nsAString& aSpecifier,
|
||||
uint32_t aLineNumber, uint32_t aColumnNumber)
|
||||
{
|
||||
// TODO: How can we get the line number of the failed import?
|
||||
nsAutoCString url;
|
||||
aScript->BaseURL()->GetAsciiSpec(url);
|
||||
|
||||
JS::Rooted<JSString*> filename(aCx);
|
||||
filename = JS_NewStringCopyZ(aCx, url.get());
|
||||
if (!filename) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsAutoString message(NS_LITERAL_STRING("Error resolving module specifier: "));
|
||||
message.Append(aSpecifier);
|
||||
|
||||
JS::Rooted<JSString*> string(aCx, JS_NewUCStringCopyZ(aCx, message.get()));
|
||||
if (!string) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> error(aCx);
|
||||
if (!CreateTypeError(aCx, aScript, message, &error)) {
|
||||
if (!JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, aLineNumber,
|
||||
aColumnNumber, nullptr, string, &error)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
@@ -685,28 +678,37 @@ ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI>* aUrlsOu
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::Rooted<JSObject*> moduleRecord(cx, ms->ModuleRecord());
|
||||
JS::Rooted<JSObject*> specifiers(cx, JS::GetRequestedModules(cx, moduleRecord));
|
||||
JS::Rooted<JSObject*> requestedModules(cx);
|
||||
requestedModules = JS::GetRequestedModules(cx, moduleRecord);
|
||||
MOZ_ASSERT(requestedModules);
|
||||
|
||||
uint32_t length;
|
||||
if (!JS_GetArrayLength(cx, specifiers, &length)) {
|
||||
if (!JS_GetArrayLength(cx, requestedModules, &length)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
JS::Rooted<JS::Value> element(cx);
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
if (!JS_GetElement(cx, specifiers, i, &val)) {
|
||||
if (!JS_GetElement(cx, requestedModules, i, &element)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JS::Rooted<JSString*> str(cx, JS::GetRequestedModuleSpecifier(cx, element));
|
||||
MOZ_ASSERT(str);
|
||||
|
||||
nsAutoJSString specifier;
|
||||
if (!specifier.init(cx, val)) {
|
||||
if (!specifier.init(cx, str)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Let url be the result of resolving a module specifier given module script and requested.
|
||||
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(ms, specifier);
|
||||
if (!uri) {
|
||||
nsresult rv = HandleResolveFailure(cx, ms, specifier);
|
||||
uint32_t lineNumber = 0;
|
||||
uint32_t columnNumber = 0;
|
||||
JS::GetRequestedModuleSourcePos(cx, element, &lineNumber, &columnNumber);
|
||||
|
||||
nsresult rv = HandleResolveFailure(cx, ms, specifier, lineNumber, columnNumber);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@@ -806,6 +808,11 @@ HostResolveImportedModule(JSContext* aCx, JS::Handle<JSObject*> aModule,
|
||||
auto script = static_cast<ModuleScript*>(value.toPrivate());
|
||||
MOZ_ASSERT(script->ModuleRecord() == aModule);
|
||||
|
||||
// FIXME: in-line module scripts apparently pass a null specifier?
|
||||
// At least make sure that doesn't crash the browser.
|
||||
if (!aSpecifier)
|
||||
return nullptr;
|
||||
|
||||
// Let url be the result of resolving a module specifier given referencing
|
||||
// module script and specifier.
|
||||
nsAutoJSString string;
|
||||
|
||||
@@ -122,7 +122,6 @@ run-if = e10s
|
||||
[test_storagePermissionsReject.html]
|
||||
[test_storagePermissionsRejectForeign.html]
|
||||
[test_stylesheetPI.html]
|
||||
[test_vibrator.html]
|
||||
[test_WebKitCSSMatrix.html]
|
||||
[test_windowedhistoryframes.html]
|
||||
[test_windowProperties.html]
|
||||
@@ -1,93 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Vibrator</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Although we can't test that the vibrator works properly, we can test that
|
||||
navigator.vibrate throws an exception where appropriate. -->
|
||||
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
var result;
|
||||
function expectFailure(param) {
|
||||
result = navigator.vibrate(param);
|
||||
is(result, false, 'vibrate(' + param + ') should have failed.');
|
||||
}
|
||||
|
||||
function expectSuccess(param) {
|
||||
result = navigator.vibrate(param);
|
||||
is(result, true, 'vibrate(' + param + ') must succeed.');
|
||||
}
|
||||
|
||||
function tests(aEnabled) {
|
||||
// Some edge cases that the bindings should handle for us.
|
||||
expectSuccess(null);
|
||||
expectSuccess(undefined);
|
||||
// -1 will be converted to the highest unsigned long then clamped.
|
||||
expectSuccess(-1);
|
||||
expectSuccess('a');
|
||||
// -1 will be converted to the highest unsigned long then clamped.
|
||||
expectSuccess([100, -1]);
|
||||
expectSuccess([100, 'a']);
|
||||
|
||||
var maxVibrateMs = SpecialPowers.getIntPref('dom.vibrator.max_vibrate_ms');
|
||||
var maxVibrateListLen = SpecialPowers.getIntPref('dom.vibrator.max_vibrate_list_len');
|
||||
|
||||
// If we pass a vibration pattern with a value higher than max_vibrate_ms or a
|
||||
// pattern longer than max_vibrate_list_len, the call should succeed but the
|
||||
// pattern should be modified to match the restrictions.
|
||||
|
||||
// Values will be clamped to dom.vibrator.max_vibrate_ms.
|
||||
expectSuccess(maxVibrateMs + 1);
|
||||
expectSuccess([maxVibrateMs + 1]);
|
||||
|
||||
var arr = [];
|
||||
for (var i = 0; i < maxVibrateListLen + 1; i++) {
|
||||
arr[i] = 0;
|
||||
}
|
||||
// The array will be truncated to have a length equal to dom.vibrator.max_vibrate_list_len.
|
||||
expectSuccess(arr);
|
||||
|
||||
|
||||
expectSuccess(0);
|
||||
expectSuccess([]);
|
||||
expectSuccess('1000');
|
||||
expectSuccess(1000);
|
||||
expectSuccess(1000.1);
|
||||
expectSuccess([0, 0, 0]);
|
||||
expectSuccess(['1000', 1000]);
|
||||
expectSuccess([1000, 1000]);
|
||||
expectSuccess([1000, 1000.1]);
|
||||
|
||||
// The following loop shouldn't cause us to crash. See bug 701716.
|
||||
for (var i = 0; i < 10000; i++) {
|
||||
navigator.vibrate([100, 100]);
|
||||
}
|
||||
ok(true, "Didn't crash after issuing a lot of vibrate() calls.");
|
||||
if(!aEnabled)
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPermissions([
|
||||
{type: 'vibration', allow: true, context: document}
|
||||
], function() {
|
||||
// Test with the vibrator pref enabled.
|
||||
SpecialPowers.pushPrefEnv({"set": [['dom.vibrator.enabled', true]]}, function() {
|
||||
tests(true);
|
||||
SpecialPowers.pushPrefEnv({"set": [['dom.vibrator.enabled', false]]}, tests(false));
|
||||
});
|
||||
// Everything should be the same when the vibrator is disabled -- in
|
||||
// particular, a disabled vibrator shouldn't eat failures we'd otherwise
|
||||
// observe.
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
typedef object JSON;
|
||||
typedef (ArrayBuffer or ArrayBufferView or Blob or FormData or USVString or URLSearchParams) BodyInit;
|
||||
typedef (Blob or ArrayBufferView or ArrayBuffer or FormData or URLSearchParams or ReadableStream or USVString) ResponseBodyInit;
|
||||
|
||||
[NoInterfaceObject, Exposed=(Window,Worker)]
|
||||
interface Body {
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
* http://www.w3.org/TR/beacon/#sec-beacon-method
|
||||
* https://html.spec.whatwg.org/#navigatorconcurrenthardware
|
||||
*
|
||||
* © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
|
||||
* Opera Software ASA. You are granted a license to use, reproduce
|
||||
* and create derivative works of this document.
|
||||
* © Copyright 2004-2020 Apple Computer, Inc., Mozilla Foundation,
|
||||
* Opera Software ASA and Moonchild Productions. You are granted a license to use,
|
||||
* reproduce and create derivative works of this document.
|
||||
*/
|
||||
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/#the-navigator-object
|
||||
@@ -124,14 +124,6 @@ interface NavigatorGeolocation {
|
||||
};
|
||||
Navigator implements NavigatorGeolocation;
|
||||
|
||||
// http://www.w3.org/TR/vibration/#vibration-interface
|
||||
partial interface Navigator {
|
||||
// We don't support sequences in unions yet
|
||||
//boolean vibrate ((unsigned long or sequence<unsigned long>) pattern);
|
||||
boolean vibrate(unsigned long duration);
|
||||
boolean vibrate(sequence<unsigned long> pattern);
|
||||
};
|
||||
|
||||
// http://www.w3.org/TR/pointerevents/#extensions-to-the-navigator-interface
|
||||
partial interface Navigator {
|
||||
[Pref="dom.w3c_pointer_events.enabled"]
|
||||
@@ -140,17 +132,6 @@ partial interface Navigator {
|
||||
|
||||
// Mozilla-specific extensions
|
||||
|
||||
// Chrome-only interface for Vibration API permission handling.
|
||||
partial interface Navigator {
|
||||
/* Set permission state to device vibration.
|
||||
* @param permitted permission state (true for allowing vibration)
|
||||
* @param persistent make the permission session-persistent
|
||||
*/
|
||||
[ChromeOnly]
|
||||
void setVibrationPermission(boolean permitted,
|
||||
optional boolean persistent = true);
|
||||
};
|
||||
|
||||
callback interface MozIdleObserver {
|
||||
// Time is in seconds and is read only when idle observers are added
|
||||
// and removed.
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
* https://fetch.spec.whatwg.org/#response-class
|
||||
*/
|
||||
|
||||
// This should be Constructor(optional BodyInit... but BodyInit doesn't include
|
||||
// ReadableStream yet because we don't want to expose the Streams API to Request.
|
||||
[Constructor(optional ResponseBodyInit? body, optional ResponseInit init),
|
||||
[Constructor(optional BodyInit? body, optional ResponseInit init),
|
||||
Exposed=(Window,Worker)]
|
||||
interface Response {
|
||||
[NewObject] static Response error();
|
||||
@@ -32,12 +30,6 @@ interface Response {
|
||||
};
|
||||
Response implements Body;
|
||||
|
||||
// This should be part of Body but we don't want to expose body to request yet.
|
||||
partial interface Response {
|
||||
[GetterThrows, Func="nsContentUtils::StreamsEnabled"]
|
||||
readonly attribute ReadableStream? body;
|
||||
};
|
||||
|
||||
dictionary ResponseInit {
|
||||
unsigned short status = 200;
|
||||
ByteString statusText = "OK";
|
||||
|
||||
@@ -299,7 +299,6 @@ LoadContextOptions(const char* aPrefName, void* /* aClosure */)
|
||||
.setNativeRegExp(GetWorkerPref<bool>(NS_LITERAL_CSTRING("native_regexp")))
|
||||
.setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
|
||||
.setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
|
||||
.setStreams(GetWorkerPref<bool>(NS_LITERAL_CSTRING("streams")))
|
||||
.setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")))
|
||||
.setArrayProtoValues(GetWorkerPref<bool>(
|
||||
NS_LITERAL_CSTRING("array_prototype_values")));
|
||||
|
||||
@@ -35,7 +35,6 @@ WORKER_SIMPLE_PREF("dom.serviceWorkers.testing.enabled", ServiceWorkersTestingEn
|
||||
WORKER_SIMPLE_PREF("dom.serviceWorkers.openWindow.enabled", OpenWindowEnabled, OPEN_WINDOW_ENABLED)
|
||||
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("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)
|
||||
|
||||
@@ -2830,7 +2830,7 @@ XMLHttpRequestMainThread::Send(nsIVariant* aVariant)
|
||||
nsresult rv = aVariant->GetAsJSVal(&realVal);
|
||||
if (NS_SUCCEEDED(rv) && !realVal.isPrimitive()) {
|
||||
JS::Rooted<JSObject*> obj(rootingCx, realVal.toObjectOrNull());
|
||||
RootedSpiderMonkeyInterface<ArrayBuffer> buf(rootingCx);
|
||||
RootedTypedArray<ArrayBuffer> buf(rootingCx);
|
||||
if (buf.Init(obj)) {
|
||||
RequestBody<const ArrayBuffer> body(&buf);
|
||||
return SendInternal(&body);
|
||||
|
||||
+30
-16
@@ -212,7 +212,7 @@ VerifyRGBXCorners(uint8_t* aData, const IntSize &aSize, const int32_t aStride, S
|
||||
#endif
|
||||
|
||||
static sk_sp<SkImage>
|
||||
GetSkImageForSurface(SourceSurface* aSurface)
|
||||
GetSkImageForSurface(SourceSurface* aSurface, Maybe<MutexAutoLock>* aLock)
|
||||
{
|
||||
if (!aSurface) {
|
||||
gfxDebug() << "Creating null Skia image from null SourceSurface";
|
||||
@@ -220,7 +220,7 @@ GetSkImageForSurface(SourceSurface* aSurface)
|
||||
}
|
||||
|
||||
if (aSurface->GetType() == SurfaceType::SKIA) {
|
||||
return static_cast<SourceSurfaceSkia*>(aSurface)->GetImage();
|
||||
return static_cast<SourceSurfaceSkia*>(aSurface)->GetImage(aLock);
|
||||
}
|
||||
|
||||
DataSourceSurface* surf = aSurface->GetDataSurface().take();
|
||||
@@ -390,9 +390,9 @@ ExtractAlphaBitmap(sk_sp<SkImage> aImage, SkBitmap* aResultBitmap)
|
||||
}
|
||||
|
||||
static sk_sp<SkImage>
|
||||
ExtractAlphaForSurface(SourceSurface* aSurface)
|
||||
ExtractAlphaForSurface(SourceSurface* aSurface, Maybe<MutexAutoLock>& aLock)
|
||||
{
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &aLock);
|
||||
if (!image) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -411,7 +411,11 @@ ExtractAlphaForSurface(SourceSurface* aSurface)
|
||||
}
|
||||
|
||||
static void
|
||||
SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0, Point aOffset = Point(0, 0))
|
||||
SetPaintPattern(SkPaint& aPaint,
|
||||
const Pattern& aPattern,
|
||||
Maybe<MutexAutoLock>& aLock,
|
||||
Float aAlpha = 1.0,
|
||||
Point aOffset = Point(0, 0))
|
||||
{
|
||||
switch (aPattern.GetType()) {
|
||||
case PatternType::COLOR: {
|
||||
@@ -473,7 +477,7 @@ SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0, Po
|
||||
}
|
||||
case PatternType::SURFACE: {
|
||||
const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(pat.mSurface);
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(pat.mSurface, &aLock);
|
||||
if (!image) {
|
||||
aPaint.setColor(SK_ColorTRANSPARENT);
|
||||
break;
|
||||
@@ -525,7 +529,7 @@ struct AutoPaintSetup {
|
||||
: mNeedsRestore(false), mAlpha(1.0)
|
||||
{
|
||||
Init(aCanvas, aOptions, aMaskBounds, false);
|
||||
SetPaintPattern(mPaint, aPattern, mAlpha, aOffset);
|
||||
SetPaintPattern(mPaint, aPattern, mLock, mAlpha, aOffset);
|
||||
}
|
||||
|
||||
AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds = nullptr, bool aForceGroup = false)
|
||||
@@ -579,6 +583,7 @@ struct AutoPaintSetup {
|
||||
SkPaint mPaint;
|
||||
bool mNeedsRestore;
|
||||
SkCanvas* mCanvas;
|
||||
Maybe<MutexAutoLock> mLock;
|
||||
Float mAlpha;
|
||||
};
|
||||
|
||||
@@ -601,7 +606,8 @@ DrawTargetSkia::DrawSurface(SourceSurface *aSurface,
|
||||
|
||||
MarkChanged();
|
||||
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
|
||||
Maybe<MutexAutoLock> lock;
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
@@ -654,7 +660,8 @@ DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface *aSurface,
|
||||
|
||||
MarkChanged();
|
||||
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
|
||||
Maybe<MutexAutoLock> lock;
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
@@ -1407,8 +1414,9 @@ DrawTargetSkia::Mask(const Pattern &aSource,
|
||||
MarkChanged();
|
||||
AutoPaintSetup paint(mCanvas.get(), aOptions, aSource);
|
||||
|
||||
Maybe<MutexAutoLock> lock;
|
||||
SkPaint maskPaint;
|
||||
SetPaintPattern(maskPaint, aMask);
|
||||
SetPaintPattern(maskPaint, aMask, lock);
|
||||
|
||||
SkLayerRasterizer::Builder builder;
|
||||
builder.addLayer(maskPaint);
|
||||
@@ -1427,7 +1435,8 @@ DrawTargetSkia::MaskSurface(const Pattern &aSource,
|
||||
MarkChanged();
|
||||
AutoPaintSetup paint(mCanvas.get(), aOptions, aSource, nullptr, -aOffset);
|
||||
|
||||
sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(aMask);
|
||||
Maybe<MutexAutoLock> lock;
|
||||
sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(aMask, lock);
|
||||
if (!alphaMask) {
|
||||
gfxDebug() << *this << ": MaskSurface() failed to extract alpha for mask";
|
||||
return;
|
||||
@@ -1456,7 +1465,8 @@ DrawTarget::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& a
|
||||
fullMat.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
|
||||
|
||||
// Read in the source data.
|
||||
sk_sp<SkImage> srcImage = GetSkImageForSurface(aSurface);
|
||||
Maybe<MutexAutoLock> lock;
|
||||
sk_sp<SkImage> srcImage = GetSkImageForSurface(aSurface, &lock);
|
||||
if (!srcImage) {
|
||||
return true;
|
||||
}
|
||||
@@ -1515,7 +1525,8 @@ DrawTargetSkia::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x
|
||||
|
||||
MarkChanged();
|
||||
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
|
||||
Maybe<MutexAutoLock> lock;
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
|
||||
if (!image) {
|
||||
return true;
|
||||
}
|
||||
@@ -1599,7 +1610,8 @@ already_AddRefed<SourceSurface>
|
||||
DrawTargetSkia::OptimizeGPUSourceSurface(SourceSurface *aSurface) const
|
||||
{
|
||||
// Check if the underlying SkImage already has an associated GrTexture.
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
|
||||
Maybe<MutexAutoLock> lock;
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
|
||||
if (!image || image->isTextureBacked()) {
|
||||
RefPtr<SourceSurface> surface(aSurface);
|
||||
return surface.forget();
|
||||
@@ -1716,7 +1728,8 @@ DrawTargetSkia::CopySurface(SourceSurface *aSurface,
|
||||
{
|
||||
MarkChanged();
|
||||
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
|
||||
Maybe<MutexAutoLock> lock;
|
||||
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
@@ -2061,7 +2074,8 @@ DrawTargetSkia::PopLayer()
|
||||
paint.setColor(SK_ColorTRANSPARENT);
|
||||
}
|
||||
|
||||
sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(layer.mMask);
|
||||
Maybe<MutexAutoLock> lock;
|
||||
sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(layer.mMask, lock);
|
||||
if (!alphaMask) {
|
||||
gfxDebug() << *this << ": PopLayer() failed to extract alpha for mask";
|
||||
} else {
|
||||
|
||||
@@ -40,6 +40,25 @@ SourceSurfaceSkia::GetFormat() const
|
||||
return mFormat;
|
||||
}
|
||||
|
||||
sk_sp<SkImage>
|
||||
SourceSurfaceSkia::GetImage(Maybe<MutexAutoLock>* aLock) {
|
||||
// If we were provided a lock object, we can let the caller access
|
||||
// a shared SkImage and we know it won't go away while the lock is held.
|
||||
if (aLock) {
|
||||
// We are locked, so now we can check mDrawTarget.
|
||||
// If it's null, then we're not shared and we can unlock eagerly.
|
||||
if (!mDrawTarget) {
|
||||
aLock->reset();
|
||||
}
|
||||
} else {
|
||||
// Otherwise we need to call DrawTargetWillChange to ensure we have our
|
||||
// own copy of the SkImage.
|
||||
DrawTargetWillChange();
|
||||
}
|
||||
sk_sp<SkImage> image = mImage;
|
||||
return image;
|
||||
}
|
||||
|
||||
static sk_sp<SkData>
|
||||
MakeSkData(unsigned char* aData, const IntSize& aSize, int32_t aStride)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
#include <vector>
|
||||
#include "skia/include/core/SkCanvas.h"
|
||||
#include "skia/include/core/SkImage.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
typedef mozilla::MutexAutoLock MutexAutoLock;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@@ -28,7 +32,7 @@ public:
|
||||
virtual IntSize GetSize() const;
|
||||
virtual SurfaceFormat GetFormat() const;
|
||||
|
||||
sk_sp<SkImage>& GetImage() { return mImage; }
|
||||
sk_sp<SkImage> GetImage(Maybe<MutexAutoLock>* aLock);
|
||||
|
||||
bool InitFromData(unsigned char* aData,
|
||||
const IntSize &aSize,
|
||||
|
||||
-79
@@ -94,87 +94,8 @@ WindowIsActive(nsPIDOMWindowInner* aWindow)
|
||||
return !document->Hidden();
|
||||
}
|
||||
|
||||
StaticAutoPtr<WindowIdentifier::IDArrayType> gLastIDToVibrate;
|
||||
|
||||
void InitLastIDToVibrate()
|
||||
{
|
||||
gLastIDToVibrate = new WindowIdentifier::IDArrayType();
|
||||
ClearOnShutdown(&gLastIDToVibrate);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void
|
||||
Vibrate(const nsTArray<uint32_t>& pattern, nsPIDOMWindowInner* window)
|
||||
{
|
||||
Vibrate(pattern, WindowIdentifier(window));
|
||||
}
|
||||
|
||||
void
|
||||
Vibrate(const nsTArray<uint32_t>& pattern, const WindowIdentifier &id)
|
||||
{
|
||||
AssertMainThread();
|
||||
|
||||
// Only active windows may start vibrations. If |id| hasn't gone
|
||||
// through the IPC layer -- that is, if our caller is the outside
|
||||
// world, not hal_proxy -- check whether the window is active. If
|
||||
// |id| has gone through IPC, don't check the window's visibility;
|
||||
// only the window corresponding to the bottommost process has its
|
||||
// visibility state set correctly.
|
||||
if (!id.HasTraveledThroughIPC() && !WindowIsActive(id.GetWindow())) {
|
||||
HAL_LOG("Vibrate: Window is inactive, dropping vibrate.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InSandbox()) {
|
||||
if (!gLastIDToVibrate) {
|
||||
InitLastIDToVibrate();
|
||||
}
|
||||
*gLastIDToVibrate = id.AsArray();
|
||||
}
|
||||
|
||||
// Don't forward our ID if we are not in the sandbox, because hal_impl
|
||||
// doesn't need it, and we don't want it to be tempted to read it. The
|
||||
// empty identifier will assert if it's used.
|
||||
PROXY_IF_SANDBOXED(Vibrate(pattern, InSandbox() ? id : WindowIdentifier()));
|
||||
}
|
||||
|
||||
void
|
||||
CancelVibrate(nsPIDOMWindowInner* window)
|
||||
{
|
||||
CancelVibrate(WindowIdentifier(window));
|
||||
}
|
||||
|
||||
void
|
||||
CancelVibrate(const WindowIdentifier &id)
|
||||
{
|
||||
AssertMainThread();
|
||||
|
||||
// Although only active windows may start vibrations, a window may
|
||||
// cancel its own vibration even if it's no longer active.
|
||||
//
|
||||
// After a window is marked as inactive, it sends a CancelVibrate
|
||||
// request. We want this request to cancel a playing vibration
|
||||
// started by that window, so we certainly don't want to reject the
|
||||
// cancellation request because the window is now inactive.
|
||||
//
|
||||
// But it could be the case that, after this window became inactive,
|
||||
// some other window came along and started a vibration. We don't
|
||||
// want this window's cancellation request to cancel that window's
|
||||
// actively-playing vibration!
|
||||
//
|
||||
// To solve this problem, we keep track of the id of the last window
|
||||
// to start a vibration, and only accepts cancellation requests from
|
||||
// the same window. All other cancellation requests are ignored.
|
||||
|
||||
if (InSandbox() || (gLastIDToVibrate && *gLastIDToVibrate == id.AsArray())) {
|
||||
// Don't forward our ID if we are not in the sandbox, because hal_impl
|
||||
// doesn't need it, and we don't want it to be tempted to read it. The
|
||||
// empty identifier will assert if it's used.
|
||||
PROXY_IF_SANDBOXED(CancelVibrate(InSandbox() ? id : WindowIdentifier()));
|
||||
}
|
||||
}
|
||||
|
||||
template <class InfoType>
|
||||
class ObserversManager
|
||||
{
|
||||
|
||||
@@ -50,41 +50,6 @@ typedef Observer<SystemTimezoneChangeInformation> SystemTimezoneChangeObserver;
|
||||
|
||||
namespace MOZ_HAL_NAMESPACE {
|
||||
|
||||
/**
|
||||
* Turn the default vibrator device on/off per the pattern specified
|
||||
* by |pattern|. Each element in the pattern is the number of
|
||||
* milliseconds to turn the vibrator on or off. The first element in
|
||||
* |pattern| is an "on" element, the next is "off", and so on.
|
||||
*
|
||||
* If |pattern| is empty, any in-progress vibration is canceled.
|
||||
*
|
||||
* Only an active window within an active tab may call Vibrate; calls
|
||||
* from inactive windows and windows on inactive tabs do nothing.
|
||||
*
|
||||
* If you're calling hal::Vibrate from the outside world, pass an
|
||||
* nsIDOMWindow* in place of the WindowIdentifier parameter.
|
||||
* The method with WindowIdentifier will be called automatically.
|
||||
*/
|
||||
void Vibrate(const nsTArray<uint32_t>& pattern,
|
||||
nsPIDOMWindowInner* aWindow);
|
||||
void Vibrate(const nsTArray<uint32_t>& pattern,
|
||||
const hal::WindowIdentifier &id);
|
||||
|
||||
/**
|
||||
* Cancel a vibration started by the content window identified by
|
||||
* WindowIdentifier.
|
||||
*
|
||||
* If the window was the last window to start a vibration, the
|
||||
* cancellation request will go through even if the window is not
|
||||
* active.
|
||||
*
|
||||
* As with hal::Vibrate(), if you're calling hal::CancelVibrate from the outside
|
||||
* world, pass an nsIDOMWindow*. The method with WindowIdentifier will be called
|
||||
* automatically.
|
||||
*/
|
||||
void CancelVibrate(nsPIDOMWindowInner* aWindow);
|
||||
void CancelVibrate(const hal::WindowIdentifier &id);
|
||||
|
||||
/**
|
||||
* Determine whether the device's screen is currently enabled.
|
||||
*/
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/* -*- 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 "Hal.h"
|
||||
|
||||
using mozilla::hal::WindowIdentifier;
|
||||
|
||||
namespace mozilla {
|
||||
namespace hal_impl {
|
||||
|
||||
void
|
||||
Vibrate(const nsTArray<uint32_t>& pattern, const hal::WindowIdentifier &)
|
||||
{}
|
||||
|
||||
void
|
||||
CancelVibrate(const hal::WindowIdentifier &)
|
||||
{}
|
||||
|
||||
} // namespace hal_impl
|
||||
} // namespace mozilla
|
||||
@@ -29,7 +29,6 @@ if CONFIG['OS_TARGET'] == 'Linux':
|
||||
'fallback/FallbackAlarm.cpp',
|
||||
'fallback/FallbackScreenConfiguration.cpp',
|
||||
'fallback/FallbackSensor.cpp',
|
||||
'fallback/FallbackVibration.cpp',
|
||||
'linux/LinuxMemory.cpp',
|
||||
'linux/LinuxPower.cpp',
|
||||
]
|
||||
@@ -39,7 +38,6 @@ elif CONFIG['OS_TARGET'] == 'WINNT':
|
||||
'fallback/FallbackMemory.cpp',
|
||||
'fallback/FallbackPower.cpp',
|
||||
'fallback/FallbackScreenConfiguration.cpp',
|
||||
'fallback/FallbackVibration.cpp',
|
||||
'windows/WindowsSensor.cpp',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
@@ -48,7 +46,6 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
'fallback/FallbackMemory.cpp',
|
||||
'fallback/FallbackPower.cpp',
|
||||
'fallback/FallbackScreenConfiguration.cpp',
|
||||
'fallback/FallbackVibration.cpp',
|
||||
]
|
||||
elif CONFIG['OS_TARGET'] in ('OpenBSD', 'NetBSD', 'FreeBSD', 'DragonFly'):
|
||||
UNIFIED_SOURCES += [
|
||||
@@ -57,7 +54,6 @@ elif CONFIG['OS_TARGET'] in ('OpenBSD', 'NetBSD', 'FreeBSD', 'DragonFly'):
|
||||
'fallback/FallbackPower.cpp',
|
||||
'fallback/FallbackScreenConfiguration.cpp',
|
||||
'fallback/FallbackSensor.cpp',
|
||||
'fallback/FallbackVibration.cpp',
|
||||
]
|
||||
else:
|
||||
UNIFIED_SOURCES += [
|
||||
@@ -66,7 +62,6 @@ else:
|
||||
'fallback/FallbackPower.cpp',
|
||||
'fallback/FallbackScreenConfiguration.cpp',
|
||||
'fallback/FallbackSensor.cpp',
|
||||
'fallback/FallbackVibration.cpp',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
||||
@@ -69,9 +69,6 @@ child:
|
||||
async NotifySystemTimezoneChange(SystemTimezoneChangeInformation aSystemTimezoneChangeInfo);
|
||||
|
||||
parent:
|
||||
async Vibrate(uint32_t[] pattern, uint64_t[] id, PBrowser browser);
|
||||
async CancelVibrate(uint64_t[] id, PBrowser browser);
|
||||
|
||||
async EnableNetworkNotifications();
|
||||
async DisableNetworkNotifications();
|
||||
sync GetCurrentNetworkInformation()
|
||||
|
||||
@@ -45,28 +45,6 @@ Hal()
|
||||
return sHal;
|
||||
}
|
||||
|
||||
void
|
||||
Vibrate(const nsTArray<uint32_t>& pattern, const WindowIdentifier &id)
|
||||
{
|
||||
HAL_LOG("Vibrate: Sending to parent process.");
|
||||
|
||||
AutoTArray<uint32_t, 8> p(pattern);
|
||||
|
||||
WindowIdentifier newID(id);
|
||||
newID.AppendProcessID();
|
||||
Hal()->SendVibrate(p, newID.AsArray(), TabChild::GetFrom(newID.GetWindow()));
|
||||
}
|
||||
|
||||
void
|
||||
CancelVibrate(const WindowIdentifier &id)
|
||||
{
|
||||
HAL_LOG("CancelVibrate: Sending to parent process.");
|
||||
|
||||
WindowIdentifier newID(id);
|
||||
newID.AppendProcessID();
|
||||
Hal()->SendCancelVibrate(newID.AsArray(), TabChild::GetFrom(newID.GetWindow()));
|
||||
}
|
||||
|
||||
void
|
||||
EnableNetworkNotifications()
|
||||
{
|
||||
@@ -379,36 +357,6 @@ public:
|
||||
hal::UnregisterSystemTimezoneChangeObserver(this);
|
||||
}
|
||||
|
||||
virtual bool
|
||||
RecvVibrate(InfallibleTArray<unsigned int>&& pattern,
|
||||
InfallibleTArray<uint64_t>&& id,
|
||||
PBrowserParent *browserParent) override
|
||||
{
|
||||
// We give all content vibration permission.
|
||||
// TabParent *tabParent = TabParent::GetFrom(browserParent);
|
||||
/* xxxkhuey wtf
|
||||
nsCOMPtr<nsIDOMWindow> window =
|
||||
do_QueryInterface(tabParent->GetBrowserDOMWindow());
|
||||
*/
|
||||
WindowIdentifier newID(id, nullptr);
|
||||
hal::Vibrate(pattern, newID);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
RecvCancelVibrate(InfallibleTArray<uint64_t> &&id,
|
||||
PBrowserParent *browserParent) override
|
||||
{
|
||||
//TabParent *tabParent = TabParent::GetFrom(browserParent);
|
||||
/* XXXkhuey wtf
|
||||
nsCOMPtr<nsIDOMWindow> window =
|
||||
tabParent->GetBrowserDOMWindow();
|
||||
*/
|
||||
WindowIdentifier newID(id, nullptr);
|
||||
hal::CancelVibrate(newID);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
RecvEnableNetworkNotifications() override {
|
||||
// We give all content access to this network-status information.
|
||||
|
||||
+1
-1
@@ -778,7 +778,7 @@ struct JSClass {
|
||||
// application.
|
||||
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
|
||||
#define JSCLASS_GLOBAL_SLOT_COUNT \
|
||||
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 45)
|
||||
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 46)
|
||||
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
|
||||
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
|
||||
#define JSCLASS_GLOBAL_FLAGS \
|
||||
|
||||
@@ -1,510 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* 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/. */
|
||||
|
||||
/*
|
||||
* JSAPI functions and callbacks related to WHATWG Stream objects.
|
||||
*
|
||||
* Much of the API here mirrors the JS API of ReadableStream and associated
|
||||
* classes, e.g. ReadableStreamDefaultReader, ReadableStreamBYOBReader,
|
||||
* ReadableStreamDefaultController, ReadableByteStreamController, and
|
||||
* ReadableStreamBYOBRequest.
|
||||
*
|
||||
* There are some crucial differences, though: Functionality that's exposed
|
||||
* as methods/accessors on controllers in JS is exposed as functions taking
|
||||
* ReadableStream instances instead. This is because an analysis of how
|
||||
* the API would be used showed that all functions that'd take controllers
|
||||
* would do so by first getting the controller from the stream instance it's
|
||||
* associated with and then call the function taking it. I.e., it would purely
|
||||
* add boilerplate without any gains in ease of use of the API.
|
||||
*
|
||||
* Some functions exposed here deal with ReadableStream instances that have an
|
||||
* embedding-provided underlying source. These instances are largely similar
|
||||
* to byte streams as created using |new ReadableStream({type: "bytes"})|:
|
||||
* They enable users to acquire ReadableStreamBYOBReaders and only vend chunks
|
||||
* that're typed array instances.
|
||||
*
|
||||
* When creating an "external readable stream" using
|
||||
* JS::NewReadableExternalSourceStreamObject, an underlying source and a set
|
||||
* of flags can be passed to be stored on the stream. The underlying source is
|
||||
* treated as an opaque void* pointer by the JS engine: it's purely meant as
|
||||
* a reference to be used by the embedding to identify whatever actual source
|
||||
* it uses to supply data for the stream. Similarly, the flags aren't
|
||||
* interpreted by the JS engine, but are passed to some of the callbacks below
|
||||
* and can be retrieved using JS::ReadableStreamGetEmbeddingFlags.
|
||||
*
|
||||
* External readable streams are optimized to allow the embedding to interact
|
||||
* with them with a minimum of overhead: chunks aren't enqueued as individual
|
||||
* typed array instances; instead, the embedding only updates the amount of
|
||||
* data available using ReadableStreamUpdateDataAvailableFromSource.
|
||||
* When content requests data by reading from a reader,
|
||||
* WriteIntoReadRequestBufferCallback is invoked, asking the embedding to
|
||||
* write data directly into the buffer we're about to hand to content.
|
||||
*
|
||||
* Additionally, ReadableStreamGetExternalUnderlyingSource can be used to
|
||||
* get the void* pointer to the underlying source. This is equivalent to
|
||||
* acquiring a reader for the stream in that it locks the stream until it
|
||||
* is released again using JS::ReadableStreamReleaseExternalUnderlyingSource.
|
||||
*
|
||||
* Embeddings are expected to detect situations where an API exposed to JS
|
||||
* takes a ReadableStream to read from that has an external underlying source.
|
||||
* In those situations, it might be preferable to directly perform data
|
||||
* transfers from the stream's underlying source to whatever sink the
|
||||
* embedding uses, assuming that such direct transfers can be performed
|
||||
* more efficiently.
|
||||
*/
|
||||
|
||||
#ifndef js_Stream_h
|
||||
#define js_Stream_h
|
||||
|
||||
#include "jstypes.h"
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
namespace JS {
|
||||
|
||||
/**
|
||||
* Invoked whenever a reader desires more data from a ReadableStream's
|
||||
* embedding-provided underlying source.
|
||||
*
|
||||
* The given |desiredSize| is the absolute size, not a delta from the previous
|
||||
* desired size.
|
||||
*/
|
||||
typedef void
|
||||
(* RequestReadableStreamDataCallback)(JSContext* cx, HandleObject stream,
|
||||
void* underlyingSource, uint8_t flags, size_t desiredSize);
|
||||
|
||||
/**
|
||||
* Invoked to cause the embedding to fill the given |buffer| with data from
|
||||
* the given embedding-provided underlying source.
|
||||
*
|
||||
* This can only happen after the embedding has updated the amount of data
|
||||
* available using JS::ReadableStreamUpdateDataAvailableFromSource. If at
|
||||
* least one read request is pending when
|
||||
* JS::ReadableStreamUpdateDataAvailableFromSource is called,
|
||||
* the WriteIntoReadRequestBufferCallback is invoked immediately from under
|
||||
* the call to JS::WriteIntoReadRequestBufferCallback. If not, it is invoked
|
||||
* if and when a new read request is made.
|
||||
*
|
||||
* Note: This callback *must not cause GC*, because that could potentially
|
||||
* invalidate the |buffer| pointer.
|
||||
*/
|
||||
typedef void
|
||||
(* WriteIntoReadRequestBufferCallback)(JSContext* cx, HandleObject stream,
|
||||
void* underlyingSource, uint8_t flags, void* buffer,
|
||||
size_t length, size_t* bytesWritten);
|
||||
|
||||
/**
|
||||
* Invoked in reaction to the ReadableStream being canceled to allow the
|
||||
* embedding to free the underlying source.
|
||||
*
|
||||
* This is equivalent to calling |cancel| on non-external underlying sources
|
||||
* provided to the ReadableStream constructor in JavaScript.
|
||||
*
|
||||
* The given |reason| is the JS::Value that was passed as an argument to
|
||||
* ReadableStream#cancel().
|
||||
*
|
||||
* The returned JS::Value will be used to resolve the Promise returned by
|
||||
* ReadableStream#cancel().
|
||||
*/
|
||||
typedef Value
|
||||
(* CancelReadableStreamCallback)(JSContext* cx, HandleObject stream,
|
||||
void* underlyingSource, uint8_t flags, HandleValue reason);
|
||||
|
||||
/**
|
||||
* Invoked in reaction to a ReadableStream with an embedding-provided
|
||||
* underlying source being closed.
|
||||
*/
|
||||
typedef void
|
||||
(* ReadableStreamClosedCallback)(JSContext* cx, HandleObject stream, void* underlyingSource,
|
||||
uint8_t flags);
|
||||
|
||||
/**
|
||||
* Invoked in reaction to a ReadableStream with an embedding-provided
|
||||
* underlying source being errored with the
|
||||
* given reason.
|
||||
*/
|
||||
typedef void
|
||||
(* ReadableStreamErroredCallback)(JSContext* cx, HandleObject stream, void* underlyingSource,
|
||||
uint8_t flags, HandleValue reason);
|
||||
|
||||
/**
|
||||
* Invoked in reaction to a ReadableStream with an embedding-provided
|
||||
* underlying source being finalized. Only the underlying source is passed
|
||||
* as an argument, while the ReadableStream itself is not to prevent the
|
||||
* embedding from operating on a JSObject that might not be in a valid state
|
||||
* anymore.
|
||||
*
|
||||
* Note: the ReadableStream might be finalized on a background thread. That
|
||||
* means this callback might be invoked from an arbitrary thread, which the
|
||||
* embedding must be able to handle.
|
||||
*/
|
||||
typedef void
|
||||
(* ReadableStreamFinalizeCallback)(void* underlyingSource, uint8_t flags);
|
||||
|
||||
/**
|
||||
* Sets runtime-wide callbacks to use for interacting with embedding-provided
|
||||
* hooks for operating on ReadableStream instances.
|
||||
*
|
||||
* See the documentation for the individual callback types for details.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
SetReadableStreamCallbacks(JSContext* cx,
|
||||
RequestReadableStreamDataCallback dataRequestCallback,
|
||||
WriteIntoReadRequestBufferCallback writeIntoReadRequestCallback,
|
||||
CancelReadableStreamCallback cancelCallback,
|
||||
ReadableStreamClosedCallback closedCallback,
|
||||
ReadableStreamErroredCallback erroredCallback,
|
||||
ReadableStreamFinalizeCallback finalizeCallback);
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
HasReadableStreamCallbacks(JSContext* cx);
|
||||
|
||||
/**
|
||||
* Returns a new instance of the ReadableStream builtin class in the current
|
||||
* compartment, configured as a default stream.
|
||||
* If a |proto| is passed, that gets set as the instance's [[Prototype]]
|
||||
* instead of the original value of |ReadableStream.prototype|.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
NewReadableDefaultStreamObject(JSContext* cx, HandleObject underlyingSource = nullptr,
|
||||
HandleFunction size = nullptr, double highWaterMark = 1,
|
||||
HandleObject proto = nullptr);
|
||||
|
||||
/**
|
||||
* Returns a new instance of the ReadableStream builtin class in the current
|
||||
* compartment, configured as a byte stream.
|
||||
* If a |proto| is passed, that gets set as the instance's [[Prototype]]
|
||||
* instead of the original value of |ReadableStream.prototype|.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
NewReadableByteStreamObject(JSContext* cx, HandleObject underlyingSource = nullptr,
|
||||
double highWaterMark = 0, HandleObject proto = nullptr);
|
||||
|
||||
/**
|
||||
* Returns a new instance of the ReadableStream builtin class in the current
|
||||
* compartment, with the right slot layout. If a |proto| is passed, that gets
|
||||
* set as the instance's [[Prototype]] instead of the original value of
|
||||
* |ReadableStream.prototype|.
|
||||
*
|
||||
* The instance is optimized for operating as a byte stream backed by an
|
||||
* embedding-provided underlying source, using the callbacks set via
|
||||
* |JS::SetReadableStreamCallbacks|.
|
||||
*
|
||||
* The given |flags| will be passed to all applicable callbacks and can be
|
||||
* used to disambiguate between different types of stream sources the
|
||||
* embedding might support.
|
||||
*
|
||||
* Note: the embedding is responsible for ensuring that the pointer to the
|
||||
* underlying source stays valid as long as the stream can be read from.
|
||||
* The underlying source can be freed if the tree is canceled or errored.
|
||||
* It can also be freed if the stream is destroyed. The embedding is notified
|
||||
* of that using ReadableStreamFinalizeCallback.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
NewReadableExternalSourceStreamObject(JSContext* cx, void* underlyingSource,
|
||||
uint8_t flags = 0, HandleObject proto = nullptr);
|
||||
|
||||
/**
|
||||
* Returns the flags that were passed to NewReadableExternalSourceStreamObject
|
||||
* when creating the given stream.
|
||||
*
|
||||
* Asserts that the given stream has an embedding-provided underlying source.
|
||||
*/
|
||||
extern JS_PUBLIC_API(uint8_t)
|
||||
ReadableStreamGetEmbeddingFlags(const JSObject* stream);
|
||||
|
||||
/**
|
||||
* Returns the embedding-provided underlying source of the given |stream|.
|
||||
*
|
||||
* Can be used to optimize operations if both the underlying source and the
|
||||
* intended sink are embedding-provided. In that case it might be
|
||||
* preferrable to pipe data directly from source to sink without interacting
|
||||
* with the stream at all.
|
||||
*
|
||||
* Locks the stream until ReadableStreamReleaseExternalUnderlyingSource is
|
||||
* called.
|
||||
*
|
||||
* Throws an exception if the stream is locked, i.e. if a reader has been
|
||||
* acquired for the stream, or if ReadableStreamGetExternalUnderlyingSource
|
||||
* has been used previously without releasing the external source again.
|
||||
*
|
||||
* Throws an exception if the stream isn't readable, i.e if it is errored or
|
||||
* closed. This is different from ReadableStreamGetReader because we don't
|
||||
* have a Promise to resolve/reject, which a reader provides.
|
||||
*
|
||||
* Asserts that the stream has an embedding-provided underlying source.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ReadableStreamGetExternalUnderlyingSource(JSContext* cx, HandleObject stream, void** source);
|
||||
|
||||
/**
|
||||
* Releases the embedding-provided underlying source of the given |stream|,
|
||||
* returning the stream into an unlocked state.
|
||||
*
|
||||
* Asserts that the stream was locked through
|
||||
* ReadableStreamGetExternalUnderlyingSource.
|
||||
*
|
||||
* Asserts that the stream has an embedding-provided underlying source.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
ReadableStreamReleaseExternalUnderlyingSource(JSObject* stream);
|
||||
|
||||
/**
|
||||
* Update the amount of data available at the underlying source of the given
|
||||
* |stream|.
|
||||
*
|
||||
* Can only be used for streams with an embedding-provided underlying source.
|
||||
* The JS engine will use the given value to satisfy read requests for the
|
||||
* stream by invoking the JS::WriteIntoReadRequestBuffer callback.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ReadableStreamUpdateDataAvailableFromSource(JSContext* cx, HandleObject stream,
|
||||
uint32_t availableData);
|
||||
|
||||
/**
|
||||
* Returns true if the given object is an unwrapped ReadableStream object,
|
||||
* false otherwise.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
IsReadableStream(const JSObject* obj);
|
||||
|
||||
/**
|
||||
* Returns true if the given object is an unwrapped
|
||||
* ReadableStreamDefaultReader or ReadableStreamBYOBReader object,
|
||||
* false otherwise.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
IsReadableStreamReader(const JSObject* obj);
|
||||
|
||||
/**
|
||||
* Returns true if the given object is an unwrapped
|
||||
* ReadableStreamDefaultReader object, false otherwise.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
IsReadableStreamDefaultReader(const JSObject* obj);
|
||||
|
||||
/**
|
||||
* Returns true if the given object is an unwrapped
|
||||
* ReadableStreamBYOBReader object, false otherwise.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
IsReadableStreamBYOBReader(const JSObject* obj);
|
||||
|
||||
enum class ReadableStreamMode {
|
||||
Default,
|
||||
Byte,
|
||||
ExternalSource
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the stream's ReadableStreamMode. If the mode is |Byte| or
|
||||
* |ExternalSource|, it's possible to acquire a BYOB reader for more optimized
|
||||
* operations.
|
||||
*
|
||||
* Asserts that |stream| is an unwrapped ReadableStream instance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(ReadableStreamMode)
|
||||
ReadableStreamGetMode(const JSObject* stream);
|
||||
|
||||
enum class ReadableStreamReaderMode {
|
||||
Default,
|
||||
BYOB
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the given ReadableStream is readable, false if not.
|
||||
*
|
||||
* Asserts that |stream| is an unwrapped ReadableStream instance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ReadableStreamIsReadable(const JSObject* stream);
|
||||
|
||||
/**
|
||||
* Returns true if the given ReadableStream is locked, false if not.
|
||||
*
|
||||
* Asserts that |stream| is an unwrapped ReadableStream instance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ReadableStreamIsLocked(const JSObject* stream);
|
||||
|
||||
/**
|
||||
* Returns true if the given ReadableStream is disturbed, false if not.
|
||||
*
|
||||
* Asserts that |stream| is an ReadableStream instance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ReadableStreamIsDisturbed(const JSObject* stream);
|
||||
|
||||
/**
|
||||
* Cancels the given ReadableStream with the given reason and returns a
|
||||
* Promise resolved according to the result.
|
||||
*
|
||||
* Asserts that |stream| is an unwrapped ReadableStream instance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
ReadableStreamCancel(JSContext* cx, HandleObject stream, HandleValue reason);
|
||||
|
||||
/**
|
||||
* Creates a reader of the type specified by the mode option and locks the
|
||||
* stream to the new reader.
|
||||
*
|
||||
* Asserts that |stream| is an unwrapped ReadableStream instance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
ReadableStreamGetReader(JSContext* cx, HandleObject stream, ReadableStreamReaderMode mode);
|
||||
|
||||
/**
|
||||
* Tees the given ReadableStream and stores the two resulting streams in
|
||||
* outparams. Returns false if the operation fails, e.g. because the stream is
|
||||
* locked.
|
||||
*
|
||||
* Asserts that |stream| is an unwrapped ReadableStream instance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ReadableStreamTee(JSContext* cx, HandleObject stream,
|
||||
MutableHandleObject branch1Stream, MutableHandleObject branch2Stream);
|
||||
|
||||
/**
|
||||
* Retrieves the desired combined size of additional chunks to fill the given
|
||||
* ReadableStream's queue. Stores the result in |value| and sets |hasValue| to
|
||||
* true on success, returns false on failure.
|
||||
*
|
||||
* If the stream is errored, the call will succeed but no value will be stored
|
||||
* in |value| and |hasValue| will be set to false.
|
||||
*
|
||||
* Note: This is semantically equivalent to the |desiredSize| getter on
|
||||
* the stream controller's prototype in JS. We expose it with the stream
|
||||
* itself as a target for simplicity.
|
||||
*
|
||||
* Asserts that |stream| is an unwrapped ReadableStream instance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
ReadableStreamGetDesiredSize(JSObject* stream, bool* hasValue, double* value);
|
||||
|
||||
/**
|
||||
* Closes the given ReadableStream.
|
||||
*
|
||||
* Throws a TypeError and returns false if the closing operation fails.
|
||||
*
|
||||
* Note: This is semantically equivalent to the |close| method on
|
||||
* the stream controller's prototype in JS. We expose it with the stream
|
||||
* itself as a target for simplicity.
|
||||
*
|
||||
* Asserts that |stream| is an unwrapped ReadableStream instance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ReadableStreamClose(JSContext* cx, HandleObject stream);
|
||||
|
||||
/**
|
||||
* Returns true if the given ReadableStream reader is locked, false otherwise.
|
||||
*
|
||||
* Asserts that |reader| is an unwrapped ReadableStreamDefaultReader or
|
||||
* ReadableStreamBYOBReader instance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ReadableStreamReaderIsClosed(const JSObject* reader);
|
||||
|
||||
/**
|
||||
* Enqueues the given chunk in the given ReadableStream.
|
||||
*
|
||||
* Throws a TypeError and returns false if the enqueing operation fails.
|
||||
*
|
||||
* Note: This is semantically equivalent to the |enqueue| method on
|
||||
* the stream controller's prototype in JS. We expose it with the stream
|
||||
* itself as a target for simplicity.
|
||||
*
|
||||
* If the ReadableStream has an underlying byte source, the given chunk must
|
||||
* be a typed array or a DataView. Consider using
|
||||
* ReadableByteStreamEnqueueBuffer.
|
||||
*
|
||||
* Asserts that |stream| is an unwrapped ReadableStream instance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ReadableStreamEnqueue(JSContext* cx, HandleObject stream, HandleValue chunk);
|
||||
|
||||
/**
|
||||
* Enqueues the given buffer as a chunk in the given ReadableStream.
|
||||
*
|
||||
* Throws a TypeError and returns false if the enqueing operation fails.
|
||||
*
|
||||
* Note: This is semantically equivalent to the |enqueue| method on
|
||||
* the stream controller's prototype in JS. We expose it with the stream
|
||||
* itself as a target for simplicity. Additionally, the JS version only
|
||||
* takes typed arrays and ArrayBufferView instances as arguments, whereas
|
||||
* this takes an ArrayBuffer, obviating the need to wrap it into a typed
|
||||
* array.
|
||||
*
|
||||
* Asserts that |stream| is an unwrapped ReadableStream instance and |buffer|
|
||||
* an unwrapped ArrayBuffer instance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ReadableByteStreamEnqueueBuffer(JSContext* cx, HandleObject stream, HandleObject buffer);
|
||||
|
||||
/**
|
||||
* Errors the given ReadableStream, causing all future interactions to fail
|
||||
* with the given error value.
|
||||
*
|
||||
* Throws a TypeError and returns false if the erroring operation fails.
|
||||
*
|
||||
* Note: This is semantically equivalent to the |error| method on
|
||||
* the stream controller's prototype in JS. We expose it with the stream
|
||||
* itself as a target for simplicity.
|
||||
*
|
||||
* Asserts that |stream| is an unwrapped ReadableStream instance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ReadableStreamError(JSContext* cx, HandleObject stream, HandleValue error);
|
||||
|
||||
/**
|
||||
* Cancels the given ReadableStream reader's associated stream.
|
||||
*
|
||||
* Throws a TypeError and returns false if the given reader isn't active.
|
||||
*
|
||||
* Asserts that |reader| is an unwrapped ReadableStreamDefaultReader or
|
||||
* ReadableStreamBYOBReader instance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ReadableStreamReaderCancel(JSContext* cx, HandleObject reader, HandleValue reason);
|
||||
|
||||
/**
|
||||
* Cancels the given ReadableStream reader's associated stream.
|
||||
*
|
||||
* Throws a TypeError and returns false if the given reader has pending
|
||||
* read or readInto (for default or byob readers, respectively) requests.
|
||||
*
|
||||
* Asserts that |reader| is an unwrapped ReadableStreamDefaultReader or
|
||||
* ReadableStreamBYOBReader instance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject reader);
|
||||
|
||||
/**
|
||||
* Requests a read from the reader's associated ReadableStream and returns the
|
||||
* resulting PromiseObject.
|
||||
*
|
||||
* Returns a Promise that's resolved with the read result once available or
|
||||
* rejected immediately if the stream is errored or the operation failed.
|
||||
*
|
||||
* Asserts that |reader| is an unwrapped ReadableStreamDefaultReader instance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
ReadableStreamDefaultReaderRead(JSContext* cx, HandleObject reader);
|
||||
|
||||
/**
|
||||
* Requests a read from the reader's associated ReadableStream into the given
|
||||
* ArrayBufferView and returns the resulting PromiseObject.
|
||||
*
|
||||
* Returns a Promise that's resolved with the read result once available or
|
||||
* rejected immediately if the stream is errored or the operation failed.
|
||||
*
|
||||
* Asserts that |reader| is an unwrapped ReadableStreamDefaultReader and
|
||||
* |view| an unwrapped typed array or DataView instance.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
ReadableStreamBYOBReaderRead(JSContext* cx, HandleObject reader, HandleObject view);
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif // js_Realm_h
|
||||
@@ -363,7 +363,7 @@ function InnerModuleInstantiation(module, stack, index)
|
||||
// Step 9
|
||||
let requestedModules = module.requestedModules;
|
||||
for (let i = 0; i < requestedModules.length; i++) {
|
||||
let required = requestedModules[i];
|
||||
let required = requestedModules[i].moduleSpecifier;
|
||||
let requiredModule = CallModuleResolveHook(module, required, MODULE_STATUS_UNINSTANTIATED);
|
||||
|
||||
index = InnerModuleInstantiation(requiredModule, stack, index);
|
||||
@@ -591,7 +591,7 @@ function InnerModuleEvaluation(module, stack, index)
|
||||
// Step 10
|
||||
let requestedModules = module.requestedModules;
|
||||
for (let i = 0; i < requestedModules.length; i++) {
|
||||
let required = requestedModules[i];
|
||||
let required = requestedModules[i].moduleSpecifier;
|
||||
let requiredModule =
|
||||
CallModuleResolveHook(module, required, MODULE_STATUS_INSTANTIATED);
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ GlobalObject::initImportEntryProto(JSContext* cx, Handle<GlobalObject*> global)
|
||||
if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr))
|
||||
return false;
|
||||
|
||||
global->setReservedSlot(IMPORT_ENTRY_PROTO, ObjectValue(*proto));
|
||||
global->initReservedSlot(IMPORT_ENTRY_PROTO, ObjectValue(*proto));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ GlobalObject::initExportEntryProto(JSContext* cx, Handle<GlobalObject*> global)
|
||||
if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr))
|
||||
return false;
|
||||
|
||||
global->setReservedSlot(EXPORT_ENTRY_PROTO, ObjectValue(*proto));
|
||||
global->initReservedSlot(EXPORT_ENTRY_PROTO, ObjectValue(*proto));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -240,6 +240,71 @@ ExportEntryObject::create(ExclusiveContext* cx,
|
||||
return self;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// RequestedModuleObject
|
||||
|
||||
/* static */ const Class
|
||||
RequestedModuleObject::class_ = {
|
||||
"RequestedModule",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(RequestedModuleObject::SlotCount) |
|
||||
JSCLASS_IS_ANONYMOUS
|
||||
};
|
||||
|
||||
DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, moduleSpecifier, ModuleSpecifierSlot)
|
||||
DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, lineNumber, LineNumberSlot)
|
||||
DEFINE_GETTER_FUNCTIONS(RequestedModuleObject, columnNumber, ColumnNumberSlot)
|
||||
|
||||
DEFINE_ATOM_ACCESSOR_METHOD(RequestedModuleObject, moduleSpecifier)
|
||||
DEFINE_UINT32_ACCESSOR_METHOD(RequestedModuleObject, lineNumber)
|
||||
DEFINE_UINT32_ACCESSOR_METHOD(RequestedModuleObject, columnNumber)
|
||||
|
||||
/* static */ bool
|
||||
RequestedModuleObject::isInstance(HandleValue value)
|
||||
{
|
||||
return value.isObject() && value.toObject().is<RequestedModuleObject>();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
GlobalObject::initRequestedModuleProto(JSContext* cx, Handle<GlobalObject*> global)
|
||||
{
|
||||
static const JSPropertySpec protoAccessors[] = {
|
||||
JS_PSG("moduleSpecifier", RequestedModuleObject_moduleSpecifierGetter, 0),
|
||||
JS_PSG("lineNumber", RequestedModuleObject_lineNumberGetter, 0),
|
||||
JS_PSG("columnNumber", RequestedModuleObject_columnNumberGetter, 0),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
|
||||
if (!proto)
|
||||
return false;
|
||||
|
||||
if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr))
|
||||
return false;
|
||||
|
||||
global->initReservedSlot(REQUESTED_MODULE_PROTO, ObjectValue(*proto));
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ RequestedModuleObject*
|
||||
RequestedModuleObject::create(JSContext* cx,
|
||||
HandleAtom moduleSpecifier,
|
||||
uint32_t lineNumber,
|
||||
uint32_t columnNumber)
|
||||
{
|
||||
MOZ_ASSERT(lineNumber > 0);
|
||||
|
||||
RootedObject proto(cx, cx->global()->getRequestedModulePrototype());
|
||||
RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto));
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
RootedRequestedModuleObject self(cx, &obj->as<RequestedModuleObject>());
|
||||
self->initReservedSlot(ModuleSpecifierSlot, StringValue(moduleSpecifier));
|
||||
self->initReservedSlot(LineNumberSlot, Int32Value(lineNumber));
|
||||
self->initReservedSlot(ColumnNumberSlot, Int32Value(columnNumber));
|
||||
return self;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// IndirectBindingMap
|
||||
|
||||
@@ -1074,7 +1139,8 @@ ModuleBuilder::ModuleBuilder(ExclusiveContext* cx, HandleModuleObject module,
|
||||
: cx_(cx),
|
||||
module_(cx, module),
|
||||
tokenStream_(tokenStream),
|
||||
requestedModules_(cx, AtomVector(cx)),
|
||||
requestedModuleSpecifiers_(cx, AtomSet(cx)),
|
||||
requestedModules_(cx, RequestedModuleVector(cx)),
|
||||
importedBoundNames_(cx, AtomVector(cx)),
|
||||
importEntries_(cx, ImportEntryVector(cx)),
|
||||
exportEntries_(cx, ExportEntryVector(cx)),
|
||||
@@ -1083,6 +1149,12 @@ ModuleBuilder::ModuleBuilder(ExclusiveContext* cx, HandleModuleObject module,
|
||||
starExportEntries_(cx, ExportEntryVector(cx))
|
||||
{}
|
||||
|
||||
bool
|
||||
ModuleBuilder::init()
|
||||
{
|
||||
return requestedModuleSpecifiers_.init();
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleBuilder::buildTables()
|
||||
{
|
||||
@@ -1130,24 +1202,23 @@ ModuleBuilder::buildTables()
|
||||
bool
|
||||
ModuleBuilder::initModule()
|
||||
{
|
||||
RootedArrayObject requestedModules(cx_, createArray<JSAtom*>(requestedModules_));
|
||||
RootedArrayObject requestedModules(cx_, createArray(requestedModules_));
|
||||
if (!requestedModules)
|
||||
return false;
|
||||
|
||||
RootedArrayObject importEntries(cx_, createArray<ImportEntryObject*>(importEntries_));
|
||||
RootedArrayObject importEntries(cx_, createArray(importEntries_));
|
||||
if (!importEntries)
|
||||
return false;
|
||||
|
||||
RootedArrayObject localExportEntries(cx_, createArray<ExportEntryObject*>(localExportEntries_));
|
||||
RootedArrayObject localExportEntries(cx_, createArray(localExportEntries_));
|
||||
if (!localExportEntries)
|
||||
return false;
|
||||
|
||||
RootedArrayObject indirectExportEntries(cx_);
|
||||
indirectExportEntries = createArray<ExportEntryObject*>(indirectExportEntries_);
|
||||
RootedArrayObject indirectExportEntries(cx_, createArray(indirectExportEntries_));
|
||||
if (!indirectExportEntries)
|
||||
return false;
|
||||
|
||||
RootedArrayObject starExportEntries(cx_, createArray<ExportEntryObject*>(starExportEntries_));
|
||||
RootedArrayObject starExportEntries(cx_, createArray(starExportEntries_));
|
||||
if (!starExportEntries)
|
||||
return false;
|
||||
|
||||
@@ -1169,7 +1240,7 @@ ModuleBuilder::processImport(frontend::ParseNode* pn)
|
||||
MOZ_ASSERT(pn->pn_right->isKind(PNK_STRING));
|
||||
|
||||
RootedAtom module(cx_, pn->pn_right->pn_atom);
|
||||
if (!maybeAppendRequestedModule(module))
|
||||
if (!maybeAppendRequestedModule(module, pn->pn_right))
|
||||
return false;
|
||||
|
||||
for (ParseNode* spec = pn->pn_left->pn_head; spec; spec = spec->pn_next) {
|
||||
@@ -1277,7 +1348,7 @@ ModuleBuilder::processExportFrom(frontend::ParseNode* pn)
|
||||
MOZ_ASSERT(pn->pn_right->isKind(PNK_STRING));
|
||||
|
||||
RootedAtom module(cx_, pn->pn_right->pn_atom);
|
||||
if (!maybeAppendRequestedModule(module))
|
||||
if (!maybeAppendRequestedModule(module, pn->pn_right))
|
||||
return false;
|
||||
|
||||
for (ParseNode* spec = pn->pn_left->pn_head; spec; spec = spec->pn_next) {
|
||||
@@ -1346,29 +1417,26 @@ ModuleBuilder::appendExportFromEntry(HandleAtom exportName, HandleAtom moduleReq
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleBuilder::maybeAppendRequestedModule(HandleAtom module)
|
||||
ModuleBuilder::maybeAppendRequestedModule(HandleAtom specifier, ParseNode* node)
|
||||
{
|
||||
for (auto m : requestedModules_) {
|
||||
if (m == module)
|
||||
return true;
|
||||
if (requestedModuleSpecifiers_.has(specifier)) {
|
||||
return true;
|
||||
}
|
||||
return requestedModules_.append(module);
|
||||
}
|
||||
|
||||
static Value
|
||||
MakeElementValue(JSString *string)
|
||||
{
|
||||
return StringValue(string);
|
||||
}
|
||||
|
||||
static Value
|
||||
MakeElementValue(JSObject *object)
|
||||
{
|
||||
return ObjectValue(*object);
|
||||
uint32_t line;
|
||||
uint32_t column;
|
||||
tokenStream_.srcCoords.lineNumAndColumnIndex(node->pn_pos.begin, &line, &column);
|
||||
JSContext* cx = reinterpret_cast<JSContext*>(cx_);
|
||||
RootedRequestedModuleObject req(cx, RequestedModuleObject::create(cx, specifier, line, column));
|
||||
if (!req) {
|
||||
return false;
|
||||
}
|
||||
return FreezeObject(cx, req) &&
|
||||
requestedModules_.append(req) &&
|
||||
requestedModuleSpecifiers_.put(specifier);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ArrayObject* ModuleBuilder::createArray(const GCVector<T>& vector)
|
||||
ArrayObject* ModuleBuilder::createArray(const JS::Rooted<GCVector<T>>& vector)
|
||||
{
|
||||
uint32_t length = vector.length();
|
||||
RootedArrayObject array(cx_, NewDenseFullyAllocatedArray(cx_, length));
|
||||
@@ -1377,7 +1445,7 @@ ArrayObject* ModuleBuilder::createArray(const GCVector<T>& vector)
|
||||
|
||||
array->setDenseInitializedLength(length);
|
||||
for (uint32_t i = 0; i < length; i++)
|
||||
array->initDenseElement(i, MakeElementValue(vector[i]));
|
||||
array->initDenseElement(i, ObjectValue(*vector[i]));
|
||||
|
||||
return array;
|
||||
}
|
||||
@@ -100,6 +100,32 @@ class ExportEntryObject : public NativeObject
|
||||
typedef Rooted<ExportEntryObject*> RootedExportEntryObject;
|
||||
typedef Handle<ExportEntryObject*> HandleExportEntryObject;
|
||||
|
||||
class RequestedModuleObject : public NativeObject
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
ModuleSpecifierSlot = 0,
|
||||
LineNumberSlot,
|
||||
ColumnNumberSlot,
|
||||
SlotCount
|
||||
};
|
||||
|
||||
static const Class class_;
|
||||
static JSObject* initClass(JSContext* cx, HandleObject obj);
|
||||
static bool isInstance(HandleValue value);
|
||||
static RequestedModuleObject* create(JSContext* cx,
|
||||
HandleAtom moduleSpecifier,
|
||||
uint32_t lineNumber,
|
||||
uint32_t columnNumber);
|
||||
JSAtom* moduleSpecifier() const;
|
||||
uint32_t lineNumber() const;
|
||||
uint32_t columnNumber() const;
|
||||
};
|
||||
|
||||
typedef Rooted<RequestedModuleObject*> RootedRequestedModuleObject;
|
||||
typedef Handle<RequestedModuleObject*> HandleRequestedModuleObject;
|
||||
|
||||
class IndirectBindingMap
|
||||
{
|
||||
public:
|
||||
@@ -318,6 +344,8 @@ class MOZ_STACK_CLASS ModuleBuilder
|
||||
explicit ModuleBuilder(ExclusiveContext* cx, HandleModuleObject module,
|
||||
const frontend::TokenStream& tokenStream);
|
||||
|
||||
bool init();
|
||||
|
||||
bool processImport(frontend::ParseNode* pn);
|
||||
bool processExport(frontend::ParseNode* pn);
|
||||
bool processExportFrom(frontend::ParseNode* pn);
|
||||
@@ -334,15 +362,20 @@ class MOZ_STACK_CLASS ModuleBuilder
|
||||
|
||||
private:
|
||||
using AtomVector = GCVector<JSAtom*>;
|
||||
using RootedAtomVector = JS::Rooted<AtomVector>;
|
||||
using ImportEntryVector = GCVector<ImportEntryObject*>;
|
||||
using RequestedModuleVector = GCVector<RequestedModuleObject*>;
|
||||
using AtomSet = JS::GCHashSet<JSAtom*>;
|
||||
using RootedAtomVector = JS::Rooted<AtomVector>;
|
||||
using RootedImportEntryVector = JS::Rooted<ImportEntryVector>;
|
||||
using RootedExportEntryVector = JS::Rooted<ExportEntryVector>;
|
||||
using RootedRequestedModuleVector = JS::Rooted<RequestedModuleVector>;
|
||||
using RootedAtomSet = JS::Rooted<AtomSet>;
|
||||
|
||||
ExclusiveContext* cx_;
|
||||
RootedModuleObject module_;
|
||||
const frontend::TokenStream& tokenStream_;
|
||||
RootedAtomVector requestedModules_;
|
||||
RootedAtomSet requestedModuleSpecifiers_;
|
||||
RootedRequestedModuleVector requestedModules_;
|
||||
RootedAtomVector importedBoundNames_;
|
||||
RootedImportEntryVector importEntries_;
|
||||
RootedExportEntryVector exportEntries_;
|
||||
@@ -357,10 +390,10 @@ class MOZ_STACK_CLASS ModuleBuilder
|
||||
bool appendExportFromEntry(HandleAtom exportName, HandleAtom moduleRequest,
|
||||
HandleAtom importName, frontend::ParseNode* node);
|
||||
|
||||
bool maybeAppendRequestedModule(HandleAtom module);
|
||||
bool maybeAppendRequestedModule(HandleAtom specifier, frontend::ParseNode* node);
|
||||
|
||||
template <typename T>
|
||||
ArrayObject* createArray(const GCVector<T>& vector);
|
||||
ArrayObject* createArray(const JS::Rooted<GCVector<T>>& vector);
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
@@ -3741,6 +3741,9 @@ reflect_parse(JSContext* cx, uint32_t argc, Value* vp)
|
||||
return false;
|
||||
|
||||
ModuleBuilder builder(cx, module, parser.tokenStream);
|
||||
if (!builder.init())
|
||||
return false;
|
||||
|
||||
ModuleSharedContext modulesc(cx, module, &cx->global()->emptyGlobalScope(), builder);
|
||||
pn = parser.moduleBody(&modulesc);
|
||||
if (!pn)
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,170 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* 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 builtin_Stream_h
|
||||
#define builtin_Stream_h
|
||||
|
||||
#include "builtin/Promise.h"
|
||||
#include "vm/NativeObject.h"
|
||||
|
||||
|
||||
namespace js {
|
||||
|
||||
class AutoSetNewObjectMetadata;
|
||||
|
||||
class ReadableStream : public NativeObject
|
||||
{
|
||||
public:
|
||||
static ReadableStream* createDefaultStream(JSContext* cx, HandleValue underlyingSource,
|
||||
HandleValue size, HandleValue highWaterMark,
|
||||
HandleObject proto = nullptr);
|
||||
static ReadableStream* createByteStream(JSContext* cx, HandleValue underlyingSource,
|
||||
HandleValue highWaterMark,
|
||||
HandleObject proto = nullptr);
|
||||
static ReadableStream* createExternalSourceStream(JSContext* cx, void* underlyingSource,
|
||||
uint8_t flags, HandleObject proto = nullptr);
|
||||
|
||||
bool readable() const;
|
||||
bool closed() const;
|
||||
bool errored() const;
|
||||
bool disturbed() const;
|
||||
|
||||
bool locked() const;
|
||||
|
||||
void desiredSize(bool* hasSize, double* size) const;
|
||||
|
||||
JS::ReadableStreamMode mode() const;
|
||||
|
||||
static MOZ_MUST_USE bool close(JSContext* cx, Handle<ReadableStream*> stream);
|
||||
static MOZ_MUST_USE JSObject* cancel(JSContext* cx, Handle<ReadableStream*> stream,
|
||||
HandleValue reason);
|
||||
static MOZ_MUST_USE bool error(JSContext* cx, Handle<ReadableStream*> stream,
|
||||
HandleValue error);
|
||||
|
||||
static MOZ_MUST_USE NativeObject* getReader(JSContext* cx, Handle<ReadableStream*> stream,
|
||||
JS::ReadableStreamReaderMode mode);
|
||||
|
||||
static MOZ_MUST_USE bool tee(JSContext* cx,
|
||||
Handle<ReadableStream*> stream, bool cloneForBranch2,
|
||||
MutableHandle<ReadableStream*> branch1Stream,
|
||||
MutableHandle<ReadableStream*> branch2Stream);
|
||||
|
||||
static MOZ_MUST_USE bool enqueue(JSContext* cx, Handle<ReadableStream*> stream,
|
||||
HandleValue chunk);
|
||||
static MOZ_MUST_USE bool enqueueBuffer(JSContext* cx, Handle<ReadableStream*> stream,
|
||||
Handle<ArrayBufferObject*> chunk);
|
||||
static MOZ_MUST_USE bool getExternalSource(JSContext* cx, Handle<ReadableStream*> stream,
|
||||
void** source);
|
||||
void releaseExternalSource();
|
||||
uint8_t embeddingFlags() const;
|
||||
static MOZ_MUST_USE bool updateDataAvailableFromSource(JSContext* cx,
|
||||
Handle<ReadableStream*> stream,
|
||||
uint32_t availableData);
|
||||
|
||||
enum State {
|
||||
Readable = 1 << 0,
|
||||
Closed = 1 << 1,
|
||||
Errored = 1 << 2,
|
||||
Disturbed = 1 << 3
|
||||
};
|
||||
|
||||
private:
|
||||
static MOZ_MUST_USE ReadableStream* createStream(JSContext* cx, HandleObject proto = nullptr);
|
||||
|
||||
public:
|
||||
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
|
||||
static const ClassSpec classSpec_;
|
||||
static const Class class_;
|
||||
static const ClassSpec protoClassSpec_;
|
||||
static const Class protoClass_;
|
||||
};
|
||||
|
||||
class ReadableStreamDefaultReader : public NativeObject
|
||||
{
|
||||
public:
|
||||
static MOZ_MUST_USE JSObject* read(JSContext* cx, Handle<ReadableStreamDefaultReader*> reader);
|
||||
|
||||
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
|
||||
static const ClassSpec classSpec_;
|
||||
static const Class class_;
|
||||
static const ClassSpec protoClassSpec_;
|
||||
static const Class protoClass_;
|
||||
};
|
||||
|
||||
class ReadableStreamBYOBReader : public NativeObject
|
||||
{
|
||||
public:
|
||||
static MOZ_MUST_USE JSObject* read(JSContext* cx, Handle<ReadableStreamBYOBReader*> reader,
|
||||
Handle<ArrayBufferViewObject*> view);
|
||||
|
||||
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
|
||||
static const ClassSpec classSpec_;
|
||||
static const Class class_;
|
||||
static const ClassSpec protoClassSpec_;
|
||||
static const Class protoClass_;
|
||||
};
|
||||
|
||||
bool ReadableStreamReaderIsClosed(const JSObject* reader);
|
||||
|
||||
MOZ_MUST_USE bool ReadableStreamReaderCancel(JSContext* cx, HandleObject reader,
|
||||
HandleValue reason);
|
||||
|
||||
MOZ_MUST_USE bool ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject reader);
|
||||
|
||||
class ReadableStreamDefaultController : public NativeObject
|
||||
{
|
||||
public:
|
||||
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
|
||||
static const ClassSpec classSpec_;
|
||||
static const Class class_;
|
||||
static const ClassSpec protoClassSpec_;
|
||||
static const Class protoClass_;
|
||||
};
|
||||
|
||||
class ReadableByteStreamController : public NativeObject
|
||||
{
|
||||
public:
|
||||
bool hasExternalSource();
|
||||
|
||||
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
|
||||
static const ClassSpec classSpec_;
|
||||
static const Class class_;
|
||||
static const ClassSpec protoClassSpec_;
|
||||
static const Class protoClass_;
|
||||
};
|
||||
|
||||
class ReadableStreamBYOBRequest : public NativeObject
|
||||
{
|
||||
public:
|
||||
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
|
||||
static const ClassSpec classSpec_;
|
||||
static const Class class_;
|
||||
static const ClassSpec protoClassSpec_;
|
||||
static const Class protoClass_;
|
||||
};
|
||||
|
||||
class ByteLengthQueuingStrategy : public NativeObject
|
||||
{
|
||||
public:
|
||||
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
|
||||
static const ClassSpec classSpec_;
|
||||
static const Class class_;
|
||||
static const ClassSpec protoClassSpec_;
|
||||
static const Class protoClass_;
|
||||
};
|
||||
|
||||
class CountQueuingStrategy : public NativeObject
|
||||
{
|
||||
public:
|
||||
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
|
||||
static const ClassSpec classSpec_;
|
||||
static const Class class_;
|
||||
static const ClassSpec protoClassSpec_;
|
||||
static const Class protoClass_;
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* builtin_Stream_h */
|
||||
@@ -1435,14 +1435,6 @@ RejectPromise(JSContext* cx, unsigned argc, Value* vp)
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool
|
||||
StreamsAreEnabled(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setBoolean(cx->options().streams());
|
||||
return true;
|
||||
}
|
||||
|
||||
static unsigned finalizeCount = 0;
|
||||
|
||||
static void
|
||||
@@ -4180,10 +4172,6 @@ JS_FN_HELP("rejectPromise", RejectPromise, 2, 0,
|
||||
"rejectPromise(promise, reason)",
|
||||
" Reject a Promise by calling the JSAPI function JS::RejectPromise."),
|
||||
|
||||
JS_FN_HELP("streamsAreEnabled", StreamsAreEnabled, 0, 0,
|
||||
"streamsAreEnabled()",
|
||||
" Returns a boolean indicating whether WHATWG Streams are enabled for the current compartment."),
|
||||
|
||||
JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0,
|
||||
"makeFinalizeObserver()",
|
||||
" Get a special object whose finalization increases the counter returned\n"
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* 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 "TypedObjectConstants.h"
|
||||
|
||||
function ViewedArrayBufferIfReified(tarray) {
|
||||
assert(IsTypedArray(tarray), "non-typed array asked for its buffer");
|
||||
|
||||
@@ -1631,7 +1633,7 @@ function ArrayBufferSlice(start, end) {
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||
|
||||
// Steps 19-21.
|
||||
ArrayBufferCopyData(new_, 0, O, first | 0, newLen | 0, isWrapped);
|
||||
ArrayBufferCopyData(new_, O, first | 0, newLen | 0, isWrapped);
|
||||
|
||||
// Step 22.
|
||||
return new_;
|
||||
@@ -1713,7 +1715,7 @@ function SharedArrayBufferSlice(start, end) {
|
||||
ThrowTypeError(JSMSG_SHORT_SHARED_ARRAY_BUFFER_RETURNED, newLen, actualLen);
|
||||
|
||||
// Steps 16-18.
|
||||
SharedArrayBufferCopyData(new_, 0, O, first | 0, newLen | 0, isWrapped);
|
||||
SharedArrayBufferCopyData(new_, O, first | 0, newLen | 0, isWrapped);
|
||||
|
||||
// Step 19.
|
||||
return new_;
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
*/
|
||||
|
||||
#include "SelfHostingDefines.h"
|
||||
#include "TypedObjectConstants.h"
|
||||
|
||||
// Assertions and debug printing, defined here instead of in the header above
|
||||
// to make `assert` invisible to C++.
|
||||
|
||||
@@ -24,7 +24,6 @@ SOURCES += [
|
||||
'ReflectParse.cpp',
|
||||
'RegExp.cpp',
|
||||
'SIMD.cpp',
|
||||
'Stream.cpp',
|
||||
'SymbolObject.cpp',
|
||||
'TestingFunctions.cpp',
|
||||
'TypedObject.cpp',
|
||||
|
||||
@@ -405,6 +405,9 @@ BytecodeCompiler::compileModule()
|
||||
module->init(script);
|
||||
|
||||
ModuleBuilder builder(cx, module, parser->tokenStream);
|
||||
if (!builder.init())
|
||||
return nullptr;
|
||||
|
||||
ModuleSharedContext modulesc(cx, module, enclosingScope, builder);
|
||||
ParseNode* pn = parser->moduleBody(&modulesc);
|
||||
if (!pn)
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "js/HeapAPI.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/TracingAPI.h"
|
||||
#include "js/TraceKind.h"
|
||||
|
||||
struct JSRuntime;
|
||||
|
||||
@@ -272,6 +273,23 @@ struct Cell
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool needWriteBarrierPre(JS::Zone* zone);
|
||||
|
||||
template <class T>
|
||||
inline bool is() const {
|
||||
return getTraceKind() == JS::MapTypeToTraceKind<T>::kind;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline T* as() {
|
||||
MOZ_ASSERT(this->is<T>());
|
||||
return static_cast<T*>(this);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline const T* as() const {
|
||||
MOZ_ASSERT(this->is<T>());
|
||||
return static_cast<const T*>(this);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
inline bool isAligned() const;
|
||||
void dump(FILE* fp) const;
|
||||
|
||||
@@ -46,6 +46,7 @@ class RegExpObject;
|
||||
class SavedFrame;
|
||||
class Scope;
|
||||
class EnvironmentObject;
|
||||
class RequestedModuleObject;
|
||||
class ScriptSourceObject;
|
||||
class Shape;
|
||||
class SharedArrayBufferObject;
|
||||
@@ -84,6 +85,7 @@ class JitCode;
|
||||
D(js::PlainObject*) \
|
||||
D(js::PropertyName*) \
|
||||
D(js::RegExpObject*) \
|
||||
D(js::RequestedModuleObject*) \
|
||||
D(js::SavedFrame*) \
|
||||
D(js::Scope*) \
|
||||
D(js::ScriptSourceObject*) \
|
||||
|
||||
@@ -5,7 +5,7 @@ function testRequestedModules(source, expected) {
|
||||
var actual = module.requestedModules;
|
||||
assertEq(actual.length, expected.length);
|
||||
for (var i = 0; i < actual.length; i++) {
|
||||
assertEq(actual[i], expected[i]);
|
||||
assertEq(actual[i].moduleSpecifier, expected[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-17
@@ -11,7 +11,7 @@ FINAL_LIBRARY = "js"
|
||||
# Includes should be relative to parent path
|
||||
LOCAL_INCLUDES += ["!..", ".."]
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'AliasAnalysis.cpp',
|
||||
'AliasAnalysisShared.cpp',
|
||||
'AlignmentMaskAnalysis.cpp',
|
||||
@@ -83,14 +83,14 @@ SOURCES += [
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_DEBUG']:
|
||||
SOURCES += ['Disassembler.cpp']
|
||||
UNIFIED_SOURCES += ['Disassembler.cpp']
|
||||
|
||||
if not CONFIG['ENABLE_ION']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'none/Trampoline-none.cpp'
|
||||
]
|
||||
elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'x86-shared/Architecture-x86-shared.cpp',
|
||||
'x86-shared/Assembler-x86-shared.cpp',
|
||||
'x86-shared/AssemblerBuffer-x86-shared.cpp',
|
||||
@@ -101,11 +101,11 @@ elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
|
||||
'x86-shared/MacroAssembler-x86-shared.cpp',
|
||||
'x86-shared/MoveEmitter-x86-shared.cpp',
|
||||
]
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'x86-shared/Disassembler-x86-shared.cpp', # using namespace js::jit::X86Encoding;
|
||||
]
|
||||
if CONFIG['JS_CODEGEN_X64']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'x64/Assembler-x64.cpp',
|
||||
'x64/Bailouts-x64.cpp',
|
||||
'x64/BaselineCompiler-x64.cpp',
|
||||
@@ -117,7 +117,7 @@ elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
|
||||
'x64/Trampoline-x64.cpp',
|
||||
]
|
||||
else:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'x86/Assembler-x86.cpp',
|
||||
'x86/Bailouts-x86.cpp',
|
||||
'x86/BaselineCompiler-x86.cpp',
|
||||
@@ -129,7 +129,7 @@ elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
|
||||
'x86/Trampoline-x86.cpp',
|
||||
]
|
||||
elif CONFIG['JS_CODEGEN_ARM']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'arm/Architecture-arm.cpp',
|
||||
'arm/Assembler-arm.cpp',
|
||||
'arm/Bailouts-arm.cpp',
|
||||
@@ -145,16 +145,16 @@ elif CONFIG['JS_CODEGEN_ARM']:
|
||||
'arm/Trampoline-arm.cpp',
|
||||
]
|
||||
if CONFIG['JS_SIMULATOR_ARM']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'arm/Simulator-arm.cpp'
|
||||
]
|
||||
elif CONFIG['OS_ARCH'] == 'Darwin':
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'arm/llvm-compiler-rt/arm/aeabi_idivmod.S',
|
||||
'arm/llvm-compiler-rt/arm/aeabi_uidivmod.S',
|
||||
]
|
||||
elif CONFIG['JS_CODEGEN_ARM64']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'arm64/Architecture-arm64.cpp',
|
||||
'arm64/Assembler-arm64.cpp',
|
||||
'arm64/Bailouts-arm64.cpp',
|
||||
@@ -177,14 +177,14 @@ elif CONFIG['JS_CODEGEN_ARM64']:
|
||||
'arm64/vixl/Utils-vixl.cpp'
|
||||
]
|
||||
if CONFIG['JS_SIMULATOR_ARM64']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'arm64/vixl/Debugger-vixl.cpp',
|
||||
'arm64/vixl/Logic-vixl.cpp',
|
||||
'arm64/vixl/MozSimulator-vixl.cpp',
|
||||
'arm64/vixl/Simulator-vixl.cpp'
|
||||
]
|
||||
elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'mips-shared/Architecture-mips-shared.cpp',
|
||||
'mips-shared/Assembler-mips-shared.cpp',
|
||||
'mips-shared/Bailouts-mips-shared.cpp',
|
||||
@@ -196,7 +196,7 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
|
||||
'mips-shared/MoveEmitter-mips-shared.cpp',
|
||||
]
|
||||
if CONFIG['JS_CODEGEN_MIPS32']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'mips32/Architecture-mips32.cpp',
|
||||
'mips32/Assembler-mips32.cpp',
|
||||
'mips32/Bailouts-mips32.cpp',
|
||||
@@ -210,11 +210,11 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
|
||||
'mips32/Trampoline-mips32.cpp',
|
||||
]
|
||||
if CONFIG['JS_SIMULATOR_MIPS32']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'mips32/Simulator-mips32.cpp'
|
||||
]
|
||||
elif CONFIG['JS_CODEGEN_MIPS64']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'mips64/Architecture-mips64.cpp',
|
||||
'mips64/Assembler-mips64.cpp',
|
||||
'mips64/Bailouts-mips64.cpp',
|
||||
@@ -228,6 +228,6 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
|
||||
'mips64/Trampoline-mips64.cpp',
|
||||
]
|
||||
if CONFIG['JS_SIMULATOR_MIPS64']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'mips64/Simulator-mips64.cpp'
|
||||
]
|
||||
@@ -3,6 +3,8 @@
|
||||
# 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/.
|
||||
|
||||
FILES_PER_UNIFIED_FILE = 6
|
||||
|
||||
# Also set in shell/moz.build
|
||||
DEFINES['ENABLE_SHARED_ARRAY_BUFFER'] = True
|
||||
|
||||
|
||||
+1
-29
@@ -593,35 +593,7 @@ MSG_DEF(JSMSG_RETURN_NOT_CALLABLE, 0, JSEXN_TYPEERR, "property 'return' of i
|
||||
MSG_DEF(JSMSG_ITERATOR_NO_THROW, 0, JSEXN_TYPEERR, "iterator does not have a 'throw' method")
|
||||
|
||||
// Async Iteration
|
||||
MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF, 0, JSEXN_SYNTAXERR, "'for await' loop should be used with 'of'")
|
||||
MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF, 0, JSEXN_TYPEERR, "'for await' loop should be used with 'of'")
|
||||
MSG_DEF(JSMSG_NOT_AN_ASYNC_GENERATOR, 0, JSEXN_TYPEERR, "Not an async generator")
|
||||
MSG_DEF(JSMSG_NOT_AN_ASYNC_ITERATOR, 0, JSEXN_TYPEERR, "Not an async from sync iterator")
|
||||
MSG_DEF(JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.asyncIterator]() returned a non-object value")
|
||||
|
||||
// ReadableStream
|
||||
MSG_DEF(JSMSG_READABLESTREAM_UNDERLYINGSOURCE_TYPE_WRONG,0, JSEXN_RANGEERR,"'underlyingSource.type' must be \"bytes\" or undefined.")
|
||||
MSG_DEF(JSMSG_READABLESTREAM_INVALID_READER_MODE, 0, JSEXN_RANGEERR,"'mode' must be \"byob\" or undefined.")
|
||||
MSG_DEF(JSMSG_NUMBER_MUST_BE_FINITE_NON_NEGATIVE, 1, JSEXN_RANGEERR, "'{0}' must be a finite, non-negative number.")
|
||||
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_BYTESWRITTEN, 0, JSEXN_RANGEERR, "'bytesWritten' exceeds remaining length.")
|
||||
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_VIEW_SIZE, 0, JSEXN_RANGEERR, "view size does not match requested data.")
|
||||
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_VIEW_OFFSET, 0, JSEXN_RANGEERR, "view offset does not match requested position.")
|
||||
MSG_DEF(JSMSG_READABLESTREAM_NOT_LOCKED, 1, JSEXN_TYPEERR, "'{0}' may only be called on a locked stream.")
|
||||
MSG_DEF(JSMSG_READABLESTREAM_LOCKED, 0, JSEXN_TYPEERR, "A Reader may only be created for an unlocked ReadableStream.")
|
||||
MSG_DEF(JSMSG_READABLESTREAM_NOT_BYTE_STREAM_CONTROLLER, 1, JSEXN_TYPEERR, "{0} requires a ReadableByteStreamController.")
|
||||
MSG_DEF(JSMSG_READABLESTREAM_NOT_DEFAULT_CONTROLLER, 1, JSEXN_TYPEERR, "{0} requires a ReadableStreamDefaultController.")
|
||||
MSG_DEF(JSMSG_READABLESTREAM_CONTROLLER_SET, 0, JSEXN_TYPEERR, "The ReadableStream already has a controller defined.")
|
||||
MSG_DEF(JSMSG_READABLESTREAMREADER_NOT_OWNED, 1, JSEXN_TYPEERR, "The ReadableStream reader method '{0}' may only be called on a reader owned by a stream.")
|
||||
MSG_DEF(JSMSG_READABLESTREAMREADER_NOT_EMPTY, 1, JSEXN_TYPEERR, "The ReadableStream reader method '{0}' may not be called on a reader with read requests.")
|
||||
MSG_DEF(JSMSG_READABLESTREAMBYOBREADER_READ_EMPTY_VIEW, 0, JSEXN_TYPEERR, "ReadableStreamBYOBReader.read() was passed an empty TypedArrayBuffer view.")
|
||||
MSG_DEF(JSMSG_READABLESTREAMREADER_RELEASED, 0, JSEXN_TYPEERR, "The ReadableStream reader was released.")
|
||||
MSG_DEF(JSMSG_READABLESTREAMCONTROLLER_CLOSED, 1, JSEXN_TYPEERR, "'{0}' called on a stream already closing.")
|
||||
MSG_DEF(JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, 1, JSEXN_TYPEERR, "'{0}' may only be called on a stream in the 'readable' state.")
|
||||
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNKSIZE,0, JSEXN_RANGEERR, "ReadableByteStreamController requires a positive integer or undefined for 'autoAllocateChunkSize'.")
|
||||
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNK, 1, JSEXN_TYPEERR, "{0} passed a bad chunk.")
|
||||
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_CLOSE_PENDING_PULL, 0, JSEXN_TYPEERR, "The ReadableByteStreamController cannot be closed while the buffer is being filled.")
|
||||
MSG_DEF(JSMSG_READABLESTREAMBYOBREQUEST_NO_CONTROLLER, 1, JSEXN_TYPEERR, "ReadableStreamBYOBRequest method '{0}' called on a request with no controller.")
|
||||
MSG_DEF(JSMSG_READABLESTREAMBYOBREQUEST_RESPOND_CLOSED, 0, JSEXN_TYPEERR, "ReadableStreamBYOBRequest method 'respond' called with non-zero number of bytes with a closed controller.")
|
||||
MSG_DEF(JSMSG_READABLESTREAM_METHOD_NOT_IMPLEMENTED, 1, JSEXN_TYPEERR, "ReadableStream method {0} not yet implemented")
|
||||
|
||||
// Other Stream-related
|
||||
MSG_DEF(JSMSG_STREAM_INVALID_HIGHWATERMARK, 0, JSEXN_RANGEERR, "'highWaterMark' must be a non-negative, non-NaN number.")
|
||||
@@ -126,11 +126,6 @@ if CONFIG['ENABLE_ION']:
|
||||
'testJitRValueAlloc.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['ENABLE_STREAMS']:
|
||||
UNIFIED_SOURCES += [
|
||||
'testReadableStream.cpp',
|
||||
]
|
||||
|
||||
DEFINES['EXPORT_JS_API'] = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include "js/RequiredDefines.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/SliceBudget.h"
|
||||
#include "js/Stream.h"
|
||||
#include "js/StructuredClone.h"
|
||||
#include "js/TracingAPI.h"
|
||||
#include "js/TrackedOptimizationInfo.h"
|
||||
|
||||
@@ -1,676 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 "jsapi.h"
|
||||
|
||||
#include "jsapi-tests/tests.h"
|
||||
|
||||
using namespace JS;
|
||||
|
||||
char test_buffer_data[] = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
static JSObject*
|
||||
NewDefaultStream(JSContext* cx, HandleObject source = nullptr, HandleFunction size = nullptr,
|
||||
double highWaterMark = 1, HandleObject proto = nullptr)
|
||||
{
|
||||
RootedObject stream(cx, NewReadableDefaultStreamObject(cx, source, size, highWaterMark,
|
||||
proto));
|
||||
MOZ_ASSERT_IF(stream, IsReadableStream(stream));
|
||||
return stream;
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
NewByteStream(JSContext* cx, double highWaterMark = 0, HandleObject proto = nullptr)
|
||||
{
|
||||
RootedObject source(cx, JS_NewPlainObject(cx));
|
||||
MOZ_ASSERT(source);
|
||||
|
||||
RootedObject stream(cx, NewReadableByteStreamObject(cx, source, highWaterMark, proto));
|
||||
MOZ_ASSERT_IF(stream, IsReadableStream(stream));
|
||||
return stream;
|
||||
}
|
||||
|
||||
static bool dataRequestCBCalled = false;
|
||||
static void
|
||||
DataRequestCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags,
|
||||
size_t desiredSize)
|
||||
{
|
||||
MOZ_ASSERT(!dataRequestCBCalled, "Invalid test setup");
|
||||
dataRequestCBCalled = true;
|
||||
}
|
||||
|
||||
static bool writeIntoRequestBufferCBCalled = false;
|
||||
static void
|
||||
WriteIntoRequestBufferCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags,
|
||||
void* buffer, size_t length, size_t* bytesWritten)
|
||||
{
|
||||
MOZ_ASSERT(!writeIntoRequestBufferCBCalled, "Invalid test setup");
|
||||
MOZ_ASSERT(length <= sizeof(test_buffer_data));
|
||||
memcpy(buffer, test_buffer_data, length);
|
||||
writeIntoRequestBufferCBCalled = true;
|
||||
*bytesWritten = length;
|
||||
}
|
||||
|
||||
static bool cancelStreamCBCalled = false;
|
||||
static Value cancelStreamReason;
|
||||
static Value
|
||||
CancelStreamCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags,
|
||||
HandleValue reason)
|
||||
{
|
||||
MOZ_ASSERT(!cancelStreamCBCalled, "Invalid test setup");
|
||||
cancelStreamCBCalled = true;
|
||||
cancelStreamReason = reason;
|
||||
return reason;
|
||||
}
|
||||
|
||||
static bool streamClosedCBCalled = false;
|
||||
static Value streamClosedReason;
|
||||
static void
|
||||
StreamClosedCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags)
|
||||
{
|
||||
MOZ_ASSERT(!streamClosedCBCalled, "Invalid test setup");
|
||||
streamClosedCBCalled = true;
|
||||
}
|
||||
|
||||
static bool streamErroredCBCalled = false;
|
||||
static Value streamErroredReason;
|
||||
static void
|
||||
StreamErroredCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags,
|
||||
HandleValue reason)
|
||||
{
|
||||
MOZ_ASSERT(!streamErroredCBCalled, "Invalid test setup");
|
||||
streamErroredCBCalled = true;
|
||||
streamErroredReason = reason;
|
||||
}
|
||||
|
||||
static bool finalizeStreamCBCalled = false;
|
||||
static void* finalizedStreamUnderlyingSource;
|
||||
static void
|
||||
FinalizeStreamCB(void* underlyingSource, uint8_t flags)
|
||||
{
|
||||
MOZ_ASSERT(!finalizeStreamCBCalled, "Invalid test setup");
|
||||
finalizeStreamCBCalled = true;
|
||||
finalizedStreamUnderlyingSource = underlyingSource;
|
||||
}
|
||||
|
||||
static void
|
||||
ResetCallbacks()
|
||||
{
|
||||
dataRequestCBCalled = false;
|
||||
writeIntoRequestBufferCBCalled = false;
|
||||
cancelStreamReason = UndefinedValue();
|
||||
cancelStreamCBCalled = false;
|
||||
streamClosedCBCalled = false;
|
||||
streamErroredCBCalled = false;
|
||||
finalizeStreamCBCalled = false;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetIterResult(JSContext* cx, HandleObject promise, MutableHandleValue value, bool* done)
|
||||
{
|
||||
RootedObject iterResult(cx, &GetPromiseResult(promise).toObject());
|
||||
|
||||
bool found;
|
||||
if (!JS_HasProperty(cx, iterResult, "value", &found))
|
||||
return false;
|
||||
MOZ_ASSERT(found);
|
||||
if (!JS_HasProperty(cx, iterResult, "done", &found))
|
||||
return false;
|
||||
MOZ_ASSERT(found);
|
||||
|
||||
RootedValue doneVal(cx);
|
||||
if (!JS_GetProperty(cx, iterResult, "value", value))
|
||||
return false;
|
||||
if (!JS_GetProperty(cx, iterResult, "done", &doneVal))
|
||||
return false;
|
||||
|
||||
*done = doneVal.toBoolean();
|
||||
MOZ_ASSERT_IF(*done, value.isUndefined());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
GetReadChunk(JSContext* cx, HandleObject readRequest)
|
||||
{
|
||||
MOZ_ASSERT(GetPromiseState(readRequest) == PromiseState::Fulfilled);
|
||||
RootedValue resultVal(cx, GetPromiseResult(readRequest));
|
||||
MOZ_ASSERT(resultVal.isObject());
|
||||
RootedObject result(cx, &resultVal.toObject());
|
||||
RootedValue chunkVal(cx);
|
||||
JS_GetProperty(cx, result, "value", &chunkVal);
|
||||
return &chunkVal.toObject();
|
||||
}
|
||||
|
||||
BEGIN_TEST(testReadableStream_NewReadableStream)
|
||||
{
|
||||
RootedObject stream(cx, NewDefaultStream(cx));
|
||||
CHECK(stream);
|
||||
CHECK(ReadableStreamGetMode(stream) == ReadableStreamMode::Default);
|
||||
return true;
|
||||
}
|
||||
END_TEST(testReadableStream_NewReadableStream)
|
||||
|
||||
BEGIN_TEST(testReadableStream_NewReadableByteStream)
|
||||
{
|
||||
RootedObject stream(cx, NewByteStream(cx));
|
||||
CHECK(stream);
|
||||
CHECK(ReadableStreamGetMode(stream) == ReadableStreamMode::Byte);
|
||||
return true;
|
||||
}
|
||||
END_TEST(testReadableStream_NewReadableByteStream)
|
||||
|
||||
BEGIN_TEST(testReadableStream_ReadableStreamGetReaderDefault)
|
||||
{
|
||||
RootedObject stream(cx, NewDefaultStream(cx));
|
||||
CHECK(stream);
|
||||
|
||||
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default));
|
||||
CHECK(reader);
|
||||
CHECK(IsReadableStreamDefaultReader(reader));
|
||||
CHECK(ReadableStreamIsLocked(stream));
|
||||
CHECK(!ReadableStreamReaderIsClosed(reader));
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testReadableStream_ReadableStreamGetReaderDefault)
|
||||
|
||||
BEGIN_TEST(testReadableStream_ReadableStreamGetReaderBYOB)
|
||||
{
|
||||
RootedObject stream(cx, NewByteStream(cx));
|
||||
CHECK(stream);
|
||||
|
||||
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::BYOB));
|
||||
CHECK(reader);
|
||||
CHECK(IsReadableStreamBYOBReader(reader));
|
||||
CHECK(ReadableStreamIsLocked(stream));
|
||||
CHECK(!ReadableStreamReaderIsClosed(reader));
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testReadableStream_ReadableStreamGetReaderBYOB)
|
||||
|
||||
BEGIN_TEST(testReadableStream_ReadableStreamTee)
|
||||
{
|
||||
RootedObject stream(cx, NewDefaultStream(cx));
|
||||
CHECK(stream);
|
||||
|
||||
RootedObject leftStream(cx);
|
||||
RootedObject rightStream(cx);
|
||||
CHECK(ReadableStreamTee(cx, stream, &leftStream, &rightStream));
|
||||
CHECK(ReadableStreamIsLocked(stream));
|
||||
CHECK(leftStream);
|
||||
CHECK(IsReadableStream(leftStream));
|
||||
CHECK(rightStream);
|
||||
CHECK(IsReadableStream(rightStream));
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testReadableStream_ReadableStreamTee)
|
||||
|
||||
BEGIN_TEST(testReadableStream_ReadableStreamEnqueue)
|
||||
{
|
||||
RootedObject stream(cx, NewDefaultStream(cx));
|
||||
CHECK(stream);
|
||||
|
||||
RootedObject chunk(cx, JS_NewPlainObject(cx));
|
||||
CHECK(chunk);
|
||||
RootedValue chunkVal(cx, ObjectValue(*chunk));
|
||||
CHECK(ReadableStreamEnqueue(cx, stream, chunkVal));
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testReadableStream_ReadableStreamEnqueue)
|
||||
|
||||
BEGIN_TEST(testReadableStream_ReadableByteStreamEnqueue)
|
||||
{
|
||||
RootedObject stream(cx, NewDefaultStream(cx));
|
||||
CHECK(stream);
|
||||
|
||||
RootedObject chunk(cx, JS_NewUint8Array(cx, 42));
|
||||
CHECK(chunk);
|
||||
CHECK(!ReadableByteStreamEnqueueBuffer(cx, stream, chunk));
|
||||
CHECK(JS_IsExceptionPending(cx));
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testReadableStream_ReadableByteStreamEnqueue)
|
||||
|
||||
BEGIN_TEST(testReadableStream_ReadableStreamDefaultReaderRead)
|
||||
{
|
||||
RootedObject stream(cx, NewDefaultStream(cx));
|
||||
CHECK(stream);
|
||||
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default));
|
||||
CHECK(reader);
|
||||
|
||||
RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
|
||||
CHECK(request);
|
||||
CHECK(IsPromiseObject(request));
|
||||
CHECK(GetPromiseState(request) == PromiseState::Pending);
|
||||
|
||||
RootedObject chunk(cx, JS_NewPlainObject(cx));
|
||||
CHECK(chunk);
|
||||
RootedValue chunkVal(cx, ObjectValue(*chunk));
|
||||
CHECK(ReadableStreamEnqueue(cx, stream, chunkVal));
|
||||
|
||||
CHECK(GetReadChunk(cx, request) == chunk);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testReadableStream_ReadableStreamDefaultReaderRead)
|
||||
|
||||
BEGIN_TEST(testReadableStream_ReadableByteStreamDefaultReaderRead)
|
||||
{
|
||||
RootedObject stream(cx, NewByteStream(cx));
|
||||
CHECK(stream);
|
||||
|
||||
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default));
|
||||
CHECK(reader);
|
||||
|
||||
RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
|
||||
CHECK(request);
|
||||
CHECK(IsPromiseObject(request));
|
||||
CHECK(GetPromiseState(request) == PromiseState::Pending);
|
||||
|
||||
size_t length = sizeof(test_buffer_data);
|
||||
RootedObject buffer(cx, JS_NewArrayBufferWithExternalContents(cx, length, test_buffer_data));
|
||||
CHECK(buffer);
|
||||
RootedObject chunk(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 0, length));
|
||||
CHECK(chunk);
|
||||
bool isShared;
|
||||
CHECK(!JS_IsDetachedArrayBufferObject(buffer));
|
||||
|
||||
CHECK(ReadableByteStreamEnqueueBuffer(cx, stream, chunk));
|
||||
|
||||
CHECK(JS_IsDetachedArrayBufferObject(buffer));
|
||||
RootedObject readChunk(cx, GetReadChunk(cx, request));
|
||||
CHECK(JS_IsUint8Array(readChunk));
|
||||
void* readBufferData;
|
||||
{
|
||||
JS::AutoCheckCannotGC autoNoGC(cx);
|
||||
readBufferData = JS_GetArrayBufferViewData(readChunk, &isShared, autoNoGC);
|
||||
}
|
||||
CHECK(readBufferData);
|
||||
CHECK(!memcmp(test_buffer_data, readBufferData, length));
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testReadableStream_ReadableByteStreamDefaultReaderRead)
|
||||
|
||||
BEGIN_TEST(testReadableStream_ReadableByteStreamBYOBReaderRead)
|
||||
{
|
||||
RootedObject stream(cx, NewByteStream(cx));
|
||||
CHECK(stream);
|
||||
|
||||
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::BYOB));
|
||||
CHECK(reader);
|
||||
|
||||
size_t length = sizeof(test_buffer_data);
|
||||
RootedObject targetArray(cx, JS_NewUint8Array(cx, length));
|
||||
bool isShared;
|
||||
|
||||
RootedObject request(cx, ReadableStreamBYOBReaderRead(cx, reader, targetArray));
|
||||
CHECK(request);
|
||||
CHECK(IsPromiseObject(request));
|
||||
CHECK(GetPromiseState(request) == PromiseState::Pending);
|
||||
CHECK(JS_IsDetachedArrayBufferObject(JS_GetArrayBufferViewBuffer(cx, targetArray, &isShared)));
|
||||
|
||||
RootedObject buffer(cx, JS_NewArrayBufferWithExternalContents(cx, length, test_buffer_data));
|
||||
CHECK(buffer);
|
||||
CHECK(!JS_IsDetachedArrayBufferObject(buffer));
|
||||
|
||||
CHECK(ReadableByteStreamEnqueueBuffer(cx, stream, buffer));
|
||||
|
||||
CHECK(JS_IsDetachedArrayBufferObject(buffer));
|
||||
RootedObject readChunk(cx, GetReadChunk(cx, request));
|
||||
CHECK(JS_IsUint8Array(readChunk));
|
||||
void* readBufferData;
|
||||
{
|
||||
JS::AutoCheckCannotGC autoNoGC(cx);
|
||||
readBufferData = JS_GetArrayBufferViewData(readChunk, &isShared, autoNoGC);
|
||||
}
|
||||
CHECK(readBufferData);
|
||||
CHECK(!memcmp(test_buffer_data, readBufferData, length));
|
||||
// TODO: eliminate the memcpy that happens here.
|
||||
// CHECK(readBufferData == test_buffer_data);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testReadableStream_ReadableByteStreamBYOBReaderRead)
|
||||
|
||||
BEGIN_TEST(testReadableStream_ReadableStreamDefaultReaderClose)
|
||||
{
|
||||
SetReadableStreamCallbacks(cx, &DataRequestCB, &WriteIntoRequestBufferCB,
|
||||
&CancelStreamCB, &StreamClosedCB, &StreamErroredCB,
|
||||
&FinalizeStreamCB);
|
||||
RootedObject stream(cx, NewDefaultStream(cx));
|
||||
CHECK(stream);
|
||||
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default));
|
||||
CHECK(reader);
|
||||
|
||||
RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
|
||||
CHECK(request);
|
||||
CHECK(IsPromiseObject(request));
|
||||
CHECK(GetPromiseState(request) == PromiseState::Pending);
|
||||
|
||||
CHECK(ReadableStreamClose(cx, stream));
|
||||
|
||||
bool done;
|
||||
RootedValue value(cx);
|
||||
CHECK(GetPromiseState(request) == PromiseState::Fulfilled);
|
||||
CHECK(GetIterResult(cx, request, &value, &done));
|
||||
CHECK(value.isUndefined());
|
||||
CHECK(done);
|
||||
|
||||
// The callbacks are only invoked for external streams.
|
||||
CHECK(!streamClosedCBCalled);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testReadableStream_ReadableStreamDefaultReaderClose)
|
||||
|
||||
BEGIN_TEST(testReadableStream_ReadableStreamDefaultReaderError)
|
||||
{
|
||||
ResetCallbacks();
|
||||
SetReadableStreamCallbacks(cx, &DataRequestCB, &WriteIntoRequestBufferCB,
|
||||
&CancelStreamCB, &StreamClosedCB, &StreamErroredCB,
|
||||
&FinalizeStreamCB);
|
||||
RootedObject stream(cx, NewDefaultStream(cx));
|
||||
CHECK(stream);
|
||||
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default));
|
||||
CHECK(reader);
|
||||
|
||||
RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
|
||||
CHECK(request);
|
||||
CHECK(IsPromiseObject(request));
|
||||
CHECK(GetPromiseState(request) == PromiseState::Pending);
|
||||
|
||||
CHECK(ReadableStreamIsLocked(stream));
|
||||
CHECK(ReadableStreamIsReadable(stream));
|
||||
RootedValue error(cx, Int32Value(42));
|
||||
CHECK(ReadableStreamError(cx, stream, error));
|
||||
|
||||
CHECK(GetPromiseState(request) == PromiseState::Rejected);
|
||||
RootedValue reason(cx, GetPromiseResult(request));
|
||||
CHECK(reason.isInt32());
|
||||
CHECK(reason.toInt32() == 42);
|
||||
|
||||
// The callbacks are only invoked for external streams.
|
||||
CHECK(!streamErroredCBCalled);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testReadableStream_ReadableStreamDefaultReaderError)
|
||||
|
||||
static JSObject*
|
||||
NewExternalSourceStream(JSContext* cx, void* underlyingSource,
|
||||
RequestReadableStreamDataCallback dataRequestCallback,
|
||||
WriteIntoReadRequestBufferCallback writeIntoReadRequestCallback,
|
||||
CancelReadableStreamCallback cancelCallback,
|
||||
ReadableStreamClosedCallback closedCallback,
|
||||
ReadableStreamErroredCallback erroredCallback,
|
||||
ReadableStreamFinalizeCallback finalizeCallback)
|
||||
{
|
||||
SetReadableStreamCallbacks(cx, dataRequestCallback, writeIntoReadRequestCallback,
|
||||
cancelCallback, closedCallback, erroredCallback,
|
||||
finalizeCallback);
|
||||
RootedObject stream(cx, NewReadableExternalSourceStreamObject(cx, underlyingSource));
|
||||
MOZ_ASSERT_IF(stream, IsReadableStream(stream));
|
||||
return stream;
|
||||
}
|
||||
|
||||
BEGIN_TEST(testReadableStream_CreateReadableByteStreamWithExternalSource)
|
||||
{
|
||||
ResetCallbacks();
|
||||
|
||||
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
|
||||
&WriteIntoRequestBufferCB, &CancelStreamCB,
|
||||
&StreamClosedCB, &StreamErroredCB,
|
||||
&FinalizeStreamCB));
|
||||
CHECK(stream);
|
||||
CHECK(ReadableStreamGetMode(stream) == JS::ReadableStreamMode::ExternalSource);
|
||||
void* underlyingSource;
|
||||
CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource));
|
||||
CHECK(underlyingSource == &test_buffer_data);
|
||||
CHECK(ReadableStreamIsLocked(stream));
|
||||
ReadableStreamReleaseExternalUnderlyingSource(stream);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testReadableStream_CreateReadableByteStreamWithExternalSource)
|
||||
|
||||
BEGIN_TEST(testReadableStream_ExternalSourceCancel)
|
||||
{
|
||||
ResetCallbacks();
|
||||
|
||||
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
|
||||
&WriteIntoRequestBufferCB, &CancelStreamCB,
|
||||
&StreamClosedCB, &StreamErroredCB,
|
||||
&FinalizeStreamCB));
|
||||
CHECK(stream);
|
||||
RootedValue reason(cx, Int32Value(42));
|
||||
CHECK(ReadableStreamCancel(cx, stream, reason));
|
||||
CHECK(cancelStreamCBCalled);
|
||||
CHECK(cancelStreamReason == reason);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testReadableStream_ExternalSourceCancel)
|
||||
|
||||
BEGIN_TEST(testReadableStream_ExternalSourceGetReader)
|
||||
{
|
||||
ResetCallbacks();
|
||||
|
||||
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
|
||||
&WriteIntoRequestBufferCB, &CancelStreamCB,
|
||||
&StreamClosedCB, &StreamErroredCB,
|
||||
&FinalizeStreamCB));
|
||||
CHECK(stream);
|
||||
|
||||
RootedValue streamVal(cx, ObjectValue(*stream));
|
||||
CHECK(JS_SetProperty(cx, global, "stream", streamVal));
|
||||
RootedValue rval(cx);
|
||||
EVAL("stream.getReader()", &rval);
|
||||
CHECK(rval.isObject());
|
||||
RootedObject reader(cx, &rval.toObject());
|
||||
CHECK(IsReadableStreamDefaultReader(reader));
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testReadableStream_ExternalSourceGetReader)
|
||||
|
||||
BEGIN_TEST(testReadableStream_ExternalSourceUpdateAvailableData)
|
||||
{
|
||||
ResetCallbacks();
|
||||
|
||||
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
|
||||
&WriteIntoRequestBufferCB, &CancelStreamCB,
|
||||
&StreamClosedCB, &StreamErroredCB,
|
||||
&FinalizeStreamCB));
|
||||
CHECK(stream);
|
||||
|
||||
ReadableStreamUpdateDataAvailableFromSource(cx, stream, 1024);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testReadableStream_ExternalSourceUpdateAvailableData)
|
||||
|
||||
struct ReadFromExternalSourceFixture : public JSAPITest
|
||||
{
|
||||
virtual ~ReadFromExternalSourceFixture() {}
|
||||
|
||||
bool readWithoutDataAvailable(const char* evalSrc, const char* evalSrc2,
|
||||
uint32_t writtenLength)
|
||||
{
|
||||
ResetCallbacks();
|
||||
definePrint();
|
||||
|
||||
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
|
||||
&WriteIntoRequestBufferCB,
|
||||
&CancelStreamCB,
|
||||
&StreamClosedCB, &StreamErroredCB,
|
||||
&FinalizeStreamCB));
|
||||
CHECK(stream);
|
||||
js::RunJobs(cx);
|
||||
void* underlyingSource;
|
||||
CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource));
|
||||
CHECK(underlyingSource == &test_buffer_data);
|
||||
CHECK(ReadableStreamIsLocked(stream));
|
||||
ReadableStreamReleaseExternalUnderlyingSource(stream);
|
||||
|
||||
RootedValue streamVal(cx, ObjectValue(*stream));
|
||||
CHECK(JS_SetProperty(cx, global, "stream", streamVal));
|
||||
|
||||
RootedValue rval(cx);
|
||||
EVAL(evalSrc, &rval);
|
||||
CHECK(dataRequestCBCalled);
|
||||
CHECK(!writeIntoRequestBufferCBCalled);
|
||||
CHECK(rval.isObject());
|
||||
RootedObject promise(cx, &rval.toObject());
|
||||
CHECK(IsPromiseObject(promise));
|
||||
CHECK(GetPromiseState(promise) == PromiseState::Pending);
|
||||
|
||||
size_t length = sizeof(test_buffer_data);
|
||||
ReadableStreamUpdateDataAvailableFromSource(cx, stream, length);
|
||||
|
||||
CHECK(writeIntoRequestBufferCBCalled);
|
||||
CHECK(GetPromiseState(promise) == PromiseState::Fulfilled);
|
||||
RootedValue iterVal(cx);
|
||||
bool done;
|
||||
if (!GetIterResult(cx, promise, &iterVal, &done))
|
||||
return false;
|
||||
|
||||
CHECK(!done);
|
||||
RootedObject chunk(cx, &iterVal.toObject());
|
||||
CHECK(JS_IsUint8Array(chunk));
|
||||
|
||||
{
|
||||
JS::AutoCheckCannotGC noGC(cx);
|
||||
bool dummy;
|
||||
void* buffer = JS_GetArrayBufferViewData(chunk, &dummy, noGC);
|
||||
CHECK(!memcmp(buffer, test_buffer_data, writtenLength));
|
||||
}
|
||||
|
||||
dataRequestCBCalled = false;
|
||||
writeIntoRequestBufferCBCalled = false;
|
||||
EVAL(evalSrc2, &rval);
|
||||
CHECK(dataRequestCBCalled);
|
||||
CHECK(!writeIntoRequestBufferCBCalled);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readWithDataAvailable(const char* evalSrc, uint32_t writtenLength) {
|
||||
ResetCallbacks();
|
||||
definePrint();
|
||||
|
||||
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
|
||||
&WriteIntoRequestBufferCB, &CancelStreamCB,
|
||||
&StreamClosedCB, &StreamErroredCB,
|
||||
&FinalizeStreamCB));
|
||||
CHECK(stream);
|
||||
void* underlyingSource;
|
||||
CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource));
|
||||
CHECK(underlyingSource == &test_buffer_data);
|
||||
CHECK(ReadableStreamIsLocked(stream));
|
||||
ReadableStreamReleaseExternalUnderlyingSource(stream);
|
||||
|
||||
size_t length = sizeof(test_buffer_data);
|
||||
ReadableStreamUpdateDataAvailableFromSource(cx, stream, length);
|
||||
|
||||
RootedValue streamVal(cx, ObjectValue(*stream));
|
||||
CHECK(JS_SetProperty(cx, global, "stream", streamVal));
|
||||
|
||||
RootedValue rval(cx);
|
||||
EVAL(evalSrc, &rval);
|
||||
CHECK(writeIntoRequestBufferCBCalled);
|
||||
CHECK(rval.isObject());
|
||||
RootedObject promise(cx, &rval.toObject());
|
||||
CHECK(IsPromiseObject(promise));
|
||||
CHECK(GetPromiseState(promise) == PromiseState::Fulfilled);
|
||||
RootedValue iterVal(cx);
|
||||
bool done;
|
||||
if (!GetIterResult(cx, promise, &iterVal, &done))
|
||||
return false;
|
||||
|
||||
CHECK(!done);
|
||||
RootedObject chunk(cx, &iterVal.toObject());
|
||||
CHECK(JS_IsUint8Array(chunk));
|
||||
|
||||
{
|
||||
JS::AutoCheckCannotGC noGC(cx);
|
||||
bool dummy;
|
||||
void* buffer = JS_GetArrayBufferViewData(chunk, &dummy, noGC);
|
||||
CHECK(!memcmp(buffer, test_buffer_data, writtenLength));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,
|
||||
testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable)
|
||||
{
|
||||
return readWithoutDataAvailable("r = stream.getReader(); r.read()", "r.read()", sizeof(test_buffer_data));
|
||||
}
|
||||
END_FIXTURE_TEST(ReadFromExternalSourceFixture,
|
||||
testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable)
|
||||
|
||||
BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,
|
||||
testReadableStream_ExternalSourceCloseWithPendingRead)
|
||||
{
|
||||
CHECK(readWithoutDataAvailable("r = stream.getReader(); request0 = r.read(); "
|
||||
"request1 = r.read(); request0", "r.read()",
|
||||
sizeof(test_buffer_data)));
|
||||
|
||||
RootedValue val(cx);
|
||||
CHECK(JS_GetProperty(cx, global, "request1", &val));
|
||||
CHECK(val.isObject());
|
||||
RootedObject request(cx, &val.toObject());
|
||||
CHECK(IsPromiseObject(request));
|
||||
CHECK(GetPromiseState(request) == PromiseState::Pending);
|
||||
|
||||
CHECK(JS_GetProperty(cx, global, "stream", &val));
|
||||
RootedObject stream(cx, &val.toObject());
|
||||
ReadableStreamClose(cx, stream);
|
||||
|
||||
val = GetPromiseResult(request);
|
||||
MOZ_ASSERT(val.isObject());
|
||||
RootedObject result(cx, &val.toObject());
|
||||
|
||||
JS_GetProperty(cx, result, "done", &val);
|
||||
CHECK(val.isBoolean());
|
||||
CHECK(val.toBoolean() == true);
|
||||
|
||||
JS_GetProperty(cx, result, "value", &val);
|
||||
CHECK(val.isUndefined());
|
||||
return true;
|
||||
}
|
||||
END_FIXTURE_TEST(ReadFromExternalSourceFixture,
|
||||
testReadableStream_ExternalSourceCloseWithPendingRead)
|
||||
|
||||
BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,
|
||||
testReadableStream_ExternalSourceReadDefaultWithDataAvailable)
|
||||
{
|
||||
return readWithDataAvailable("r = stream.getReader(); r.read()", sizeof(test_buffer_data));
|
||||
}
|
||||
END_FIXTURE_TEST(ReadFromExternalSourceFixture,
|
||||
testReadableStream_ExternalSourceReadDefaultWithDataAvailable)
|
||||
|
||||
BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,
|
||||
testReadableStream_ExternalSourceReadBYOBWithoutDataAvailable)
|
||||
{
|
||||
return readWithoutDataAvailable("r = stream.getReader({mode: 'byob'}); r.read(new Uint8Array(63))", "r.read(new Uint8Array(10))", 10);
|
||||
}
|
||||
END_FIXTURE_TEST(ReadFromExternalSourceFixture,
|
||||
testReadableStream_ExternalSourceReadBYOBWithoutDataAvailable)
|
||||
|
||||
BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,
|
||||
testReadableStream_ExternalSourceReadBYOBWithDataAvailable)
|
||||
{
|
||||
return readWithDataAvailable("r = stream.getReader({mode: 'byob'}); r.read(new Uint8Array(10))", 10);
|
||||
}
|
||||
END_FIXTURE_TEST(ReadFromExternalSourceFixture,
|
||||
testReadableStream_ExternalSourceReadBYOBWithDataAvailable)
|
||||
@@ -81,10 +81,6 @@ JSObject* JSAPITest::createGlobal(JSPrincipals* principals)
|
||||
/* Create the global object. */
|
||||
JS::RootedObject newGlobal(cx);
|
||||
JS::CompartmentOptions options;
|
||||
#ifdef ENABLE_STREAMS
|
||||
options.creationOptions().setStreamsEnabled(true);
|
||||
#endif
|
||||
printf("enabled\n");
|
||||
options.behaviors().setVersion(JSVERSION_LATEST);
|
||||
newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), principals, JS::FireOnNewGlobalHook,
|
||||
options);
|
||||
|
||||
+21
-368
@@ -47,7 +47,6 @@
|
||||
#include "builtin/MapObject.h"
|
||||
#include "builtin/Promise.h"
|
||||
#include "builtin/RegExp.h"
|
||||
#include "builtin/Stream.h"
|
||||
#include "builtin/SymbolObject.h"
|
||||
#ifdef ENABLE_SIMD
|
||||
# include "builtin/SIMD.h"
|
||||
@@ -4730,13 +4729,28 @@ JS::GetRequestedModules(JSContext* cx, JS::HandleObject moduleArg)
|
||||
return &moduleArg->as<ModuleObject>().requestedModules();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSScript*)
|
||||
JS::GetModuleScript(JSContext* cx, JS::HandleObject moduleArg)
|
||||
JS_PUBLIC_API(JSString*)
|
||||
JS::GetRequestedModuleSpecifier(JSContext* cx, JS::HandleValue value)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, moduleArg);
|
||||
return moduleArg->as<ModuleObject>().script();
|
||||
assertSameCompartment(cx, value);
|
||||
JSObject* obj = &value.toObject();
|
||||
return obj->as<RequestedModuleObject>().moduleSpecifier();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::GetRequestedModuleSourcePos(JSContext* cx, JS::HandleValue value,
|
||||
uint32_t* lineNumber, uint32_t* columnNumber)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, value);
|
||||
MOZ_ASSERT(lineNumber);
|
||||
MOZ_ASSERT(columnNumber);
|
||||
auto& requested = value.toObject().as<RequestedModuleObject>();
|
||||
*lineNumber = requested.lineNumber();
|
||||
*columnNumber = requested.columnNumber();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject*)
|
||||
@@ -5017,6 +5031,7 @@ CallOriginalPromiseThenImpl(JSContext* cx, JS::HandleObject promiseObj,
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject*)
|
||||
@@ -5043,7 +5058,7 @@ JS::AddPromiseReactions(JSContext* cx, JS::HandleObject promiseObj,
|
||||
* Unforgeable version of Promise.all for internal use.
|
||||
*
|
||||
* Takes a dense array of Promise objects and returns a promise that's
|
||||
* resolved with an array of resolution values when all those promises have
|
||||
* resolved with an array of resolution values when all those promises ahve
|
||||
* been resolved, or rejected with the rejection value of the first rejected
|
||||
* promise.
|
||||
*
|
||||
@@ -5058,368 +5073,6 @@ JS::GetWaitForAllPromise(JSContext* cx, const JS::AutoObjectVector& promises)
|
||||
return js::GetWaitForAllPromise(cx, promises);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject*)
|
||||
JS::NewReadableDefaultStreamObject(JSContext* cx,
|
||||
JS::HandleObject underlyingSource /* = nullptr */,
|
||||
JS::HandleFunction size /* = nullptr */,
|
||||
double highWaterMark /* = 1 */,
|
||||
JS::HandleObject proto /* = nullptr */)
|
||||
{
|
||||
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
RootedObject source(cx, underlyingSource);
|
||||
if (!source) {
|
||||
source = NewBuiltinClassInstance<PlainObject>(cx);
|
||||
if (!source)
|
||||
return nullptr;
|
||||
}
|
||||
RootedValue sourceVal(cx, ObjectValue(*source));
|
||||
RootedValue sizeVal(cx, size ? ObjectValue(*size) : UndefinedValue());
|
||||
RootedValue highWaterMarkVal(cx, NumberValue(highWaterMark));
|
||||
return ReadableStream::createDefaultStream(cx, sourceVal, sizeVal, highWaterMarkVal, proto);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject*)
|
||||
JS::NewReadableByteStreamObject(JSContext* cx,
|
||||
JS::HandleObject underlyingSource /* = nullptr */,
|
||||
double highWaterMark /* = 1 */,
|
||||
JS::HandleObject proto /* = nullptr */)
|
||||
{
|
||||
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
RootedObject source(cx, underlyingSource);
|
||||
if (!source) {
|
||||
source = NewBuiltinClassInstance<PlainObject>(cx);
|
||||
if (!source)
|
||||
return nullptr;
|
||||
}
|
||||
RootedValue sourceVal(cx, ObjectValue(*source));
|
||||
RootedValue highWaterMarkVal(cx, NumberValue(highWaterMark));
|
||||
return ReadableStream::createByteStream(cx, sourceVal, highWaterMarkVal, proto);
|
||||
}
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS::SetReadableStreamCallbacks(JSContext* cx,
|
||||
JS::RequestReadableStreamDataCallback dataRequestCallback,
|
||||
JS::WriteIntoReadRequestBufferCallback writeIntoReadRequestCallback,
|
||||
JS::CancelReadableStreamCallback cancelCallback,
|
||||
JS::ReadableStreamClosedCallback closedCallback,
|
||||
JS::ReadableStreamErroredCallback erroredCallback,
|
||||
JS::ReadableStreamFinalizeCallback finalizeCallback)
|
||||
{
|
||||
MOZ_ASSERT(dataRequestCallback);
|
||||
MOZ_ASSERT(writeIntoReadRequestCallback);
|
||||
MOZ_ASSERT(cancelCallback);
|
||||
MOZ_ASSERT(closedCallback);
|
||||
MOZ_ASSERT(erroredCallback);
|
||||
MOZ_ASSERT(finalizeCallback);
|
||||
|
||||
JSRuntime* rt = cx->runtime();
|
||||
|
||||
MOZ_ASSERT(!rt->readableStreamDataRequestCallback);
|
||||
MOZ_ASSERT(!rt->readableStreamWriteIntoReadRequestCallback);
|
||||
MOZ_ASSERT(!rt->readableStreamCancelCallback);
|
||||
MOZ_ASSERT(!rt->readableStreamClosedCallback);
|
||||
MOZ_ASSERT(!rt->readableStreamErroredCallback);
|
||||
MOZ_ASSERT(!rt->readableStreamFinalizeCallback);
|
||||
|
||||
rt->readableStreamDataRequestCallback = dataRequestCallback;
|
||||
rt->readableStreamWriteIntoReadRequestCallback = writeIntoReadRequestCallback;
|
||||
rt->readableStreamCancelCallback = cancelCallback;
|
||||
rt->readableStreamClosedCallback = closedCallback;
|
||||
rt->readableStreamErroredCallback = erroredCallback;
|
||||
rt->readableStreamFinalizeCallback = finalizeCallback;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::HasReadableStreamCallbacks(JSContext* cx)
|
||||
{
|
||||
return cx->runtime()->readableStreamDataRequestCallback;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject*)
|
||||
JS::NewReadableExternalSourceStreamObject(JSContext* cx, void* underlyingSource,
|
||||
uint8_t flags /* = 0 */,
|
||||
HandleObject proto /* = nullptr */)
|
||||
{
|
||||
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
#ifdef DEBUG
|
||||
JSRuntime* rt = cx->runtime();
|
||||
MOZ_ASSERT(rt->readableStreamDataRequestCallback);
|
||||
MOZ_ASSERT(rt->readableStreamWriteIntoReadRequestCallback);
|
||||
MOZ_ASSERT(rt->readableStreamCancelCallback);
|
||||
MOZ_ASSERT(rt->readableStreamClosedCallback);
|
||||
MOZ_ASSERT(rt->readableStreamErroredCallback);
|
||||
MOZ_ASSERT(rt->readableStreamFinalizeCallback);
|
||||
#endif // DEBUG
|
||||
|
||||
return ReadableStream::createExternalSourceStream(cx, underlyingSource, flags, proto);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(uint8_t)
|
||||
JS::ReadableStreamGetEmbeddingFlags(const JSObject* stream)
|
||||
{
|
||||
return stream->as<ReadableStream>().embeddingFlags();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::IsReadableStream(const JSObject* obj)
|
||||
{
|
||||
return obj->is<ReadableStream>();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::IsReadableStreamReader(const JSObject* obj)
|
||||
{
|
||||
return obj->is<ReadableStreamDefaultReader>() || obj->is<ReadableStreamBYOBReader>();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::IsReadableStreamDefaultReader(const JSObject* obj)
|
||||
{
|
||||
return obj->is<ReadableStreamDefaultReader>();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::IsReadableStreamBYOBReader(const JSObject* obj)
|
||||
{
|
||||
return obj->is<ReadableStreamBYOBReader>();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::ReadableStreamIsReadable(const JSObject* stream)
|
||||
{
|
||||
return stream->as<ReadableStream>().readable();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::ReadableStreamIsLocked(const JSObject* stream)
|
||||
{
|
||||
return stream->as<ReadableStream>().locked();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::ReadableStreamIsDisturbed(const JSObject* stream)
|
||||
{
|
||||
return stream->as<ReadableStream>().disturbed();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject*)
|
||||
JS::ReadableStreamCancel(JSContext* cx, HandleObject streamObj, HandleValue reason)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, streamObj);
|
||||
assertSameCompartment(cx, reason);
|
||||
|
||||
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
|
||||
return ReadableStream::cancel(cx, stream, reason);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JS::ReadableStreamMode)
|
||||
JS::ReadableStreamGetMode(const JSObject* stream)
|
||||
{
|
||||
return stream->as<ReadableStream>().mode();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject*)
|
||||
JS::ReadableStreamGetReader(JSContext* cx, HandleObject streamObj, ReadableStreamReaderMode mode)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, streamObj);
|
||||
|
||||
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
|
||||
return ReadableStream::getReader(cx, stream, mode);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::ReadableStreamGetExternalUnderlyingSource(JSContext* cx, HandleObject streamObj, void** source)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, streamObj);
|
||||
|
||||
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
|
||||
return ReadableStream::getExternalSource(cx, stream, source);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::ReadableStreamReleaseExternalUnderlyingSource(JSObject* stream)
|
||||
{
|
||||
stream->as<ReadableStream>().releaseExternalSource();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::ReadableStreamUpdateDataAvailableFromSource(JSContext* cx, JS::HandleObject streamObj,
|
||||
uint32_t availableData)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, streamObj);
|
||||
|
||||
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
|
||||
return ReadableStream::updateDataAvailableFromSource(cx, stream, availableData);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::ReadableStreamTee(JSContext* cx, HandleObject streamObj,
|
||||
MutableHandleObject branch1Obj, MutableHandleObject branch2Obj)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, streamObj);
|
||||
|
||||
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
|
||||
Rooted<ReadableStream*> branch1Stream(cx);
|
||||
Rooted<ReadableStream*> branch2Stream(cx);
|
||||
|
||||
if (!ReadableStream::tee(cx, stream, false, &branch1Stream, &branch2Stream))
|
||||
return false;
|
||||
|
||||
branch1Obj.set(branch1Stream);
|
||||
branch2Obj.set(branch2Stream);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::ReadableStreamGetDesiredSize(JSObject* streamObj, bool* hasValue, double* value)
|
||||
{
|
||||
streamObj->as<ReadableStream>().desiredSize(hasValue, value);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::ReadableStreamClose(JSContext* cx, HandleObject streamObj)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, streamObj);
|
||||
|
||||
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
|
||||
return ReadableStream::close(cx, stream);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::ReadableStreamEnqueue(JSContext* cx, HandleObject streamObj, HandleValue chunk)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, streamObj);
|
||||
assertSameCompartment(cx, chunk);
|
||||
|
||||
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
|
||||
if (stream->mode() != JS::ReadableStreamMode::Default) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_READABLESTREAM_NOT_DEFAULT_CONTROLLER,
|
||||
"JS::ReadableStreamEnqueue");
|
||||
return false;
|
||||
}
|
||||
return ReadableStream::enqueue(cx, stream, chunk);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::ReadableByteStreamEnqueueBuffer(JSContext* cx, HandleObject streamObj, HandleObject chunkObj)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, streamObj);
|
||||
assertSameCompartment(cx, chunkObj);
|
||||
|
||||
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
|
||||
if (stream->mode() != JS::ReadableStreamMode::Byte) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_READABLESTREAM_NOT_BYTE_STREAM_CONTROLLER,
|
||||
"JS::ReadableByteStreamEnqueueBuffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
Rooted<ArrayBufferObject*> buffer(cx);
|
||||
if (chunkObj->is<ArrayBufferViewObject>()) {
|
||||
bool dummy;
|
||||
buffer = &JS_GetArrayBufferViewBuffer(cx, chunkObj, &dummy)->as<ArrayBufferObject>();
|
||||
} else if (chunkObj->is<ArrayBufferObject>()) {
|
||||
buffer = &chunkObj->as<ArrayBufferObject>();
|
||||
} else {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNK,
|
||||
"JS::ReadableByteStreamEnqueueBuffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
return ReadableStream::enqueueBuffer(cx, stream, buffer);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::ReadableStreamError(JSContext* cx, HandleObject streamObj, HandleValue error)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, streamObj);
|
||||
assertSameCompartment(cx, error);
|
||||
|
||||
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
|
||||
return js::ReadableStream::error(cx, stream, error);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::ReadableStreamReaderIsClosed(const JSObject* reader)
|
||||
{
|
||||
return js::ReadableStreamReaderIsClosed(reader);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::ReadableStreamReaderCancel(JSContext* cx, HandleObject reader, HandleValue reason)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, reader);
|
||||
assertSameCompartment(cx, reason);
|
||||
|
||||
return js::ReadableStreamReaderCancel(cx, reader, reason);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject reader)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, reader);
|
||||
|
||||
return js::ReadableStreamReaderReleaseLock(cx, reader);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject*)
|
||||
JS::ReadableStreamDefaultReaderRead(JSContext* cx, HandleObject readerObj)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, readerObj);
|
||||
|
||||
Rooted<ReadableStreamDefaultReader*> reader(cx, &readerObj->as<ReadableStreamDefaultReader>());
|
||||
return js::ReadableStreamDefaultReader::read(cx, reader);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject*)
|
||||
JS::ReadableStreamBYOBReaderRead(JSContext* cx, HandleObject readerObj, HandleObject viewObj)
|
||||
{
|
||||
AssertHeapIsIdle(cx);
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, readerObj);
|
||||
assertSameCompartment(cx, viewObj);
|
||||
|
||||
Rooted<ReadableStreamBYOBReader*> reader(cx, &readerObj->as<ReadableStreamBYOBReader>());
|
||||
Rooted<ArrayBufferViewObject*> view(cx, &viewObj->as<ArrayBufferViewObject>());
|
||||
return js::ReadableStreamBYOBReader::read(cx, reader, view);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::SetAsyncTaskCallbacks(JSContext* cx, JS::StartAsyncTaskCallback start,
|
||||
JS::FinishAsyncTaskCallback finish)
|
||||
|
||||
+17
-20
@@ -35,7 +35,6 @@
|
||||
#include "js/Principals.h"
|
||||
#include "js/Realm.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/Stream.h"
|
||||
#include "js/TracingAPI.h"
|
||||
#include "js/UniquePtr.h"
|
||||
#include "js/Utility.h"
|
||||
@@ -1170,16 +1169,6 @@ class JS_PUBLIC_API(ContextOptions) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool streams() const { return streams_; }
|
||||
ContextOptions& setStreams(bool flag) {
|
||||
streams_ = flag;
|
||||
return *this;
|
||||
}
|
||||
ContextOptions& toggleStreams() {
|
||||
streams_ = !streams_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool nativeRegExp() const { return nativeRegExp_; }
|
||||
ContextOptions& setNativeRegExp(bool flag) {
|
||||
nativeRegExp_ = flag;
|
||||
@@ -1262,7 +1251,6 @@ class JS_PUBLIC_API(ContextOptions) {
|
||||
bool strictMode_ : 1;
|
||||
bool extraWarnings_ : 1;
|
||||
bool arrayProtoValues_ : 1;
|
||||
bool streams_ : 1;
|
||||
};
|
||||
|
||||
JS_PUBLIC_API(ContextOptions&)
|
||||
@@ -4398,18 +4386,27 @@ ModuleEvaluate(JSContext* cx, JS::HandleObject moduleRecord);
|
||||
* Get a list of the module specifiers used by a source text module
|
||||
* record to request importation of modules.
|
||||
*
|
||||
* The result is a JavaScript array of string values. To extract the individual
|
||||
* values use only JS_GetArrayLength and JS_GetElement with indices 0 to
|
||||
* length - 1.
|
||||
* The result is a JavaScript array of object values. To extract the individual
|
||||
* values use only JS_GetArrayLength and JS_GetElement with indices 0 to length
|
||||
* - 1.
|
||||
*
|
||||
* The element values are objects with the following properties:
|
||||
* - moduleSpecifier: the module specifier string
|
||||
* - lineNumber: the line number of the import in the source text
|
||||
* - columnNumber: the column number of the import in the source text
|
||||
*
|
||||
* These property values can be extracted with GetRequestedModuleSpecifier() and
|
||||
* GetRequestedModuleSourcePos()
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSObject*)
|
||||
GetRequestedModules(JSContext* cx, JS::HandleObject moduleRecord);
|
||||
|
||||
/*
|
||||
* Get the script associated with a module.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSScript*)
|
||||
GetModuleScript(JSContext* cx, JS::HandleObject moduleRecord);
|
||||
extern JS_PUBLIC_API(JSString*)
|
||||
GetRequestedModuleSpecifier(JSContext* cx, JS::HandleValue requestedModuleObject);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
GetRequestedModuleSourcePos(JSContext* cx, JS::HandleValue requestedModuleObject,
|
||||
uint32_t* lineNumber, uint32_t* columnNumber);
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
|
||||
@@ -1778,9 +1778,6 @@ UnwrapArrayBufferView(JSObject* obj);
|
||||
extern JS_FRIEND_API(JSObject*)
|
||||
UnwrapSharedArrayBuffer(JSObject* obj);
|
||||
|
||||
extern JS_FRIEND_API(JSObject*)
|
||||
UnwrapReadableStream(JSObject* obj);
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
@@ -1997,12 +1994,6 @@ JS_IsArrayBufferViewObject(JSObject* obj);
|
||||
extern JS_FRIEND_API(uint32_t)
|
||||
JS_GetArrayBufferViewByteLength(JSObject* obj);
|
||||
|
||||
/**
|
||||
* More generic name for JS_GetTypedArrayByteOffset to cover DataViews as well
|
||||
*/
|
||||
extern JS_FRIEND_API(uint32_t)
|
||||
JS_GetArrayBufferViewByteOffset(JSObject* obj);
|
||||
|
||||
/*
|
||||
* Return a pointer to the start of the data referenced by a typed array. The
|
||||
* data is still owned by the typed array, and should not be modified on
|
||||
|
||||
+3
-8
@@ -947,13 +947,6 @@ static const JSFunctionSpec number_methods[] = {
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
bool
|
||||
js::IsInteger(const Value& val)
|
||||
{
|
||||
return val.isInt32() ||
|
||||
(mozilla::IsFinite(val.toDouble()) && JS::ToInteger(val.toDouble()) == val.toDouble());
|
||||
}
|
||||
|
||||
// ES6 draft ES6 15.7.3.12
|
||||
static bool
|
||||
Number_isInteger(JSContext* cx, unsigned argc, Value* vp)
|
||||
@@ -964,7 +957,9 @@ Number_isInteger(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
Value val = args[0];
|
||||
args.rval().setBoolean(js::IsInteger(val));
|
||||
args.rval().setBoolean(val.isInt32() ||
|
||||
(mozilla::IsFinite(val.toDouble()) &&
|
||||
JS::ToInteger(val.toDouble()) == val.toDouble()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,10 +72,6 @@ Int32ToString(ExclusiveContext* cx, int32_t i);
|
||||
extern JSAtom*
|
||||
Int32ToAtom(ExclusiveContext* cx, int32_t si);
|
||||
|
||||
// ES6 15.7.3.12
|
||||
extern bool
|
||||
IsInteger(const Value& val);
|
||||
|
||||
/*
|
||||
* Convert an integer or double (contained in the given value) to a string and
|
||||
* append to the given buffer.
|
||||
|
||||
+6
-17
@@ -106,23 +106,12 @@ IF_SIMD(real,imaginary)(SIMD, 44, InitSimdClass, OCLASP(Si
|
||||
real(TypedArray, 46, InitViaClassSpec, &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \
|
||||
IF_SAB(real,imaginary)(Atomics, 47, InitAtomicsClass, OCLASP(Atomics)) \
|
||||
real(SavedFrame, 48, InitViaClassSpec, &js::SavedFrame::class_) \
|
||||
real(Promise, 49, InitViaClassSpec, OCLASP(Promise)) \
|
||||
real(ReadableStream, 50, InitViaClassSpec, &js::ReadableStream::class_) \
|
||||
real(ReadableStreamDefaultReader, 51, InitViaClassSpec, &js::ReadableStreamDefaultReader::class_) \
|
||||
real(ReadableStreamBYOBReader, 52, InitViaClassSpec, &js::ReadableStreamBYOBReader::class_) \
|
||||
real(ReadableStreamDefaultController, 53, InitViaClassSpec, &js::ReadableStreamDefaultController::class_) \
|
||||
real(ReadableByteStreamController, 54, InitViaClassSpec, &js::ReadableByteStreamController::class_) \
|
||||
real(ReadableStreamBYOBRequest, 55, InitViaClassSpec, &js::ReadableStreamBYOBRequest::class_) \
|
||||
imaginary(WritableStream, 56, dummy, dummy) \
|
||||
imaginary(WritableStreamDefaultWriter, 57, dummy, dummy) \
|
||||
imaginary(WritableStreamDefaultController, 58, dummy, dummy) \
|
||||
real(ByteLengthQueuingStrategy, 59, InitViaClassSpec, &js::ByteLengthQueuingStrategy::class_) \
|
||||
real(CountQueuingStrategy, 60, InitViaClassSpec, &js::CountQueuingStrategy::class_) \
|
||||
real(WebAssembly, 61, InitWebAssemblyClass, CLASP(WebAssembly)) \
|
||||
imaginary(WasmModule, 62, dummy, dummy) \
|
||||
imaginary(WasmInstance, 63, dummy, dummy) \
|
||||
imaginary(WasmMemory, 64, dummy, dummy) \
|
||||
imaginary(WasmTable, 65, dummy, dummy)
|
||||
real(WebAssembly, 49, InitWebAssemblyClass, CLASP(WebAssembly)) \
|
||||
imaginary(WasmModule, 50, dummy, dummy) \
|
||||
imaginary(WasmInstance, 51, dummy, dummy) \
|
||||
imaginary(WasmMemory, 52, dummy, dummy) \
|
||||
imaginary(WasmTable, 53, dummy, dummy) \
|
||||
real(Promise, 54, InitViaClassSpec, OCLASP(Promise)) \
|
||||
|
||||
#define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)
|
||||
|
||||
|
||||
+18
-10
@@ -83,7 +83,6 @@ EXPORTS.js += [
|
||||
'../public/RequiredDefines.h',
|
||||
'../public/RootingAPI.h',
|
||||
'../public/SliceBudget.h',
|
||||
'../public/Stream.h',
|
||||
'../public/StructuredClone.h',
|
||||
'../public/SweepingAPI.h',
|
||||
'../public/TraceKind.h',
|
||||
@@ -132,22 +131,18 @@ if not CONFIG['JS_DISABLE_SHELL']:
|
||||
if CONFIG['MOZ_VTUNE']:
|
||||
DIRS += ['vtune']
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'jsalloc.cpp',
|
||||
'jsapi.cpp',
|
||||
'jsarray.cpp',
|
||||
'jsatom.cpp',
|
||||
'jsbool.cpp',
|
||||
'jscntxt.cpp',
|
||||
'jscompartment.cpp',
|
||||
'jsdate.cpp',
|
||||
'jsdtoa.cpp',
|
||||
'jsexn.cpp',
|
||||
'jsfriendapi.cpp',
|
||||
'jsfun.cpp',
|
||||
'jsgc.cpp',
|
||||
'jsiter.cpp',
|
||||
'jsmath.cpp',
|
||||
'jsnativestack.cpp',
|
||||
'jsnum.cpp',
|
||||
'jsobj.cpp',
|
||||
@@ -157,16 +152,24 @@ SOURCES += [
|
||||
'jspropertytree.cpp',
|
||||
'jsscript.cpp',
|
||||
'jsstr.cpp',
|
||||
'jsutil.cpp',
|
||||
'jsweakmap.cpp',
|
||||
]
|
||||
|
||||
# Can't be built unified
|
||||
SOURCES += [
|
||||
'jsarray.cpp',
|
||||
'jsatom.cpp',
|
||||
'jsdtoa.cpp',
|
||||
'jsmath.cpp',
|
||||
'jsutil.cpp',
|
||||
]
|
||||
|
||||
# Suppress warnings in third-party code.
|
||||
# We are keeping this in the main moz.build because it is file specific
|
||||
if CONFIG['CLANG_CXX'] or CONFIG['GNU_CXX']:
|
||||
SOURCES['jsdtoa.cpp'].flags += ['-Wno-implicit-fallthrough']
|
||||
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'vm/ArgumentsObject.cpp',
|
||||
'vm/ArrayBufferObject.cpp',
|
||||
'vm/AsyncFunction.cpp',
|
||||
@@ -186,7 +189,6 @@ SOURCES += [
|
||||
'vm/GlobalObject.cpp',
|
||||
'vm/HelperThreads.cpp',
|
||||
'vm/Id.cpp',
|
||||
'vm/Initialization.cpp',
|
||||
'vm/Interpreter.cpp',
|
||||
'vm/JSONParser.cpp',
|
||||
'vm/MemoryMetrics.cpp',
|
||||
@@ -228,12 +230,18 @@ SOURCES += [
|
||||
'vm/Xdr.cpp',
|
||||
]
|
||||
|
||||
# Can't be built unified
|
||||
SOURCES += [
|
||||
'vm/Initialization.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['JS_POSIX_NSPR']:
|
||||
SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'vm/PosixNSPR.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['ENABLE_TRACE_LOGGING']:
|
||||
# Can't be built unified
|
||||
SOURCES += [
|
||||
'vm/TraceLogging.cpp',
|
||||
'vm/TraceLoggingGraph.cpp',
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file forward-defines Irregexp classes that need to be visible
|
||||
// to the rest of Spidermonkey and re-exports them into js::irregexp.
|
||||
|
||||
#ifndef regexp_RegExpTypes_h
|
||||
#define regexp_RegExpTypes_h
|
||||
|
||||
namespace js {
|
||||
class MatchPairs;
|
||||
}
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
struct InputOutputData {
|
||||
const void* inputStart;
|
||||
const void* inputEnd;
|
||||
|
||||
// Index into inputStart (in chars) at which to begin matching.
|
||||
size_t startIndex;
|
||||
|
||||
js::MatchPairs* matches;
|
||||
|
||||
template <typename CharT>
|
||||
InputOutputData(const CharT* inputStart, const CharT* inputEnd,
|
||||
size_t startIndex, js::MatchPairs* matches)
|
||||
: inputStart(inputStart),
|
||||
inputEnd(inputEnd),
|
||||
startIndex(startIndex),
|
||||
matches(matches)
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
|
||||
namespace js {
|
||||
namespace irregexp {
|
||||
|
||||
using InputOutputData = v8::internal::InputOutputData;
|
||||
|
||||
} // namespace irregexp
|
||||
} // namespace js
|
||||
|
||||
#endif // regexp_RegExpTypes_h
|
||||
@@ -1,3 +1,2 @@
|
||||
This code was most recently imported from the following version of V8:
|
||||
|
||||
https://github.com/v8/v8/tree/2599d3cc208a3a4873be517285220abd8416c3d7/src/regexp
|
||||
Imported using import-irregexp.py from:
|
||||
https://github.com/v8/v8/tree/560f2d8bb3f3a72d78e1a7d7654235d53fdcc83c/src/regexp
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019 the V8 project authors. All rights reserved.
|
||||
// Copyright 2020 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
@@ -7,18 +7,19 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "unicode/uchar.h"
|
||||
#include "unicode/uniset.h"
|
||||
#include "regexp/special-case.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// The following code generates BuildSpecialAddSet() and BuildIgnoreSet()
|
||||
// functions into "src/regexp/special-case.cc".
|
||||
// See more details in http://shorturl.at/adfO5
|
||||
void PrintSet(std::ofstream& out, const char* func_name,
|
||||
static const uc32 kSurrogateStart = 0xd800;
|
||||
static const uc32 kSurrogateEnd = 0xdfff;
|
||||
static const uc32 kNonBmpStart = 0x10000;
|
||||
|
||||
// The following code generates "src/regexp/special-case.cc".
|
||||
void PrintSet(std::ofstream& out, const char* name,
|
||||
const icu::UnicodeSet& set) {
|
||||
out << "icu::UnicodeSet " << func_name << "() {\n"
|
||||
out << "icu::UnicodeSet Build" << name << "() {\n"
|
||||
<< " icu::UnicodeSet set;\n";
|
||||
for (int32_t i = 0; i < set.getRangeCount(); i++) {
|
||||
if (set.getRangeStart(i) == set.getRangeEnd(i)) {
|
||||
@@ -30,73 +31,113 @@ void PrintSet(std::ofstream& out, const char* func_name,
|
||||
}
|
||||
out << " set.freeze();\n"
|
||||
<< " return set;\n"
|
||||
<< "}\n";
|
||||
<< "}\n\n";
|
||||
|
||||
out << "struct " << name << "Data {\n"
|
||||
<< " " << name << "Data() : set(Build" << name << "()) {}\n"
|
||||
<< " const icu::UnicodeSet set;\n"
|
||||
<< "};\n\n";
|
||||
|
||||
out << "//static\n"
|
||||
<< "const icu::UnicodeSet& RegExpCaseFolding::" << name << "() {\n"
|
||||
<< " static base::LazyInstance<" << name << "Data>::type set =\n"
|
||||
<< " LAZY_INSTANCE_INITIALIZER;\n"
|
||||
<< " return set.Pointer()->set;\n"
|
||||
<< "}\n\n";
|
||||
}
|
||||
|
||||
void PrintSpecial(std::ofstream& out) {
|
||||
icu::UnicodeSet current;
|
||||
icu::UnicodeSet processed(0xd800, 0xdbff); // Ignore surrogate range.
|
||||
icu::UnicodeSet special_add;
|
||||
icu::UnicodeSet ignore;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
icu::UnicodeSet upper("[\\p{Lu}]", status);
|
||||
CHECK(U_SUCCESS(status));
|
||||
// Iterate through all chars in BMP except ASCII and Surrogate.
|
||||
for (UChar32 i = 0x80; i < 0x010000; i++) {
|
||||
// Ignore those characters which is already processed.
|
||||
if (!processed.contains(i)) {
|
||||
current.set(i, i);
|
||||
current.closeOver(USET_CASE_INSENSITIVE);
|
||||
|
||||
// Remember we already processed current.
|
||||
processed.addAll(current);
|
||||
// Iterate through all chars in BMP except surrogates.
|
||||
for (UChar32 i = 0; i < kNonBmpStart; i++) {
|
||||
if (i >= kSurrogateStart && i <= kSurrogateEnd) {
|
||||
continue; // Ignore surrogate range
|
||||
}
|
||||
current.set(i, i);
|
||||
current.closeOver(USET_CASE_INSENSITIVE);
|
||||
|
||||
// All uppercase characters in current.
|
||||
icu::UnicodeSet keep_upper(current);
|
||||
keep_upper.retainAll(upper);
|
||||
|
||||
// Check if we have more than one uppercase character in current.
|
||||
// If there are more than one uppercase character, then it is a special
|
||||
// set which need to be added into either "Special Add" set or "Ignore"
|
||||
// set.
|
||||
int32_t number_of_upper = 0;
|
||||
for (int32_t i = 0; i < keep_upper.getRangeCount() && i <= 1; i++) {
|
||||
number_of_upper +=
|
||||
keep_upper.getRangeEnd(i) - keep_upper.getRangeStart(i) + 1;
|
||||
// Check to see if all characters in the case-folding equivalence
|
||||
// class as defined by UnicodeSet::closeOver all map to the same
|
||||
// canonical value.
|
||||
UChar32 canonical = RegExpCaseFolding::Canonicalize(i);
|
||||
bool class_has_matching_canonical_char = false;
|
||||
bool class_has_non_matching_canonical_char = false;
|
||||
for (int32_t j = 0; j < current.getRangeCount(); j++) {
|
||||
for (UChar32 c = current.getRangeStart(j); c <= current.getRangeEnd(j);
|
||||
c++) {
|
||||
if (c == i) {
|
||||
continue;
|
||||
}
|
||||
UChar32 other_canonical = RegExpCaseFolding::Canonicalize(c);
|
||||
if (canonical == other_canonical) {
|
||||
class_has_matching_canonical_char = true;
|
||||
} else {
|
||||
class_has_non_matching_canonical_char = true;
|
||||
}
|
||||
}
|
||||
if (number_of_upper > 1) {
|
||||
// Add all non uppercase characters (could be Ll or Mn) to special add
|
||||
// set.
|
||||
current.removeAll(upper);
|
||||
special_add.addAll(current);
|
||||
|
||||
// Add the uppercase characters of non uppercase character to
|
||||
// special add set.
|
||||
CHECK_GT(current.getRangeCount(), 0);
|
||||
UChar32 main_upper = u_toupper(current.getRangeStart(0));
|
||||
special_add.add(main_upper);
|
||||
|
||||
// Add all uppercase except the main upper to ignore set.
|
||||
keep_upper.remove(main_upper);
|
||||
ignore.addAll(keep_upper);
|
||||
}
|
||||
// If any other character in i's equivalence class has a
|
||||
// different canonical value, then i needs special handling. If
|
||||
// no other character shares a canonical value with i, we can
|
||||
// ignore i when adding alternatives for case-independent
|
||||
// comparison. If at least one other character shares a
|
||||
// canonical value, then i needs special handling.
|
||||
if (class_has_non_matching_canonical_char) {
|
||||
if (class_has_matching_canonical_char) {
|
||||
special_add.add(i);
|
||||
} else {
|
||||
ignore.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any ASCII
|
||||
special_add.remove(0x0000, 0x007f);
|
||||
PrintSet(out, "BuildIgnoreSet", ignore);
|
||||
PrintSet(out, "BuildSpecialAddSet", special_add);
|
||||
// Verify that no Unicode equivalence class contains two non-trivial
|
||||
// JS equivalence classes. Every character in SpecialAddSet has the
|
||||
// same canonical value as every other non-IgnoreSet character in
|
||||
// its Unicode equivalence class. Therefore, if we call closeOver on
|
||||
// a set containing no IgnoreSet characters, the only characters
|
||||
// that must be removed from the result are in IgnoreSet. This fact
|
||||
// is used in CharacterRange::AddCaseEquivalents.
|
||||
for (int32_t i = 0; i < special_add.getRangeCount(); i++) {
|
||||
for (UChar32 c = special_add.getRangeStart(i);
|
||||
c <= special_add.getRangeEnd(i); c++) {
|
||||
UChar32 canonical = RegExpCaseFolding::Canonicalize(c);
|
||||
current.set(c, c);
|
||||
current.closeOver(USET_CASE_INSENSITIVE);
|
||||
current.removeAll(ignore);
|
||||
for (int32_t j = 0; j < current.getRangeCount(); j++) {
|
||||
for (UChar32 c2 = current.getRangeStart(j);
|
||||
c2 <= current.getRangeEnd(j); c2++) {
|
||||
CHECK_EQ(canonical, RegExpCaseFolding::Canonicalize(c2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrintSet(out, "IgnoreSet", ignore);
|
||||
PrintSet(out, "SpecialAddSet", special_add);
|
||||
}
|
||||
|
||||
void WriteHeader(const char* header_filename) {
|
||||
std::ofstream out(header_filename);
|
||||
out << std::hex << std::setfill('0') << std::setw(4);
|
||||
|
||||
out << "// Automatically generated by regexp/gen-regexp-special-case.cc\n"
|
||||
<< "// The following functions are used to build icu::UnicodeSet\n"
|
||||
<< "// for specical cases different between Unicode and ECMA262.\n"
|
||||
out << "// Copyright 2020 the V8 project authors. All rights reserved.\n"
|
||||
<< "// Use of this source code is governed by a BSD-style license that\n"
|
||||
<< "// can be found in the LICENSE file.\n\n"
|
||||
<< "// Automatically generated by regexp/gen-regexp-special-case.cc\n\n"
|
||||
<< "// The following functions are used to build UnicodeSets\n"
|
||||
<< "// for special cases where the case-folding algorithm used by\n"
|
||||
<< "// UnicodeSet::closeOver(USET_CASE_INSENSITIVE) does not match\n"
|
||||
<< "// the algorithm defined in ECMAScript 2020 21.2.2.8.2 (Runtime\n"
|
||||
<< "// Semantics: Canonicalize) step 3.\n\n"
|
||||
<< "#ifdef V8_INTL_SUPPORT\n"
|
||||
<< "#include \"src/base/lazy-instance.h\"\n\n"
|
||||
<< "#include \"src/regexp/special-case.h\"\n\n"
|
||||
<< "#include \"unicode/uniset.h\"\n"
|
||||
<< "namespace v8 {\n"
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# 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/.
|
||||
|
||||
# This script handles all the mechanical steps of importing irregexp from v8:
|
||||
#
|
||||
# 1. Acquire the source: either from github, or optionally from a local copy of v8.
|
||||
# 2. Copy the contents of v8/src/regexp into js/src/regexp
|
||||
# - Exclude files that we have chosen not to import.
|
||||
# 3. While doing so, update #includes:
|
||||
# - Change "src/regexp/*" to "regexp/*".
|
||||
# - Remove other v8-specific headers completely.
|
||||
# 4. Add '#include "regexp/regexp-shim.h" in the necessary places.
|
||||
# 5. Update the VERSION file to include the correct git hash.
|
||||
#
|
||||
# Usage:
|
||||
# cd path/to/js/src/regexp
|
||||
# ./import-irregexp.py --path path/to/v8/src/regexp
|
||||
#
|
||||
# Alternatively, without the --path argument, import-irregexp.py will
|
||||
# clone v8 from github into a temporary directory.
|
||||
#
|
||||
# After running this script, changes to the shim code may be necessary
|
||||
# to account for changes in upstream irregexp.
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_hash(path):
|
||||
# Get the hash for the current git revision
|
||||
cwd = os.getcwd()
|
||||
os.chdir(path)
|
||||
command = ['git', 'rev-parse', 'HEAD']
|
||||
result = subprocess.check_output(command, encoding='utf-8')
|
||||
os.chdir(cwd)
|
||||
return result.rstrip()
|
||||
|
||||
|
||||
def copy_and_update_includes(src_path, dst_path):
|
||||
# List of header files that need to include the shim header
|
||||
need_shim = ['property-sequences.h',
|
||||
'regexp-ast.h',
|
||||
'regexp-bytecode-peephole.h',
|
||||
'regexp-bytecodes.h',
|
||||
'regexp-dotprinter.h',
|
||||
'regexp.h',
|
||||
'regexp-macro-assembler.h',
|
||||
'regexp-stack.h',
|
||||
'special-case.h']
|
||||
|
||||
src = open(str(src_path), 'r')
|
||||
dst = open(str(dst_path), 'w')
|
||||
|
||||
# 1. Rewrite includes of V8 regexp headers:
|
||||
regexp_include = re.compile('#include "src/regexp')
|
||||
regexp_include_new = '#include "regexp'
|
||||
|
||||
# 2. Remove includes of other V8 headers
|
||||
other_include = re.compile('#include "src/')
|
||||
|
||||
# 3. If needed, add '#include "regexp/regexp-shim.h"'.
|
||||
# Note: We get a little fancy to ensure that header files are
|
||||
# in alphabetic order. `need_to_add_shim` is true if we still
|
||||
# have to add the shim header in this file. `adding_shim_now`
|
||||
# is true if we have found a '#include "src/*' and we are just
|
||||
# waiting to find something alphabetically smaller (or an empty
|
||||
# line) so that we can insert the shim header in the right place.
|
||||
need_to_add_shim = src_path.name in need_shim
|
||||
adding_shim_now = False
|
||||
|
||||
for line in src:
|
||||
if adding_shim_now:
|
||||
if (line == '\n' or line > '#include "src/regexp/regexp-shim.h"'):
|
||||
dst.write('#include "regexp/regexp-shim.h"\n')
|
||||
need_to_add_shim = False
|
||||
adding_shim_now = False
|
||||
|
||||
if regexp_include.search(line):
|
||||
dst.write(re.sub(regexp_include, regexp_include_new, line))
|
||||
elif other_include.search(line):
|
||||
if need_to_add_shim:
|
||||
adding_shim_now = True
|
||||
else:
|
||||
dst.write(line)
|
||||
|
||||
|
||||
def import_from(srcdir, dstdir):
|
||||
excluded = ['OWNERS',
|
||||
'regexp.cc',
|
||||
'regexp-utils.cc',
|
||||
'regexp-utils.h',
|
||||
'regexp-macro-assembler-arch.h']
|
||||
|
||||
for file in srcdir.iterdir():
|
||||
if file.is_dir():
|
||||
continue
|
||||
if str(file.name) in excluded:
|
||||
continue
|
||||
copy_and_update_includes(file, dstdir / file.name)
|
||||
|
||||
# Update VERSION file
|
||||
hash = get_hash(srcdir)
|
||||
version_file = open(str(dstdir / 'VERSION'), 'w')
|
||||
version_file.write('Imported using import-irregexp.py from:\n')
|
||||
version_file.write('https://github.com/v8/v8/tree/%s/src/regexp\n' % hash)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import tempfile
|
||||
|
||||
# This script should be run from js/src/regexp to work correctly.
|
||||
current_path = Path(os.getcwd())
|
||||
expected_path = 'js/src/regexp'
|
||||
if not current_path.match(expected_path):
|
||||
raise RuntimeError('%s must be run from %s' % (sys.argv[0],
|
||||
expected_path))
|
||||
|
||||
parser = argparse.ArgumentParser(description='Import irregexp from v8')
|
||||
parser.add_argument('-p', '--path', help='path to v8/src/regexp')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.path:
|
||||
src_path = Path(args.path)
|
||||
|
||||
if not (src_path / 'regexp.h').exists():
|
||||
print('Usage:\n import-irregexp.py --path <path/to/v8/src/regexp>')
|
||||
sys.exit(1)
|
||||
import_from(src_path, current_path)
|
||||
sys.exit(0)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tempdir:
|
||||
v8_git = 'https://github.com/v8/v8.git'
|
||||
clone = 'git clone --depth 1 %s %s' % (v8_git, tempdir)
|
||||
os.system(clone)
|
||||
src_path = Path(tempdir) / 'src/regexp'
|
||||
import_from(src_path, current_path)
|
||||
@@ -22,10 +22,16 @@ SOURCES += [
|
||||
'regexp-interpreter.cc',
|
||||
'regexp-macro-assembler-tracer.cc',
|
||||
'regexp-macro-assembler.cc',
|
||||
'regexp-native-macro-assembler.cc',
|
||||
'regexp-parser.cc',
|
||||
'regexp-shim.cc',
|
||||
'regexp-stack.cc',
|
||||
'util/unicode.cc'
|
||||
]
|
||||
|
||||
if CONFIG['ENABLE_INTL_API']:
|
||||
CXXFLAGS += ['-DV8_INTL_SUPPORT']
|
||||
SOURCES += ['property-sequences.cc']
|
||||
SOURCES += [
|
||||
'property-sequences.cc',
|
||||
'special-case.cc'
|
||||
]
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
|
||||
#include "regexp/regexp-shim.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef V8_REGEXP_REGEXP_AST_H_
|
||||
#define V8_REGEXP_REGEXP_AST_H_
|
||||
|
||||
#include "regexp/regexp-shim.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@@ -457,7 +458,11 @@ class RegExpQuantifier final : public RegExpTree {
|
||||
class RegExpCapture final : public RegExpTree {
|
||||
public:
|
||||
explicit RegExpCapture(int index)
|
||||
: body_(nullptr), index_(index), name_(nullptr) {}
|
||||
: body_(nullptr),
|
||||
index_(index),
|
||||
min_match_(0),
|
||||
max_match_(0),
|
||||
name_(nullptr) {}
|
||||
void* Accept(RegExpVisitor* visitor, void* data) override;
|
||||
RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
|
||||
static RegExpNode* ToNode(RegExpTree* body, int index,
|
||||
@@ -467,10 +472,14 @@ class RegExpCapture final : public RegExpTree {
|
||||
bool IsAnchoredAtEnd() override;
|
||||
Interval CaptureRegisters() override;
|
||||
bool IsCapture() override;
|
||||
int min_match() override { return body_->min_match(); }
|
||||
int max_match() override { return body_->max_match(); }
|
||||
int min_match() override { return min_match_; }
|
||||
int max_match() override { return max_match_; }
|
||||
RegExpTree* body() { return body_; }
|
||||
void set_body(RegExpTree* body) { body_ = body; }
|
||||
void set_body(RegExpTree* body) {
|
||||
body_ = body;
|
||||
min_match_ = body->min_match();
|
||||
max_match_ = body->max_match();
|
||||
}
|
||||
int index() const { return index_; }
|
||||
const ZoneVector<uc16>* name() const { return name_; }
|
||||
void set_name(const ZoneVector<uc16>* name) { name_ = name; }
|
||||
@@ -480,12 +489,17 @@ class RegExpCapture final : public RegExpTree {
|
||||
private:
|
||||
RegExpTree* body_;
|
||||
int index_;
|
||||
int min_match_;
|
||||
int max_match_;
|
||||
const ZoneVector<uc16>* name_;
|
||||
};
|
||||
|
||||
class RegExpGroup final : public RegExpTree {
|
||||
public:
|
||||
explicit RegExpGroup(RegExpTree* body) : body_(body) {}
|
||||
explicit RegExpGroup(RegExpTree* body)
|
||||
: body_(body),
|
||||
min_match_(body->min_match()),
|
||||
max_match_(body->max_match()) {}
|
||||
void* Accept(RegExpVisitor* visitor, void* data) override;
|
||||
RegExpNode* ToNode(RegExpCompiler* compiler,
|
||||
RegExpNode* on_success) override {
|
||||
@@ -495,13 +509,15 @@ class RegExpGroup final : public RegExpTree {
|
||||
bool IsAnchoredAtStart() override { return body_->IsAnchoredAtStart(); }
|
||||
bool IsAnchoredAtEnd() override { return body_->IsAnchoredAtEnd(); }
|
||||
bool IsGroup() override;
|
||||
int min_match() override { return body_->min_match(); }
|
||||
int max_match() override { return body_->max_match(); }
|
||||
int min_match() override { return min_match_; }
|
||||
int max_match() override { return max_match_; }
|
||||
Interval CaptureRegisters() override { return body_->CaptureRegisters(); }
|
||||
RegExpTree* body() { return body_; }
|
||||
|
||||
private:
|
||||
RegExpTree* body_;
|
||||
int min_match_;
|
||||
int max_match_;
|
||||
};
|
||||
|
||||
class RegExpLookaround final : public RegExpTree {
|
||||
|
||||
@@ -327,13 +327,11 @@ void RegExpBytecodeGenerator::CheckNotBackReference(int start_reg,
|
||||
}
|
||||
|
||||
void RegExpBytecodeGenerator::CheckNotBackReferenceIgnoreCase(
|
||||
int start_reg, bool read_backward, bool unicode, Label* on_not_equal) {
|
||||
int start_reg, bool read_backward, Label* on_not_equal) {
|
||||
DCHECK_LE(0, start_reg);
|
||||
DCHECK_GE(kMaxRegister, start_reg);
|
||||
Emit(read_backward ? (unicode ? BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD
|
||||
: BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD)
|
||||
: (unicode ? BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE
|
||||
: BC_CHECK_NOT_BACK_REF_NO_CASE),
|
||||
Emit(read_backward ? BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD
|
||||
: BC_CHECK_NOT_BACK_REF_NO_CASE,
|
||||
start_reg);
|
||||
EmitOrLink(on_not_equal);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ class V8_EXPORT_PRIVATE RegExpBytecodeGenerator : public RegExpMacroAssembler {
|
||||
virtual void CheckNotBackReference(int start_reg, bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
|
||||
bool read_backward, bool unicode,
|
||||
bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void IfRegisterLT(int register_index, int comparand, Label* if_lt);
|
||||
virtual void IfRegisterGE(int register_index, int comparand, Label* if_ge);
|
||||
|
||||
@@ -428,7 +428,6 @@ BytecodeArgumentMapping BytecodeSequenceNode::ArgumentMapping(
|
||||
size_t index) const {
|
||||
DCHECK(IsSequence());
|
||||
DCHECK(argument_mapping_ != nullptr);
|
||||
DCHECK_GE(index, 0);
|
||||
DCHECK_LT(index, argument_mapping_->size());
|
||||
|
||||
return argument_mapping_->at(index);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef V8_REGEXP_REGEXP_BYTECODE_PEEPHOLE_H_
|
||||
#define V8_REGEXP_REGEXP_BYTECODE_PEEPHOLE_H_
|
||||
|
||||
#include "regexp/regexp-shim.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef V8_REGEXP_REGEXP_BYTECODES_H_
|
||||
#define V8_REGEXP_REGEXP_BYTECODES_H_
|
||||
|
||||
#include "regexp/regexp-shim.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@@ -99,12 +100,12 @@ STATIC_ASSERT(1 << BYTECODE_SHIFT > BYTECODE_MASK);
|
||||
V(CHECK_BIT_IN_TABLE, 34, 24) /* bc8 pad24 addr32 bits128 */ \
|
||||
V(CHECK_LT, 35, 8) /* bc8 pad8 uc16 addr32 */ \
|
||||
V(CHECK_GT, 36, 8) /* bc8 pad8 uc16 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF, 37, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE, 38, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE, 39, 8) \
|
||||
V(CHECK_NOT_BACK_REF, 37, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE, 38, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE, 39, 8) /* UNUSED */ \
|
||||
V(CHECK_NOT_BACK_REF_BACKWARD, 40, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD, 41, 8) /* bc8 reg_idx24 addr32 */ \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD, 42, 8) \
|
||||
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD, 42, 8) /* UNUSED */ \
|
||||
V(CHECK_NOT_REGS_EQUAL, 43, 12) /* bc8 regidx24 reg_idx32 addr32 */ \
|
||||
V(CHECK_REGISTER_LT, 44, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
V(CHECK_REGISTER_GE, 45, 12) /* bc8 reg_idx24 value32 addr32 */ \
|
||||
|
||||
@@ -1137,39 +1137,6 @@ Vector<const int> CharacterRange::GetWordBounds() {
|
||||
return Vector<const int>(kWordRanges, kWordRangeCount - 1);
|
||||
}
|
||||
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
struct IgnoreSet {
|
||||
IgnoreSet() : set(BuildIgnoreSet()) {}
|
||||
const icu::UnicodeSet set;
|
||||
};
|
||||
|
||||
struct SpecialAddSet {
|
||||
SpecialAddSet() : set(BuildSpecialAddSet()) {}
|
||||
const icu::UnicodeSet set;
|
||||
};
|
||||
|
||||
icu::UnicodeSet BuildAsciiAToZSet() {
|
||||
icu::UnicodeSet set('a', 'z');
|
||||
set.add('A', 'Z');
|
||||
set.freeze();
|
||||
return set;
|
||||
}
|
||||
|
||||
struct AsciiAToZSet {
|
||||
AsciiAToZSet() : set(BuildAsciiAToZSet()) {}
|
||||
const icu::UnicodeSet set;
|
||||
};
|
||||
|
||||
static base::LazyInstance<IgnoreSet>::type ignore_set =
|
||||
LAZY_INSTANCE_INITIALIZER;
|
||||
|
||||
static base::LazyInstance<SpecialAddSet>::type special_add_set =
|
||||
LAZY_INSTANCE_INITIALIZER;
|
||||
|
||||
static base::LazyInstance<AsciiAToZSet>::type ascii_a_to_z_set =
|
||||
LAZY_INSTANCE_INITIALIZER;
|
||||
#endif // V8_INTL_SUPPORT
|
||||
|
||||
// static
|
||||
void CharacterRange::AddCaseEquivalents(Isolate* isolate, Zone* zone,
|
||||
ZoneList<CharacterRange>* ranges,
|
||||
@@ -1192,75 +1159,22 @@ void CharacterRange::AddCaseEquivalents(Isolate* isolate, Zone* zone,
|
||||
others.add(from, to);
|
||||
}
|
||||
|
||||
// Set of characters already added to ranges that do not need to be added
|
||||
// again.
|
||||
// Compute the set of additional characters that should be added,
|
||||
// using UnicodeSet::closeOver. ECMA 262 defines slightly different
|
||||
// case-folding rules than Unicode, so some characters that are
|
||||
// added by closeOver do not match anything other than themselves in
|
||||
// JS. For example, 'ſ' (U+017F LATIN SMALL LETTER LONG S) is the
|
||||
// same case-insensitive character as 's' or 'S' according to
|
||||
// Unicode, but does not match any other character in JS. To handle
|
||||
// this case, we add such characters to the IgnoreSet and filter
|
||||
// them out. We filter twice: once before calling closeOver (to
|
||||
// prevent 'ſ' from adding 's'), and once after calling closeOver
|
||||
// (to prevent 's' from adding 'ſ'). See regexp/special-case.h for
|
||||
// more information.
|
||||
icu::UnicodeSet already_added(others);
|
||||
|
||||
// Set of characters in ranges that are in the 52 ASCII characters [a-zA-Z].
|
||||
icu::UnicodeSet in_ascii_a_to_z(others);
|
||||
in_ascii_a_to_z.retainAll(ascii_a_to_z_set.Pointer()->set);
|
||||
|
||||
// Remove all chars in [a-zA-Z] from others.
|
||||
others.removeAll(in_ascii_a_to_z);
|
||||
|
||||
// Set of characters in ranges that are overlapping with special add set.
|
||||
icu::UnicodeSet in_special_add(others);
|
||||
in_special_add.retainAll(special_add_set.Pointer()->set);
|
||||
|
||||
others.removeAll(in_special_add);
|
||||
|
||||
// Ignore all chars in ignore set.
|
||||
others.removeAll(ignore_set.Pointer()->set);
|
||||
|
||||
// For most of the chars in ranges that is still in others, find the case
|
||||
// equivlant set by calling closeOver(USET_CASE_INSENSITIVE).
|
||||
others.removeAll(RegExpCaseFolding::IgnoreSet());
|
||||
others.closeOver(USET_CASE_INSENSITIVE);
|
||||
|
||||
// Because closeOver(USET_CASE_INSENSITIVE) may add ASCII [a-zA-Z] to others,
|
||||
// but ECMA262 "i" mode won't consider that, remove them from others.
|
||||
// Ex: U+017F add 'S' and 's' to others.
|
||||
others.removeAll(ascii_a_to_z_set.Pointer()->set);
|
||||
|
||||
// Special handling for in_ascii_a_to_z.
|
||||
for (int32_t i = 0; i < in_ascii_a_to_z.getRangeCount(); i++) {
|
||||
UChar32 start = in_ascii_a_to_z.getRangeStart(i);
|
||||
UChar32 end = in_ascii_a_to_z.getRangeEnd(i);
|
||||
// Check if it is uppercase A-Z by checking bit 6.
|
||||
if (start & 0x0020) {
|
||||
// Add the lowercases
|
||||
others.add(start & 0x005F, end & 0x005F);
|
||||
} else {
|
||||
// Add the uppercases
|
||||
others.add(start | 0x0020, end | 0x0020);
|
||||
}
|
||||
}
|
||||
|
||||
// Special handling for chars in "Special Add" set.
|
||||
for (int32_t i = 0; i < in_special_add.getRangeCount(); i++) {
|
||||
UChar32 end = in_special_add.getRangeEnd(i);
|
||||
for (UChar32 ch = in_special_add.getRangeStart(i); ch <= end; ch++) {
|
||||
// Add the uppercase of this character if itself is not an uppercase
|
||||
// character.
|
||||
// Note: The if condiction cannot be u_islower(ch) because ch could be
|
||||
// neither uppercase nor lowercase but Mn.
|
||||
if (!u_isupper(ch)) {
|
||||
others.add(u_toupper(ch));
|
||||
}
|
||||
icu::UnicodeSet candidates(ch, ch);
|
||||
candidates.closeOver(USET_CASE_INSENSITIVE);
|
||||
for (int32_t j = 0; j < candidates.getRangeCount(); j++) {
|
||||
UChar32 end2 = candidates.getRangeEnd(j);
|
||||
for (UChar32 ch2 = candidates.getRangeStart(j); ch2 <= end2; ch2++) {
|
||||
// Add character that is not uppercase to others.
|
||||
if (!u_isupper(ch2)) {
|
||||
others.add(ch2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all characters which already in the ranges.
|
||||
others.removeAll(RegExpCaseFolding::IgnoreSet());
|
||||
others.removeAll(already_added);
|
||||
|
||||
// Add others to the ranges
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#include "regexp/regexp-compiler.h"
|
||||
|
||||
#include "regexp/regexp-macro-assembler-arch.h"
|
||||
#include "regexp/regexp-macro-assembler-tracer.h"
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#include "regexp/special-case.h"
|
||||
#endif // V8_INTL_SUPPORT
|
||||
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#include "unicode/locid.h"
|
||||
@@ -237,20 +239,15 @@ RegExpCompiler::RegExpCompiler(Isolate* isolate, Zone* zone, int capture_count,
|
||||
RegExpCompiler::CompilationResult RegExpCompiler::Assemble(
|
||||
Isolate* isolate, RegExpMacroAssembler* macro_assembler, RegExpNode* start,
|
||||
int capture_count, Handle<String> pattern) {
|
||||
#ifdef DEBUG
|
||||
if (FLAG_trace_regexp_assembler)
|
||||
macro_assembler_ = new RegExpMacroAssemblerTracer(isolate, macro_assembler);
|
||||
else
|
||||
#endif
|
||||
macro_assembler_ = macro_assembler;
|
||||
macro_assembler_ = macro_assembler;
|
||||
|
||||
std::vector<RegExpNode*> work_list;
|
||||
ZoneVector<RegExpNode*> work_list(zone());
|
||||
work_list_ = &work_list;
|
||||
Label fail;
|
||||
macro_assembler_->PushBacktrack(&fail);
|
||||
Trace new_trace;
|
||||
start->Emit(this, &new_trace);
|
||||
macro_assembler_->Bind(&fail);
|
||||
macro_assembler_->BindJumpTarget(&fail);
|
||||
macro_assembler_->Fail();
|
||||
while (!work_list.empty()) {
|
||||
RegExpNode* node = work_list.back();
|
||||
@@ -264,14 +261,9 @@ RegExpCompiler::CompilationResult RegExpCompiler::Assemble(
|
||||
}
|
||||
|
||||
Handle<HeapObject> code = macro_assembler_->GetCode(pattern);
|
||||
isolate->IncreaseTotalRegexpCodeGenerated(code->Size());
|
||||
isolate->IncreaseTotalRegexpCodeGenerated(code);
|
||||
work_list_ = nullptr;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (FLAG_trace_regexp_assembler) {
|
||||
delete macro_assembler_;
|
||||
}
|
||||
#endif
|
||||
return {*code, next_register_};
|
||||
}
|
||||
|
||||
@@ -557,7 +549,7 @@ void Trace::Flush(RegExpCompiler* compiler, RegExpNode* successor) {
|
||||
}
|
||||
|
||||
// On backtrack we need to restore state.
|
||||
assembler->Bind(&undo);
|
||||
assembler->BindJumpTarget(&undo);
|
||||
RestoreAffectedRegisters(assembler, max_register, registers_to_pop,
|
||||
registers_to_clear);
|
||||
if (backtrack() == nullptr) {
|
||||
@@ -720,32 +712,34 @@ static int GetCaseIndependentLetters(Isolate* isolate, uc16 character,
|
||||
unibrow::uchar* letters,
|
||||
int letter_length) {
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
// Special case for U+017F which has upper case in ASCII range.
|
||||
if (character == 0x017f) {
|
||||
if (RegExpCaseFolding::IgnoreSet().contains(character)) {
|
||||
letters[0] = character;
|
||||
return 1;
|
||||
}
|
||||
bool in_special_add_set =
|
||||
RegExpCaseFolding::SpecialAddSet().contains(character);
|
||||
|
||||
icu::UnicodeSet set;
|
||||
set.add(character);
|
||||
set = set.closeOver(USET_CASE_INSENSITIVE);
|
||||
|
||||
UChar32 canon = 0;
|
||||
if (in_special_add_set) {
|
||||
canon = RegExpCaseFolding::Canonicalize(character);
|
||||
}
|
||||
|
||||
int32_t range_count = set.getRangeCount();
|
||||
int items = 0;
|
||||
for (int32_t i = 0; i < range_count; i++) {
|
||||
UChar32 start = set.getRangeStart(i);
|
||||
UChar32 end = set.getRangeEnd(i);
|
||||
CHECK(end - start + items <= letter_length);
|
||||
// Only add to the output if character is not in ASCII range
|
||||
// or the case equivalent character is in ASCII range.
|
||||
// #sec-runtime-semantics-canonicalize-ch
|
||||
// 3.g If the numeric value of ch ≥ 128 and the numeric value of cu < 128,
|
||||
// return ch.
|
||||
if (!((start >= 128) && (character < 128))) {
|
||||
// No range have start and end span across code point 128.
|
||||
DCHECK((start >= 128) == (end >= 128));
|
||||
for (UChar32 cu = start; cu <= end; cu++) {
|
||||
if (one_byte_subject && cu > String::kMaxOneByteCharCode) break;
|
||||
letters[items++] = (unibrow::uchar)(cu);
|
||||
for (UChar32 cu = start; cu <= end; cu++) {
|
||||
if (one_byte_subject && cu > String::kMaxOneByteCharCode) break;
|
||||
if (in_special_add_set && RegExpCaseFolding::Canonicalize(cu) != canon) {
|
||||
continue;
|
||||
}
|
||||
letters[items++] = (unibrow::uchar)(cu);
|
||||
}
|
||||
}
|
||||
return items;
|
||||
@@ -852,10 +846,6 @@ static bool ShortCutEmitCharacterPair(RegExpMacroAssembler* macro_assembler,
|
||||
return false;
|
||||
}
|
||||
|
||||
using EmitCharacterFunction = bool(Isolate* isolate, RegExpCompiler* compiler,
|
||||
uc16 c, Label* on_failure, int cp_offset,
|
||||
bool check, bool preloaded);
|
||||
|
||||
// Only emits letters (things that have case). Only used for case independent
|
||||
// matches.
|
||||
static inline bool EmitAtomLetter(Isolate* isolate, RegExpCompiler* compiler,
|
||||
@@ -1843,13 +1833,13 @@ RegExpNode* TextNode::FilterOneByte(int depth) {
|
||||
if (elm.text_type() == TextElement::ATOM) {
|
||||
Vector<const uc16> quarks = elm.atom()->data();
|
||||
for (int j = 0; j < quarks.length(); j++) {
|
||||
uint16_t c = quarks[j];
|
||||
uc16 c = quarks[j];
|
||||
if (elm.atom()->ignore_case()) {
|
||||
c = unibrow::Latin1::TryConvertToLatin1(c);
|
||||
}
|
||||
if (c > unibrow::Latin1::kMaxChar) return set_replacement(nullptr);
|
||||
// Replace quark in case we converted to Latin-1.
|
||||
uint16_t* writable_quarks = const_cast<uint16_t*>(quarks.begin());
|
||||
uc16* writable_quarks = const_cast<uc16*>(quarks.begin());
|
||||
writable_quarks[j] = c;
|
||||
}
|
||||
} else {
|
||||
@@ -2304,7 +2294,6 @@ void TextNode::TextEmitPass(RegExpCompiler* compiler, TextEmitPassType pass,
|
||||
for (int j = preloaded ? 0 : quarks.length() - 1; j >= 0; j--) {
|
||||
if (first_element_checked && i == 0 && j == 0) continue;
|
||||
if (DeterminedAlready(quick_check, elm.cp_offset() + j)) continue;
|
||||
EmitCharacterFunction* emit_function = nullptr;
|
||||
uc16 quark = quarks[j];
|
||||
if (elm.atom()->ignore_case()) {
|
||||
// Everywhere else we assume that a non-Latin-1 character cannot match
|
||||
@@ -2312,6 +2301,9 @@ void TextNode::TextEmitPass(RegExpCompiler* compiler, TextEmitPassType pass,
|
||||
// invalid by using the Latin1 equivalent instead.
|
||||
quark = unibrow::Latin1::TryConvertToLatin1(quark);
|
||||
}
|
||||
bool needs_bounds_check =
|
||||
*checked_up_to < cp_offset + j || read_backward();
|
||||
bool bounds_checked = false;
|
||||
switch (pass) {
|
||||
case NON_LATIN1_MATCH:
|
||||
DCHECK(one_byte);
|
||||
@@ -2321,24 +2313,24 @@ void TextNode::TextEmitPass(RegExpCompiler* compiler, TextEmitPassType pass,
|
||||
}
|
||||
break;
|
||||
case NON_LETTER_CHARACTER_MATCH:
|
||||
emit_function = &EmitAtomNonLetter;
|
||||
bounds_checked =
|
||||
EmitAtomNonLetter(isolate, compiler, quark, backtrack,
|
||||
cp_offset + j, needs_bounds_check, preloaded);
|
||||
break;
|
||||
case SIMPLE_CHARACTER_MATCH:
|
||||
emit_function = &EmitSimpleCharacter;
|
||||
bounds_checked = EmitSimpleCharacter(isolate, compiler, quark,
|
||||
backtrack, cp_offset + j,
|
||||
needs_bounds_check, preloaded);
|
||||
break;
|
||||
case CASE_CHARACTER_MATCH:
|
||||
emit_function = &EmitAtomLetter;
|
||||
bounds_checked =
|
||||
EmitAtomLetter(isolate, compiler, quark, backtrack,
|
||||
cp_offset + j, needs_bounds_check, preloaded);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (emit_function != nullptr) {
|
||||
bool bounds_check = *checked_up_to < cp_offset + j || read_backward();
|
||||
bool bound_checked =
|
||||
emit_function(isolate, compiler, quark, backtrack, cp_offset + j,
|
||||
bounds_check, preloaded);
|
||||
if (bound_checked) UpdateBoundsCheck(cp_offset + j, checked_up_to);
|
||||
}
|
||||
if (bounds_checked) UpdateBoundsCheck(cp_offset + j, checked_up_to);
|
||||
}
|
||||
} else {
|
||||
DCHECK_EQ(TextElement::CHAR_CLASS, elm.text_type());
|
||||
@@ -3424,8 +3416,8 @@ void BackReferenceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
|
||||
|
||||
DCHECK_EQ(start_reg_ + 1, end_reg_);
|
||||
if (IgnoreCase(flags_)) {
|
||||
assembler->CheckNotBackReferenceIgnoreCase(
|
||||
start_reg_, read_backward(), IsUnicode(flags_), trace->backtrack());
|
||||
assembler->CheckNotBackReferenceIgnoreCase(start_reg_, read_backward(),
|
||||
trace->backtrack());
|
||||
} else {
|
||||
assembler->CheckNotBackReference(start_reg_, read_backward(),
|
||||
trace->backtrack());
|
||||
@@ -3597,12 +3589,17 @@ template <typename... Propagators>
|
||||
class Analysis : public NodeVisitor {
|
||||
public:
|
||||
Analysis(Isolate* isolate, bool is_one_byte)
|
||||
: isolate_(isolate), is_one_byte_(is_one_byte), error_message_(nullptr) {}
|
||||
: isolate_(isolate),
|
||||
is_one_byte_(is_one_byte),
|
||||
error_(RegExpError::kNone) {}
|
||||
|
||||
void EnsureAnalyzed(RegExpNode* that) {
|
||||
StackLimitCheck check(isolate());
|
||||
if (check.HasOverflowed()) {
|
||||
fail("Stack overflow");
|
||||
if (FLAG_correctness_fuzzer_suppressions) {
|
||||
FATAL("Analysis: Aborting on stack overflow");
|
||||
}
|
||||
fail(RegExpError::kAnalysisStackOverflow);
|
||||
return;
|
||||
}
|
||||
if (that->info()->been_analyzed || that->info()->being_analyzed) return;
|
||||
@@ -3612,12 +3609,12 @@ class Analysis : public NodeVisitor {
|
||||
that->info()->been_analyzed = true;
|
||||
}
|
||||
|
||||
bool has_failed() { return error_message_ != nullptr; }
|
||||
const char* error_message() {
|
||||
DCHECK(error_message_ != nullptr);
|
||||
return error_message_;
|
||||
bool has_failed() { return error_ != RegExpError::kNone; }
|
||||
RegExpError error() {
|
||||
DCHECK(error_ != RegExpError::kNone);
|
||||
return error_;
|
||||
}
|
||||
void fail(const char* error_message) { error_message_ = error_message; }
|
||||
void fail(RegExpError error) { error_ = error; }
|
||||
|
||||
Isolate* isolate() const { return isolate_; }
|
||||
|
||||
@@ -3702,19 +3699,19 @@ class Analysis : public NodeVisitor {
|
||||
private:
|
||||
Isolate* isolate_;
|
||||
bool is_one_byte_;
|
||||
const char* error_message_;
|
||||
RegExpError error_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(Analysis);
|
||||
};
|
||||
|
||||
const char* AnalyzeRegExp(Isolate* isolate, bool is_one_byte,
|
||||
RegExpError AnalyzeRegExp(Isolate* isolate, bool is_one_byte,
|
||||
RegExpNode* node) {
|
||||
Analysis<AssertionPropagator, EatsAtLeastPropagator> analysis(isolate,
|
||||
is_one_byte);
|
||||
DCHECK_EQ(node->info()->been_analyzed, false);
|
||||
analysis.EnsureAnalyzed(node);
|
||||
DCHECK_IMPLIES(analysis.has_failed(), analysis.error_message() != nullptr);
|
||||
return analysis.has_failed() ? analysis.error_message() : nullptr;
|
||||
DCHECK_IMPLIES(analysis.has_failed(), analysis.error() != RegExpError::kNone);
|
||||
return analysis.has_failed() ? analysis.error() : RegExpError::kNone;
|
||||
}
|
||||
|
||||
void BackReferenceNode::FillInBMInfo(Isolate* isolate, int offset, int budget,
|
||||
|
||||
@@ -422,10 +422,7 @@ struct PreloadState {
|
||||
// Analysis performs assertion propagation and computes eats_at_least_ values.
|
||||
// See the comments on AssertionPropagator and EatsAtLeastPropagator for more
|
||||
// details.
|
||||
//
|
||||
// This method returns nullptr on success or a null-terminated failure message
|
||||
// on failure.
|
||||
const char* AnalyzeRegExp(Isolate* isolate, bool is_one_byte, RegExpNode* node);
|
||||
RegExpError AnalyzeRegExp(Isolate* isolate, bool is_one_byte, RegExpNode* node);
|
||||
|
||||
class FrequencyCollator {
|
||||
public:
|
||||
@@ -502,18 +499,17 @@ class RegExpCompiler {
|
||||
}
|
||||
|
||||
struct CompilationResult final {
|
||||
explicit CompilationResult(const char* error_message)
|
||||
: error_message(error_message) {}
|
||||
explicit CompilationResult(RegExpError err) : error(err) {}
|
||||
CompilationResult(Object code, int registers)
|
||||
: code(code), num_registers(registers) {}
|
||||
|
||||
static CompilationResult RegExpTooBig() {
|
||||
return CompilationResult("RegExp too big");
|
||||
return CompilationResult(RegExpError::kTooLarge);
|
||||
}
|
||||
|
||||
bool Succeeded() const { return error_message == nullptr; }
|
||||
bool Succeeded() const { return error == RegExpError::kNone; }
|
||||
|
||||
const char* const error_message = nullptr;
|
||||
const RegExpError error = RegExpError::kNone;
|
||||
Object code;
|
||||
int num_registers = 0;
|
||||
};
|
||||
@@ -575,7 +571,7 @@ class RegExpCompiler {
|
||||
int next_register_;
|
||||
int unicode_lookaround_stack_register_;
|
||||
int unicode_lookaround_position_register_;
|
||||
std::vector<RegExpNode*>* work_list_;
|
||||
ZoneVector<RegExpNode*>* work_list_;
|
||||
int recursion_depth_;
|
||||
RegExpMacroAssembler* macro_assembler_;
|
||||
bool one_byte_;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef V8_REGEXP_REGEXP_DOTPRINTER_H_
|
||||
#define V8_REGEXP_REGEXP_DOTPRINTER_H_
|
||||
|
||||
#include "regexp/regexp-shim.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2020 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "regexp/regexp-error.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
const char* kRegExpErrorStrings[] = {
|
||||
#define TEMPLATE(NAME, STRING) STRING,
|
||||
REGEXP_ERROR_MESSAGES(TEMPLATE)
|
||||
#undef TEMPLATE
|
||||
};
|
||||
|
||||
const char* RegExpErrorString(RegExpError error) {
|
||||
DCHECK_LT(error, RegExpError::NumErrors);
|
||||
return kRegExpErrorStrings[static_cast<int>(error)];
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright 2020 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef V8_REGEXP_REGEXP_ERROR_H_
|
||||
#define V8_REGEXP_REGEXP_ERROR_H_
|
||||
|
||||
#include "regexp-shim.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
#define REGEXP_ERROR_MESSAGES(T) \
|
||||
T(None, "") \
|
||||
T(StackOverflow, "Maximum call stack size exceeded") \
|
||||
T(AnalysisStackOverflow, "Stack overflow") \
|
||||
T(TooLarge, "Regular expression too large") \
|
||||
T(UnterminatedGroup, "Unterminated group") \
|
||||
T(UnmatchedParen, "Unmatched ')'") \
|
||||
T(EscapeAtEndOfPattern, "\\ at end of pattern") \
|
||||
T(InvalidPropertyName, "Invalid property name") \
|
||||
T(InvalidEscape, "Invalid escape") \
|
||||
T(InvalidDecimalEscape, "Invalid decimal escape") \
|
||||
T(InvalidUnicodeEscape, "Invalid Unicode escape") \
|
||||
T(NothingToRepeat, "Nothing to repeat") \
|
||||
T(LoneQuantifierBrackets, "Lone quantifier brackets") \
|
||||
T(RangeOutOfOrder, "numbers out of order in {} quantifier") \
|
||||
T(IncompleteQuantifier, "Incomplete quantifier") \
|
||||
T(InvalidQuantifier, "Invalid quantifier") \
|
||||
T(InvalidGroup, "Invalid group") \
|
||||
T(MultipleFlagDashes, "Multiple dashes in flag group") \
|
||||
T(RepeatedFlag, "Repeated flag in flag group") \
|
||||
T(InvalidFlagGroup, "Invalid flag group") \
|
||||
T(TooManyCaptures, "Too many captures") \
|
||||
T(InvalidCaptureGroupName, "Invalid capture group name") \
|
||||
T(DuplicateCaptureGroupName, "Duplicate capture group name") \
|
||||
T(InvalidNamedReference, "Invalid named reference") \
|
||||
T(InvalidNamedCaptureReference, "Invalid named capture referenced") \
|
||||
T(InvalidClassEscape, "Invalid class escape") \
|
||||
T(InvalidClassPropertyName, "Invalid property name in character class") \
|
||||
T(InvalidCharacterClass, "Invalid character class") \
|
||||
T(UnterminatedCharacterClass, "Unterminated character class") \
|
||||
T(OutOfOrderCharacterClass, "Range out of order in character class")
|
||||
|
||||
enum class RegExpError : uint32_t {
|
||||
#define TEMPLATE(NAME, STRING) k##NAME,
|
||||
REGEXP_ERROR_MESSAGES(TEMPLATE)
|
||||
#undef TEMPLATE
|
||||
NumErrors
|
||||
};
|
||||
|
||||
V8_EXPORT_PRIVATE const char* RegExpErrorString(RegExpError error);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_REGEXP_REGEXP_ERROR_H_
|
||||
@@ -28,18 +28,18 @@ namespace internal {
|
||||
namespace {
|
||||
|
||||
bool BackRefMatchesNoCase(Isolate* isolate, int from, int current, int len,
|
||||
Vector<const uc16> subject, bool unicode) {
|
||||
Vector<const uc16> subject) {
|
||||
Address offset_a =
|
||||
reinterpret_cast<Address>(const_cast<uc16*>(&subject.at(from)));
|
||||
Address offset_b =
|
||||
reinterpret_cast<Address>(const_cast<uc16*>(&subject.at(current)));
|
||||
size_t length = len * kUC16Size;
|
||||
return RegExpMacroAssembler::CaseInsensitiveCompareUC16(
|
||||
offset_a, offset_b, length, unicode ? nullptr : isolate) == 1;
|
||||
return RegExpMacroAssembler::CaseInsensitiveCompareUC16(offset_a, offset_b,
|
||||
length, isolate) == 1;
|
||||
}
|
||||
|
||||
bool BackRefMatchesNoCase(Isolate* isolate, int from, int current, int len,
|
||||
Vector<const uint8_t> subject, bool unicode) {
|
||||
Vector<const uint8_t> subject) {
|
||||
// For Latin1 characters the unicode flag makes no difference.
|
||||
for (int i = 0; i < len; i++) {
|
||||
unsigned int old_char = subject[from++];
|
||||
@@ -82,11 +82,17 @@ int32_t Load32Aligned(const byte* pc) {
|
||||
return *reinterpret_cast<const int32_t*>(pc);
|
||||
}
|
||||
|
||||
int32_t Load16Aligned(const byte* pc) {
|
||||
// TODO(jgruber): Rename to Load16AlignedUnsigned.
|
||||
uint32_t Load16Aligned(const byte* pc) {
|
||||
DCHECK_EQ(0, reinterpret_cast<intptr_t>(pc) & 1);
|
||||
return *reinterpret_cast<const uint16_t*>(pc);
|
||||
}
|
||||
|
||||
int32_t Load16AlignedSigned(const byte* pc) {
|
||||
DCHECK_EQ(0, reinterpret_cast<intptr_t>(pc) & 1);
|
||||
return *reinterpret_cast<const int16_t*>(pc);
|
||||
}
|
||||
|
||||
// A simple abstraction over the backtracking stack used by the interpreter.
|
||||
//
|
||||
// Despite the name 'backtracking' stack, it's actually used as a generic stack
|
||||
@@ -734,26 +740,14 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
|
||||
DISPATCH();
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from >= 0 && len > 0) {
|
||||
if (current + len > subject.length() ||
|
||||
!BackRefMatchesNoCase(isolate, from, current, len, subject, true)) {
|
||||
SET_PC_FROM_OFFSET(Load32Aligned(pc + 4));
|
||||
DISPATCH();
|
||||
}
|
||||
current += len;
|
||||
}
|
||||
ADVANCE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE);
|
||||
DISPATCH();
|
||||
UNREACHABLE(); // TODO(jgruber): Remove this unused bytecode.
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from >= 0 && len > 0) {
|
||||
if (current + len > subject.length() ||
|
||||
!BackRefMatchesNoCase(isolate, from, current, len, subject,
|
||||
false)) {
|
||||
!BackRefMatchesNoCase(isolate, from, current, len, subject)) {
|
||||
SET_PC_FROM_OFFSET(Load32Aligned(pc + 4));
|
||||
DISPATCH();
|
||||
}
|
||||
@@ -763,27 +757,14 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
|
||||
DISPATCH();
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from >= 0 && len > 0) {
|
||||
if (current - len < 0 ||
|
||||
!BackRefMatchesNoCase(isolate, from, current - len, len, subject,
|
||||
true)) {
|
||||
SET_PC_FROM_OFFSET(Load32Aligned(pc + 4));
|
||||
DISPATCH();
|
||||
}
|
||||
current -= len;
|
||||
}
|
||||
ADVANCE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD);
|
||||
DISPATCH();
|
||||
UNREACHABLE(); // TODO(jgruber): Remove this unused bytecode.
|
||||
}
|
||||
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD) {
|
||||
int from = registers[insn >> BYTECODE_SHIFT];
|
||||
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
|
||||
if (from >= 0 && len > 0) {
|
||||
if (current - len < 0 ||
|
||||
!BackRefMatchesNoCase(isolate, from, current - len, len, subject,
|
||||
false)) {
|
||||
!BackRefMatchesNoCase(isolate, from, current - len, len, subject)) {
|
||||
SET_PC_FROM_OFFSET(Load32Aligned(pc + 4));
|
||||
DISPATCH();
|
||||
}
|
||||
@@ -828,7 +809,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
|
||||
}
|
||||
BYTECODE(SKIP_UNTIL_CHAR) {
|
||||
int load_offset = (insn >> BYTECODE_SHIFT);
|
||||
uint32_t advance = Load16Aligned(pc + 4);
|
||||
int32_t advance = Load16AlignedSigned(pc + 4);
|
||||
uint32_t c = Load16Aligned(pc + 6);
|
||||
while (static_cast<uintptr_t>(current + load_offset) <
|
||||
static_cast<uintptr_t>(subject.length())) {
|
||||
@@ -844,7 +825,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
|
||||
}
|
||||
BYTECODE(SKIP_UNTIL_CHAR_AND) {
|
||||
int load_offset = (insn >> BYTECODE_SHIFT);
|
||||
uint16_t advance = Load16Aligned(pc + 4);
|
||||
int32_t advance = Load16AlignedSigned(pc + 4);
|
||||
uint16_t c = Load16Aligned(pc + 6);
|
||||
uint32_t mask = Load32Aligned(pc + 8);
|
||||
int32_t maximum_offset = Load32Aligned(pc + 12);
|
||||
@@ -862,7 +843,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
|
||||
}
|
||||
BYTECODE(SKIP_UNTIL_CHAR_POS_CHECKED) {
|
||||
int load_offset = (insn >> BYTECODE_SHIFT);
|
||||
uint16_t advance = Load16Aligned(pc + 4);
|
||||
int32_t advance = Load16AlignedSigned(pc + 4);
|
||||
uint16_t c = Load16Aligned(pc + 6);
|
||||
int32_t maximum_offset = Load32Aligned(pc + 8);
|
||||
while (static_cast<uintptr_t>(current + maximum_offset) <=
|
||||
@@ -879,7 +860,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
|
||||
}
|
||||
BYTECODE(SKIP_UNTIL_BIT_IN_TABLE) {
|
||||
int load_offset = (insn >> BYTECODE_SHIFT);
|
||||
uint32_t advance = Load16Aligned(pc + 4);
|
||||
int32_t advance = Load16AlignedSigned(pc + 4);
|
||||
const byte* table = pc + 8;
|
||||
while (static_cast<uintptr_t>(current + load_offset) <
|
||||
static_cast<uintptr_t>(subject.length())) {
|
||||
@@ -895,7 +876,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
|
||||
}
|
||||
BYTECODE(SKIP_UNTIL_GT_OR_NOT_BIT_IN_TABLE) {
|
||||
int load_offset = (insn >> BYTECODE_SHIFT);
|
||||
uint16_t advance = Load16Aligned(pc + 4);
|
||||
int32_t advance = Load16AlignedSigned(pc + 4);
|
||||
uint16_t limit = Load16Aligned(pc + 6);
|
||||
const byte* table = pc + 8;
|
||||
while (static_cast<uintptr_t>(current + load_offset) <
|
||||
@@ -916,7 +897,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
|
||||
}
|
||||
BYTECODE(SKIP_UNTIL_CHAR_OR_CHAR) {
|
||||
int load_offset = (insn >> BYTECODE_SHIFT);
|
||||
uint32_t advance = Load32Aligned(pc + 4);
|
||||
int32_t advance = Load32Aligned(pc + 4);
|
||||
uint16_t c = Load16Aligned(pc + 8);
|
||||
uint16_t c2 = Load16Aligned(pc + 10);
|
||||
while (static_cast<uintptr_t>(current + load_offset) <
|
||||
@@ -1016,6 +997,8 @@ IrregexpInterpreter::Result IrregexpInterpreter::MatchInternal(
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
|
||||
|
||||
// This method is called through an external reference from RegExpExecInternal
|
||||
// builtin.
|
||||
IrregexpInterpreter::Result IrregexpInterpreter::MatchForCallFromJs(
|
||||
@@ -1042,6 +1025,8 @@ IrregexpInterpreter::Result IrregexpInterpreter::MatchForCallFromJs(
|
||||
start_position, call_origin);
|
||||
}
|
||||
|
||||
#endif // !COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
|
||||
|
||||
IrregexpInterpreter::Result IrregexpInterpreter::MatchForCallFromRuntime(
|
||||
Isolate* isolate, Handle<JSRegExp> regexp, Handle<String> subject_string,
|
||||
int* registers, int registers_length, int start_position) {
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Copyright 2020 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// This file implements the NativeRegExpMacroAssembler interface for
|
||||
// SpiderMonkey. It provides the same interface as each of V8's
|
||||
// architecture-specific implementations.
|
||||
|
||||
#ifndef RegexpMacroAssemblerArch_h
|
||||
#define RegexpMacroAssemblerArch_h
|
||||
|
||||
#include "jit/MacroAssembler.h"
|
||||
#include "regexp/regexp-macro-assembler.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
struct FrameData {
|
||||
// Character position at the start of the input, stored as a
|
||||
// negative offset from the end of the string (input_end_pointer_).
|
||||
size_t inputStart;
|
||||
|
||||
// The backtrack_stack_pointer_ register points to the top of the stack.
|
||||
// This points to the bottom of the backtrack stack.
|
||||
void* backtrackStackBase;
|
||||
|
||||
// Copy of the input MatchPairs.
|
||||
int32_t* matches; // pointer to capture array
|
||||
int32_t numMatches; // size of capture array
|
||||
};
|
||||
|
||||
class SMRegExpMacroAssembler final : public NativeRegExpMacroAssembler {
|
||||
public:
|
||||
SMRegExpMacroAssembler(JSContext* cx, Isolate* isolate,
|
||||
js::jit::StackMacroAssembler& masm, Zone* zone,
|
||||
Mode mode, uint32_t num_capture_registers);
|
||||
virtual ~SMRegExpMacroAssembler() {} // Nothing to do here
|
||||
|
||||
virtual int stack_limit_slack();
|
||||
virtual IrregexpImplementation Implementation();
|
||||
|
||||
virtual bool Succeed();
|
||||
virtual void Fail();
|
||||
|
||||
virtual void AdvanceCurrentPosition(int by);
|
||||
virtual void PopCurrentPosition();
|
||||
virtual void PushCurrentPosition();
|
||||
virtual void SetCurrentPositionFromEnd(int by);
|
||||
|
||||
virtual void Backtrack();
|
||||
virtual void Bind(Label* label);
|
||||
virtual void GoTo(Label* label);
|
||||
virtual void PushBacktrack(Label* label);
|
||||
|
||||
virtual void CheckCharacter(uint32_t c, Label* on_equal);
|
||||
virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal);
|
||||
virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
|
||||
virtual void CheckCharacterLT(uc16 limit, Label* on_less);
|
||||
virtual void CheckCharacterAfterAnd(uint32_t c, uint32_t mask,
|
||||
Label* on_equal);
|
||||
virtual void CheckNotCharacterAfterAnd(uint32_t c, uint32_t mask,
|
||||
Label* on_not_equal);
|
||||
virtual void CheckNotCharacterAfterMinusAnd(uc16 c, uc16 minus, uc16 mask,
|
||||
Label* on_not_equal);
|
||||
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
|
||||
virtual void CheckCharacterInRange(uc16 from, uc16 to, Label* on_in_range);
|
||||
virtual void CheckCharacterNotInRange(uc16 from, uc16 to,
|
||||
Label* on_not_in_range);
|
||||
virtual void CheckAtStart(int cp_offset, Label* on_at_start);
|
||||
virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start);
|
||||
virtual void CheckPosition(int cp_offset, Label* on_outside_input);
|
||||
virtual void CheckBitInTable(Handle<ByteArray> table, Label* on_bit_set);
|
||||
virtual bool CheckSpecialCharacterClass(uc16 type, Label* on_no_match);
|
||||
virtual void CheckNotBackReference(int start_reg, bool read_backward,
|
||||
Label* on_no_match);
|
||||
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
|
||||
bool read_backward,
|
||||
Label* on_no_match);
|
||||
|
||||
virtual void LoadCurrentCharacterImpl(int cp_offset, Label* on_end_of_input,
|
||||
bool check_bounds, int characters,
|
||||
int eats_at_least);
|
||||
|
||||
virtual void AdvanceRegister(int reg, int by);
|
||||
virtual void IfRegisterGE(int reg, int comparand, Label* if_ge);
|
||||
virtual void IfRegisterLT(int reg, int comparand, Label* if_lt);
|
||||
virtual void IfRegisterEqPos(int reg, Label* if_eq);
|
||||
virtual void PopRegister(int register_index);
|
||||
virtual void PushRegister(int register_index,
|
||||
StackCheckFlag check_stack_limit);
|
||||
virtual void ReadCurrentPositionFromRegister(int reg);
|
||||
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
|
||||
virtual void ReadStackPointerFromRegister(int reg);
|
||||
virtual void WriteStackPointerToRegister(int reg);
|
||||
virtual void SetRegister(int register_index, int to);
|
||||
virtual void ClearRegisters(int reg_from, int reg_to);
|
||||
|
||||
virtual Handle<HeapObject> GetCode(Handle<String> source);
|
||||
|
||||
private:
|
||||
size_t frameSize_ = 0;
|
||||
|
||||
void createStackFrame();
|
||||
void initFrameAndRegs();
|
||||
void successHandler();
|
||||
void exitHandler();
|
||||
void backtrackHandler();
|
||||
void stackOverflowHandler();
|
||||
|
||||
// Push a register on the backtrack stack.
|
||||
void Push(js::jit::Register value);
|
||||
|
||||
// Pop a value from the backtrack stack.
|
||||
void Pop(js::jit::Register target);
|
||||
|
||||
void CheckAtStartImpl(int cp_offset, Label* on_cond,
|
||||
js::jit::Assembler::Condition cond);
|
||||
void CheckCharacterImpl(js::jit::Imm32 c, Label* on_cond,
|
||||
js::jit::Assembler::Condition cond);
|
||||
void CheckCharacterAfterAndImpl(uint32_t c, uint32_t and_with, Label* on_cond,
|
||||
bool negate);
|
||||
void CheckCharacterInRangeImpl(uc16 from, uc16 to, Label* on_cond,
|
||||
js::jit::Assembler::Condition cond);
|
||||
void CheckNotBackReferenceImpl(int start_reg, bool read_backward,
|
||||
Label* on_no_match, bool ignore_case);
|
||||
|
||||
void LoadCurrentCharacterUnchecked(int cp_offset, int characters);
|
||||
|
||||
void JumpOrBacktrack(Label* to);
|
||||
|
||||
// MacroAssembler methods that take a Label can be called with a
|
||||
// null label, which means that we should backtrack if we would jump
|
||||
// to that label. This is a helper to avoid writing out the same
|
||||
// logic a dozen times.
|
||||
inline js::jit::Label* LabelOrBacktrack(Label* to) {
|
||||
return to ? to->inner() : &backtrack_label_;
|
||||
}
|
||||
|
||||
void CheckBacktrackStackLimit();
|
||||
|
||||
static bool GrowBacktrackStack(RegExpStack* regexp_stack);
|
||||
|
||||
static uint32_t CaseInsensitiveCompareStrings(const char16_t* substring1,
|
||||
const char16_t* substring2,
|
||||
size_t byteLength);
|
||||
static uint32_t CaseInsensitiveCompareUCStrings(const char16_t* substring1,
|
||||
const char16_t* substring2,
|
||||
size_t byteLength);
|
||||
|
||||
inline int char_size() { return static_cast<int>(mode_); }
|
||||
inline js::jit::Scale factor() {
|
||||
return mode_ == UC16 ? js::jit::TimesTwo : js::jit::TimesOne;
|
||||
}
|
||||
|
||||
js::jit::Address inputStart() {
|
||||
return js::jit::Address(masm_.getStackPointer(),
|
||||
offsetof(FrameData, inputStart));
|
||||
}
|
||||
js::jit::Address backtrackStackBase() {
|
||||
return js::jit::Address(masm_.getStackPointer(),
|
||||
offsetof(FrameData, backtrackStackBase));
|
||||
}
|
||||
js::jit::Address matches() {
|
||||
return js::jit::Address(masm_.getStackPointer(),
|
||||
offsetof(FrameData, matches));
|
||||
}
|
||||
js::jit::Address numMatches() {
|
||||
return js::jit::Address(masm_.getStackPointer(),
|
||||
offsetof(FrameData, numMatches));
|
||||
}
|
||||
|
||||
// The stack-pointer-relative location of a regexp register.
|
||||
js::jit::Address register_location(int register_index) {
|
||||
return js::jit::Address(masm_.getStackPointer(),
|
||||
register_offset(register_index));
|
||||
}
|
||||
|
||||
int32_t register_offset(int register_index) {
|
||||
MOZ_ASSERT(register_index >= 0 && register_index <= kMaxRegister);
|
||||
if (num_registers_ <= register_index) {
|
||||
num_registers_ = register_index + 1;
|
||||
}
|
||||
static_assert(alignof(uintptr_t) <= alignof(FrameData));
|
||||
return sizeof(FrameData) + register_index * sizeof(uintptr_t*);
|
||||
}
|
||||
|
||||
JSContext* cx_;
|
||||
js::jit::StackMacroAssembler& masm_;
|
||||
|
||||
/*
|
||||
* This assembler uses the following registers:
|
||||
*
|
||||
* - current_character_:
|
||||
* Contains the character (or characters) currently being examined.
|
||||
* Must be loaded using LoadCurrentCharacter before using any of the
|
||||
* dispatch methods. After a matching pass for a global regexp,
|
||||
* temporarily stores the index of capture start.
|
||||
* - current_position_:
|
||||
* Current position in input *as negative byte offset from end of string*.
|
||||
* - input_end_pointer_:
|
||||
* Points to byte after last character in the input. current_position_ is
|
||||
* relative to this.
|
||||
* - backtrack_stack_pointer_:
|
||||
* Points to tip of the (heap-allocated) backtrack stack. The stack grows
|
||||
* downward (like the native stack).
|
||||
* - temp0_, temp1_, temp2_:
|
||||
* Scratch registers.
|
||||
*
|
||||
* The native stack pointer is used to access arguments (InputOutputData),
|
||||
* local variables (FrameData), and irregexp's internal virtual registers
|
||||
* (see register_location).
|
||||
*/
|
||||
|
||||
js::jit::Register current_character_;
|
||||
js::jit::Register current_position_;
|
||||
js::jit::Register input_end_pointer_;
|
||||
js::jit::Register backtrack_stack_pointer_;
|
||||
js::jit::Register temp0_, temp1_, temp2_;
|
||||
|
||||
js::jit::Label entry_label_;
|
||||
js::jit::Label start_label_;
|
||||
js::jit::Label backtrack_label_;
|
||||
js::jit::Label success_label_;
|
||||
js::jit::Label exit_label_;
|
||||
js::jit::Label stack_overflow_label_;
|
||||
js::jit::Label exit_with_exception_label_;
|
||||
|
||||
// When we generate the code to push a backtrack label's address
|
||||
// onto the backtrack stack, we don't know its final address. We
|
||||
// have to patch it after linking. This is slightly delicate, as the
|
||||
// Label itself (which is allocated on the stack) may not exist by
|
||||
// the time we link. The approach is as follows:
|
||||
//
|
||||
// 1. When we push a label on the backtrack stack (PushBacktrack),
|
||||
// we bind the label's patchOffset_ field to the offset within
|
||||
// the code that should be overwritten. This works because each
|
||||
// label is only pushed by a single instruction.
|
||||
//
|
||||
// 2. When we bind a label (Bind), we check to see if it has a
|
||||
// bound patchOffset_. If it does, we create a LabelPatch mapping
|
||||
// its patch offset to the offset of the label itself.
|
||||
//
|
||||
// 3. While linking the code, we walk the list of label patches
|
||||
// and patch the code accordingly.
|
||||
class LabelPatch {
|
||||
public:
|
||||
LabelPatch(js::jit::CodeOffset patchOffset, size_t labelOffset)
|
||||
: patchOffset_(patchOffset), labelOffset_(labelOffset) {}
|
||||
|
||||
js::jit::CodeOffset patchOffset_;
|
||||
size_t labelOffset_ = 0;
|
||||
};
|
||||
|
||||
js::Vector<LabelPatch, 4, js::SystemAllocPolicy> labelPatches_;
|
||||
void AddLabelPatch(js::jit::CodeOffset patchOffset, size_t labelOffset) {
|
||||
js::AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (!labelPatches_.emplaceBack(patchOffset, labelOffset)) {
|
||||
oomUnsafe.crash("Irregexp label patch");
|
||||
}
|
||||
}
|
||||
|
||||
Mode mode_;
|
||||
int num_registers_;
|
||||
int num_capture_registers_;
|
||||
js::jit::LiveGeneralRegisterSet savedRegisters_;
|
||||
|
||||
public:
|
||||
using TableVector =
|
||||
js::Vector<PseudoHandle<ByteArrayData>, 4, js::SystemAllocPolicy>;
|
||||
TableVector& tables() { return tables_; }
|
||||
|
||||
private:
|
||||
TableVector tables_;
|
||||
void AddTable(PseudoHandle<ByteArrayData> table) {
|
||||
js::AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (!tables_.append(std::move(table))) {
|
||||
oomUnsafe.crash("Irregexp table append");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // RegexpMacroAssemblerArch_h
|
||||
Loaded 100 of 251 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user