/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* 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/. */
// Step 5.1 (implicit in above check) // Step 5.2. Extract callback. // // Implementation Note: The specification demands that if the size doesn't // exist, we instead would provide an algorithm that returns 1. Instead, we // will teach callers that a missing callback should simply return 1, rather // than gin up a fake callback here. // // This decision may need to be revisited if the default action ever diverges // within the specification.
RefPtr<QueuingStrategySize> sizeAlgorithm =
aStrategy.mSize.WasPassed() ? &aStrategy.mSize.Value() : nullptr;
// https://streams.spec.whatwg.org/#readable-stream-from-iterable class ReadableStreamFromAlgorithms final
: public UnderlyingSourceAlgorithmsWrapper { public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
ReadableStreamFromAlgorithms, UnderlyingSourceAlgorithmsWrapper)
// Step 1. Let nextResult be IteratorNext(iteratorRecord).
JS::Rooted<JS::Value> nextResult(aCx); if (!JS::IteratorNext(aCx, iteratorRecord, &nextResult)) { // Step 2. If nextResult is an abrupt completion, return a promise // rejected with nextResult.[[Value]].
aRv.StealExceptionFromJSContext(aCx); return nullptr;
}
// Step 3. Let nextPromise be a promise resolved with nextResult.[[Value]].
RefPtr<Promise> nextPromise = Promise::CreateInfallible(mGlobal);
nextPromise->MaybeResolve(nextResult);
// Step 4. Return the result of reacting to nextPromise with the following // fulfillment steps, given iterResult: auto result = nextPromise->ThenWithCycleCollectedArgs(
[](JSContext* aCx, JS::Handle<JS::Value> aIterResult, ErrorResult& aRv, const RefPtr<ReadableStreamDefaultController>& aController)
MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION -> already_AddRefed<Promise> {
aRv.MightThrowJSException();
// Step 4.1. If Type(iterResult) is not Object, throw a TypeError. if (!aIterResult.isObject()) {
aRv.ThrowTypeError("next() returned a non-object value"); return nullptr;
}
// Step 5. Let cancelAlgorithm be the following steps, given reason:
MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CancelCallbackImpl(
JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
ErrorResult& aRv) override {
aRv.MightThrowJSException();
// Step 1. Let iterator be iteratorRecord.[[Iterator]].
JS::Rooted<JS::Value> iterator(aCx); if (!JS::GetIteratorRecordIterator(aCx, iteratorRecord, &iterator)) {
aRv.StealExceptionFromJSContext(aCx); return nullptr;
}
// Step 2. Let returnMethod be GetMethod(iterator, "return").
JS::Rooted<JS::Value> returnMethod(aCx); if (!JS::GetReturnMethod(aCx, iterator, &returnMethod)) { // Step 3. If returnMethod is an abrupt completion, return a promise // rejected with returnMethod.[[Value]].
aRv.StealExceptionFromJSContext(aCx); return nullptr;
}
// Step 4. If returnMethod.[[Value]] is undefined, return a promise resolved // with undefined. if (returnMethod.isUndefined()) { return Promise::CreateResolvedWithUndefined(mGlobal, aRv);
}
// Step 5. Let returnResult be Call(returnMethod.[[Value]], iterator, « // reason »).
JS::Rooted<JS::Value> reason(aCx, aReason.Value()); if (!JS_WrapValue(aCx, &reason)) {
JS_ClearPendingException(aCx);
aRv.Throw(NS_ERROR_UNEXPECTED); return nullptr;
}
JS::Rooted<JS::Value> returnResult(aCx); if (!JS::Call(aCx, iterator, returnMethod, JS::HandleValueArray(reason),
&returnResult)) { // Step 6. If returnResult is an abrupt completion, return a promise // rejected with returnResult.[[Value]].
aRv.StealExceptionFromJSContext(aCx); return nullptr;
}
// Step 7. Let returnPromise be a promise resolved with // returnResult.[[Value]].
RefPtr<Promise> returnPromise = Promise::CreateInfallible(mGlobal);
returnPromise->MaybeResolve(returnResult);
// Step 8. Return the result of reacting to returnPromise with the following // fulfillment steps, given iterResult: auto result = returnPromise->ThenWithCycleCollectedArgs(
[](JSContext* aCx, JS::Handle<JS::Value> aIterResult, ErrorResult& aRv)
MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION -> already_AddRefed<Promise> { // Step 8.1. If Type(iterResult) is not Object, throw a TypeError. if (!aIterResult.isObject()) {
aRv.ThrowTypeError("return() returned a non-object value"); return nullptr;
}
// Step 6. if (reader->IsDefault()) { // Step 6.1. Let readRequests be reader.[[readRequests]]. // Move LinkedList out of DefaultReader onto stack to avoid the potential // for concurrent modification, which could invalidate the iterator. // // See https://bugs.chromium.org/p/chromium/issues/detail?id=1045874 as an // example of the kind of issue that could occur.
LinkedList<RefPtr<ReadRequest>> readRequests =
std::move(reader->AsDefault()->ReadRequests());
// Step 6.2. Set reader.[[readRequests]] to an empty list. // Note: The std::move already cleared this anyway.
reader->AsDefault()->ReadRequests().clear();
// Step 6.3. For each readRequest of readRequests, // Drain the local list and destroy elements along the way. while (RefPtr<ReadRequest> readRequest = readRequests.popFirst()) { // Step 6.3.1. Perform readRequest’s close steps.
readRequest->CloseSteps(aCx, aRv); if (aRv.Failed()) { return;
}
}
}
}
// Step 6. if (reader && reader->IsBYOB()) { // Step 6.1. Let readIntoRequests be reader.[[readIntoRequests]].
LinkedList<RefPtr<ReadIntoRequest>> readIntoRequests =
std::move(reader->AsBYOB()->ReadIntoRequests());
// Step 6.2. Set reader.[[readIntoRequests]] to an empty list. // Note: The std::move already cleared this anyway.
reader->AsBYOB()->ReadIntoRequests().clear();
// Step 6.3. For each readIntoRequest of readIntoRequests, while (RefPtr<ReadIntoRequest> readIntoRequest =
readIntoRequests.popFirst()) { // Step 6.3.1.Perform readIntoRequest’s close steps, given undefined.
readIntoRequest->CloseSteps(aCx, JS::UndefinedHandleValue, aRv); if (aRv.Failed()) { return nullptr;
}
}
}
// ThenWithCycleCollectedArgs will carry promise, keeping it alive until the // callback executes.
Result<RefPtr<Promise>, nsresult> returnResult =
sourceCancelPromise->ThenWithCycleCollectedArgs(
[](JSContext*, JS::Handle<JS::Value>, ErrorResult&,
RefPtr<Promise> newPromise) {
newPromise->MaybeResolveWithUndefined(); return newPromise.forget();
},
promise);
if (returnResult.isErr()) {
aRv.Throw(returnResult.unwrapErr()); return nullptr;
}
return returnResult.unwrap().forget();
}
} // namespace streams_abstract
// https://streams.spec.whatwg.org/#rs-cancel
already_AddRefed<Promise> ReadableStream::Cancel(JSContext* aCx,
JS::Handle<JS::Value> aReason,
ErrorResult& aRv) { // Step 1. If ! IsReadableStreamLocked(this) is true, // return a promise rejected with a TypeError exception. if (Locked()) {
aRv.ThrowTypeError("Cannot cancel a stream locked by a reader."); return nullptr;
}
// https://streams.spec.whatwg.org/#rs-pipe-through
MOZ_CAN_RUN_SCRIPT already_AddRefed<ReadableStream> ReadableStream::PipeThrough( const ReadableWritablePair& aTransform, const StreamPipeOptions& aOptions,
ErrorResult& aRv) { // Step 1: If ! IsReadableStreamLocked(this) is true, throw a TypeError // exception. if (IsReadableStreamLocked(this)) {
aRv.ThrowTypeError("Cannot pipe from a locked stream."); return nullptr;
}
// Step 2: If ! IsWritableStreamLocked(transform["writable"]) is true, throw a // TypeError exception. if (IsWritableStreamLocked(aTransform.mWritable)) {
aRv.ThrowTypeError("Cannot pipe to a locked stream."); return nullptr;
}
// Step 3: Let signal be options["signal"] if it exists, or undefined // otherwise.
RefPtr<AbortSignal> signal =
aOptions.mSignal.WasPassed() ? &aOptions.mSignal.Value() : nullptr;
// Steps 3-12 are contained in the construction of Tee State.
RefPtr<TeeState> teeState = TeeState::Create(aStream, aCloneForBranch2, aRv); if (aRv.Failed()) { return;
}
// Step 13 - 16 auto branch1Algorithms = MakeRefPtr<ReadableStreamDefaultTeeSourceAlgorithms>(
teeState, TeeBranch::Branch1); auto branch2Algorithms = MakeRefPtr<ReadableStreamDefaultTeeSourceAlgorithms>(
teeState, TeeBranch::Branch2);
// https://streams.spec.whatwg.org/#rs-pipe-to
already_AddRefed<Promise> ReadableStream::PipeTo(
WritableStream& aDestination, const StreamPipeOptions& aOptions,
ErrorResult& aRv) { // Step 1. If !IsReadableStreamLocked(this) is true, return a promise rejected // with a TypeError exception. if (IsReadableStreamLocked(this)) {
aRv.ThrowTypeError("Cannot pipe from a locked stream."); return nullptr;
}
// Step 2. If !IsWritableStreamLocked(destination) is true, return a promise // rejected with a TypeError exception. if (IsWritableStreamLocked(&aDestination)) {
aRv.ThrowTypeError("Cannot pipe to a locked stream."); return nullptr;
}
// Step 3. Let signal be options["signal"] if it exists, or undefined // otherwise.
RefPtr<AbortSignal> signal =
aOptions.mSignal.WasPassed() ? &aOptions.mSignal.Value() : nullptr;
// Step 2. Set iterator’s reader to reader.
aData.mReader = reader;
// Step 3. Let preventCancel be args[0]["preventCancel"]. // Step 4. Set iterator’s prevent cancel to preventCancel.
aData.mPreventCancel = aOptions.mPreventCancel;
}
// Step 2. Assert: reader.[[stream]] is not undefined.
MOZ_ASSERT(reader->GetStream());
// Step 3. Assert: reader.[[readRequests]] is empty, as the async iterator // machinery guarantees that any previous calls to next() have settled before // this is called.
MOZ_ASSERT(reader->ReadRequests().isEmpty());
// Step 4. If iterator’s prevent cancel is false: if (!aIterator->Data().mPreventCancel) { // Step 4.1. Let result be ! ReadableStreamReaderGenericCancel(reader, arg).
RefPtr<ReadableStream> stream(reader->GetStream());
RefPtr<Promise> result = ReadableStreamCancel(aCx, stream, aValue, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr;
}
MOZ_DIAGNOSTIC_ASSERT(
reader->GetStream(), "We shouldn't have a null stream here (bug 1821169)."); if (!reader->GetStream()) {
aRv.Throw(NS_ERROR_FAILURE); return nullptr;
}
// Step 3. Let controller be a new ReadableByteStreamController.
RefPtr<ReadableByteStreamController> controller = new ReadableByteStreamController(aGlobal);
// https://streams.spec.whatwg.org/#readablestream-set-up // (except this instead creates a new ReadableStream rather than accepting an // existing instance) // _BOUNDARY because `aAlgorithms->StartCallback` (called by // SetUpReadableStreamDefaultController below) should not be able to run script // in this case.
MOZ_CAN_RUN_SCRIPT_BOUNDARY already_AddRefed<ReadableStream>
ReadableStream::CreateNative(JSContext* aCx, nsIGlobalObject* aGlobal,
UnderlyingSourceAlgorithmsWrapper& aAlgorithms,
mozilla::Maybe<double> aHighWaterMark,
QueuingStrategySize* aSizeAlgorithm,
ErrorResult& aRv) { // an optional number highWaterMark (default 1) double highWaterMark = aHighWaterMark.valueOr(1); // and if given, highWaterMark must be a non-negative, non-NaN number.
MOZ_ASSERT(IsNonNegativeNumber(highWaterMark));
// Step 1: Let startAlgorithm be an algorithm that returns undefined. // Step 2: Let pullAlgorithmWrapper be an algorithm that runs these steps: // Step 3: Let cancelAlgorithmWrapper be an algorithm that runs these steps: // (Done by UnderlyingSourceAlgorithmsWrapper)
// Step 4: If sizeAlgorithm was not given, then set it to an algorithm that // returns 1. (Callers will treat nullptr as such, see // ReadableStream::Constructor for details)
// https://streams.spec.whatwg.org/#readablestream-set-up-with-byte-reading-support // _BOUNDARY because `aAlgorithms->StartCallback` (called by // SetUpReadableByteStreamController below) should not be able to run script in // this case.
MOZ_CAN_RUN_SCRIPT_BOUNDARY void ReadableStream::SetUpByteNative(
JSContext* aCx, UnderlyingSourceAlgorithmsWrapper& aAlgorithms,
mozilla::Maybe<double> aHighWaterMark, ErrorResult& aRv) { // an optional number highWaterMark (default 0) double highWaterMark = aHighWaterMark.valueOr(0); // and if given, highWaterMark must be a non-negative, non-NaN number.
MOZ_ASSERT(IsNonNegativeNumber(highWaterMark));
// Step 1: Let startAlgorithm be an algorithm that returns undefined. // Step 2: Let pullAlgorithmWrapper be an algorithm that runs these steps: // Step 3: Let cancelAlgorithmWrapper be an algorithm that runs these steps: // (Done by UnderlyingSourceAlgorithmsWrapper)
// Step 4: Perform ! InitializeReadableStream(stream). // (Covered by constructor)
// Step 5: Let controller be a new ReadableByteStreamController. auto controller = MakeRefPtr<ReadableByteStreamController>(GetParentObject());
// Step 2.2: Assert: chunk is an ArrayBufferView.
MOZ_ASSERT(aChunk.isObject() &&
JS_IsArrayBufferViewObject(&aChunk.toObject()));
JS::Rooted<JSObject*> chunk(aCx, &aChunk.toObject());
// Step 3: Let byobView be the current BYOB request view for stream.
JS::Rooted<JSObject*> byobView(aCx);
CurrentBYOBRequestView(aCx, *controller, &byobView, aRv); if (aRv.Failed()) { return;
}
// Step 4: If byobView is non-null, and chunk.[[ViewedArrayBuffer]] is // byobView.[[ViewedArrayBuffer]], then: if (byobView && HasSameBufferView(aCx, chunk, byobView, aRv)) { // Step 4.1: Assert: chunk.[[ByteOffset]] is byobView.[[ByteOffset]].
MOZ_ASSERT(JS_GetArrayBufferViewByteOffset(chunk) ==
JS_GetArrayBufferViewByteOffset(byobView)); // Step 4.2: Assert: chunk.[[ByteLength]] ≤ byobView.[[ByteLength]].
MOZ_ASSERT(JS_GetArrayBufferViewByteLength(chunk) <=
JS_GetArrayBufferViewByteLength(byobView)); // Step 4.3: Perform ? // ReadableByteStreamControllerRespond(stream.[[controller]], // chunk.[[ByteLength]]).
ReadableByteStreamControllerRespond(
aCx, controller, JS_GetArrayBufferViewByteLength(chunk), aRv); return;
}
// https://streams.spec.whatwg.org/#readablestream-get-a-reader // To get a reader for a ReadableStream stream, return ? // AcquireReadableStreamDefaultReader(stream). The result will be a // ReadableStreamDefaultReader.
already_AddRefed<mozilla::dom::ReadableStreamDefaultReader>
ReadableStream::GetReader(ErrorResult& aRv) { return AcquireReadableStreamDefaultReader(this, aRv);
}
} // namespace mozilla::dom
¤ Dauer der Verarbeitung: 0.64 Sekunden
(vorverarbeitet)
¤