void FetchStreamReader::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) { if (mStreamClosed) { return; } // This promise should be resolved with { done: boolean, value: something }, // "value" is interesting only if done is false. // We don't want to play with JS api, let's WebIDL bindings doing it for us. // FetchReadableStreamReadDataDone is a dictionary with just a boolean, if the // parsing succeeded, we can proceed with the parsing of the "value", which it // must be a Uint8Array. FetchReadableStreamReadDataDone valueDone; if (!valueDone.Init(aCx, aValue)) { JS_ClearPendingException(aCx); CloseAndRelease(aCx, NS_ERROR_DOM_INVALID_STATE_ERR); return; } if (valueDone.mDone) { // Stream is completed. CloseAndRelease(aCx, NS_BASE_STREAM_CLOSED); return; } UniquePtr<FetchReadableStreamReadDataArray> value( new FetchReadableStreamReadDataArray); if (!value->Init(aCx, aValue) || !value->mValue.WasPassed()) { JS_ClearPendingException(aCx); CloseAndRelease(aCx, NS_ERROR_DOM_INVALID_STATE_ERR); return; } Uint8Array& array = value->mValue.Value(); array.ComputeLengthAndData(); uint32_t len = array.Length(); if (len == 0) { // If there is nothing to read, let's do another reading. OnOutputStreamReady(mPipeOut); return; } MOZ_DIAGNOSTIC_ASSERT(!mBuffer); mBuffer = Move(value); mBufferOffset = 0; mBufferRemaining = len; nsresult rv = WriteBuffer(); if (NS_FAILED(rv)) { // DOMException only understands errors from domerr.msg, so we normalize to // identifying an abort if the write fails. CloseAndRelease(aCx, NS_ERROR_DOM_ABORT_ERR); } }
void FetchStreamReader::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) { ReportErrorToConsole(aCx, aValue); CloseAndRelease(aCx, NS_ERROR_FAILURE); }
void FetchStreamReader::StartConsuming(JSContext* aCx, JS::HandleObject aStream, JS::MutableHandle<JSObject*> aReader, ErrorResult& aRv) { MOZ_DIAGNOSTIC_ASSERT(!mReader); MOZ_DIAGNOSTIC_ASSERT(aStream); JS::Rooted<JSObject*> reader(aCx, JS::ReadableStreamGetReader(aCx, aStream, JS::ReadableStreamReaderMode::Default)); if (!reader) { aRv.StealExceptionFromJSContext(aCx); CloseAndRelease(aCx, NS_ERROR_DOM_INVALID_STATE_ERR); return; } mReader = reader; aReader.set(reader); aRv = mPipeOut->AsyncWait(this, 0, 0, mOwningEventTarget); if (NS_WARN_IF(aRv.Failed())) { return; } }
void FetchStreamReader::StartConsuming(JSContext* aCx, JS::HandleObject aStream, JS::MutableHandle<JSObject*> aReader, ErrorResult& aRv) { MOZ_DIAGNOSTIC_ASSERT(!mReader); MOZ_DIAGNOSTIC_ASSERT(aStream); aRv.MightThrowJSException(); // Here, by spec, we can pick any global we want. Just to avoid extra // cross-compartment steps, we want to create the reader in the same // compartment of the owning Fetch Body object. // The same global will be used to retrieve data from this reader. JSAutoRealm ar(aCx, mGlobal->GetGlobalJSObject()); JS::Rooted<JSObject*> reader( aCx, JS::ReadableStreamGetReader(aCx, aStream, JS::ReadableStreamReaderMode::Default)); if (!reader) { aRv.StealExceptionFromJSContext(aCx); CloseAndRelease(aCx, NS_ERROR_DOM_INVALID_STATE_ERR); return; } mReader = reader; aReader.set(reader); aRv = mPipeOut->AsyncWait(this, 0, 0, mOwningEventTarget); if (NS_WARN_IF(aRv.Failed())) { return; } }
NS_IMETHODIMP FetchStreamReader::OnOutputStreamReady(nsIAsyncOutputStream* aStream) { NS_ASSERT_OWNINGTHREAD(FetchStreamReader); MOZ_ASSERT(aStream == mPipeOut); MOZ_ASSERT(mReader); if (mStreamClosed) { return NS_OK; } if (mBuffer) { return WriteBuffer(); } // TODO: We need to verify this is the correct global per the spec. // See bug 1385890. AutoEntryScript aes(mGlobal, "ReadableStreamReader.read", !mWorkerHolder); JS::Rooted<JSObject*> reader(aes.cx(), mReader); JS::Rooted<JSObject*> promise(aes.cx(), JS::ReadableStreamDefaultReaderRead(aes.cx(), reader)); if (NS_WARN_IF(!promise)) { // Let's close the stream. CloseAndRelease(aes.cx(), NS_ERROR_DOM_INVALID_STATE_ERR); return NS_ERROR_FAILURE; } RefPtr<Promise> domPromise = Promise::CreateFromExisting(mGlobal, promise); if (NS_WARN_IF(!domPromise)) { // Let's close the stream. CloseAndRelease(aes.cx(), NS_ERROR_DOM_INVALID_STATE_ERR); return NS_ERROR_FAILURE; } // Let's wait. domPromise->AppendNativeHandler(this); return NS_OK; }
NS_IMETHODIMP FetchStreamReader::OnOutputStreamReady(nsIAsyncOutputStream* aStream) { NS_ASSERT_OWNINGTHREAD(FetchStreamReader); MOZ_ASSERT(aStream == mPipeOut); MOZ_ASSERT(mReader); if (mStreamClosed) { return NS_OK; } if (mBuffer) { return WriteBuffer(); } // Here we can retrieve data from the reader using any global we want because // it is not observable. We want to use the reader's global, which is also the // Response's one. AutoEntryScript aes(mGlobal, "ReadableStreamReader.read", !mWorkerRef); JS::Rooted<JSObject*> reader(aes.cx(), mReader); JS::Rooted<JSObject*> promise( aes.cx(), JS::ReadableStreamDefaultReaderRead(aes.cx(), reader)); if (NS_WARN_IF(!promise)) { // Let's close the stream. CloseAndRelease(aes.cx(), NS_ERROR_DOM_INVALID_STATE_ERR); return NS_ERROR_FAILURE; } RefPtr<Promise> domPromise = Promise::CreateFromExisting(mGlobal, promise); if (NS_WARN_IF(!domPromise)) { // Let's close the stream. CloseAndRelease(aes.cx(), NS_ERROR_DOM_INVALID_STATE_ERR); return NS_ERROR_FAILURE; } // Let's wait. domPromise->AppendNativeHandler(this); return NS_OK; }