372static std::optional<Vector<uint8_t>> tryAllocate(JSC::ExecState* exec, JSC::JSPromiseDeferred* promise, const char* data, size_t byteSize)
373{
374 Vector<uint8_t> arrayBuffer;
375 if (!arrayBuffer.tryReserveCapacity(byteSize)) {
376 promise->reject(exec, createOutOfMemoryError(exec));
377 return std::nullopt;
378 }
379
380 arrayBuffer.grow(byteSize);
381 memcpy(arrayBuffer.data(), data, byteSize);
382
383 return arrayBuffer;
384}
385
386static bool isResponseCorrect(JSC::ExecState* exec, FetchResponse* inputResponse, JSC::JSPromiseDeferred* promise)
387{
388 bool isResponseCorsSameOrigin = inputResponse->type() == ResourceResponse::Type::Basic || inputResponse->type() == ResourceResponse::Type::Cors || inputResponse->type() == ResourceResponse::Type::Default;
389
390 if (!isResponseCorsSameOrigin) {
391 promise->reject(exec, createTypeError(exec, ASCIILiteral("Response is not CORS-same-origin")));
392 return false;
393 }
394
395 if (!inputResponse->ok()) {
396 promise->reject(exec, createTypeError(exec, ASCIILiteral("Response has not returned OK status")));
397 return false;
398 }
399
400 auto contentType = inputResponse->headers().fastGet(HTTPHeaderName::ContentType);
401 if (!equalLettersIgnoringASCIICase(contentType, "application/wasm")) {
402 promise->reject(exec, createTypeError(exec, ASCIILiteral("Unexpected response MIME type. Expected 'application/wasm'")));
403 return false;
404 }
405
406 return true;
407}
408
409void JSDOMWindowBase::compileStreaming(JSC::JSGlobalObject* globalObject, JSC::ExecState* exec, JSC::JSPromiseDeferred* promise, JSC::JSValue source)
410{
411 handleStreamingAction(globalObject, exec, promise, source, [promise] (JSC::ExecState* exec, const char* data, size_t byteSize) mutable {
412 if (auto arrayBuffer = tryAllocate(exec, promise, data, byteSize))
413 JSC::WebAssemblyPrototype::webAssemblyModuleValidateAsync(exec, promise, WTFMove(*arrayBuffer));
414 });
415}
416
417void JSDOMWindowBase::instantiateStreaming(JSC::JSGlobalObject* globalObject, JSC::ExecState* exec, JSC::JSPromiseDeferred* promise, JSC::JSValue source, JSC::JSObject* importedObject)
418{
419 handleStreamingAction(globalObject, exec, promise, source, [promise, importedObject] (JSC::ExecState* exec, const char* data, size_t byteSize) mutable {
420 if (auto arrayBuffer = tryAllocate(exec, promise, data, byteSize))
421 JSC::WebAssemblyPrototype::webAssemblyModuleInstantinateAsync(exec, promise, WTFMove(*arrayBuffer), importedObject);
422 });
423}
424
425void JSDOMWindowBase::handleStreamingAction(JSC::JSGlobalObject* globalObject, JSC::ExecState* exec, JSC::JSPromiseDeferred* promise, JSC::JSValue source, std::function<void(JSC::ExecState* exec, const char* data, size_t byteSize)>&& callback)
426{
427 ASSERT(source);
428 VM& vm = exec->vm();
429 if (auto inputResponse = JSFetchResponse::toWrapped(vm, source))
430 handleResponseOnStreamingAction(globalObject, exec, inputResponse, promise, WTFMove(callback));
431 else
432 promise->reject(exec, createTypeError(exec, ASCIILiteral("first argument must be an Response or Promise for Response")));
433}
434
435void JSDOMWindowBase::handleResponseOnStreamingAction(JSC::JSGlobalObject* globalObject, JSC::ExecState* exec, FetchResponse* inputResponse, JSC::JSPromiseDeferred* promise, std::function<void(JSC::ExecState* exec, const char* data, size_t byteSize)>&& actionCallback)
436{
437 if (!isResponseCorrect(exec, inputResponse, promise))
438 return;
439
440 if (inputResponse->isBodyReceivedByChunk()) {
441 inputResponse->consumeBodyReceivedByChunk([promise, callback = WTFMove(actionCallback), globalObject, data = SharedBuffer::create()] (auto&& result) mutable {
442 ExecState* exec = globalObject->globalExec();
443 if (result.hasException()) {
444 promise->reject(exec, createTypeError(exec, result.exception().message()));
445 return;
446 }
447
448 if (auto chunk = result.returnValue())
449 data->append(reinterpret_cast<const char*>(chunk->data), chunk->size);
450 else {
451 VM& vm = exec->vm();
452 JSLockHolder lock(vm);
453
454 callback(exec, data->data(), data->size());
455 }
456 });
457 return;
458 }
459
460 auto body = inputResponse->consumeBody();
461 WTF::switchOn(body, [&] (Ref<FormData>& formData) {
462 if (auto buffer = formData->asSharedBuffer()) {
463 VM& vm = exec->vm();
464 JSLockHolder lock(vm);
465
466 auto callback = WTFMove(actionCallback);
467 callback(exec, buffer->data(), buffer->size());
468 return;
469 }
470 // FIXME: http://webkit.org/b/184886> Implement loading for the Blob type
471 promise->reject(exec, createTypeError(exec, ASCIILiteral("Unexpected Response's Content-type")));
472 }, [&] (Ref<SharedBuffer>& buffer) {
473 VM& vm = exec->vm();
474 JSLockHolder lock(vm);
475
476 auto callback = WTFMove(actionCallback);
477 callback(exec, buffer->data(), buffer->size());
478 }, [&] (std::nullptr_t&) {
479 promise->reject(exec, createTypeError(exec, ASCIILiteral("Unexpected Response's Content-type")));
480 });
481}
482