mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
Compare commits
3 Commits
bun-v1.3.4
...
claude/xml
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa88639fe5 | ||
|
|
214987bfe2 | ||
|
|
adc5d148f3 |
@@ -131,6 +131,7 @@
|
||||
#include "JSWebSocket.h"
|
||||
#include "JSWorker.h"
|
||||
#include "JSWritableStream.h"
|
||||
#include "JSXMLHttpRequest.h"
|
||||
#include "JSWritableStreamDefaultController.h"
|
||||
#include "JSWritableStreamDefaultWriter.h"
|
||||
#include "libusockets.h"
|
||||
@@ -1510,6 +1511,7 @@ WEBCORE_GENERATED_CONSTRUCTOR_GETTER(TransformStreamDefaultController)
|
||||
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(URLSearchParams);
|
||||
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(WebSocket);
|
||||
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(Worker);
|
||||
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(XMLHttpRequest);
|
||||
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(WritableStream);
|
||||
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(WritableStreamDefaultController);
|
||||
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(WritableStreamDefaultWriter);
|
||||
|
||||
@@ -337,6 +337,7 @@ public:
|
||||
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForSQLTransaction;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForCloseEvent;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForWebSocket;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForXMLHttpRequest;
|
||||
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForWebXRBoundedReferenceSpace;
|
||||
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForWebXRFrame;
|
||||
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForWebXRHand;
|
||||
|
||||
@@ -320,6 +320,7 @@ public:
|
||||
// std::unique_ptr<IsoSubspace> m_subspaceForSQLTransaction;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForCloseEvent;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForWebSocket;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForXMLHttpRequest;
|
||||
// std::unique_ptr<IsoSubspace> m_subspaceForWebXRBoundedReferenceSpace;
|
||||
// std::unique_ptr<IsoSubspace> m_subspaceForWebXRFrame;
|
||||
// std::unique_ptr<IsoSubspace> m_subspaceForWebXRHand;
|
||||
|
||||
@@ -38,7 +38,8 @@ namespace WebCore {
|
||||
macro(message) \
|
||||
macro(change) \
|
||||
macro(messageerror) \
|
||||
macro(resourcetimingbufferfull)
|
||||
macro(resourcetimingbufferfull) \
|
||||
macro(readystatechange)
|
||||
|
||||
struct EventNames {
|
||||
WTF_MAKE_NONCOPYABLE(EventNames);
|
||||
|
||||
819
src/bun.js/bindings/webcore/JSXMLHttpRequest.cpp
Normal file
819
src/bun.js/bindings/webcore/JSXMLHttpRequest.cpp
Normal file
@@ -0,0 +1,819 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2012 Google Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of Apple Inc. ("Apple") nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "JSXMLHttpRequest.h"
|
||||
|
||||
#include "ActiveDOMObject.h"
|
||||
#include "EventNames.h"
|
||||
#include "ExtendedDOMClientIsoSubspaces.h"
|
||||
#include "ExtendedDOMIsoSubspaces.h"
|
||||
#include "IDLTypes.h"
|
||||
#include "JSDOMAttribute.h"
|
||||
#include "JSDOMBinding.h"
|
||||
#include "JSDOMConstructor.h"
|
||||
#include "JSDOMConvertBase.h"
|
||||
#include "JSDOMConvertBufferSource.h"
|
||||
#include "JSDOMConvertInterface.h"
|
||||
#include "JSDOMConvertNullable.h"
|
||||
#include "JSDOMConvertNumbers.h"
|
||||
#include "JSDOMConvertStrings.h"
|
||||
#include "JSDOMConvertBoolean.h"
|
||||
#include "JSDOMExceptionHandling.h"
|
||||
#include "JSDOMGlobalObjectInlines.h"
|
||||
#include "JSDOMOperation.h"
|
||||
#include "JSDOMWrapperCache.h"
|
||||
#include "JSEventListener.h"
|
||||
#include "ScriptExecutionContext.h"
|
||||
#include "WebCoreJSClientData.h"
|
||||
#include <JavaScriptCore/HeapAnalyzer.h>
|
||||
#include <JavaScriptCore/JSCInlines.h>
|
||||
#include <JavaScriptCore/JSDestructibleObjectHeapCellType.h>
|
||||
#include <JavaScriptCore/SlotVisitorMacros.h>
|
||||
#include <JavaScriptCore/SubspaceInlines.h>
|
||||
#include <wtf/GetPtr.h>
|
||||
#include <wtf/PointerPreparations.h>
|
||||
#include <wtf/URL.h>
|
||||
// Forward declarations instead of includes for now
|
||||
// These will be properly included once the classes are generated
|
||||
namespace WebCore {
|
||||
class JSBlob;
|
||||
class JSDOMFormData;
|
||||
class JSURLSearchParams;
|
||||
}
|
||||
#include "JSXMLHttpRequestUpload.h"
|
||||
|
||||
namespace WebCore {
|
||||
using namespace JSC;
|
||||
|
||||
// Functions
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsXMLHttpRequestPrototypeFunction_open);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsXMLHttpRequestPrototypeFunction_setRequestHeader);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsXMLHttpRequestPrototypeFunction_send);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsXMLHttpRequestPrototypeFunction_abort);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsXMLHttpRequestPrototypeFunction_getResponseHeader);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsXMLHttpRequestPrototypeFunction_getAllResponseHeaders);
|
||||
static JSC_DECLARE_HOST_FUNCTION(jsXMLHttpRequestPrototypeFunction_overrideMimeType);
|
||||
|
||||
// Function body declarations
|
||||
static inline JSC::EncodedJSValue jsXMLHttpRequestPrototypeFunction_openBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSXMLHttpRequest>::ClassParameter castedThis);
|
||||
static inline JSC::EncodedJSValue jsXMLHttpRequestPrototypeFunction_setRequestHeaderBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSXMLHttpRequest>::ClassParameter castedThis);
|
||||
static inline JSC::EncodedJSValue jsXMLHttpRequestPrototypeFunction_sendBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSXMLHttpRequest>::ClassParameter castedThis);
|
||||
static inline JSC::EncodedJSValue jsXMLHttpRequestPrototypeFunction_abortBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSXMLHttpRequest>::ClassParameter castedThis);
|
||||
static inline JSC::EncodedJSValue jsXMLHttpRequestPrototypeFunction_getResponseHeaderBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSXMLHttpRequest>::ClassParameter castedThis);
|
||||
static inline JSC::EncodedJSValue jsXMLHttpRequestPrototypeFunction_getAllResponseHeadersBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSXMLHttpRequest>::ClassParameter castedThis);
|
||||
static inline JSC::EncodedJSValue jsXMLHttpRequestPrototypeFunction_overrideMimeTypeBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSXMLHttpRequest>::ClassParameter castedThis);
|
||||
|
||||
// Attributes
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsXMLHttpRequestConstructor);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsXMLHttpRequest_readyState);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsXMLHttpRequest_status);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsXMLHttpRequest_statusText);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsXMLHttpRequest_responseText);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsXMLHttpRequest_responseURL);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsXMLHttpRequest_response);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsXMLHttpRequest_responseType);
|
||||
static JSC_DECLARE_CUSTOM_SETTER(setJSXMLHttpRequest_responseType);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsXMLHttpRequest_timeout);
|
||||
static JSC_DECLARE_CUSTOM_SETTER(setJSXMLHttpRequest_timeout);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsXMLHttpRequest_withCredentials);
|
||||
static JSC_DECLARE_CUSTOM_SETTER(setJSXMLHttpRequest_withCredentials);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsXMLHttpRequest_upload);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsXMLHttpRequest_onreadystatechange);
|
||||
static JSC_DECLARE_CUSTOM_SETTER(setJSXMLHttpRequest_onreadystatechange);
|
||||
|
||||
class JSXMLHttpRequestPrototype final : public JSC::JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
static JSXMLHttpRequestPrototype* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
JSXMLHttpRequestPrototype* ptr = new (NotNull, JSC::allocateCell<JSXMLHttpRequestPrototype>(vm)) JSXMLHttpRequestPrototype(vm, globalObject, structure);
|
||||
ptr->finishCreation(vm);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSXMLHttpRequestPrototype, Base);
|
||||
return &vm.plainObjectSpace();
|
||||
}
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
}
|
||||
|
||||
private:
|
||||
JSXMLHttpRequestPrototype(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure)
|
||||
: JSC::JSNonFinalObject(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&);
|
||||
};
|
||||
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSXMLHttpRequestPrototype, JSXMLHttpRequestPrototype::Base);
|
||||
|
||||
using JSXMLHttpRequestDOMConstructor = JSDOMConstructor<JSXMLHttpRequest>;
|
||||
|
||||
template<> const ClassInfo JSXMLHttpRequestDOMConstructor::s_info = { "XMLHttpRequest"_s, nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(JSXMLHttpRequestDOMConstructor) };
|
||||
|
||||
/* Hash table for constructor */
|
||||
static const HashTableValue JSXMLHttpRequestConstructorTableValues[] = {
|
||||
{ "UNSENT"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { HashTableValue::ConstantType, 0 } },
|
||||
{ "OPENED"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { HashTableValue::ConstantType, 1 } },
|
||||
{ "HEADERS_RECEIVED"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { HashTableValue::ConstantType, 2 } },
|
||||
{ "LOADING"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { HashTableValue::ConstantType, 3 } },
|
||||
{ "DONE"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { HashTableValue::ConstantType, 4 } },
|
||||
};
|
||||
|
||||
static_assert(XMLHttpRequest::UNSENT == 0, "UNSENT in XMLHttpRequest does not match value from IDL");
|
||||
static_assert(XMLHttpRequest::OPENED == 1, "OPENED in XMLHttpRequest does not match value from IDL");
|
||||
static_assert(XMLHttpRequest::HEADERS_RECEIVED == 2, "HEADERS_RECEIVED in XMLHttpRequest does not match value from IDL");
|
||||
static_assert(XMLHttpRequest::LOADING == 3, "LOADING in XMLHttpRequest does not match value from IDL");
|
||||
static_assert(XMLHttpRequest::DONE == 4, "DONE in XMLHttpRequest does not match value from IDL");
|
||||
|
||||
static inline JSC::EncodedJSValue constructJSXMLHttpRequest(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame)
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* castedThis = jsCast<JSXMLHttpRequestDOMConstructor*>(callFrame->jsCallee());
|
||||
auto* context = castedThis->scriptExecutionContext();
|
||||
if (!context)
|
||||
return throwConstructorScriptExecutionContextUnavailableError(*lexicalGlobalObject, throwScope, "XMLHttpRequest");
|
||||
auto object = XMLHttpRequest::create(*context);
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
auto jsValue = toJSNewlyCreated<IDLInterface<XMLHttpRequest>>(*lexicalGlobalObject, *castedThis->globalObject(), throwScope, WTFMove(object));
|
||||
RETURN_IF_EXCEPTION(throwScope, { });
|
||||
setSubclassStructureIfNeeded<XMLHttpRequest>(lexicalGlobalObject, callFrame, asObject(jsValue));
|
||||
return JSValue::encode(jsValue);
|
||||
}
|
||||
|
||||
/* Hash table for prototype */
|
||||
static const HashTableValue JSXMLHttpRequestPrototypeTableValues[] = {
|
||||
{ "constructor"_s, static_cast<unsigned>(JSC::PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsXMLHttpRequestConstructor, 0 } },
|
||||
{ "readyState"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsXMLHttpRequest_readyState, 0 } },
|
||||
{ "status"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsXMLHttpRequest_status, 0 } },
|
||||
{ "statusText"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsXMLHttpRequest_statusText, 0 } },
|
||||
{ "responseText"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsXMLHttpRequest_responseText, 0 } },
|
||||
{ "responseURL"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsXMLHttpRequest_responseURL, 0 } },
|
||||
{ "response"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsXMLHttpRequest_response, 0 } },
|
||||
{ "responseType"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsXMLHttpRequest_responseType, setJSXMLHttpRequest_responseType } },
|
||||
{ "timeout"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsXMLHttpRequest_timeout, setJSXMLHttpRequest_timeout } },
|
||||
{ "withCredentials"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsXMLHttpRequest_withCredentials, setJSXMLHttpRequest_withCredentials } },
|
||||
{ "upload"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsXMLHttpRequest_upload, 0 } },
|
||||
{ "onreadystatechange"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsXMLHttpRequest_onreadystatechange, setJSXMLHttpRequest_onreadystatechange } },
|
||||
{ "open"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsXMLHttpRequestPrototypeFunction_open, 2 } },
|
||||
{ "setRequestHeader"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsXMLHttpRequestPrototypeFunction_setRequestHeader, 2 } },
|
||||
{ "send"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsXMLHttpRequestPrototypeFunction_send, 0 } },
|
||||
{ "abort"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsXMLHttpRequestPrototypeFunction_abort, 0 } },
|
||||
{ "getResponseHeader"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsXMLHttpRequestPrototypeFunction_getResponseHeader, 1 } },
|
||||
{ "getAllResponseHeaders"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsXMLHttpRequestPrototypeFunction_getAllResponseHeaders, 0 } },
|
||||
{ "overrideMimeType"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsXMLHttpRequestPrototypeFunction_overrideMimeType, 1 } },
|
||||
// Constants
|
||||
{ "UNSENT"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { HashTableValue::ConstantType, 0 } },
|
||||
{ "OPENED"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { HashTableValue::ConstantType, 1 } },
|
||||
{ "HEADERS_RECEIVED"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { HashTableValue::ConstantType, 2 } },
|
||||
{ "LOADING"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { HashTableValue::ConstantType, 3 } },
|
||||
{ "DONE"_s, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::ConstantInteger, NoIntrinsic, { HashTableValue::ConstantType, 4 } },
|
||||
};
|
||||
|
||||
const ClassInfo JSXMLHttpRequestPrototype::s_info = { "XMLHttpRequest"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSXMLHttpRequestPrototype) };
|
||||
|
||||
void JSXMLHttpRequestPrototype::finishCreation(VM& vm)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
reifyStaticProperties(vm, JSXMLHttpRequest::info(), JSXMLHttpRequestPrototypeTableValues, *this);
|
||||
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
|
||||
}
|
||||
|
||||
const ClassInfo JSXMLHttpRequest::s_info = { "XMLHttpRequest"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSXMLHttpRequest) };
|
||||
|
||||
JSXMLHttpRequest::JSXMLHttpRequest(Structure* structure, JSDOMGlobalObject& globalObject, Ref<XMLHttpRequest>&& impl)
|
||||
: JSEventTarget(structure, globalObject, WTFMove(impl))
|
||||
{
|
||||
}
|
||||
|
||||
void JSXMLHttpRequest::finishCreation(VM& vm)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
ASSERT(inherits(info()));
|
||||
}
|
||||
|
||||
JSObject* JSXMLHttpRequest::createPrototype(VM& vm, JSDOMGlobalObject& globalObject)
|
||||
{
|
||||
return JSXMLHttpRequestPrototype::create(vm, &globalObject, JSXMLHttpRequestPrototype::createStructure(vm, &globalObject, JSEventTarget::prototype(vm, globalObject)));
|
||||
}
|
||||
|
||||
JSObject* JSXMLHttpRequest::prototype(VM& vm, JSDOMGlobalObject& globalObject)
|
||||
{
|
||||
return getDOMPrototype<JSXMLHttpRequest>(vm, globalObject);
|
||||
}
|
||||
|
||||
JSValue JSXMLHttpRequest::getConstructor(VM& vm, const JSGlobalObject* globalObject)
|
||||
{
|
||||
return getDOMConstructor<JSXMLHttpRequestDOMConstructor, DOMConstructorID::XMLHttpRequest>(vm, *jsCast<const JSDOMGlobalObject*>(globalObject));
|
||||
}
|
||||
|
||||
void JSXMLHttpRequest::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer)
|
||||
{
|
||||
auto* thisObject = jsCast<JSXMLHttpRequest*>(cell);
|
||||
analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped());
|
||||
Base::analyzeHeap(cell, analyzer);
|
||||
}
|
||||
|
||||
// Attribute getters
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsXMLHttpRequestConstructor, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
return JSValue::encode(JSXMLHttpRequest::getConstructor(JSC::getVM(lexicalGlobalObject), lexicalGlobalObject));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsXMLHttpRequest_readyState, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSXMLHttpRequest*>(JSValue::decode(thisValue));
|
||||
if (!thisObject)
|
||||
return JSValue::encode(jsUndefined());
|
||||
auto& impl = thisObject->wrapped();
|
||||
RELEASE_AND_RETURN(throwScope, JSValue::encode(jsNumber(static_cast<int>(impl.readyState()))));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsXMLHttpRequest_status, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSXMLHttpRequest*>(JSValue::decode(thisValue));
|
||||
if (!thisObject)
|
||||
return JSValue::encode(jsUndefined());
|
||||
auto& impl = thisObject->wrapped();
|
||||
RELEASE_AND_RETURN(throwScope, JSValue::encode(jsNumber(impl.status())));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsXMLHttpRequest_statusText, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSXMLHttpRequest*>(JSValue::decode(thisValue));
|
||||
if (!thisObject)
|
||||
return JSValue::encode(jsUndefined());
|
||||
auto& impl = thisObject->wrapped();
|
||||
RELEASE_AND_RETURN(throwScope, JSValue::encode(jsStringWithCache(vm, impl.statusText())));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsXMLHttpRequest_responseText, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSXMLHttpRequest*>(JSValue::decode(thisValue));
|
||||
if (!thisObject)
|
||||
return JSValue::encode(jsUndefined());
|
||||
auto& impl = thisObject->wrapped();
|
||||
RELEASE_AND_RETURN(throwScope, JSValue::encode(jsStringWithCache(vm, impl.responseText())));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsXMLHttpRequest_responseURL, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSXMLHttpRequest*>(JSValue::decode(thisValue));
|
||||
if (!thisObject)
|
||||
return JSValue::encode(jsUndefined());
|
||||
auto& impl = thisObject->wrapped();
|
||||
RELEASE_AND_RETURN(throwScope, JSValue::encode(jsStringWithCache(vm, impl.responseURL())));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsXMLHttpRequest_response, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSXMLHttpRequest*>(JSValue::decode(thisValue));
|
||||
if (!thisObject)
|
||||
return JSValue::encode(jsUndefined());
|
||||
auto& impl = thisObject->wrapped();
|
||||
RELEASE_AND_RETURN(throwScope, JSValue::encode(impl.response(&*lexicalGlobalObject)));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsXMLHttpRequest_responseType, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSXMLHttpRequest*>(JSValue::decode(thisValue));
|
||||
if (!thisObject)
|
||||
return JSValue::encode(jsUndefined());
|
||||
auto& impl = thisObject->wrapped();
|
||||
|
||||
// Convert ResponseType enum to string
|
||||
String responseTypeString;
|
||||
switch (impl.responseType()) {
|
||||
case XMLHttpRequest::ResponseType::Empty:
|
||||
responseTypeString = ""_s;
|
||||
break;
|
||||
case XMLHttpRequest::ResponseType::ArrayBuffer:
|
||||
responseTypeString = "arraybuffer"_s;
|
||||
break;
|
||||
case XMLHttpRequest::ResponseType::Blob:
|
||||
responseTypeString = "blob"_s;
|
||||
break;
|
||||
case XMLHttpRequest::ResponseType::Document:
|
||||
responseTypeString = "document"_s;
|
||||
break;
|
||||
case XMLHttpRequest::ResponseType::JSON:
|
||||
responseTypeString = "json"_s;
|
||||
break;
|
||||
case XMLHttpRequest::ResponseType::Text:
|
||||
responseTypeString = "text"_s;
|
||||
break;
|
||||
}
|
||||
RELEASE_AND_RETURN(throwScope, JSValue::encode(jsStringWithCache(vm, responseTypeString)));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_SETTER(setJSXMLHttpRequest_responseType, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSXMLHttpRequest*>(JSValue::decode(thisValue));
|
||||
if (!thisObject)
|
||||
return false;
|
||||
auto& impl = thisObject->wrapped();
|
||||
|
||||
auto responseTypeString = convert<IDLDOMString>(*lexicalGlobalObject, JSValue::decode(encodedValue));
|
||||
RETURN_IF_EXCEPTION(throwScope, false);
|
||||
|
||||
XMLHttpRequest::ResponseType responseType;
|
||||
if (responseTypeString.isEmpty())
|
||||
responseType = XMLHttpRequest::ResponseType::Empty;
|
||||
else if (responseTypeString == "arraybuffer"_s)
|
||||
responseType = XMLHttpRequest::ResponseType::ArrayBuffer;
|
||||
else if (responseTypeString == "blob"_s)
|
||||
responseType = XMLHttpRequest::ResponseType::Blob;
|
||||
else if (responseTypeString == "document"_s)
|
||||
responseType = XMLHttpRequest::ResponseType::Document;
|
||||
else if (responseTypeString == "json"_s)
|
||||
responseType = XMLHttpRequest::ResponseType::JSON;
|
||||
else if (responseTypeString == "text"_s)
|
||||
responseType = XMLHttpRequest::ResponseType::Text;
|
||||
else
|
||||
return false; // Invalid value, ignore
|
||||
|
||||
auto result = impl.setResponseType(responseType);
|
||||
if (result.hasException()) {
|
||||
propagateException(*lexicalGlobalObject, throwScope, result.releaseException());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsXMLHttpRequest_timeout, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSXMLHttpRequest*>(JSValue::decode(thisValue));
|
||||
if (!thisObject)
|
||||
return JSValue::encode(jsUndefined());
|
||||
auto& impl = thisObject->wrapped();
|
||||
RELEASE_AND_RETURN(throwScope, JSValue::encode(jsNumber(impl.timeout())));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_SETTER(setJSXMLHttpRequest_timeout, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSXMLHttpRequest*>(JSValue::decode(thisValue));
|
||||
if (!thisObject)
|
||||
return false;
|
||||
auto& impl = thisObject->wrapped();
|
||||
|
||||
auto timeout = convert<IDLUnsignedLong>(*lexicalGlobalObject, JSValue::decode(encodedValue));
|
||||
RETURN_IF_EXCEPTION(throwScope, false);
|
||||
|
||||
auto result = impl.setTimeout(timeout);
|
||||
if (result.hasException()) {
|
||||
propagateException(*lexicalGlobalObject, throwScope, result.releaseException());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsXMLHttpRequest_withCredentials, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSXMLHttpRequest*>(JSValue::decode(thisValue));
|
||||
if (!thisObject)
|
||||
return JSValue::encode(jsUndefined());
|
||||
auto& impl = thisObject->wrapped();
|
||||
RELEASE_AND_RETURN(throwScope, JSValue::encode(jsBoolean(impl.withCredentials())));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_SETTER(setJSXMLHttpRequest_withCredentials, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSXMLHttpRequest*>(JSValue::decode(thisValue));
|
||||
if (!thisObject)
|
||||
return false;
|
||||
auto& impl = thisObject->wrapped();
|
||||
|
||||
auto withCredentials = convert<IDLBoolean>(*lexicalGlobalObject, JSValue::decode(encodedValue));
|
||||
RETURN_IF_EXCEPTION(throwScope, false);
|
||||
|
||||
auto result = impl.setWithCredentials(withCredentials);
|
||||
if (result.hasException()) {
|
||||
propagateException(*lexicalGlobalObject, throwScope, result.releaseException());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsXMLHttpRequest_upload, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSXMLHttpRequest*>(JSValue::decode(thisValue));
|
||||
if (!thisObject)
|
||||
return JSValue::encode(jsUndefined());
|
||||
// TODO: Return proper JSXMLHttpRequestUpload object
|
||||
RELEASE_AND_RETURN(throwScope, JSValue::encode(jsNull()));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsXMLHttpRequest_onreadystatechange, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* thisObject = jsDynamicCast<JSXMLHttpRequest*>(JSValue::decode(thisValue));
|
||||
if (!thisObject)
|
||||
return JSValue::encode(jsUndefined());
|
||||
return JSValue::encode(eventHandlerAttribute(thisObject->wrapped(), eventNames().readystatechangeEvent, worldForDOMObject(*thisObject)));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_SETTER(setJSXMLHttpRequest_onreadystatechange, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName))
|
||||
{
|
||||
auto* thisObject = jsDynamicCast<JSXMLHttpRequest*>(JSValue::decode(thisValue));
|
||||
if (!thisObject)
|
||||
return false;
|
||||
|
||||
setEventHandlerAttribute<JSEventListener>(thisObject->wrapped(), eventNames().readystatechangeEvent, JSValue::decode(encodedValue), *thisObject);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Function implementations
|
||||
JSC_DEFINE_HOST_FUNCTION(jsXMLHttpRequestPrototypeFunction_open, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* castedThis = IDLOperation<JSXMLHttpRequest>::cast(*lexicalGlobalObject, *callFrame);
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
return jsXMLHttpRequestPrototypeFunction_openBody(lexicalGlobalObject, callFrame, castedThis);
|
||||
}
|
||||
|
||||
static inline JSC::EncodedJSValue jsXMLHttpRequestPrototypeFunction_openBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSXMLHttpRequest>::ClassParameter castedThis)
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto& impl = castedThis->wrapped();
|
||||
|
||||
if (callFrame->argumentCount() < 2) {
|
||||
throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
auto method = convert<IDLDOMString>(*lexicalGlobalObject, callFrame->uncheckedArgument(0));
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
|
||||
auto url = convert<IDLDOMString>(*lexicalGlobalObject, callFrame->uncheckedArgument(1));
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
|
||||
if (callFrame->argumentCount() == 2) {
|
||||
auto result = impl.open(method, url);
|
||||
if (result.hasException()) {
|
||||
propagateException(*lexicalGlobalObject, throwScope, result.releaseException());
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
} else {
|
||||
auto async = convert<IDLBoolean>(*lexicalGlobalObject, callFrame->argument(2));
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
|
||||
auto user = callFrame->argumentCount() > 3 ? convert<IDLDOMString>(*lexicalGlobalObject, callFrame->uncheckedArgument(3)) : String();
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
|
||||
auto password = callFrame->argumentCount() > 4 ? convert<IDLDOMString>(*lexicalGlobalObject, callFrame->uncheckedArgument(4)) : String();
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
|
||||
auto result = impl.open(method, url, async, user, password);
|
||||
if (result.hasException()) {
|
||||
propagateException(*lexicalGlobalObject, throwScope, result.releaseException());
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
}
|
||||
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsXMLHttpRequestPrototypeFunction_setRequestHeader, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* castedThis = IDLOperation<JSXMLHttpRequest>::cast(*lexicalGlobalObject, *callFrame);
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
return jsXMLHttpRequestPrototypeFunction_setRequestHeaderBody(lexicalGlobalObject, callFrame, castedThis);
|
||||
}
|
||||
|
||||
static inline JSC::EncodedJSValue jsXMLHttpRequestPrototypeFunction_setRequestHeaderBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSXMLHttpRequest>::ClassParameter castedThis)
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto& impl = castedThis->wrapped();
|
||||
|
||||
if (callFrame->argumentCount() < 2) {
|
||||
throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
auto header = convert<IDLDOMString>(*lexicalGlobalObject, callFrame->uncheckedArgument(0));
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
|
||||
auto value = convert<IDLDOMString>(*lexicalGlobalObject, callFrame->uncheckedArgument(1));
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
|
||||
auto result = impl.setRequestHeader(header, value);
|
||||
if (result.hasException()) {
|
||||
propagateException(*lexicalGlobalObject, throwScope, result.releaseException());
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsXMLHttpRequestPrototypeFunction_send, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* castedThis = IDLOperation<JSXMLHttpRequest>::cast(*lexicalGlobalObject, *callFrame);
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
return jsXMLHttpRequestPrototypeFunction_sendBody(lexicalGlobalObject, callFrame, castedThis);
|
||||
}
|
||||
|
||||
static inline JSC::EncodedJSValue jsXMLHttpRequestPrototypeFunction_sendBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSXMLHttpRequest>::ClassParameter castedThis)
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto& impl = castedThis->wrapped();
|
||||
|
||||
ExceptionOr<void> result;
|
||||
|
||||
if (callFrame->argumentCount() == 0) {
|
||||
result = impl.send();
|
||||
} else {
|
||||
auto bodyValue = callFrame->uncheckedArgument(0);
|
||||
|
||||
// Try different body types
|
||||
if (bodyValue.isString()) {
|
||||
auto body = convert<IDLDOMString>(*lexicalGlobalObject, bodyValue);
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
result = impl.send(body);
|
||||
} else if (auto* arrayBuffer = jsDynamicCast<JSArrayBuffer*>(bodyValue)) {
|
||||
result = impl.send(arrayBuffer->impl());
|
||||
} else if (auto* arrayBufferView = jsDynamicCast<JSArrayBufferView*>(bodyValue)) {
|
||||
result = impl.send(arrayBufferView->unsharedImpl());
|
||||
// TODO: Enable once JSBlob, JSDOMFormData, JSURLSearchParams are available
|
||||
// } else if (auto* blob = jsDynamicCast<JSBlob*>(bodyValue)) {
|
||||
// result = impl.send(&blob->wrapped());
|
||||
// } else if (auto* formData = jsDynamicCast<JSDOMFormData*>(bodyValue)) {
|
||||
// result = impl.send(&formData->wrapped());
|
||||
// } else if (auto* urlSearchParams = jsDynamicCast<JSURLSearchParams*>(bodyValue)) {
|
||||
// result = impl.send(&urlSearchParams->wrapped());
|
||||
} else {
|
||||
// Default to empty send
|
||||
result = impl.send();
|
||||
}
|
||||
}
|
||||
|
||||
if (result.hasException()) {
|
||||
propagateException(*lexicalGlobalObject, throwScope, result.releaseException());
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsXMLHttpRequestPrototypeFunction_abort, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* castedThis = IDLOperation<JSXMLHttpRequest>::cast(*lexicalGlobalObject, *callFrame);
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
return jsXMLHttpRequestPrototypeFunction_abortBody(lexicalGlobalObject, callFrame, castedThis);
|
||||
}
|
||||
|
||||
static inline JSC::EncodedJSValue jsXMLHttpRequestPrototypeFunction_abortBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSXMLHttpRequest>::ClassParameter castedThis)
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto& impl = castedThis->wrapped();
|
||||
|
||||
impl.abort();
|
||||
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsXMLHttpRequestPrototypeFunction_getResponseHeader, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* castedThis = IDLOperation<JSXMLHttpRequest>::cast(*lexicalGlobalObject, *callFrame);
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
return jsXMLHttpRequestPrototypeFunction_getResponseHeaderBody(lexicalGlobalObject, callFrame, castedThis);
|
||||
}
|
||||
|
||||
static inline JSC::EncodedJSValue jsXMLHttpRequestPrototypeFunction_getResponseHeaderBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSXMLHttpRequest>::ClassParameter castedThis)
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto& impl = castedThis->wrapped();
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
auto name = convert<IDLDOMString>(*lexicalGlobalObject, callFrame->uncheckedArgument(0));
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
|
||||
auto result = impl.getResponseHeader(name);
|
||||
|
||||
return JSValue::encode(result.isNull() ? jsNull() : jsStringWithCache(vm, result));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsXMLHttpRequestPrototypeFunction_getAllResponseHeaders, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* castedThis = IDLOperation<JSXMLHttpRequest>::cast(*lexicalGlobalObject, *callFrame);
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
return jsXMLHttpRequestPrototypeFunction_getAllResponseHeadersBody(lexicalGlobalObject, callFrame, castedThis);
|
||||
}
|
||||
|
||||
static inline JSC::EncodedJSValue jsXMLHttpRequestPrototypeFunction_getAllResponseHeadersBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSXMLHttpRequest>::ClassParameter castedThis)
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto& impl = castedThis->wrapped();
|
||||
|
||||
auto result = impl.getAllResponseHeaders();
|
||||
|
||||
return JSValue::encode(jsStringWithCache(vm, result));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsXMLHttpRequestPrototypeFunction_overrideMimeType, (JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto* castedThis = IDLOperation<JSXMLHttpRequest>::cast(*lexicalGlobalObject, *callFrame);
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
return jsXMLHttpRequestPrototypeFunction_overrideMimeTypeBody(lexicalGlobalObject, callFrame, castedThis);
|
||||
}
|
||||
|
||||
static inline JSC::EncodedJSValue jsXMLHttpRequestPrototypeFunction_overrideMimeTypeBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSXMLHttpRequest>::ClassParameter castedThis)
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto& impl = castedThis->wrapped();
|
||||
|
||||
if (callFrame->argumentCount() < 1) {
|
||||
throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
auto mime = convert<IDLDOMString>(*lexicalGlobalObject, callFrame->uncheckedArgument(0));
|
||||
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
|
||||
|
||||
auto result = impl.overrideMimeType(mime);
|
||||
if (result.hasException()) {
|
||||
propagateException(*lexicalGlobalObject, throwScope, result.releaseException());
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
// Subspace implementation
|
||||
JSC::GCClient::IsoSubspace* JSXMLHttpRequest::subspaceForImpl(VM& vm)
|
||||
{
|
||||
return WebCore::subspaceForImpl<JSXMLHttpRequest, UseCustomHeapCellType::No>(
|
||||
vm,
|
||||
[](auto& spaces) { return spaces.m_clientSubspaceForXMLHttpRequest.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForXMLHttpRequest = std::forward<decltype(space)>(space); },
|
||||
[](auto& spaces) { return spaces.m_subspaceForXMLHttpRequest.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_subspaceForXMLHttpRequest = std::forward<decltype(space)>(space); }
|
||||
);
|
||||
}
|
||||
|
||||
size_t JSXMLHttpRequest::estimatedSize(JSCell* cell, VM& vm)
|
||||
{
|
||||
auto* thisObject = jsCast<JSXMLHttpRequest*>(cell);
|
||||
return Base::estimatedSize(thisObject, vm) + thisObject->wrapped().memoryCost();
|
||||
}
|
||||
|
||||
XMLHttpRequest* JSXMLHttpRequest::toWrapped(VM& vm, JSValue value)
|
||||
{
|
||||
if (auto* wrapper = jsDynamicCast<JSXMLHttpRequest*>(value))
|
||||
return &wrapper->wrapped();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Owner implementation
|
||||
bool JSXMLHttpRequestOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, AbstractSlotVisitor& visitor, ASCIILiteral* reason)
|
||||
{
|
||||
auto* thisObject = jsCast<JSXMLHttpRequest*>(handle.slot()->asCell());
|
||||
if (thisObject->wrapped().hasPendingActivity()) {
|
||||
if (reason)
|
||||
*reason = "XMLHttpRequest has pending activity"_s;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void JSXMLHttpRequestOwner::finalize(JSC::Handle<JSC::Unknown> handle, void* context)
|
||||
{
|
||||
auto* thisObject = static_cast<JSXMLHttpRequest*>(handle.slot()->asCell());
|
||||
auto& world = *static_cast<DOMWrapperWorld*>(context);
|
||||
uncacheWrapper(world, &thisObject->wrapped(), thisObject);
|
||||
}
|
||||
|
||||
JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, XMLHttpRequest& impl)
|
||||
{
|
||||
return wrap(lexicalGlobalObject, globalObject, impl);
|
||||
}
|
||||
|
||||
JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObject, Ref<XMLHttpRequest>&& impl)
|
||||
{
|
||||
return createWrapper<XMLHttpRequest>(globalObject, WTFMove(impl));
|
||||
}
|
||||
|
||||
template<> JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSXMLHttpRequestDOMConstructor::construct(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame)
|
||||
{
|
||||
return constructJSXMLHttpRequest(lexicalGlobalObject, callFrame);
|
||||
}
|
||||
|
||||
template<> JSC::JSValue JSXMLHttpRequestDOMConstructor::prototypeForStructure(JSC::VM& vm, const JSDOMGlobalObject& globalObject)
|
||||
{
|
||||
return JSEventTarget::getConstructor(vm, &globalObject);
|
||||
}
|
||||
|
||||
template<> void JSXMLHttpRequestDOMConstructor::initializeProperties(VM& vm, JSDOMGlobalObject& globalObject)
|
||||
{
|
||||
putDirect(vm, vm.propertyNames->length, jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);
|
||||
JSString* nameString = jsNontrivialString(vm, "XMLHttpRequest"_s);
|
||||
m_originalName.set(vm, this, nameString);
|
||||
putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);
|
||||
putDirect(vm, vm.propertyNames->prototype, JSXMLHttpRequest::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete);
|
||||
reifyStaticProperties(vm, JSXMLHttpRequest::info(), JSXMLHttpRequestConstructorTableValues, *this);
|
||||
}
|
||||
|
||||
JSC::JSValue getXMLHttpRequestConstructor(Zig::GlobalObject* globalObject)
|
||||
{
|
||||
return JSXMLHttpRequest::getConstructor(globalObject->vm(), globalObject);
|
||||
}
|
||||
|
||||
} // namespace WebCore
|
||||
123
src/bun.js/bindings/webcore/JSXMLHttpRequest.h
Normal file
123
src/bun.js/bindings/webcore/JSXMLHttpRequest.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2012 Google Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of Apple Inc. ("Apple") nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "JSDOMWrapper.h"
|
||||
#include "JSEventTarget.h"
|
||||
#include "XMLHttpRequest.h"
|
||||
#include <wtf/NeverDestroyed.h>
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
class JSXMLHttpRequest : public JSEventTarget {
|
||||
public:
|
||||
using Base = JSEventTarget;
|
||||
using DOMWrapped = XMLHttpRequest;
|
||||
|
||||
static JSXMLHttpRequest* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<XMLHttpRequest>&& impl)
|
||||
{
|
||||
JSXMLHttpRequest* ptr = new (NotNull, JSC::allocateCell<JSXMLHttpRequest>(globalObject->vm())) JSXMLHttpRequest(structure, *globalObject, WTFMove(impl));
|
||||
ptr->finishCreation(globalObject->vm());
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&);
|
||||
static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&);
|
||||
static XMLHttpRequest* toWrapped(JSC::VM&, JSC::JSValue);
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), JSC::NonArray);
|
||||
}
|
||||
|
||||
static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*);
|
||||
|
||||
template<typename, JSC::SubspaceAccess mode>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
|
||||
return nullptr;
|
||||
return subspaceForImpl(vm);
|
||||
}
|
||||
|
||||
static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm);
|
||||
static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&);
|
||||
static size_t estimatedSize(JSCell*, JSC::VM&);
|
||||
|
||||
XMLHttpRequest& wrapped() const
|
||||
{
|
||||
return static_cast<XMLHttpRequest&>(Base::wrapped());
|
||||
}
|
||||
|
||||
protected:
|
||||
JSXMLHttpRequest(JSC::Structure*, JSDOMGlobalObject&, Ref<XMLHttpRequest>&&);
|
||||
|
||||
void finishCreation(JSC::VM&);
|
||||
};
|
||||
|
||||
class JSXMLHttpRequestOwner final : public JSC::WeakHandleOwner {
|
||||
public:
|
||||
bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::AbstractSlotVisitor&, ASCIILiteral*) final;
|
||||
void finalize(JSC::Handle<JSC::Unknown>, void* context) final;
|
||||
};
|
||||
|
||||
inline JSC::WeakHandleOwner* wrapperOwner(DOMWrapperWorld&, XMLHttpRequest*)
|
||||
{
|
||||
static NeverDestroyed<JSXMLHttpRequestOwner> owner;
|
||||
return &owner.get();
|
||||
}
|
||||
|
||||
inline void* wrapperKey(XMLHttpRequest* wrappableObject)
|
||||
{
|
||||
return wrappableObject;
|
||||
}
|
||||
|
||||
JSC::JSValue toJS(JSC::JSGlobalObject*, JSDOMGlobalObject*, XMLHttpRequest&);
|
||||
inline JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, XMLHttpRequest* impl)
|
||||
{
|
||||
return impl ? toJS(lexicalGlobalObject, globalObject, *impl) : JSC::jsNull();
|
||||
}
|
||||
|
||||
JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject*, Ref<XMLHttpRequest>&&);
|
||||
inline JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* globalObject, RefPtr<XMLHttpRequest>&& impl)
|
||||
{
|
||||
return impl ? toJSNewlyCreated(lexicalGlobalObject, globalObject, impl.releaseNonNull()) : JSC::jsNull();
|
||||
}
|
||||
|
||||
template<> struct JSDOMWrapperConverterTraits<XMLHttpRequest> {
|
||||
using WrapperClass = JSXMLHttpRequest;
|
||||
using ToWrappedReturnType = XMLHttpRequest*;
|
||||
};
|
||||
|
||||
JSC::JSValue getXMLHttpRequestConstructor(Zig::GlobalObject* globalObject);
|
||||
|
||||
} // namespace WebCore
|
||||
30
src/bun.js/bindings/webcore/JSXMLHttpRequestUpload.h
Normal file
30
src/bun.js/bindings/webcore/JSXMLHttpRequestUpload.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "JSDOMWrapper.h"
|
||||
#include "JSEventTarget.h"
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
class XMLHttpRequestUpload;
|
||||
|
||||
class JSXMLHttpRequestUpload : public JSEventTarget {
|
||||
public:
|
||||
using Base = JSEventTarget;
|
||||
using DOMWrapped = XMLHttpRequestUpload;
|
||||
|
||||
static JSXMLHttpRequestUpload* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref<XMLHttpRequestUpload>&& impl)
|
||||
{
|
||||
return nullptr; // Stub for now
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
XMLHttpRequestUpload& wrapped() const;
|
||||
};
|
||||
|
||||
template<> struct JSDOMWrapperConverterTraits<XMLHttpRequestUpload> {
|
||||
using WrapperClass = JSXMLHttpRequestUpload;
|
||||
using ToWrappedReturnType = XMLHttpRequestUpload*;
|
||||
};
|
||||
|
||||
} // namespace WebCore
|
||||
808
src/bun.js/bindings/webcore/XMLHttpRequest.cpp
Normal file
808
src/bun.js/bindings/webcore/XMLHttpRequest.cpp
Normal file
@@ -0,0 +1,808 @@
|
||||
/*
|
||||
* Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2005, 2006 Alexey Proskuryakov <ap@nypop.com>
|
||||
* Copyright (C) 2011 Google Inc. All rights reserved.
|
||||
* Copyright (C) 2012 Intel Corporation
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "XMLHttpRequest.h"
|
||||
|
||||
#include "XMLHttpRequestUpload.h"
|
||||
// TODO: Enable these when available in Bun
|
||||
// #include "Blob.h"
|
||||
// #include "DOMFormData.h"
|
||||
// #include "Document.h"
|
||||
#include "Event.h"
|
||||
#include "EventNames.h"
|
||||
#include "HTTPParsers.h"
|
||||
#include "ScriptExecutionContext.h"
|
||||
#include "JSDOMGlobalObject.h"
|
||||
// #include "URLSearchParams.h"
|
||||
#include <JavaScriptCore/ArrayBuffer.h>
|
||||
#include <JavaScriptCore/ArrayBufferView.h>
|
||||
#include <JavaScriptCore/JSCJSValue.h>
|
||||
#include <JavaScriptCore/JSONObject.h>
|
||||
#include <wtf/text/CString.h>
|
||||
#include <wtf/text/StringBuilder.h>
|
||||
#include <chrono>
|
||||
|
||||
// External Zig functions for XMLHttpRequest implementation
|
||||
extern "C" {
|
||||
void* Bun__XMLHttpRequest_create(JSC::JSGlobalObject* globalThis);
|
||||
JSC::EncodedJSValue Bun__XMLHttpRequest_send(
|
||||
void* xhr_ptr,
|
||||
JSC::JSGlobalObject* globalThis,
|
||||
const char* method,
|
||||
const char* url,
|
||||
JSC::EncodedJSValue headers,
|
||||
JSC::EncodedJSValue body,
|
||||
uint32_t timeout_ms,
|
||||
bool with_credentials
|
||||
);
|
||||
void Bun__XMLHttpRequest_abort(void* xhr_ptr);
|
||||
uint16_t Bun__XMLHttpRequest_getStatus(void* xhr_ptr);
|
||||
JSC::EncodedJSValue Bun__XMLHttpRequest_getResponseHeaders(void* xhr_ptr, JSC::JSGlobalObject* globalThis);
|
||||
void Bun__XMLHttpRequest_destroy(void* xhr_ptr);
|
||||
}
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
// XMLHttpRequestUpload implementation
|
||||
ScriptExecutionContext* XMLHttpRequestUpload::scriptExecutionContext() const
|
||||
{
|
||||
return m_xmlHttpRequest ? m_xmlHttpRequest->scriptExecutionContext() : nullptr;
|
||||
}
|
||||
|
||||
void XMLHttpRequestUpload::dispatchProgressEvent(const AtomString& type, bool lengthComputable, unsigned long long loaded, unsigned long long total)
|
||||
{
|
||||
// TODO: Create and dispatch ProgressEvent
|
||||
dispatchEvent(Event::create(type, Event::CanBubble::No, Event::IsCancelable::No));
|
||||
}
|
||||
|
||||
void XMLHttpRequestUpload::dispatchEventAndLoadEnd(const AtomString& type)
|
||||
{
|
||||
dispatchProgressEvent(type, false, 0, 0);
|
||||
// TODO: Add loadend event when available
|
||||
}
|
||||
|
||||
bool XMLHttpRequestUpload::hasEventListeners() const
|
||||
{
|
||||
// Simplified - just check if we have any listeners
|
||||
return EventTargetWithInlineData::hasEventListeners();
|
||||
}
|
||||
|
||||
// XMLHttpRequest implementation
|
||||
XMLHttpRequest::XMLHttpRequest(ScriptExecutionContext& context)
|
||||
: ContextDestructionObserver(&context)
|
||||
, m_upload(XMLHttpRequestUpload::create(this))
|
||||
{
|
||||
// Get the global object from the context to create Zig tasklet
|
||||
if (auto* globalObject = context.globalObject()) {
|
||||
m_tasklet = Bun__XMLHttpRequest_create(globalObject);
|
||||
}
|
||||
}
|
||||
|
||||
XMLHttpRequest::~XMLHttpRequest()
|
||||
{
|
||||
if (m_tasklet) {
|
||||
Bun__XMLHttpRequest_destroy(m_tasklet);
|
||||
m_tasklet = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ExceptionOr<Ref<XMLHttpRequest>> XMLHttpRequest::create(ScriptExecutionContext& context)
|
||||
{
|
||||
return adoptRef(*new XMLHttpRequest(context));
|
||||
}
|
||||
|
||||
void XMLHttpRequest::changeState(State newState)
|
||||
{
|
||||
if (m_readyState == newState)
|
||||
return;
|
||||
|
||||
m_readyState = newState;
|
||||
|
||||
if (m_readyState != OPENED)
|
||||
m_sendFlag = false;
|
||||
|
||||
dispatchReadyStateChangeEvent();
|
||||
}
|
||||
|
||||
void XMLHttpRequest::dispatchReadyStateChangeEvent()
|
||||
{
|
||||
if (!scriptExecutionContext())
|
||||
return;
|
||||
|
||||
dispatchEvent(Event::create(eventNames().readystatechangeEvent, Event::CanBubble::No, Event::IsCancelable::No));
|
||||
}
|
||||
|
||||
void XMLHttpRequest::dispatchProgressEvent(const AtomString& type, bool lengthComputable, unsigned long long loaded, unsigned long long total)
|
||||
{
|
||||
// TODO: Create and dispatch ProgressEvent
|
||||
dispatchEvent(Event::create(type, Event::CanBubble::No, Event::IsCancelable::No));
|
||||
}
|
||||
|
||||
ExceptionOr<void> XMLHttpRequest::open(const String& method, const String& url)
|
||||
{
|
||||
return open(method, url, true, String(), String());
|
||||
}
|
||||
|
||||
ExceptionOr<void> XMLHttpRequest::open(const String& method, const String& urlString, bool async, const String& user, const String& password)
|
||||
{
|
||||
if (!scriptExecutionContext())
|
||||
return Exception { ExceptionCode::InvalidStateError };
|
||||
|
||||
// Validate method
|
||||
if (method.isEmpty())
|
||||
return Exception { ExceptionCode::SyntaxError, "Method cannot be empty"_s };
|
||||
|
||||
String normalizedMethod = normalizeHTTPMethod(method);
|
||||
if (!isAllowedHTTPMethod(normalizedMethod))
|
||||
return Exception { ExceptionCode::SecurityError, makeString("'"_s, method, "' is not a valid HTTP method."_s) };
|
||||
|
||||
// Parse URL
|
||||
URL url(URL(), urlString);
|
||||
if (!url.isValid())
|
||||
return Exception { ExceptionCode::SyntaxError, "Invalid URL"_s };
|
||||
|
||||
// Validate URL scheme
|
||||
if (!url.protocolIsInHTTPFamily())
|
||||
return Exception { ExceptionCode::SyntaxError, "URL scheme must be either 'http' or 'https'"_s };
|
||||
|
||||
// Synchronous requests are not supported
|
||||
if (!async)
|
||||
return Exception { ExceptionCode::InvalidAccessError, "Synchronous XMLHttpRequest is not supported"_s };
|
||||
|
||||
// Clear any previous state
|
||||
abort();
|
||||
clearRequest();
|
||||
clearResponse();
|
||||
m_errorFlag = false;
|
||||
m_uploadComplete = false;
|
||||
|
||||
m_method = normalizedMethod;
|
||||
m_url = url;
|
||||
m_async = async;
|
||||
m_user = user;
|
||||
m_password = password;
|
||||
|
||||
// Create fresh headers
|
||||
m_requestHeaders = FetchHeaders::create(FetchHeaders::Guard::None);
|
||||
|
||||
changeState(OPENED);
|
||||
|
||||
return { };
|
||||
}
|
||||
|
||||
ExceptionOr<void> XMLHttpRequest::setRequestHeader(const String& name, const String& value)
|
||||
{
|
||||
if (m_readyState != OPENED)
|
||||
return Exception { ExceptionCode::InvalidStateError, "XMLHttpRequest must be opened before setting request headers"_s };
|
||||
|
||||
if (m_sendFlag)
|
||||
return Exception { ExceptionCode::InvalidStateError, "Cannot set request headers after send()"_s };
|
||||
|
||||
// Validate header name/value
|
||||
if (!isValidHTTPToken(name))
|
||||
return Exception { ExceptionCode::SyntaxError, makeString("'"_s, name, "' is not a valid HTTP header field name."_s) };
|
||||
|
||||
if (!isAllowedHTTPHeader(name))
|
||||
return { }; // Silently ignore forbidden headers
|
||||
|
||||
if (!m_requestHeaders)
|
||||
m_requestHeaders = FetchHeaders::create(FetchHeaders::Guard::None);
|
||||
|
||||
m_requestHeaders->append(name, value);
|
||||
|
||||
return { };
|
||||
}
|
||||
|
||||
ExceptionOr<void> XMLHttpRequest::send()
|
||||
{
|
||||
return sendInternal();
|
||||
}
|
||||
|
||||
ExceptionOr<void> XMLHttpRequest::send(const String& body)
|
||||
{
|
||||
m_requestBodyString = body;
|
||||
return sendInternal();
|
||||
}
|
||||
|
||||
ExceptionOr<void> XMLHttpRequest::send(RefPtr<Document>) { return { }; }
|
||||
ExceptionOr<void> XMLHttpRequest::send(RefPtr<Blob>) { return { }; }
|
||||
ExceptionOr<void> XMLHttpRequest::send(RefPtr<DOMFormData>) { return { }; }
|
||||
ExceptionOr<void> XMLHttpRequest::send(RefPtr<URLSearchParams>) { return { }; }
|
||||
|
||||
// TODO: Enable when Document is available
|
||||
// ExceptionOr<void> XMLHttpRequest::send(RefPtr<Document> body)
|
||||
// {
|
||||
// m_requestDocument = body;
|
||||
// return sendInternal();
|
||||
// }
|
||||
|
||||
// TODO: Enable when Blob is available
|
||||
// ExceptionOr<void> XMLHttpRequest::send(RefPtr<Blob> body)
|
||||
// {
|
||||
// m_requestBlob = body;
|
||||
// return sendInternal();
|
||||
// }
|
||||
|
||||
ExceptionOr<void> XMLHttpRequest::send(RefPtr<JSC::ArrayBuffer> body)
|
||||
{
|
||||
m_requestArrayBuffer = body;
|
||||
return sendInternal();
|
||||
}
|
||||
|
||||
ExceptionOr<void> XMLHttpRequest::send(RefPtr<JSC::ArrayBufferView> body)
|
||||
{
|
||||
m_requestArrayBufferView = body;
|
||||
return sendInternal();
|
||||
}
|
||||
|
||||
// TODO: Enable when DOMFormData is available
|
||||
// ExceptionOr<void> XMLHttpRequest::send(RefPtr<DOMFormData> body)
|
||||
// {
|
||||
// m_requestFormData = body;
|
||||
// return sendInternal();
|
||||
// }
|
||||
|
||||
// TODO: Enable when URLSearchParams is available
|
||||
// ExceptionOr<void> XMLHttpRequest::send(RefPtr<URLSearchParams> body)
|
||||
// {
|
||||
// m_requestURLSearchParams = body;
|
||||
// return sendInternal();
|
||||
// }
|
||||
|
||||
ExceptionOr<void> XMLHttpRequest::sendInternal()
|
||||
{
|
||||
if (m_readyState != OPENED)
|
||||
return Exception { ExceptionCode::InvalidStateError, "XMLHttpRequest must be opened before send()"_s };
|
||||
|
||||
if (m_sendFlag)
|
||||
return Exception { ExceptionCode::InvalidStateError, "XMLHttpRequest send already in progress"_s };
|
||||
|
||||
if (!scriptExecutionContext())
|
||||
return Exception { ExceptionCode::InvalidStateError };
|
||||
|
||||
m_errorFlag = false;
|
||||
m_sendFlag = true;
|
||||
|
||||
if (m_timeout > 0)
|
||||
m_sendTime = std::chrono::steady_clock::now();
|
||||
|
||||
// TODO: Dispatch upload loadstart event when event names are available
|
||||
// TODO: Dispatch loadstart event when event names are available
|
||||
|
||||
// TODO: Actually send the request via Zig
|
||||
// For now, we'll just simulate completion
|
||||
if (m_tasklet && scriptExecutionContext()) {
|
||||
auto* globalObject = scriptExecutionContext()->globalObject();
|
||||
if (globalObject) {
|
||||
CString methodStr = m_method.utf8();
|
||||
CString urlStr = m_url.string().utf8();
|
||||
|
||||
// Convert headers to JSValue
|
||||
JSC::JSValue headersValue = JSC::jsUndefined();
|
||||
if (m_requestHeaders) {
|
||||
// TODO: Convert headers to JSValue
|
||||
}
|
||||
|
||||
// Convert body to JSValue
|
||||
JSC::JSValue bodyValue = JSC::jsUndefined();
|
||||
if (!m_requestBodyString.isEmpty()) {
|
||||
// TODO: Convert body string to JSValue
|
||||
}
|
||||
|
||||
// Call Zig send function
|
||||
Bun__XMLHttpRequest_send(
|
||||
m_tasklet,
|
||||
globalObject,
|
||||
methodStr.data(),
|
||||
urlStr.data(),
|
||||
JSC::JSValue::encode(headersValue),
|
||||
JSC::JSValue::encode(bodyValue),
|
||||
m_timeout,
|
||||
m_withCredentials
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return { };
|
||||
}
|
||||
|
||||
void XMLHttpRequest::abort()
|
||||
{
|
||||
if (m_tasklet) {
|
||||
Bun__XMLHttpRequest_abort(m_tasklet);
|
||||
}
|
||||
|
||||
// bool hadPendingActivity = hasPendingActivity();
|
||||
|
||||
m_errorFlag = true;
|
||||
clearRequest();
|
||||
|
||||
if (m_readyState == OPENED && m_sendFlag || m_readyState == HEADERS_RECEIVED || m_readyState == LOADING) {
|
||||
m_sendFlag = false;
|
||||
changeState(DONE);
|
||||
|
||||
if (m_upload) {
|
||||
m_upload->dispatchEventAndLoadEnd(eventNames().abortEvent);
|
||||
m_uploadComplete = true;
|
||||
}
|
||||
|
||||
dispatchProgressEvent(eventNames().abortEvent, false, 0, 0);
|
||||
// TODO: Dispatch loadend event when available
|
||||
}
|
||||
|
||||
m_readyState = UNSENT;
|
||||
}
|
||||
|
||||
ExceptionOr<void> XMLHttpRequest::overrideMimeType(const String& mime)
|
||||
{
|
||||
if (m_readyState >= LOADING)
|
||||
return Exception { ExceptionCode::InvalidStateError, "Cannot override MIME type after loading has started"_s };
|
||||
|
||||
m_mimeTypeOverride = mime;
|
||||
return { };
|
||||
}
|
||||
|
||||
ExceptionOr<void> XMLHttpRequest::setTimeout(unsigned timeout)
|
||||
{
|
||||
if (m_readyState != OPENED || m_sendFlag)
|
||||
return Exception { ExceptionCode::InvalidStateError };
|
||||
|
||||
m_timeout = timeout;
|
||||
return { };
|
||||
}
|
||||
|
||||
ExceptionOr<void> XMLHttpRequest::setWithCredentials(bool value)
|
||||
{
|
||||
if (m_readyState != UNSENT && m_readyState != OPENED)
|
||||
return Exception { ExceptionCode::InvalidStateError };
|
||||
|
||||
if (m_sendFlag)
|
||||
return Exception { ExceptionCode::InvalidStateError };
|
||||
|
||||
m_withCredentials = value;
|
||||
return { };
|
||||
}
|
||||
|
||||
ExceptionOr<void> XMLHttpRequest::setResponseType(ResponseType type)
|
||||
{
|
||||
if (m_readyState >= LOADING)
|
||||
return Exception { ExceptionCode::InvalidStateError };
|
||||
|
||||
// Document response type is only valid for async requests
|
||||
if (type == ResponseType::Document && !m_async)
|
||||
return Exception { ExceptionCode::InvalidStateError };
|
||||
|
||||
m_responseType = type;
|
||||
return { };
|
||||
}
|
||||
|
||||
String XMLHttpRequest::responseText() const
|
||||
{
|
||||
if (m_responseType != ResponseType::Empty && m_responseType != ResponseType::Text)
|
||||
return String();
|
||||
|
||||
if (m_readyState != LOADING && m_readyState != DONE)
|
||||
return String();
|
||||
|
||||
if (m_errorFlag)
|
||||
return String();
|
||||
|
||||
Locker locker { m_responseLock };
|
||||
|
||||
if (!m_responseText.isNull())
|
||||
return m_responseText;
|
||||
|
||||
if (m_responseData.isEmpty())
|
||||
return emptyString();
|
||||
|
||||
// Decode response data as UTF-8
|
||||
auto dataSpan = m_responseData.span();
|
||||
m_responseText = String(dataSpan);
|
||||
return m_responseText;
|
||||
}
|
||||
|
||||
RefPtr<JSC::ArrayBuffer> XMLHttpRequest::responseArrayBuffer() const
|
||||
{
|
||||
if (m_responseType != ResponseType::ArrayBuffer)
|
||||
return nullptr;
|
||||
|
||||
if (m_readyState != DONE)
|
||||
return nullptr;
|
||||
|
||||
if (m_errorFlag)
|
||||
return nullptr;
|
||||
|
||||
Locker locker { m_responseLock };
|
||||
|
||||
if (m_responseArrayBuffer)
|
||||
return m_responseArrayBuffer;
|
||||
|
||||
if (m_responseData.isEmpty())
|
||||
return nullptr;
|
||||
|
||||
auto dataSpan = m_responseData.span();
|
||||
if (dataSpan.size() > 0) {
|
||||
auto buffer = JSC::ArrayBuffer::createUninitialized(dataSpan.size(), 1);
|
||||
memcpy(buffer->data(), dataSpan.data(), dataSpan.size());
|
||||
m_responseArrayBuffer = WTFMove(buffer);
|
||||
}
|
||||
return m_responseArrayBuffer;
|
||||
}
|
||||
|
||||
RefPtr<Blob> XMLHttpRequest::responseBlob() const { return nullptr; }
|
||||
// TODO: Enable when Blob is available
|
||||
// RefPtr<Blob> XMLHttpRequest::responseBlob() const
|
||||
// {
|
||||
// if (m_responseType != ResponseType::Blob)
|
||||
// return nullptr;
|
||||
//
|
||||
// if (m_readyState != DONE)
|
||||
// return nullptr;
|
||||
//
|
||||
// if (m_errorFlag)
|
||||
// return nullptr;
|
||||
//
|
||||
// Locker locker { m_responseLock };
|
||||
//
|
||||
// if (m_responseBlob)
|
||||
// return m_responseBlob;
|
||||
//
|
||||
// if (m_responseData.isEmpty())
|
||||
// return nullptr;
|
||||
//
|
||||
// // TODO: Create Blob from response data
|
||||
// // m_responseBlob = Blob::create(m_responseData, ...);
|
||||
// return m_responseBlob;
|
||||
// }
|
||||
|
||||
RefPtr<Document> XMLHttpRequest::responseDocument() const { return nullptr; }
|
||||
// TODO: Enable when Document is available
|
||||
// RefPtr<Document> XMLHttpRequest::responseDocument() const
|
||||
// {
|
||||
// if (m_responseType != ResponseType::Document)
|
||||
// return nullptr;
|
||||
//
|
||||
// if (m_readyState != DONE)
|
||||
// return nullptr;
|
||||
//
|
||||
// if (m_errorFlag)
|
||||
// return nullptr;
|
||||
//
|
||||
// // Document response type is not implemented
|
||||
// return nullptr;
|
||||
// }
|
||||
|
||||
RefPtr<Document> XMLHttpRequest::responseXML() const { return nullptr; }
|
||||
// TODO: Enable when Document is available
|
||||
// RefPtr<Document> XMLHttpRequest::responseXML() const
|
||||
// {
|
||||
// // responseXML is essentially responseDocument for XML content
|
||||
// if (m_responseType != ResponseType::Empty && m_responseType != ResponseType::Document)
|
||||
// return nullptr;
|
||||
//
|
||||
// return responseDocument();
|
||||
// }
|
||||
|
||||
JSC::JSValue XMLHttpRequest::responseJSON(JSC::JSGlobalObject* globalObject) const
|
||||
{
|
||||
if (m_responseType != ResponseType::JSON)
|
||||
return JSC::jsNull();
|
||||
|
||||
if (m_readyState != DONE)
|
||||
return JSC::jsNull();
|
||||
|
||||
if (m_errorFlag)
|
||||
return JSC::jsNull();
|
||||
|
||||
if (m_responseJSON)
|
||||
return m_responseJSON.get();
|
||||
|
||||
String text = responseText();
|
||||
if (text.isEmpty())
|
||||
return JSC::jsNull();
|
||||
|
||||
// Parse JSON
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSC::JSValue jsonValue = JSC::JSONParse(globalObject, text);
|
||||
if (scope.exception()) {
|
||||
scope.clearException();
|
||||
return JSC::jsNull();
|
||||
}
|
||||
|
||||
m_responseJSON.set(vm, jsonValue);
|
||||
return jsonValue;
|
||||
}
|
||||
|
||||
JSC::JSValue XMLHttpRequest::response(JSC::JSGlobalObject* globalObject) const
|
||||
{
|
||||
switch (m_responseType) {
|
||||
case ResponseType::Empty:
|
||||
case ResponseType::Text:
|
||||
return JSC::jsString(globalObject->vm(), responseText());
|
||||
|
||||
case ResponseType::ArrayBuffer:
|
||||
// TODO: Convert ArrayBuffer to JSValue
|
||||
// if (auto buffer = responseArrayBuffer())
|
||||
// return JSC::toJS(globalObject, globalObject, buffer.get());
|
||||
return JSC::jsNull();
|
||||
|
||||
case ResponseType::Blob:
|
||||
// TODO: Convert Blob to JSValue
|
||||
return JSC::jsNull();
|
||||
|
||||
case ResponseType::Document:
|
||||
// TODO: Convert Document to JSValue
|
||||
return JSC::jsNull();
|
||||
|
||||
case ResponseType::JSON:
|
||||
return responseJSON(globalObject);
|
||||
}
|
||||
|
||||
return JSC::jsNull();
|
||||
}
|
||||
|
||||
String XMLHttpRequest::getResponseHeader(const String& name) const
|
||||
{
|
||||
if (m_readyState < HEADERS_RECEIVED || m_errorFlag)
|
||||
return String();
|
||||
|
||||
if (!m_responseHeaders)
|
||||
return String();
|
||||
|
||||
auto result = m_responseHeaders->get(name);
|
||||
return result.hasException() ? String() : result.releaseReturnValue();
|
||||
}
|
||||
|
||||
String XMLHttpRequest::getAllResponseHeaders() const
|
||||
{
|
||||
if (m_readyState < HEADERS_RECEIVED || m_errorFlag)
|
||||
return String();
|
||||
|
||||
if (!m_responseHeaders)
|
||||
return String();
|
||||
|
||||
// TODO: Iterate headers when API is available
|
||||
return String();
|
||||
}
|
||||
|
||||
void XMLHttpRequest::didReceiveResponse(unsigned short status, const String& statusText, const FetchHeaders::Init& headers)
|
||||
{
|
||||
m_status = status;
|
||||
m_statusText = statusText;
|
||||
|
||||
m_responseHeaders = FetchHeaders::create(FetchHeaders::Guard::None);
|
||||
// TODO: Add headers when API is available
|
||||
|
||||
changeState(HEADERS_RECEIVED);
|
||||
}
|
||||
|
||||
void XMLHttpRequest::didReceiveData(const uint8_t* data, size_t length)
|
||||
{
|
||||
if (m_errorFlag)
|
||||
return;
|
||||
|
||||
{
|
||||
Locker locker { m_responseLock };
|
||||
m_responseData.appendRange(data, data + length);
|
||||
m_receivedLength += length;
|
||||
}
|
||||
|
||||
if (m_readyState != LOADING)
|
||||
changeState(LOADING);
|
||||
|
||||
// TODO: Dispatch progress event when available
|
||||
}
|
||||
|
||||
void XMLHttpRequest::didFinishLoading()
|
||||
{
|
||||
if (m_errorFlag)
|
||||
return;
|
||||
|
||||
m_sendFlag = false;
|
||||
changeState(DONE);
|
||||
|
||||
// Dispatch final events
|
||||
if (m_upload && !m_uploadComplete) {
|
||||
// TODO: Dispatch load and loadend events when available
|
||||
m_uploadComplete = true;
|
||||
}
|
||||
|
||||
// TODO: Dispatch load and loadend events when available
|
||||
}
|
||||
|
||||
void XMLHttpRequest::didFailWithError(const String& error)
|
||||
{
|
||||
m_errorFlag = true;
|
||||
m_sendFlag = false;
|
||||
|
||||
clearResponse();
|
||||
changeState(DONE);
|
||||
|
||||
// Dispatch error events
|
||||
if (m_upload && !m_uploadComplete) {
|
||||
m_upload->dispatchEventAndLoadEnd(eventNames().errorEvent);
|
||||
m_uploadComplete = true;
|
||||
}
|
||||
|
||||
dispatchProgressEvent(eventNames().errorEvent, false, 0, 0);
|
||||
// TODO: Dispatch loadend event when available
|
||||
}
|
||||
|
||||
ExceptionOr<void> XMLHttpRequest::sendInternal(RefPtr<Document>) { return sendInternal(); }
|
||||
ExceptionOr<void> XMLHttpRequest::sendInternal(RefPtr<Blob>) { return sendInternal(); }
|
||||
ExceptionOr<void> XMLHttpRequest::sendInternal(RefPtr<JSC::ArrayBuffer>) { return sendInternal(); }
|
||||
ExceptionOr<void> XMLHttpRequest::sendInternal(RefPtr<JSC::ArrayBufferView>) { return sendInternal(); }
|
||||
ExceptionOr<void> XMLHttpRequest::sendInternal(RefPtr<DOMFormData>) { return sendInternal(); }
|
||||
ExceptionOr<void> XMLHttpRequest::sendInternal(const String&) { return sendInternal(); }
|
||||
ExceptionOr<void> XMLHttpRequest::sendInternal(RefPtr<URLSearchParams>) { return sendInternal(); }
|
||||
|
||||
void XMLHttpRequest::clearRequest()
|
||||
{
|
||||
// m_requestDocument = nullptr;
|
||||
// m_requestBlob = nullptr;
|
||||
m_requestArrayBuffer = nullptr;
|
||||
m_requestArrayBufferView = nullptr;
|
||||
// m_requestFormData = nullptr;
|
||||
// m_requestURLSearchParams = nullptr;
|
||||
m_requestBodyString = String();
|
||||
}
|
||||
|
||||
void XMLHttpRequest::clearResponse()
|
||||
{
|
||||
Locker locker { m_responseLock };
|
||||
|
||||
m_status = 0;
|
||||
m_statusText = String();
|
||||
m_responseHeaders = nullptr;
|
||||
m_responseData.clear();
|
||||
m_responseText = String();
|
||||
m_responseArrayBuffer = nullptr;
|
||||
// m_responseBlob = nullptr;
|
||||
// m_responseDocument = nullptr;
|
||||
m_responseJSON.clear();
|
||||
m_receivedLength = 0;
|
||||
m_expectedLength = 0;
|
||||
}
|
||||
|
||||
bool XMLHttpRequest::isAllowedHTTPMethod(const String& method) const
|
||||
{
|
||||
// Forbidden methods per spec
|
||||
static const char* const forbiddenMethods[] = {
|
||||
"CONNECT",
|
||||
"TRACE",
|
||||
"TRACK"
|
||||
};
|
||||
|
||||
for (auto* forbidden : forbiddenMethods) {
|
||||
if (equalIgnoringASCIICase(method, String::fromUTF8(forbidden)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XMLHttpRequest::isAllowedHTTPHeader(const String& name) const
|
||||
{
|
||||
// Forbidden headers per spec
|
||||
static const char* const forbiddenHeaders[] = {
|
||||
"Accept-Charset",
|
||||
"Accept-Encoding",
|
||||
"Access-Control-Request-Headers",
|
||||
"Access-Control-Request-Method",
|
||||
"Connection",
|
||||
"Content-Length",
|
||||
"Cookie",
|
||||
"Cookie2",
|
||||
"Date",
|
||||
"DNT",
|
||||
"Expect",
|
||||
"Host",
|
||||
"Keep-Alive",
|
||||
"Origin",
|
||||
"Referer",
|
||||
"TE",
|
||||
"Trailer",
|
||||
"Transfer-Encoding",
|
||||
"Upgrade",
|
||||
"Via"
|
||||
};
|
||||
|
||||
for (auto* forbidden : forbiddenHeaders) {
|
||||
if (equalIgnoringASCIICase(name, String::fromUTF8(forbidden)))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Also forbid headers starting with "Proxy-" or "Sec-"
|
||||
if (name.startsWithIgnoringASCIICase("proxy-"_s) || name.startsWithIgnoringASCIICase("sec-"_s))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String XMLHttpRequest::normalizeHTTPMethod(const String& method) const
|
||||
{
|
||||
// Normalize method names per spec
|
||||
static const char* const methods[] = {
|
||||
"DELETE",
|
||||
"GET",
|
||||
"HEAD",
|
||||
"OPTIONS",
|
||||
"POST",
|
||||
"PUT"
|
||||
};
|
||||
|
||||
for (auto* m : methods) {
|
||||
if (equalIgnoringASCIICase(method, String::fromUTF8(m)))
|
||||
return String::fromUTF8(m);
|
||||
}
|
||||
|
||||
return method.convertToASCIIUppercase();
|
||||
}
|
||||
|
||||
bool XMLHttpRequest::hasPendingActivity() const
|
||||
{
|
||||
return m_readyState != UNSENT && m_readyState != DONE;
|
||||
}
|
||||
|
||||
void XMLHttpRequest::stop()
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void XMLHttpRequest::suspend()
|
||||
{
|
||||
// TODO: Implement suspend
|
||||
}
|
||||
|
||||
void XMLHttpRequest::resume()
|
||||
{
|
||||
// TODO: Implement resume
|
||||
}
|
||||
|
||||
size_t XMLHttpRequest::memoryCost() const
|
||||
{
|
||||
size_t cost = sizeof(*this);
|
||||
|
||||
cost += m_method.sizeInBytes();
|
||||
cost += m_url.string().sizeInBytes();
|
||||
cost += m_user.sizeInBytes();
|
||||
cost += m_password.sizeInBytes();
|
||||
cost += m_statusText.sizeInBytes();
|
||||
cost += m_responseURL.sizeInBytes();
|
||||
cost += m_mimeTypeOverride.sizeInBytes();
|
||||
cost += m_requestBodyString.sizeInBytes();
|
||||
|
||||
{
|
||||
Locker locker { m_responseLock };
|
||||
cost += m_responseData.capacity();
|
||||
cost += m_responseText.sizeInBytes();
|
||||
|
||||
if (m_responseArrayBuffer)
|
||||
cost += m_responseArrayBuffer->byteLength();
|
||||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
} // namespace WebCore
|
||||
247
src/bun.js/bindings/webcore/XMLHttpRequest.h
Normal file
247
src/bun.js/bindings/webcore/XMLHttpRequest.h
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2005, 2006 Alexey Proskuryakov <ap@nypop.com>
|
||||
* Copyright (C) 2011 Google Inc. All rights reserved.
|
||||
* Copyright (C) 2012 Intel Corporation
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ContextDestructionObserver.h"
|
||||
#include "EventTarget.h"
|
||||
#include "EventTargetInterfaces.h"
|
||||
#include "ExceptionOr.h"
|
||||
#include "FetchHeaders.h"
|
||||
#include <wtf/URL.h>
|
||||
#include <wtf/text/StringBuilder.h>
|
||||
#include <wtf/HashSet.h>
|
||||
#include <wtf/Lock.h>
|
||||
#include <wtf/Vector.h>
|
||||
#include <JavaScriptCore/ArrayBuffer.h>
|
||||
#include <JavaScriptCore/ArrayBufferView.h>
|
||||
#include <JavaScriptCore/Strong.h>
|
||||
|
||||
namespace JSC {
|
||||
class ArrayBuffer;
|
||||
class ArrayBufferView;
|
||||
}
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
class Blob;
|
||||
class DOMFormData;
|
||||
class Document;
|
||||
class URLSearchParams;
|
||||
class XMLHttpRequestUpload;
|
||||
class JSBlob;
|
||||
|
||||
// XMLHttpRequest implementation matching the IDL spec
|
||||
class XMLHttpRequest final : public RefCounted<XMLHttpRequest>, public EventTargetWithInlineData, public ContextDestructionObserver {
|
||||
WTF_MAKE_TZONE_ALLOCATED(XMLHttpRequest);
|
||||
|
||||
public:
|
||||
// State as per XMLHttpRequest spec
|
||||
enum State : uint8_t {
|
||||
UNSENT = 0,
|
||||
OPENED = 1,
|
||||
HEADERS_RECEIVED = 2,
|
||||
LOADING = 3,
|
||||
DONE = 4
|
||||
};
|
||||
|
||||
// Response types matching IDL spec
|
||||
enum class ResponseType : uint8_t {
|
||||
Empty, // ""
|
||||
ArrayBuffer, // "arraybuffer"
|
||||
Blob, // "blob"
|
||||
Document, // "document"
|
||||
JSON, // "json"
|
||||
Text // "text"
|
||||
};
|
||||
|
||||
static ExceptionOr<Ref<XMLHttpRequest>> create(ScriptExecutionContext&);
|
||||
~XMLHttpRequest();
|
||||
|
||||
// XMLHttpRequest interface methods - matching IDL spec
|
||||
|
||||
// open() overloads
|
||||
ExceptionOr<void> open(const String& method, const String& url);
|
||||
ExceptionOr<void> open(const String& method, const String& url, bool async, const String& user, const String& password);
|
||||
|
||||
ExceptionOr<void> setRequestHeader(const String& name, const String& value);
|
||||
|
||||
// send() overloads - matching XMLHttpRequestBodyInit union type
|
||||
ExceptionOr<void> send();
|
||||
ExceptionOr<void> send(RefPtr<Document>);
|
||||
ExceptionOr<void> send(RefPtr<Blob>);
|
||||
ExceptionOr<void> send(RefPtr<JSC::ArrayBuffer>);
|
||||
ExceptionOr<void> send(RefPtr<JSC::ArrayBufferView>);
|
||||
ExceptionOr<void> send(RefPtr<DOMFormData>);
|
||||
ExceptionOr<void> send(const String&); // USVString
|
||||
ExceptionOr<void> send(RefPtr<URLSearchParams>);
|
||||
|
||||
void abort();
|
||||
ExceptionOr<void> overrideMimeType(const String& mime);
|
||||
|
||||
// Properties - matching IDL spec
|
||||
State readyState() const { return m_readyState; }
|
||||
unsigned short status() const { return m_status; }
|
||||
String statusText() const { return m_statusText; }
|
||||
String responseText() const;
|
||||
String responseURL() const { return m_responseURL; }
|
||||
|
||||
String getResponseHeader(const String& name) const;
|
||||
String getAllResponseHeaders() const;
|
||||
|
||||
ResponseType responseType() const { return m_responseType; }
|
||||
ExceptionOr<void> setResponseType(ResponseType);
|
||||
|
||||
// response attribute with CustomGetter in IDL
|
||||
JSC::JSValue response(JSC::JSGlobalObject*) const;
|
||||
RefPtr<JSC::ArrayBuffer> responseArrayBuffer() const;
|
||||
RefPtr<Blob> responseBlob() const;
|
||||
RefPtr<Document> responseDocument() const;
|
||||
JSC::JSValue responseJSON(JSC::JSGlobalObject*) const;
|
||||
|
||||
// responseXML - Window only in IDL
|
||||
RefPtr<Document> responseXML() const;
|
||||
|
||||
unsigned timeout() const { return m_timeout; }
|
||||
ExceptionOr<void> setTimeout(unsigned timeout);
|
||||
|
||||
bool withCredentials() const { return m_withCredentials; }
|
||||
ExceptionOr<void> setWithCredentials(bool);
|
||||
|
||||
XMLHttpRequestUpload* upload() const { return m_upload.get(); }
|
||||
|
||||
// Resolve ambiguity from multiple inheritance
|
||||
using RefCounted::ref;
|
||||
using RefCounted::deref;
|
||||
|
||||
// Event handlers
|
||||
// Note: These need proper DOMWrapperWorld handling which will be added in JSXMLHttpRequest bindings
|
||||
|
||||
// EventTarget overrides
|
||||
ScriptExecutionContext* scriptExecutionContext() const final { return ContextDestructionObserver::scriptExecutionContext(); }
|
||||
void refEventTarget() final { RefCounted::ref(); }
|
||||
void derefEventTarget() final { RefCounted::deref(); }
|
||||
EventTargetInterface eventTargetInterface() const final { return XMLHttpRequestEventTargetInterfaceType; }
|
||||
|
||||
// ActiveDOMObject behavior
|
||||
bool hasPendingActivity() const;
|
||||
void stop();
|
||||
void suspend();
|
||||
void resume();
|
||||
const char* activeDOMObjectName() const { return "XMLHttpRequest"; }
|
||||
|
||||
// Zig tasklet integration
|
||||
void* tasklet() const { return m_tasklet; }
|
||||
void setTasklet(void* tasklet) { m_tasklet = tasklet; }
|
||||
|
||||
// Network callbacks
|
||||
void didReceiveResponse(unsigned short status, const String& statusText, const FetchHeaders::Init& headers);
|
||||
void didReceiveData(const uint8_t* data, size_t length);
|
||||
void didFinishLoading();
|
||||
void didFailWithError(const String& error);
|
||||
|
||||
// Memory reporting
|
||||
size_t memoryCost() const;
|
||||
|
||||
private:
|
||||
explicit XMLHttpRequest(ScriptExecutionContext&);
|
||||
|
||||
void changeState(State);
|
||||
void clearRequest();
|
||||
void clearResponse();
|
||||
ExceptionOr<void> sendInternal();
|
||||
ExceptionOr<void> sendInternal(RefPtr<Document>);
|
||||
ExceptionOr<void> sendInternal(RefPtr<Blob>);
|
||||
ExceptionOr<void> sendInternal(RefPtr<JSC::ArrayBuffer>);
|
||||
ExceptionOr<void> sendInternal(RefPtr<JSC::ArrayBufferView>);
|
||||
ExceptionOr<void> sendInternal(RefPtr<DOMFormData>);
|
||||
ExceptionOr<void> sendInternal(const String&);
|
||||
ExceptionOr<void> sendInternal(RefPtr<URLSearchParams>);
|
||||
|
||||
void processResponse();
|
||||
void dispatchReadyStateChangeEvent();
|
||||
void dispatchProgressEvent(const AtomString& type, bool lengthComputable, unsigned long long loaded, unsigned long long total);
|
||||
|
||||
bool isAllowedHTTPMethod(const String& method) const;
|
||||
bool isAllowedHTTPHeader(const String& name) const;
|
||||
String normalizeHTTPMethod(const String& method) const;
|
||||
|
||||
// Member variables
|
||||
State m_readyState { UNSENT };
|
||||
bool m_async { true };
|
||||
bool m_includeCredentials { false };
|
||||
bool m_withCredentials { false };
|
||||
bool m_sendFlag { false };
|
||||
bool m_uploadComplete { false };
|
||||
bool m_uploadEventsAllowed { false };
|
||||
bool m_responseCacheIsValid { false };
|
||||
bool m_errorFlag { false };
|
||||
|
||||
String m_method;
|
||||
URL m_url;
|
||||
String m_user;
|
||||
String m_password;
|
||||
|
||||
// Request data
|
||||
RefPtr<FetchHeaders> m_requestHeaders;
|
||||
// RefPtr<Document> m_requestDocument;
|
||||
// RefPtr<Blob> m_requestBlob;
|
||||
RefPtr<JSC::ArrayBuffer> m_requestArrayBuffer;
|
||||
RefPtr<JSC::ArrayBufferView> m_requestArrayBufferView;
|
||||
// RefPtr<DOMFormData> m_requestFormData;
|
||||
// RefPtr<URLSearchParams> m_requestURLSearchParams;
|
||||
String m_requestBodyString;
|
||||
|
||||
// Response data
|
||||
ResponseType m_responseType { ResponseType::Empty };
|
||||
String m_responseURL;
|
||||
unsigned short m_status { 0 };
|
||||
String m_statusText;
|
||||
RefPtr<FetchHeaders> m_responseHeaders;
|
||||
String m_mimeTypeOverride;
|
||||
|
||||
// Response body storage
|
||||
Vector<uint8_t> m_responseData;
|
||||
mutable String m_responseText;
|
||||
mutable RefPtr<JSC::ArrayBuffer> m_responseArrayBuffer;
|
||||
// mutable RefPtr<Blob> m_responseBlob;
|
||||
// mutable RefPtr<Document> m_responseDocument;
|
||||
mutable JSC::Strong<JSC::Unknown> m_responseJSON;
|
||||
|
||||
// Configuration
|
||||
unsigned m_timeout { 0 };
|
||||
std::optional<std::chrono::steady_clock::time_point> m_sendTime;
|
||||
|
||||
// Upload object
|
||||
RefPtr<XMLHttpRequestUpload> m_upload;
|
||||
|
||||
// Progress tracking
|
||||
unsigned long long m_receivedLength { 0 };
|
||||
unsigned long long m_expectedLength { 0 };
|
||||
|
||||
// Zig tasklet handle for network operations
|
||||
void* m_tasklet { nullptr };
|
||||
|
||||
// Locks for thread safety
|
||||
mutable Lock m_responseLock;
|
||||
};
|
||||
|
||||
} // namespace WebCore
|
||||
85
src/bun.js/bindings/webcore/XMLHttpRequestUpload.h
Normal file
85
src/bun.js/bindings/webcore/XMLHttpRequestUpload.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of Apple Inc. ("Apple") nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "EventTarget.h"
|
||||
#include <wtf/Forward.h>
|
||||
#include <wtf/RefCounted.h>
|
||||
#include <wtf/RefPtr.h>
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
class XMLHttpRequest;
|
||||
|
||||
// XMLHttpRequestUpload allows tracking of upload progress
|
||||
// It's a separate EventTarget as per the spec
|
||||
class XMLHttpRequestUpload final : public RefCounted<XMLHttpRequestUpload>, public EventTargetWithInlineData {
|
||||
WTF_MAKE_TZONE_ALLOCATED(XMLHttpRequestUpload);
|
||||
public:
|
||||
static Ref<XMLHttpRequestUpload> create(XMLHttpRequest* xhr)
|
||||
{
|
||||
return adoptRef(*new XMLHttpRequestUpload(xhr));
|
||||
}
|
||||
|
||||
~XMLHttpRequestUpload() = default;
|
||||
|
||||
// Resolve ambiguity from multiple inheritance
|
||||
using RefCounted::ref;
|
||||
using RefCounted::deref;
|
||||
|
||||
// EventTarget implementation
|
||||
void refEventTarget() final { RefCounted::ref(); }
|
||||
void derefEventTarget() final { RefCounted::deref(); }
|
||||
|
||||
EventTargetInterface eventTargetInterface() const final
|
||||
{
|
||||
return XMLHttpRequestUploadEventTargetInterfaceType;
|
||||
}
|
||||
|
||||
ScriptExecutionContext* scriptExecutionContext() const final;
|
||||
|
||||
// Progress event dispatching
|
||||
void dispatchProgressEvent(const AtomString& type, bool lengthComputable, unsigned long long loaded, unsigned long long total);
|
||||
void dispatchEventAndLoadEnd(const AtomString& type);
|
||||
|
||||
// Connection to parent XMLHttpRequest
|
||||
XMLHttpRequest* xmlHttpRequest() const { return m_xmlHttpRequest; }
|
||||
|
||||
bool hasEventListeners() const;
|
||||
|
||||
private:
|
||||
explicit XMLHttpRequestUpload(XMLHttpRequest* xhr)
|
||||
: m_xmlHttpRequest(xhr)
|
||||
{
|
||||
}
|
||||
|
||||
XMLHttpRequest* m_xmlHttpRequest;
|
||||
};
|
||||
|
||||
} // namespace WebCore
|
||||
@@ -2753,3 +2753,87 @@ const Response = jsc.WebCore.Response;
|
||||
|
||||
const Blob = jsc.WebCore.Blob;
|
||||
const AnyBlob = jsc.WebCore.Blob.Any;
|
||||
|
||||
// XMLHttpRequest support - creates a FetchTasklet similar to Bun__fetch
|
||||
pub const XMLHttpRequestTasklet = struct {
|
||||
fetch_tasklet: *FetchTasklet,
|
||||
ready_state: u8 = 0, // UNSENT = 0
|
||||
status: u16 = 0,
|
||||
status_text: []const u8 = "",
|
||||
response_headers: bun.String = bun.String.empty,
|
||||
response_url: bun.String = bun.String.empty,
|
||||
|
||||
pub fn create(
|
||||
allocator: std.mem.Allocator,
|
||||
) !*XMLHttpRequestTasklet {
|
||||
const xhr = try allocator.create(XMLHttpRequestTasklet);
|
||||
xhr.* = .{
|
||||
.fetch_tasklet = undefined,
|
||||
};
|
||||
return xhr;
|
||||
}
|
||||
|
||||
pub fn deinit(this: *XMLHttpRequestTasklet, allocator: std.mem.Allocator) void {
|
||||
this.response_headers.deref();
|
||||
this.response_url.deref();
|
||||
allocator.destroy(this);
|
||||
}
|
||||
};
|
||||
|
||||
export fn Bun__XMLHttpRequest_create(
|
||||
globalThis: *jsc.JSGlobalObject,
|
||||
) ?*anyopaque {
|
||||
_ = globalThis;
|
||||
const allocator = VirtualMachine.get().allocator;
|
||||
const xhr = XMLHttpRequestTasklet.create(allocator) catch return null;
|
||||
return @ptrCast(xhr);
|
||||
}
|
||||
|
||||
export fn Bun__XMLHttpRequest_send(
|
||||
xhr_ptr: ?*anyopaque,
|
||||
globalThis: *jsc.JSGlobalObject,
|
||||
method_str: [*:0]const u8,
|
||||
url_str: [*:0]const u8,
|
||||
headers_jsvalue: jsc.JSValue,
|
||||
body_jsvalue: jsc.JSValue,
|
||||
timeout_ms: u32,
|
||||
with_credentials: bool,
|
||||
) jsc.JSValue {
|
||||
_ = xhr_ptr;
|
||||
_ = globalThis;
|
||||
_ = method_str;
|
||||
_ = url_str;
|
||||
_ = headers_jsvalue;
|
||||
_ = body_jsvalue;
|
||||
_ = timeout_ms;
|
||||
_ = with_credentials;
|
||||
|
||||
// TODO: Implement actual XMLHttpRequest send logic
|
||||
// For now, return undefined
|
||||
return .zero;
|
||||
}
|
||||
|
||||
export fn Bun__XMLHttpRequest_abort(xhr_ptr: ?*anyopaque) void {
|
||||
const xhr = @as(*XMLHttpRequestTasklet, @ptrCast(@alignCast(xhr_ptr orelse return)));
|
||||
// TODO: Implement abort for fetch tasklet
|
||||
if (xhr.fetch_tasklet.http) |_| {
|
||||
// http_ptr.cancel();
|
||||
}
|
||||
xhr.ready_state = 4; // DONE
|
||||
}
|
||||
|
||||
export fn Bun__XMLHttpRequest_getStatus(xhr_ptr: ?*anyopaque) u16 {
|
||||
const xhr = @as(*XMLHttpRequestTasklet, @ptrCast(@alignCast(xhr_ptr orelse return 0)));
|
||||
return xhr.status;
|
||||
}
|
||||
|
||||
export fn Bun__XMLHttpRequest_getResponseHeaders(xhr_ptr: ?*anyopaque, globalThis: *jsc.JSGlobalObject) jsc.JSValue {
|
||||
const xhr = @as(*XMLHttpRequestTasklet, @ptrCast(@alignCast(xhr_ptr orelse return .zero)));
|
||||
return xhr.response_headers.toJS(globalThis);
|
||||
}
|
||||
|
||||
export fn Bun__XMLHttpRequest_destroy(xhr_ptr: ?*anyopaque) void {
|
||||
const xhr = @as(*XMLHttpRequestTasklet, @ptrCast(@alignCast(xhr_ptr orelse return)));
|
||||
const allocator = VirtualMachine.get().allocator;
|
||||
xhr.deinit(allocator);
|
||||
}
|
||||
|
||||
109
test/js/web/xmlhttprequest.test.ts
Normal file
109
test/js/web/xmlhttprequest.test.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { test, expect, describe } from "bun:test";
|
||||
|
||||
describe("XMLHttpRequest", () => {
|
||||
test("XMLHttpRequest is defined", () => {
|
||||
expect(typeof XMLHttpRequest).toBe("function");
|
||||
});
|
||||
|
||||
test("XMLHttpRequest constants", () => {
|
||||
expect(XMLHttpRequest.UNSENT).toBe(0);
|
||||
expect(XMLHttpRequest.OPENED).toBe(1);
|
||||
expect(XMLHttpRequest.HEADERS_RECEIVED).toBe(2);
|
||||
expect(XMLHttpRequest.LOADING).toBe(3);
|
||||
expect(XMLHttpRequest.DONE).toBe(4);
|
||||
});
|
||||
|
||||
test("can create XMLHttpRequest instance", () => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
expect(xhr).toBeDefined();
|
||||
expect(xhr.readyState).toBe(XMLHttpRequest.UNSENT);
|
||||
});
|
||||
|
||||
test("has required properties", () => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
// Properties
|
||||
expect(xhr.readyState).toBe(0);
|
||||
expect(xhr.status).toBe(0);
|
||||
expect(xhr.statusText).toBe("");
|
||||
expect(xhr.responseText).toBe("");
|
||||
expect(xhr.responseURL).toBe("");
|
||||
expect(xhr.responseType).toBe("");
|
||||
expect(xhr.response).toBeNull();
|
||||
expect(xhr.timeout).toBe(0);
|
||||
expect(xhr.withCredentials).toBe(false);
|
||||
expect(xhr.upload).toBeDefined();
|
||||
|
||||
// Methods
|
||||
expect(typeof xhr.open).toBe("function");
|
||||
expect(typeof xhr.setRequestHeader).toBe("function");
|
||||
expect(typeof xhr.send).toBe("function");
|
||||
expect(typeof xhr.abort).toBe("function");
|
||||
expect(typeof xhr.getResponseHeader).toBe("function");
|
||||
expect(typeof xhr.getAllResponseHeaders).toBe("function");
|
||||
expect(typeof xhr.overrideMimeType).toBe("function");
|
||||
});
|
||||
|
||||
test("open() method", () => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
expect(() => {
|
||||
xhr.open("GET", "http://example.com");
|
||||
}).not.toThrow();
|
||||
expect(xhr.readyState).toBe(XMLHttpRequest.OPENED);
|
||||
});
|
||||
|
||||
test("setRequestHeader() requires open() first", () => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
expect(() => {
|
||||
xhr.setRequestHeader("Content-Type", "application/json");
|
||||
}).toThrow();
|
||||
|
||||
xhr.open("GET", "http://example.com");
|
||||
expect(() => {
|
||||
xhr.setRequestHeader("Content-Type", "application/json");
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
test("abort() method", () => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "http://example.com");
|
||||
expect(() => {
|
||||
xhr.abort();
|
||||
}).not.toThrow();
|
||||
expect(xhr.readyState).toBe(XMLHttpRequest.DONE);
|
||||
});
|
||||
|
||||
test("instance constants", () => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
expect(xhr.UNSENT).toBe(0);
|
||||
expect(xhr.OPENED).toBe(1);
|
||||
expect(xhr.HEADERS_RECEIVED).toBe(2);
|
||||
expect(xhr.LOADING).toBe(3);
|
||||
expect(xhr.DONE).toBe(4);
|
||||
});
|
||||
|
||||
test("responseType setter/getter", () => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.responseType = "json";
|
||||
expect(xhr.responseType).toBe("json");
|
||||
|
||||
xhr.responseType = "arraybuffer";
|
||||
expect(xhr.responseType).toBe("arraybuffer");
|
||||
});
|
||||
|
||||
test("timeout setter/getter", () => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "http://example.com");
|
||||
xhr.timeout = 5000;
|
||||
expect(xhr.timeout).toBe(5000);
|
||||
});
|
||||
|
||||
test("withCredentials setter/getter", () => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.withCredentials = true;
|
||||
expect(xhr.withCredentials).toBe(true);
|
||||
|
||||
xhr.withCredentials = false;
|
||||
expect(xhr.withCredentials).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user