From b5a24b12845e8373af68081f05c0d5667133f6bc Mon Sep 17 00:00:00 2001 From: wahahahaha Date: Tue, 13 Aug 2024 13:28:02 +0800 Subject: [PATCH 01/18] Implement FormData, spec test code included. Implement FormData --- .gitignore | 3 + bridge/CMakeLists.txt | 5 + bridge/bindings/qjs/binding_initializer.cc | 3 + bridge/bindings/qjs/converter_impl.h | 32 +++ bridge/bindings/qjs/exception_state.h | 2 +- bridge/bindings/qjs/member_installer.cc | 11 +- bridge/bindings/qjs/wrapper_type_info.h | 5 + bridge/core/api/form_data.cc | 189 ++++++++++++++++++ bridge/core/api/form_data.d.ts | 16 ++ bridge/core/api/form_data.h | 60 ++++++ bridge/core/api/form_data_part.cc | 86 ++++++++ bridge/core/api/form_data_part.h | 55 +++++ bridge/core/binding_object.cc | 4 + bridge/core/binding_object.h | 2 +- bridge/core/fileapi/blob_part.h | 5 +- bridge/polyfill/src/array-buffer.ts_ | 16 ++ bridge/polyfill/src/blob.ts_ | 13 ++ bridge/polyfill/src/formdata.ts | 19 ++ bridge/polyfill/src/index.ts | 36 +++- buildAndRunF.sh | 21 ++ .../lib/bridge/match_snapshots.dart | 1 - integration_tests/lib/bridge/test_input.dart | 1 - .../lib/custom_elements/flutter_listview.dart | 1 - .../lib/custom_elements/flutter_swiper.dart | 1 - integration_tests/lib/test_module.dart | 1 - integration_tests/spec_group.json | 1 + integration_tests/specs/api/formdata.ts | 40 ++++ webf/example/assets/bundle.html | 4 + webf/example/assets/bundle.js | 80 +++++++- webf/lib/module.dart | 3 + webf/lib/src/dom/element_registry.dart | 1 - webf/lib/src/module/arraybuffer.dart | 58 ++++++ webf/lib/src/module/blob.dart | 85 ++++++++ webf/lib/src/module/form_data.dart | 83 ++++++++ webf/lib/src/module/module_manager.dart | 3 + webf/lib/src/svg/text.dart | 1 - 36 files changed, 932 insertions(+), 15 deletions(-) create mode 100644 bridge/core/api/form_data.cc create mode 100644 bridge/core/api/form_data.d.ts create mode 100644 bridge/core/api/form_data.h create mode 100644 bridge/core/api/form_data_part.cc create mode 100644 bridge/core/api/form_data_part.h create mode 100644 bridge/polyfill/src/array-buffer.ts_ create mode 100644 bridge/polyfill/src/blob.ts_ create mode 100644 bridge/polyfill/src/formdata.ts create mode 100644 buildAndRunF.sh create mode 100644 integration_tests/specs/api/formdata.ts create mode 100644 webf/lib/src/module/arraybuffer.dart create mode 100644 webf/lib/src/module/blob.dart create mode 100644 webf/lib/src/module/form_data.dart diff --git a/.gitignore b/.gitignore index 062b86230f..749ba2194e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .vscode .history +.cache *.log *.apk *.ap_ @@ -48,6 +49,8 @@ build/ *~ *.swp +**/vendor/** + # NDK obj/ yarn.lock diff --git a/bridge/CMakeLists.txt b/bridge/CMakeLists.txt index 85d9171f2f..dc6b25fa03 100644 --- a/bridge/CMakeLists.txt +++ b/bridge/CMakeLists.txt @@ -1,5 +1,7 @@ cmake_minimum_required(VERSION 3.10.0) set(CMAKE_OSX_DEPLOYMENT_TARGET 10.11) +set(CMAKE_C_COMPILER_LAUNCHER ccache) +set(CMAKE_CXX_COMPILER_LAUNCHER ccache) project(WebF) set(CMAKE_OSX_DEPLOYMENT_TARGET 10.11) @@ -266,6 +268,8 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs") # Core sources webf_bridge.cc core/api/api.cc + core/api/form_data.cc + core/api/form_data_part.cc core/executing_context.cc core/script_forbidden_scope.cc core/script_state.cc @@ -412,6 +416,7 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs") out/qjs_window.cc out/qjs_location.cc out/qjs_blob.cc + out/qjs_form_data.cc out/qjs_event.cc out/qjs_add_event_listener_options.cc out/qjs_event_listener_options.cc diff --git a/bridge/bindings/qjs/binding_initializer.cc b/bridge/bindings/qjs/binding_initializer.cc index 3a25fbc33f..bb75beb41a 100644 --- a/bridge/bindings/qjs/binding_initializer.cc +++ b/bridge/bindings/qjs/binding_initializer.cc @@ -8,6 +8,7 @@ #include "qjs_animation_event.h" #include "qjs_blob.h" +#include "qjs_form_data.h" #include "qjs_bounding_client_rect.h" #include "qjs_canvas_gradient.h" #include "qjs_canvas_pattern.h" @@ -200,6 +201,8 @@ void InstallBindings(ExecutingContext* context) { QJSSVGStyleElement::Install(context); QJSSVGLineElement::Install(context); + QJSFormData::Install(context); + // Legacy bindings, not standard. QJSElementAttributes::Install(context); } diff --git a/bridge/bindings/qjs/converter_impl.h b/bridge/bindings/qjs/converter_impl.h index b02ca3f0a2..dc8883bc87 100644 --- a/bridge/bindings/qjs/converter_impl.h +++ b/bridge/bindings/qjs/converter_impl.h @@ -6,6 +6,7 @@ #ifndef BRIDGE_BINDINGS_QJS_CONVERTER_IMPL_H_ #define BRIDGE_BINDINGS_QJS_CONVERTER_IMPL_H_ +#include #include #include "atomic_string.h" #include "bindings/qjs/union_base.h" @@ -14,6 +15,7 @@ #include "core/dom/events/event_target.h" #include "core/dom/node_list.h" #include "core/fileapi/blob_part.h" +#include "core/api/form_data_part.h" #include "core/fileapi/blob_property_bag.h" #include "core/frame/window.h" #include "core/html/html_body_element.h" @@ -384,6 +386,30 @@ struct Converter : public ConverterBase { } }; +template <> +struct Converter : public ConverterBase { + using ImplType = FormDataPart::ImplType; + static ImplType FromValue(JSContext* ctx, JSValue value, ExceptionState& exception_state) { + assert(!JS_IsException(value)); + return FormDataPart::Create(ctx, value, exception_state); + } + + static JSValue ToValue(JSContext* ctx, ImplType data) { + if (data == nullptr) + return JS_NULL; + + return data->ToQuickJS(ctx); + } + + static JSValue ToValue(JSContext* ctx, FormDataPart* data) { + if (data == nullptr) + return JS_NULL; + + return data->ToQuickJS(ctx); + } +}; + + template <> struct Converter : public ConverterBase { using ImplType = BlobPart::ImplType; @@ -398,6 +424,12 @@ struct Converter : public ConverterBase { return data->ToQuickJS(ctx); } + static JSValue ToValue(JSContext* ctx, std::shared_ptr data) { + if (data == nullptr) + return JS_NULL; + + return data->ToQuickJS(ctx); + } }; template <> diff --git a/bridge/bindings/qjs/exception_state.h b/bridge/bindings/qjs/exception_state.h index edf36a3bf7..6bb400f74d 100644 --- a/bridge/bindings/qjs/exception_state.h +++ b/bridge/bindings/qjs/exception_state.h @@ -13,7 +13,7 @@ namespace webf { -enum ErrorType { TypeError, InternalError, RangeError, ReferenceError, SyntaxError }; +enum ErrorType { TypeError, InternalError, RangeError, ReferenceError, SyntaxError,ArgumentError }; // ExceptionState is a scope-like class and provides a way to store an exception. class ExceptionState { diff --git a/bridge/bindings/qjs/member_installer.cc b/bridge/bindings/qjs/member_installer.cc index ffece4d1b4..eca299c1e2 100644 --- a/bridge/bindings/qjs/member_installer.cc +++ b/bridge/bindings/qjs/member_installer.cc @@ -5,6 +5,7 @@ #include "member_installer.h" #include +#include #include "core/executing_context.h" #include "qjs_engine_patch.h" @@ -67,14 +68,18 @@ void MemberInstaller::InstallAttributes(ExecutingContext* context, } } } - +const char* fn_form_data_delete="form_data_delete"; void MemberInstaller::InstallFunctions(ExecutingContext* context, JSValue root, std::initializer_list config) { JSContext* ctx = context->ctx(); for (auto& c : config) { - JSValue function = JS_NewCFunction(ctx, c.function, c.name, c.length); - JS_DefinePropertyValueStr(ctx, root, c.name, function, c.flag); + std::string name = c.name; + if(c.name==fn_form_data_delete){ + name = "delete"; + } + JSValue function = JS_NewCFunction(ctx, c.function, name.c_str(), c.length); + JS_DefinePropertyValueStr(ctx, root, name.c_str(), function, c.flag); } } diff --git a/bridge/bindings/qjs/wrapper_type_info.h b/bridge/bindings/qjs/wrapper_type_info.h index 3aa7c313ad..457dc84139 100644 --- a/bridge/bindings/qjs/wrapper_type_info.h +++ b/bridge/bindings/qjs/wrapper_type_info.h @@ -115,7 +115,12 @@ enum { JS_CLASS_SVG_LENGTH, JS_CLASS_SVG_ANIMATED_LENGTH, + // + JS_CLASS_FORM_DATA, + JS_CLASS_CUSTOM_CLASS_INIT_COUNT /* last entry for predefined classes */ + + }; // Callback when get property using index. diff --git a/bridge/core/api/form_data.cc b/bridge/core/api/form_data.cc new file mode 100644 index 0000000000..dee0ff4a05 --- /dev/null +++ b/bridge/core/api/form_data.cc @@ -0,0 +1,189 @@ +#include "form_data.h" +#include +#include "bindings/qjs/atomic_string.h" +#include "core/api/form_data_part.h" +#include "core/executing_context.h" +#include "core/fileapi/blob_part.h" + +namespace webf { +const char* className= "FormData"; +FormData* FormData::Create(ExecutingContext* context, ExceptionState& exception_state) { + return MakeGarbageCollected(context->ctx()); +} + +FormData::FormData(ExecutingContext* context, NativeBindingObject* native_binding_object) + : BindingObject(context->ctx(), native_binding_object) {} + +NativeValue FormData::HandleCallFromDartSide(const AtomicString& method, + int32_t argc, + const NativeValue* argv, + Dart_Handle dart_object) { + return Native_NewNull(); +} + +bool FormData::IsFormData() const { + return true; +} + +void FormData::append(const AtomicString& name, + const std::shared_ptr& value, + const AtomicString& fileName, + ExceptionState& exception_state) { + // 验证参数有效性 + if (name.IsEmpty()) { + exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty."); + return; + } + + // 创建 FormDataPart 对象 + auto form_data_part = std::make_shared(name.ToStdString(ctx()), value, fileName.ToStdString(ctx())); + + // 添加数据 + _parts.push_back(form_data_part); +} + +void FormData::form_data_delete(const AtomicString& name, ExceptionState& exception_state) { + // 验证参数有效性 + if (name.IsEmpty()) { + exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty."); + return; + } + + // 删除数据 + _parts.erase(std::remove_if(_parts.begin(), _parts.end(), + [name, this](const std::shared_ptr& part) { + return part->GetName() == name.ToStdString(ctx()); + }), + _parts.end()); +} + +webf::BlobPart* FormData::get(const AtomicString& name, ExceptionState& exception_state) { + // 验证参数有效性 + if (name.IsEmpty()) { + exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty."); + return nullptr; + } + + // 查找数据 + for (const auto& part : _parts) { + if (part->GetName() == name.ToStdString(ctx())) { + return &*part->getFirst(); + } + } + + // 如果没有找到,则返回 nullptr + return nullptr; +} + +std::vector FormData::getAll(const AtomicString& name, ExceptionState& exception_state) { + std::vector result; + + // 验证参数有效性 + if (name.IsEmpty()) { + exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty."); + return result; + } + + // 收集数据 + for (const auto& part : _parts) { + if (part->GetName() == name.ToStdString(ctx())) { + for (const auto& value : part->GetValues()) { + result.push_back(std::make_shared(value)); + } + } + } + + return result; +} + +bool FormData::has(const AtomicString& name, ExceptionState& exception_state) { + // 验证参数有效性 + if (name.IsEmpty()) { + exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty."); + return false; + } + + // 检查数据 + for (const auto& part : _parts) { + if (part->GetName() == name.ToStdString(ctx())) { + return true; + } + } + + return false; +} + +void FormData::set(const AtomicString& name, + const std::shared_ptr& value, + const AtomicString& fileName, + ExceptionState& exception_state) { + // 验证参数有效性 + if (name.IsEmpty()) { + exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty."); + return; + } + + // 移除已存在的相同名称的条目 + _parts.erase(std::remove_if(_parts.begin(), _parts.end(), + [name, this](const std::shared_ptr& part) { + return part->GetName() == name.ToStdString(ctx()); + }), + _parts.end()); + + // 创建 FormDataPart 对象 + auto form_data_part = std::make_shared(name.ToStdString(ctx()), value, fileName.ToStdString(ctx())); + + // 添加新的数据 + _parts.push_back(form_data_part); +} + +void FormData::forEach(const std::shared_ptr& callback, + const ScriptValue& thisArg, + ExceptionState& exception_state) { + if (!callback || !callback->IsFunction(ctx())) { + exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The callback function must be callable."); + return; + } + // callbackFn:(value: BlobPart, key: string, parent: FormData) => void + for (const auto& part : _parts) { + ScriptValue args[3]; + /*value*/ args[0] = ScriptValue(ctx(), part->ToQuickJS(ctx())); + /*key*/ args[1] = ScriptValue(ctx(), AtomicString(ctx(), part->GetName())); + + // TODO: which parent??? + /*parent*/ args[2] = ScriptValue(ctx(), this->ToQuickJS()); + + // 调用回调函数 + ScriptValue result = callback->Invoke(ctx(), thisArg, 3, args); + if (result.IsException()) { + exception_state.ThrowException(ctx(), result.QJSValue()); + return; + } + } +} + +// 实现 keys() 方法 +std::vector FormData::keys(ExceptionState& exception_state) const { + std::vector keys; + for (const auto& part : _parts) { + keys.push_back(AtomicString(ctx(), part->GetName())); + } + return keys; +} + +// 实现 values() 方法 +std::vector> FormData::values(ExceptionState& exception_state) const { + std::vector> values; + for (const auto& part : _parts) { + for (const auto& value : part->GetValues()) { + values.push_back(std::make_shared(value)); + } + } + return values; +} + +// 实现 entries() 方法 +std::vector> FormData::entries(ExceptionState& exception_state) const { + return std::vector>(_parts.begin(), _parts.end()); +} +} // namespace webf \ No newline at end of file diff --git a/bridge/core/api/form_data.d.ts b/bridge/core/api/form_data.d.ts new file mode 100644 index 0000000000..e7a945e23a --- /dev/null +++ b/bridge/core/api/form_data.d.ts @@ -0,0 +1,16 @@ +// type FormDataEntryValue = File | string; +type FormDataPart={} +export interface FormData { + new():FormData; + append(name: string, value: BlobPart, fileName?: string): void; + // this method name will be fixed to **delete** when MemberInstaller::InstallFunctions is called + form_data_delete(name: string): void; + get(name: string): BlobPart + getAll(name: string): BlobPart[]; + has(name: string): boolean; + set(name: string, value: BlobPart, fileName?: string): void; + forEach(callbackfn: Function, thisArg?: any): void; + keys():string[] + values():BlobPart[] + entries():FormDataPart[] +} diff --git a/bridge/core/api/form_data.h b/bridge/core/api/form_data.h new file mode 100644 index 0000000000..d794c2d474 --- /dev/null +++ b/bridge/core/api/form_data.h @@ -0,0 +1,60 @@ + +#ifndef WEBF_CORE_API_FORM_DATA_H_ +#define WEBF_CORE_API_FORM_DATA_H_ +#include +#include +#include "bindings/qjs/atomic_string.h" +#include "bindings/qjs/script_wrappable.h" +#include "core/api/form_data_part.h" +#include "core/binding_object.h" + +// #define IMPLE_TYPE_USING_SHARED_PTR +#include "core/api/form_data_part.h" +#include "core/fileapi/blob_part.h" + +namespace webf{ + // 定义回调函数类型 + class FormData :public BindingObject{ + DEFINE_WRAPPERTYPEINFO(); + + public: + using ImplType = FormData*; + + + FormData() = delete; + static FormData* Create(ExecutingContext* context, ExceptionState& exception_state); + explicit FormData(JSContext* ctx) : BindingObject(ctx){}; + explicit FormData(ExecutingContext* context, NativeBindingObject* native_binding_object); + NativeValue HandleCallFromDartSide(const AtomicString& method, + int32_t argc, + const NativeValue* argv, + Dart_Handle dart_object) override; + bool IsFormData() const override; + void append(const AtomicString& name, const std::shared_ptr& value,ExceptionState& exception_state){ + append(name,value,AtomicString::Empty(),exception_state); + } + void append(const AtomicString& name, const std::shared_ptr& value, const AtomicString& fileName,ExceptionState& exception_state); + void form_data_delete(const AtomicString& name,ExceptionState& exception_state); + BlobPart* get(const AtomicString& name,ExceptionState& exception_state); + std::vector getAll(const AtomicString& name,ExceptionState& exception_state); + bool has(const AtomicString& name,ExceptionState& exception_state); + void set(const AtomicString& name, const std::shared_ptr& value,ExceptionState& exception_state){ + set(name,value,AtomicString::Empty(),exception_state); + } + void set(const AtomicString& name, const std::shared_ptr& value, const AtomicString& fileName,ExceptionState& exception_state); + void forEach(const std::shared_ptr& callback, ExceptionState& exception_state){ + forEach(callback,webf::ScriptValue::Empty(ctx()),exception_state); + } + void forEach(const std::shared_ptr& callback, const webf::ScriptValue& thisArg,ExceptionState& exception_state); + std::vector keys(ExceptionState& exception_state) const; + std::vector> values(ExceptionState& exception_state) const; + std::vector> entries(ExceptionState& exception_state) const; + private: + std::vector> _parts; + }; + template <> + struct DowncastTraits { + static bool AllowFrom(const BindingObject& binding_object) { return binding_object.IsFormData(); } + }; +} +#endif \ No newline at end of file diff --git a/bridge/core/api/form_data_part.cc b/bridge/core/api/form_data_part.cc new file mode 100644 index 0000000000..603d8f20be --- /dev/null +++ b/bridge/core/api/form_data_part.cc @@ -0,0 +1,86 @@ +#include "form_data_part.h" +#include "bindings/qjs/atomic_string.h" +#include "bindings/qjs/exception_state.h" +#include "core/fileapi/blob_part.h" +#include "qjs_blob.h" +namespace webf { + +std::shared_ptr FormDataPart::Create(JSContext* ctx, + JSValue value, + ExceptionState& exception_state) { + // auto* context = ExecutingContext::From(ctx); + // if (!JS_IsString(name)) { + // exception_state.ThrowException(ctx, ErrorType::ArgumentError, "Expect a string type of name."); + // return nullptr; + // } + // const char* name_buffer = JS_ToCString(ctx, name); + + // // Create from string. + // if (JS_IsString(value)) { + // const char* buffer = JS_ToCString(ctx, value); + // auto result = std::make_shared(name_buffer, + // BlobPart::Create(ctx, value, exception_state)); + // JS_FreeCString(ctx, name_buffer); + // JS_FreeCString(ctx, buffer); + // return result; + // } + + // // Create from BlobPart + // if (BlobPart::HasInstance(context, value)) { + // auto blob_part = toScriptWrappable(value); + // auto result = std::make_shared(name_buffer, blob_part); + // JS_FreeCString(ctx, name_buffer); + // return result; + // } + + // // Only BlobPart or string type is allowed in form_data according to the current implementation. + // exception_state.ThrowException(ctx, ErrorType::TypeError, "Only BlobPart or string values are allowed in FormData."); + // JS_FreeCString(ctx, name_buffer); + return std::make_shared(); +} + +JSValue FormDataPart::ToQuickJS(JSContext* ctx) const { + JSValue arr = JS_NewArray(ctx); + if (JS_IsNull(arr)) { + // Handle error creating array + return JS_EXCEPTION; + } + + // Convert name to JSValue and add to the array + JSValue nameValue = JS_NewString(ctx, name_.c_str()); + if (JS_IsNull(nameValue)) { + // Handle error creating string + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; + } + JS_SetPropertyUint32(ctx, arr, 0, nameValue); + + // Assuming there's a way to convert BlobPart to JSValue, which is not shown here. + if (!values_.empty()) { + JSValue value = values_[0].ToQuickJS(ctx); + if (JS_IsNull(value)) { + // Handle error converting BlobPart to JSValue + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; + } + JS_SetPropertyUint32(ctx, arr, 1, value); + } else { + // If values_ is empty, set the second element to null + JS_SetPropertyUint32(ctx, arr, 1, JS_NULL); + } + + return arr; +} +// JSValue FormDataPart::ToQuickJS(JSContext* ctx) const { +// // Assuming there's a way to convert BlobPart to JSValue, which is not shown here. +// if (!values_.empty()) { +// return values_[0].ToQuickJS(ctx); +// } +// return JS_NULL; +// } + +void FormDataPart::AddValue(const BlobPart& value) { + values_.push_back(value); +} + +} // namespace webf \ No newline at end of file diff --git a/bridge/core/api/form_data_part.h b/bridge/core/api/form_data_part.h new file mode 100644 index 0000000000..c0470632c2 --- /dev/null +++ b/bridge/core/api/form_data_part.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2019-2022 The Kraken authors. All rights reserved. + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ +#ifndef BRIDGE_API_FORM_DATA_PART_H_ +#define BRIDGE_API_FORM_DATA_PART_H_ + +#include +#include +#include +#include +#include +#include "bindings/qjs/exception_state.h" +#include "core/fileapi/blob_part.h" +namespace webf { + +class BlobPart; +class FormDataPart { + public: + using ImplType = std::shared_ptr; + enum class ContentType { kFile,kBlob, kString }; + + static std::shared_ptr Create(JSContext* ctx, JSValue value, ExceptionState& exception_state); + explicit FormDataPart(std::string name, const std::shared_ptr data) { + this->name_=std::move(name); + AddValue(*data); + }; + explicit FormDataPart(){} + explicit FormDataPart(std::string name){ + this->name_=std::move(name); + } + explicit FormDataPart(std::string name, const std::shared_ptr data,std::string fileName) { + this->name_=std::move(name); + AddValue(*data); + //todo: filename is not used. + }; + JSValue ToQuickJS(JSContext* ctx) const; + const std::string& GetName() const { return name_; } + const std::vector& GetValues() const { return values_; } + void AddValue(const BlobPart& value); +std::shared_ptr getFirst() const { + if (values_.empty()) { + return nullptr; + } else { + return std::make_shared(values_[0]); + } +} + private: + std::string name_; + std::vector values_; +}; + +} // namespace webf + +#endif // BRIDGE_CORE_FILEAPI_BLOB_PART_H_ diff --git a/bridge/core/binding_object.cc b/bridge/core/binding_object.cc index b138e01fa5..e911bb5280 100644 --- a/bridge/core/binding_object.cc +++ b/bridge/core/binding_object.cc @@ -430,4 +430,8 @@ bool BindingObject::IsCanvasGradient() const { return false; } +bool BindingObject::IsFormData() const { + return false; +} + } // namespace webf diff --git a/bridge/core/binding_object.h b/bridge/core/binding_object.h index ec0d328cd1..c662047068 100644 --- a/bridge/core/binding_object.h +++ b/bridge/core/binding_object.h @@ -136,7 +136,7 @@ class BindingObject : public ScriptWrappable { virtual bool IsTouchList() const; virtual bool IsComputedCssStyleDeclaration() const; virtual bool IsCanvasGradient() const; - + virtual bool IsFormData() const; protected: void TrackPendingPromiseBindingContext(BindingObjectPromiseContext* binding_object_promise_context); void FullFillPendingPromise(BindingObjectPromiseContext* binding_object_promise_context); diff --git a/bridge/core/fileapi/blob_part.h b/bridge/core/fileapi/blob_part.h index 8ad570d692..13dade51b9 100644 --- a/bridge/core/fileapi/blob_part.h +++ b/bridge/core/fileapi/blob_part.h @@ -17,8 +17,11 @@ class Blob; class BlobPart { public: +#ifndef IMPLE_TYPE_USING_SHARED_PTR using ImplType = std::shared_ptr; - +#else + using ImplType = BlobPart*; +#endif enum class ContentType { kArrayBuffer, kArrayBufferView, kBlob, kString }; static std::shared_ptr Create(JSContext* ctx, JSValue value, ExceptionState& exception_state); diff --git a/bridge/polyfill/src/array-buffer.ts_ b/bridge/polyfill/src/array-buffer.ts_ new file mode 100644 index 0000000000..dd8781e6ed --- /dev/null +++ b/bridge/polyfill/src/array-buffer.ts_ @@ -0,0 +1,16 @@ +import {webf} from './webf'; + +export class ArrayBuffer{ + private id:string; + private byteLength?:number=0; + constructor(byteLength?:number){ + this.byteLength=byteLength; + this.id = webf.invokeModule('ArrayBufferData', 'init',[byteLength]); + } + public slice(start:number,end:number):void{ + webf.invokeModule('ArrayBufferData','slice',[this.id,start,end]); + } + public toString():string{ + return webf.invokeModule('ArrayBufferData','toString',[this.id]); + } +} \ No newline at end of file diff --git a/bridge/polyfill/src/blob.ts_ b/bridge/polyfill/src/blob.ts_ new file mode 100644 index 0000000000..330e75a449 --- /dev/null +++ b/bridge/polyfill/src/blob.ts_ @@ -0,0 +1,13 @@ +import {webf} from './webf'; +export class Blob{ + private id:string; + constructor(){ + this.id = webf.invokeModule('Blob', 'init'); + } + public append(name:string,value:any):void{ + webf.invokeModule('Blob','append',[this.id,name,value]); + } + public toString():string{ + return webf.invokeModule('Blob','toString',[this.id]); + } +} \ No newline at end of file diff --git a/bridge/polyfill/src/formdata.ts b/bridge/polyfill/src/formdata.ts new file mode 100644 index 0000000000..8b4ce7556a --- /dev/null +++ b/bridge/polyfill/src/formdata.ts @@ -0,0 +1,19 @@ +import {webf} from './webf'; + +// type FormDataEntryValue = File | string; +type FormDataPart=[key:string,value:any] +type BlobPart=string|File|Blob; +export interface FormData { + new():FormData; + append(name: string, value: BlobPart, fileName?: string): void; + // this method name will be fixed to **delete** when MemberInstaller::InstallFunctions is called + form_data_delete(name: string): void; + get(name: string): BlobPart + getAll(name: string): BlobPart[]; + has(name: string): boolean; + set(name: string, value: BlobPart, fileName?: string): void; + forEach(callbackfn: Function, thisArg?: any): void; + keys():string[] + values():BlobPart[] + entries():FormDataPart[] +} diff --git a/bridge/polyfill/src/index.ts b/bridge/polyfill/src/index.ts index 80fd751402..5ac51e389b 100644 --- a/bridge/polyfill/src/index.ts +++ b/bridge/polyfill/src/index.ts @@ -22,6 +22,8 @@ import { webf } from './webf'; import { WebSocket } from './websocket' import { ResizeObserver } from './resize-observer'; import { _AbortController, _AbortSignal } from './abort-signal'; +// import { FormData } from './formdata' +// import { ArrayBuffer } from './array-buffer'; defineGlobalProperty('console', console); defineGlobalProperty('Request', Request); @@ -45,7 +47,9 @@ defineGlobalProperty('WebSocket', WebSocket); defineGlobalProperty('ResizeObserver', ResizeObserver); defineGlobalProperty('AbortSignal', _AbortSignal); defineGlobalProperty('AbortController', _AbortController); - +// defineGlobalProperty('ArrayBuffer', ArrayBuffer); +// defineGlobalProperty('Blob', Blob); +// defineGlobalProperty('FormData', FormData); function defineGlobalProperty(key: string, value: any, isEnumerable: boolean = true) { Object.defineProperty(globalThis, key, { value: value, @@ -54,3 +58,33 @@ function defineGlobalProperty(key: string, value: any, isEnumerable: boolean = t configurable: true }); } +// // 定义一个函数来覆盖 getOwnPropertyDescriptor +// const getOwnPropertyDescriptorOverride = (key: string): PropertyDescriptor | undefined => { + +// // 返回原生的 getOwnPropertyDescriptor 方法的结果 +// const obj= Reflect.getOwnPropertyDescriptor(globalThis, key); +// if(obj===undefined){ +// if (key !== 'prototype' && key !== 'constructor') { +// // 尝试获取元素 +// const element = document.getElementById(key); +// if (element !== null) { +// // 如果找到了元素,则返回一个新的属性描述符 +// return { +// get: ()=>document.getElementById(key), +// enumerable: true, +// writable: false, +// configurable: true, +// }; +// } +// } +// } +// return obj; +// }; + +// // 覆盖 globalThis 的 getOwnPropertyDescriptor 方法 +// Object.defineProperty(globalThis, 'getOwnPropertyDescriptor', { +// value: getOwnPropertyDescriptorOverride, +// enumerable: false, // 通常不希望这个方法是可枚举的 +// writable: true, +// configurable: true, +// }); diff --git a/buildAndRunF.sh b/buildAndRunF.sh new file mode 100644 index 0000000000..420d6ea81f --- /dev/null +++ b/buildAndRunF.sh @@ -0,0 +1,21 @@ +runexample(){ + ret=$PWD + cd webf/example && flutter run --release + cd $ret +} +runexampled(){ + ret=$PWD + cd webf/example && flutter run + cd $ret +} +build-linux(){ + npm run build:bridge:linux:release + cp -f bridge/cmake-build-linux/compile_commands.json . +} +build-linuxd(){ + npm run build:bridge:linux && + cp -f bridge/cmake-build-linux/compile_commands.json . +} +echo To initialize the build, using build-linux or build-linuxd: +echo then use runexample for release build, or runexample for debug build + diff --git a/integration_tests/lib/bridge/match_snapshots.dart b/integration_tests/lib/bridge/match_snapshots.dart index 6102acb3a0..d00747e497 100644 --- a/integration_tests/lib/bridge/match_snapshots.dart +++ b/integration_tests/lib/bridge/match_snapshots.dart @@ -3,7 +3,6 @@ */ import 'dart:async'; import 'dart:io'; -import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:image_compare/image_compare.dart'; diff --git a/integration_tests/lib/bridge/test_input.dart b/integration_tests/lib/bridge/test_input.dart index 77afb7b68a..7b5543a071 100644 --- a/integration_tests/lib/bridge/test_input.dart +++ b/integration_tests/lib/bridge/test_input.dart @@ -7,7 +7,6 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/integration_tests/lib/custom_elements/flutter_listview.dart b/integration_tests/lib/custom_elements/flutter_listview.dart index 2b4434b3e6..75a84380cd 100644 --- a/integration_tests/lib/custom_elements/flutter_listview.dart +++ b/integration_tests/lib/custom_elements/flutter_listview.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:webf/dom.dart'; import 'package:webf/webf.dart'; class FlutterListViewElement extends WidgetElement { diff --git a/integration_tests/lib/custom_elements/flutter_swiper.dart b/integration_tests/lib/custom_elements/flutter_swiper.dart index bf3ca9e046..415bbbaa9d 100644 --- a/integration_tests/lib/custom_elements/flutter_swiper.dart +++ b/integration_tests/lib/custom_elements/flutter_swiper.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:webf/webf.dart'; import 'package:card_swiper/card_swiper.dart'; diff --git a/integration_tests/lib/test_module.dart b/integration_tests/lib/test_module.dart index 01db3c088d..2e72d79c3c 100644 --- a/integration_tests/lib/test_module.dart +++ b/integration_tests/lib/test_module.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'package:webf/webf.dart'; -import 'package:webf/dom.dart'; class DemoModule extends BaseModule { DemoModule(ModuleManager? moduleManager) : super(moduleManager); diff --git a/integration_tests/spec_group.json b/integration_tests/spec_group.json index d11a5905f1..20401da50d 100644 --- a/integration_tests/spec_group.json +++ b/integration_tests/spec_group.json @@ -2,6 +2,7 @@ { "name": "BOM", "specs": [ + "specs/api/**/*.{js,jsx,ts,tsx,html}", "specs/async-storage/**/*.{js,jsx,ts,tsx,html}", "specs/blob/**/*.{js,jsx,ts,tsx,html}", "specs/cookie/**/*.{js,jsx,ts,tsx,html}", diff --git a/integration_tests/specs/api/formdata.ts b/integration_tests/specs/api/formdata.ts new file mode 100644 index 0000000000..5f89a874d6 --- /dev/null +++ b/integration_tests/specs/api/formdata.ts @@ -0,0 +1,40 @@ +describe('FormData', () => { + it('should handle multiple appends of the same key', async () => { + // 创建一个 FormData 实例 + const formData = new FormData(); + console.log('formData', formData, 'type=', formData.constructor.name); + + // 添加一些数据 + formData.append('key1', 'value1'); + formData.append('key1', 'value1.1'); + formData.append('key2', 'value2'); + formData.set('key2', 'new-value2'); + + // 测试 get 方法 + console.log('Getting key1:', formData.get('key1')); + expect(formData.get('key1')).toBe('value1.1'); // 最后一个值 + + // 测试 getAll 方法 + console.log('Getting all key1:', formData.getAll('key1')); + expect(formData.getAll('key1')).toEqual(['value1', 'value1.1']); // 所有值 + + // 测试 has 方法 + console.log('Checking key1:', formData.has('key1')); + console.log('Checking key2:', formData.has('key2')); + expect(formData.has('key1')).toBe(true); + expect(formData.has('key2')).toBe(true); + + // 测试 del 方法 + formData.delete('key1'); + console.log('Deleting key1:', formData.has('key1')); + expect(formData.has('key1')).toBe(false); + + // 测试 forEach 方法 + const logEntries:string[] = []; + formData.forEach((value, key) => { + logEntries.push(`Iterating: Key: ${key}, Value: ${value}`); + }); + console.log(logEntries.join('\n')); + expect(logEntries).toContain('Iterating: Key: key2, Value: new-value2'); + }); + }); \ No newline at end of file diff --git a/webf/example/assets/bundle.html b/webf/example/assets/bundle.html index 431559ebb8..61ea3b5e1b 100644 --- a/webf/example/assets/bundle.html +++ b/webf/example/assets/bundle.html @@ -46,6 +46,10 @@ +

Blob, ArrayBuffer, DataView Test

+ +
  
     
+
   
 
diff --git a/webf/example/assets/bundle.js b/webf/example/assets/bundle.js
index d2d2fb9762..1aed267a25 100644
--- a/webf/example/assets/bundle.js
+++ b/webf/example/assets/bundle.js
@@ -10,4 +10,82 @@ p.appendChild(text1);
 p.appendChild(br);
 p.appendChild(text2);
 
-document.body.appendChild(p);
\ No newline at end of file
+document.body.appendChild(p);
+var button = document.getElementById('button');
+
+function testBlobArrayBufferDataView() {  
+    const output = document.getElementById('output');  
+    console.log(1);
+    // 测试 Blob  
+    const blob = new Blob(['Hello, Blob!']);  
+    console.log(2);
+    // const reader = new FileReader();  
+    // console.log(3);
+    // reader.onload = function(e) {  
+    //     output.textContent += 'Blob content: ' + e.target.result + '\n';  
+    //     console.log(4);
+    // };  
+    // console.log(5);
+    // reader.readAsText(blob);  
+    // console.log(6);
+    // 测试 ArrayBuffer 和 DataView  
+    const buffer = new ArrayBuffer(16); // 创建一个 16 字节的 ArrayBuffer  
+    console.log('buffer',typeof buffer,buffer.constructor.name);
+    const view = new DataView(buffer);  console.log(7);
+  
+    // 设置 ArrayBuffer 中的一些数据  
+    view.setInt32(0, 25, true); // 在位置 0 写入一个 32 位整数,小端字节序  
+    console.log(8);
+    view.setFloat32(4, Math.PI, true); // 在位置 4 写入一个 32 位浮点数,小端字节序  
+    console.log(9);
+    // 读取 ArrayBuffer 中的数据  
+    output.textContent += 'Int32 at position 0 (little-endian): ' + view.getInt32(0, true) + '\n';  
+    console.log(10);
+    output.textContent += 'Float32 at position 4 (little-endian): ' + view.getFloat32(4, true) + '\n';  
+    console.log(11);
+    // 展示 ArrayBuffer 的类型信息  
+    output.textContent += 'ArrayBuffer byte length: ' + buffer.byteLength + '\n';  
+    console.log(12);
+}  
+
+function testFormData(){
+    // 创建一个 FormData 实例
+    const formData = new FormData();
+    console.log('formData',formData,'type=',formData.constructor.name)
+    // 添加一些数据
+    formData.append('key1', 'value1');
+    formData.append('key1', 'value1.1');
+    formData.append('key2', 'value2');
+    formData.set('key2', 'new-value2');
+
+    // 测试 get 方法
+    console.log('Getting key1:', formData.get('key1'));
+    console.log('Getting key2:', formData.get('key2'));
+
+    // 测试 getAll 方法
+    console.log('Getting all key1:', formData.getAll('key1'));
+
+    console.log('Getting all key2:', formData.getAll('key2'));
+
+    // 测试 has 方法
+    console.log('Checking has key1:', formData.has('key1'));
+    console.log('Checking has key2:', formData.has('key2'));
+
+    // 测试 del 方法
+    formData.delete('key1');
+    console.log('Deleting key1: has key1 after delete:', formData.has('key1'));
+
+    // 测试 forEach 方法
+    formData.forEach((value, key) => {
+        console.log(`Iterating: Key: ${key}, Value: ${value}`);
+    });
+}
+  
+// 当文档加载完成时,为按钮添加点击事件监听器(可选,因为已经在 HTML 中通过 onclick 添加了)  
+document.addEventListener('DOMContentLoaded', function() {  
+    testFormData();
+    const button = document.querySelector('button');  
+    button.addEventListener('click', testBlobArrayBufferDataView);  
+});
+
+
diff --git a/webf/lib/module.dart b/webf/lib/module.dart
index 733e2ec069..fdce466e60 100644
--- a/webf/lib/module.dart
+++ b/webf/lib/module.dart
@@ -15,3 +15,6 @@ export 'src/module/history.dart';
 export 'src/module/hybrid_history.dart';
 export 'src/module/navigation.dart';
 export 'src/module/navigator.dart';
+export 'src/module/blob.dart'; 
+export 'src/module/form_data.dart';
+// export 'src/module/arraybuffer.dart';
diff --git a/webf/lib/src/dom/element_registry.dart b/webf/lib/src/dom/element_registry.dart
index 4f403e3c40..c0f364c107 100644
--- a/webf/lib/src/dom/element_registry.dart
+++ b/webf/lib/src/dom/element_registry.dart
@@ -5,7 +5,6 @@
 import 'package:webf/dom.dart';
 import 'package:webf/html.dart';
 import 'package:webf/foundation.dart';
-import 'package:webf/src/html/router_link.dart';
 import 'package:webf/svg.dart';
 
 typedef ElementCreator = Element Function(BindingContext? context);
diff --git a/webf/lib/src/module/arraybuffer.dart b/webf/lib/src/module/arraybuffer.dart
new file mode 100644
index 0000000000..fbef8b1622
--- /dev/null
+++ b/webf/lib/src/module/arraybuffer.dart
@@ -0,0 +1,58 @@
+import 'dart:typed_data';
+
+import 'package:webf/webf.dart';
+/// A simplified version of the Blob class for demonstration purposes.
+class ArrayBufferData extends BaseModule {
+  Uint8List? buffer;
+  static final Map _instanceMap = {};
+  static int _autoId=0; 
+  /// Creates a new FormData instance.
+  ArrayBufferData(ModuleManager? moduleManager) : super(moduleManager);
+  int get length => buffer!.length;
+  
+  @override
+  void dispose() {
+  }
+
+  static String fromBytes(Uint8List buffer){
+     String id=(_autoId++).toString();
+        _instanceMap[id.toString()]={
+          buffer:buffer
+        };
+        return id;
+  }
+  static ArrayBufferData getInstance(String id){
+    return _instanceMap[id.toString()];
+  }
+  
+  @override
+  String invoke(String method, params, InvokeModuleCallback callback) {
+    if(_instanceMap[params[0]]==null){
+      print('Failed to execute \'$method\' on \'fromData\': nullInstance ');
+    }
+    else {
+      switch (method) {
+        case 'init':
+          String id=(_autoId++).toString();
+          _instanceMap[id.toString()]={
+            buffer:Uint8List(params.length==0?0:params[0])
+          };
+          return id;
+        case 'fromBytes':
+          String id=(_autoId++).toString();
+          _instanceMap[id.toString()]={
+            buffer:params[1]
+          };
+          return id;
+      case 'toString':
+        return _instanceMap[params[0]]?.toString()??'';
+      default:
+        print('Failed to execute \'$method\' on \'fromData\': NoSuchMethod ');
+    }
+    }
+    return EMPTY_STRING;
+  }
+  
+  @override
+  String get name => 'ArrayBuffer';
+}
\ No newline at end of file
diff --git a/webf/lib/src/module/blob.dart b/webf/lib/src/module/blob.dart
new file mode 100644
index 0000000000..ebca421254
--- /dev/null
+++ b/webf/lib/src/module/blob.dart
@@ -0,0 +1,85 @@
+import 'dart:typed_data';
+import 'dart:convert';
+
+import 'package:webf/webf.dart';
+
+
+/// A simplified version of the Blob class for demonstration purposes.
+class Blob extends BaseModule {
+    @override
+  String get name => 'Blob';
+  static final Map _instanceMap = {};
+  static int _autoId=0; 
+  /// Creates a new FormData instance.
+  Blob(ModuleManager? moduleManager) : super(moduleManager);
+
+  Uint8List? _data;
+  final String type='application/octet-stream';
+
+  int get size => _data!.length;
+
+  Uint8List get bytes => _data!;
+
+  String StringResult() {
+    return String.fromCharCodes(_data!);
+  }
+
+  String Base64Result() {
+    return 'data:$type;base64,${base64Encode(_data!)}';
+  }
+
+  // String ArrayBufferResult() {
+  //   ArrayBufferData  ArrayBuffer = moduleManager?.getModule('ArrayBuffer')!;
+  //   return ArrayBuffer.fromBytes(_data!.buffer.asUint8List());
+  // }
+
+  Uint8List slice(int start, int end, [String contentType = '']) {
+    if (start < 0) start = 0;
+    if (end < 0) end = 0;
+    if (end > _data!.length) end = _data!.length;
+    if (start > end) start = end;
+    final slicedData = _data!.sublist(start, end);
+    return slicedData;
+  }
+
+
+  @override
+  String invoke(String method, params, InvokeModuleCallback callback) {
+    if(_instanceMap[params[0]]==null){
+      print('Failed to execute \'$method\' on \'fromData\': nullInstance ');
+    }
+    else {
+      switch (method) {
+        case 'init':
+        String id=(_autoId++).toString();
+        _instanceMap[id.toString()]={
+          _data:params[1],
+          type:params[2]
+        };
+        return id;
+      case 'StringResult':
+        return _instanceMap[params[0]]?.StringResult();
+      case 'Base64Result':
+        return _instanceMap[params[0]]?.Base64Result();
+      case 'ArrayBufferResult':
+        return _instanceMap[params[0]]?.ArrayBufferResult();
+      case 'slice':
+        String id=(_autoId++).toString();
+        Uint8List sliceData=_instanceMap[_autoId.toString()]?.slice(params[1],params[2]);
+        _instanceMap[id.toString()]={
+            _data:sliceData,
+            type:_instanceMap[_autoId.toString()]!.type
+        };
+        return id;
+      case 'toString':
+        return _instanceMap[params[0]]?.toString()??'';
+      default:
+        print('Failed to execute \'$method\' on \'fromData\': NoSuchMethod ');
+    }
+    }
+    return EMPTY_STRING;
+  }
+
+  @override
+  void dispose() {}
+}
\ No newline at end of file
diff --git a/webf/lib/src/module/form_data.dart b/webf/lib/src/module/form_data.dart
new file mode 100644
index 0000000000..521ecee6f8
--- /dev/null
+++ b/webf/lib/src/module/form_data.dart
@@ -0,0 +1,83 @@
+import 'dart:convert';
+
+import 'package:webf/webf.dart';
+
+/// A simple implementation of the FormData interface.
+class FormData extends BaseModule {
+  /// Creates a new FormData instance.
+  FormData(ModuleManager? moduleManager) : super(moduleManager);
+  static final Map _instanceMap = {};
+  static int _autoId=0; 
+  /// The list that holds the data.
+  final List> _list = [];
+ 
+  /// Adds a key-value pair to the FormData.
+  void append(String name, value) {
+     _list.add([name, value]);
+  }
+
+  /// Returns the first value associated with the given key.
+  dynamic getFirst(String name) {
+    for (var entry in _list) {
+      if (entry[0] == name) {
+        return entry[1];
+      }
+    }
+    return null;
+  }
+
+  /// Returns all values associated with the given key.
+  List getAll(String name) {
+    return _list.where((entry) => entry[0] == name).map((entry) => entry[1]).toList();
+  }
+
+  /// Serializes the FormData into a string.
+  @override
+  String toString() {
+    var entries = _list.map((entry) {
+      if (entry[1] is String) {
+        return '${Uri.encodeComponent(entry[0])}=${Uri.encodeComponent(entry[1] as String)}';
+      } else if (entry[1] is Blob) {
+        // Handle Blob serialization.
+        return '${Uri.encodeComponent(entry[0])}=${Uri.encodeComponent(entry[1].Base64Result())}';
+      } else {
+        return '${Uri.encodeComponent(entry[0])}=${Uri.encodeComponent(jsonEncode(entry[1]))}';
+      }
+    }).join('&');
+
+    return entries;
+  }
+
+  @override
+  String invoke(String method, params, InvokeModuleCallback callback) {
+    if(_instanceMap[params[0]]==null){
+      print('Failed to execute \'$method\' on \'fromData\': nullInstance ');
+    }
+    else {
+      switch (method) {
+        case 'init':
+        String id=(_autoId++).toString();
+        _instanceMap[id.toString()]={};
+        return id;
+      case 'append':
+        _instanceMap[params[0]]?.append(params[1], params[2]);
+        break;
+      case 'getFirst':
+        return jsonEncode(_instanceMap[params[0]]?.getFirst(params[1]));
+      case 'getAll':
+        return jsonEncode(_instanceMap[params[0]]?.getAll(params[1]));
+      case 'toString':
+        return _instanceMap[params[0]]?.toString()??'';
+      default:
+        print('Failed to execute \'$method\' on \'fromData\': NoSuchMethod ');
+    }
+    }
+    return EMPTY_STRING;
+  }
+
+  @override
+  void dispose() {}
+
+  @override
+  String get name => 'FormData';
+}
\ No newline at end of file
diff --git a/webf/lib/src/module/module_manager.dart b/webf/lib/src/module/module_manager.dart
index 2f1b54cf82..38e9eccfd3 100644
--- a/webf/lib/src/module/module_manager.dart
+++ b/webf/lib/src/module/module_manager.dart
@@ -44,6 +44,9 @@ void _defineModuleCreator() {
   _defineModule((ModuleManager? moduleManager) => LocalStorageModule(moduleManager));
   _defineModule((ModuleManager? moduleManager) => SessionStorageModule(moduleManager));
   _defineModule((ModuleManager? moduleManager) => WebSocketModule(moduleManager));
+  _defineModule((ModuleManager? moduleManager) => FormData(moduleManager));
+  _defineModule((ModuleManager? moduleManager) => Blob(moduleManager));
+  // _defineModule((ModuleManager? moduleManager) => ArrayBufferData(moduleManager));
 }
 
 final Map _creatorMap = {};
diff --git a/webf/lib/src/svg/text.dart b/webf/lib/src/svg/text.dart
index d720f25d05..3d0c749dde 100644
--- a/webf/lib/src/svg/text.dart
+++ b/webf/lib/src/svg/text.dart
@@ -5,7 +5,6 @@
 import 'package:webf/rendering.dart';
 import 'package:webf/svg.dart';
 
-import 'rendering/text.dart';
 
 class SVGTextElement extends SVGTextPositioningElement {
   @override

From 31dc7229c60d346b475819834c8807f4833bb891 Mon Sep 17 00:00:00 2001
From: wahahahaha 
Date: Tue, 13 Aug 2024 20:04:44 +0800
Subject: [PATCH 02/18] =?UTF-8?q?=E6=B8=85=E7=90=86=E4=BB=A3=E7=A0=81?=
 =?UTF-8?q?=EF=BC=8C=E6=92=A4=E5=9B=9E=E4=B8=8D=E5=BF=85=E8=A6=81=E7=9A=84?=
 =?UTF-8?q?=E6=94=B9=E5=8A=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 bridge/CMakeLists.txt                         |  2 -
 bridge/bindings/qjs/member_installer.cc       |  1 +
 bridge/core/api/form_data.h                   |  2 -
 bridge/core/api/form_data_part.cc             | 35 --------
 bridge/core/fileapi/blob_part.h               |  5 --
 bridge/polyfill/src/array-buffer.ts_          | 16 ----
 bridge/polyfill/src/blob.ts_                  | 13 ---
 bridge/polyfill/src/index.ts                  | 35 --------
 .../lib/bridge/match_snapshots.dart           |  2 +-
 integration_tests/lib/bridge/test_input.dart  |  2 +-
 .../lib/custom_elements/flutter_listview.dart |  1 +
 .../lib/custom_elements/flutter_swiper.dart   |  1 +
 integration_tests/lib/test_module.dart        |  1 +
 webf/example/assets/bundle.html               |  3 -
 webf/example/assets/bundle.js                 | 37 --------
 webf/lib/module.dart                          |  3 -
 webf/lib/src/dom/element_registry.dart        |  1 +
 webf/lib/src/module/arraybuffer.dart          | 58 -------------
 webf/lib/src/module/blob.dart                 | 85 -------------------
 webf/lib/src/module/form_data.dart            | 83 ------------------
 webf/lib/src/module/module_manager.dart       |  3 -
 webf/lib/src/svg/text.dart                    |  1 +
 22 files changed, 8 insertions(+), 382 deletions(-)
 delete mode 100644 bridge/polyfill/src/array-buffer.ts_
 delete mode 100644 bridge/polyfill/src/blob.ts_
 delete mode 100644 webf/lib/src/module/arraybuffer.dart
 delete mode 100644 webf/lib/src/module/blob.dart
 delete mode 100644 webf/lib/src/module/form_data.dart

diff --git a/bridge/CMakeLists.txt b/bridge/CMakeLists.txt
index dc6b25fa03..948ee61f6b 100644
--- a/bridge/CMakeLists.txt
+++ b/bridge/CMakeLists.txt
@@ -1,7 +1,5 @@
 cmake_minimum_required(VERSION 3.10.0)
 set(CMAKE_OSX_DEPLOYMENT_TARGET 10.11)
-set(CMAKE_C_COMPILER_LAUNCHER ccache)  
-set(CMAKE_CXX_COMPILER_LAUNCHER ccache)
 project(WebF)
 
 set(CMAKE_OSX_DEPLOYMENT_TARGET 10.11)
diff --git a/bridge/bindings/qjs/member_installer.cc b/bridge/bindings/qjs/member_installer.cc
index eca299c1e2..ac97374d63 100644
--- a/bridge/bindings/qjs/member_installer.cc
+++ b/bridge/bindings/qjs/member_installer.cc
@@ -68,6 +68,7 @@ void MemberInstaller::InstallAttributes(ExecutingContext* context,
     }
   }
 }
+// Replace the name placeholder to avoid using C++ keyword **delete**.
 const char* fn_form_data_delete="form_data_delete";
 void MemberInstaller::InstallFunctions(ExecutingContext* context,
                                        JSValue root,
diff --git a/bridge/core/api/form_data.h b/bridge/core/api/form_data.h
index d794c2d474..188d8a51ba 100644
--- a/bridge/core/api/form_data.h
+++ b/bridge/core/api/form_data.h
@@ -8,12 +8,10 @@
 #include "core/api/form_data_part.h"
 #include "core/binding_object.h"
 
-// #define IMPLE_TYPE_USING_SHARED_PTR
 #include "core/api/form_data_part.h"
 #include "core/fileapi/blob_part.h"
 
 namespace webf{
-    // 定义回调函数类型  
     class FormData :public BindingObject{
         DEFINE_WRAPPERTYPEINFO();
 
diff --git a/bridge/core/api/form_data_part.cc b/bridge/core/api/form_data_part.cc
index 603d8f20be..9824ee9121 100644
--- a/bridge/core/api/form_data_part.cc
+++ b/bridge/core/api/form_data_part.cc
@@ -8,34 +8,6 @@ namespace webf {
 std::shared_ptr FormDataPart::Create(JSContext* ctx,
                                                    JSValue value,
                                                    ExceptionState& exception_state) {
-  // auto* context = ExecutingContext::From(ctx);
-  // if (!JS_IsString(name)) {
-  //   exception_state.ThrowException(ctx, ErrorType::ArgumentError, "Expect a string type of name.");
-  //   return nullptr;
-  // }
-  // const char* name_buffer = JS_ToCString(ctx, name);
-
-  // // Create from string.
-  // if (JS_IsString(value)) {
-  //   const char* buffer = JS_ToCString(ctx, value);
-  //   auto result = std::make_shared(name_buffer,
-  //                                                BlobPart::Create(ctx, value, exception_state));
-  //   JS_FreeCString(ctx, name_buffer);
-  //   JS_FreeCString(ctx, buffer);
-  //   return result;
-  // }
-
-  // // Create from BlobPart
-  // if (BlobPart::HasInstance(context, value)) {
-  //   auto blob_part = toScriptWrappable(value);
-  //   auto result = std::make_shared(name_buffer, blob_part);
-  //   JS_FreeCString(ctx, name_buffer);
-  //   return result;
-  // }
-
-  // // Only BlobPart or string type is allowed in form_data according to the current implementation.
-  // exception_state.ThrowException(ctx, ErrorType::TypeError, "Only BlobPart or string values are allowed in FormData.");
-  // JS_FreeCString(ctx, name_buffer);
   return std::make_shared();
 }
 
@@ -71,13 +43,6 @@ JSValue FormDataPart::ToQuickJS(JSContext* ctx) const {
 
   return arr;
 }
-// JSValue FormDataPart::ToQuickJS(JSContext* ctx) const {
-//   // Assuming there's a way to convert BlobPart to JSValue, which is not shown here.
-//   if (!values_.empty()) {
-//     return values_[0].ToQuickJS(ctx);
-//   }
-//   return JS_NULL;
-// }
 
 void FormDataPart::AddValue(const BlobPart& value) {
   values_.push_back(value);
diff --git a/bridge/core/fileapi/blob_part.h b/bridge/core/fileapi/blob_part.h
index 13dade51b9..f2ff64c2b9 100644
--- a/bridge/core/fileapi/blob_part.h
+++ b/bridge/core/fileapi/blob_part.h
@@ -17,11 +17,6 @@ class Blob;
 
 class BlobPart {
  public:
-#ifndef IMPLE_TYPE_USING_SHARED_PTR
-  using ImplType = std::shared_ptr;
-#else
-  using ImplType = BlobPart*;
-#endif
   enum class ContentType { kArrayBuffer, kArrayBufferView, kBlob, kString };
 
   static std::shared_ptr Create(JSContext* ctx, JSValue value, ExceptionState& exception_state);
diff --git a/bridge/polyfill/src/array-buffer.ts_ b/bridge/polyfill/src/array-buffer.ts_
deleted file mode 100644
index dd8781e6ed..0000000000
--- a/bridge/polyfill/src/array-buffer.ts_
+++ /dev/null
@@ -1,16 +0,0 @@
-import {webf} from './webf';
-
-export class ArrayBuffer{
-    private id:string;
-    private byteLength?:number=0;
-    constructor(byteLength?:number){
-        this.byteLength=byteLength;
-        this.id = webf.invokeModule('ArrayBufferData', 'init',[byteLength]);
-    }
-    public slice(start:number,end:number):void{
-        webf.invokeModule('ArrayBufferData','slice',[this.id,start,end]);
-    }
-    public toString():string{
-        return webf.invokeModule('ArrayBufferData','toString',[this.id]);
-    }
-}
\ No newline at end of file
diff --git a/bridge/polyfill/src/blob.ts_ b/bridge/polyfill/src/blob.ts_
deleted file mode 100644
index 330e75a449..0000000000
--- a/bridge/polyfill/src/blob.ts_
+++ /dev/null
@@ -1,13 +0,0 @@
-import {webf} from './webf';
-export class Blob{
-    private id:string;
-    constructor(){
-        this.id = webf.invokeModule('Blob', 'init');
-    }
-    public append(name:string,value:any):void{
-        webf.invokeModule('Blob','append',[this.id,name,value]);
-    }
-    public toString():string{
-        return webf.invokeModule('Blob','toString',[this.id]);
-    }
-}
\ No newline at end of file
diff --git a/bridge/polyfill/src/index.ts b/bridge/polyfill/src/index.ts
index 5ac51e389b..9c76040c89 100644
--- a/bridge/polyfill/src/index.ts
+++ b/bridge/polyfill/src/index.ts
@@ -22,8 +22,6 @@ import { webf } from './webf';
 import { WebSocket } from './websocket'
 import { ResizeObserver } from './resize-observer';
 import { _AbortController, _AbortSignal } from './abort-signal';
-// import { FormData } from './formdata'
-// import { ArrayBuffer } from './array-buffer';
 
 defineGlobalProperty('console', console);
 defineGlobalProperty('Request', Request);
@@ -47,9 +45,6 @@ defineGlobalProperty('WebSocket', WebSocket);
 defineGlobalProperty('ResizeObserver', ResizeObserver);
 defineGlobalProperty('AbortSignal', _AbortSignal);
 defineGlobalProperty('AbortController', _AbortController);
-// defineGlobalProperty('ArrayBuffer', ArrayBuffer);
-// defineGlobalProperty('Blob', Blob);
-// defineGlobalProperty('FormData', FormData);
 function defineGlobalProperty(key: string, value: any, isEnumerable: boolean = true) {
   Object.defineProperty(globalThis, key, {
     value: value,
@@ -58,33 +53,3 @@ function defineGlobalProperty(key: string, value: any, isEnumerable: boolean = t
     configurable: true
   });
 }
-// // 定义一个函数来覆盖 getOwnPropertyDescriptor
-// const getOwnPropertyDescriptorOverride = (key: string): PropertyDescriptor | undefined => {
-  
-//   // 返回原生的 getOwnPropertyDescriptor 方法的结果
-//   const obj= Reflect.getOwnPropertyDescriptor(globalThis, key);
-//   if(obj===undefined){
-//     if (key !== 'prototype' && key !== 'constructor') {
-//       // 尝试获取元素
-//       const element = document.getElementById(key);
-//       if (element !== null) {
-//         // 如果找到了元素,则返回一个新的属性描述符
-//         return {
-//           get: ()=>document.getElementById(key),
-//           enumerable: true,
-//           writable: false,
-//           configurable: true,
-//         };
-//       }
-//     }
-//   }
-//   return obj;
-// };
-
-// // 覆盖 globalThis 的 getOwnPropertyDescriptor 方法
-// Object.defineProperty(globalThis, 'getOwnPropertyDescriptor', {
-//   value: getOwnPropertyDescriptorOverride,
-//   enumerable: false, // 通常不希望这个方法是可枚举的
-//   writable: true,
-//   configurable: true,
-// });
diff --git a/integration_tests/lib/bridge/match_snapshots.dart b/integration_tests/lib/bridge/match_snapshots.dart
index d00747e497..6c5edffc6d 100644
--- a/integration_tests/lib/bridge/match_snapshots.dart
+++ b/integration_tests/lib/bridge/match_snapshots.dart
@@ -5,7 +5,7 @@ import 'dart:async';
 import 'dart:io';
 import 'package:flutter/foundation.dart';
 import 'package:image_compare/image_compare.dart';
-
+import 'dart:typed_data';
 import 'package:image/image.dart';
 import 'package:path/path.dart' as path;
 
diff --git a/integration_tests/lib/bridge/test_input.dart b/integration_tests/lib/bridge/test_input.dart
index 7b5543a071..48e3901345 100644
--- a/integration_tests/lib/bridge/test_input.dart
+++ b/integration_tests/lib/bridge/test_input.dart
@@ -6,7 +6,7 @@
 // found in the LICENSE file.
 
 import 'dart:async';
-
+import 'package:flutter/foundation.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
 
diff --git a/integration_tests/lib/custom_elements/flutter_listview.dart b/integration_tests/lib/custom_elements/flutter_listview.dart
index 75a84380cd..2b4434b3e6 100644
--- a/integration_tests/lib/custom_elements/flutter_listview.dart
+++ b/integration_tests/lib/custom_elements/flutter_listview.dart
@@ -1,4 +1,5 @@
 import 'package:flutter/material.dart';
+import 'package:webf/dom.dart';
 import 'package:webf/webf.dart';
 
 class FlutterListViewElement extends WidgetElement {
diff --git a/integration_tests/lib/custom_elements/flutter_swiper.dart b/integration_tests/lib/custom_elements/flutter_swiper.dart
index 415bbbaa9d..bf3ca9e046 100644
--- a/integration_tests/lib/custom_elements/flutter_swiper.dart
+++ b/integration_tests/lib/custom_elements/flutter_swiper.dart
@@ -1,4 +1,5 @@
 import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
 import 'package:webf/webf.dart';
 import 'package:card_swiper/card_swiper.dart';
 
diff --git a/integration_tests/lib/test_module.dart b/integration_tests/lib/test_module.dart
index 2e72d79c3c..01db3c088d 100644
--- a/integration_tests/lib/test_module.dart
+++ b/integration_tests/lib/test_module.dart
@@ -1,5 +1,6 @@
 import 'dart:async';
 import 'package:webf/webf.dart';
+import 'package:webf/dom.dart';
 
 class DemoModule extends BaseModule {
   DemoModule(ModuleManager? moduleManager) : super(moduleManager);
diff --git a/webf/example/assets/bundle.html b/webf/example/assets/bundle.html
index 61ea3b5e1b..5803758576 100644
--- a/webf/example/assets/bundle.html
+++ b/webf/example/assets/bundle.html
@@ -46,9 +46,6 @@
     
-     

Blob, ArrayBuffer, DataView Test

- -
  
     
 
   
diff --git a/webf/example/assets/bundle.js b/webf/example/assets/bundle.js
index 1aed267a25..34e3b47695 100644
--- a/webf/example/assets/bundle.js
+++ b/webf/example/assets/bundle.js
@@ -13,41 +13,6 @@ p.appendChild(text2);
 document.body.appendChild(p);
 var button = document.getElementById('button');
 
-function testBlobArrayBufferDataView() {  
-    const output = document.getElementById('output');  
-    console.log(1);
-    // 测试 Blob  
-    const blob = new Blob(['Hello, Blob!']);  
-    console.log(2);
-    // const reader = new FileReader();  
-    // console.log(3);
-    // reader.onload = function(e) {  
-    //     output.textContent += 'Blob content: ' + e.target.result + '\n';  
-    //     console.log(4);
-    // };  
-    // console.log(5);
-    // reader.readAsText(blob);  
-    // console.log(6);
-    // 测试 ArrayBuffer 和 DataView  
-    const buffer = new ArrayBuffer(16); // 创建一个 16 字节的 ArrayBuffer  
-    console.log('buffer',typeof buffer,buffer.constructor.name);
-    const view = new DataView(buffer);  console.log(7);
-  
-    // 设置 ArrayBuffer 中的一些数据  
-    view.setInt32(0, 25, true); // 在位置 0 写入一个 32 位整数,小端字节序  
-    console.log(8);
-    view.setFloat32(4, Math.PI, true); // 在位置 4 写入一个 32 位浮点数,小端字节序  
-    console.log(9);
-    // 读取 ArrayBuffer 中的数据  
-    output.textContent += 'Int32 at position 0 (little-endian): ' + view.getInt32(0, true) + '\n';  
-    console.log(10);
-    output.textContent += 'Float32 at position 4 (little-endian): ' + view.getFloat32(4, true) + '\n';  
-    console.log(11);
-    // 展示 ArrayBuffer 的类型信息  
-    output.textContent += 'ArrayBuffer byte length: ' + buffer.byteLength + '\n';  
-    console.log(12);
-}  
-
 function testFormData(){
     // 创建一个 FormData 实例
     const formData = new FormData();
@@ -84,8 +49,6 @@ function testFormData(){
 // 当文档加载完成时,为按钮添加点击事件监听器(可选,因为已经在 HTML 中通过 onclick 添加了)  
 document.addEventListener('DOMContentLoaded', function() {  
     testFormData();
-    const button = document.querySelector('button');  
-    button.addEventListener('click', testBlobArrayBufferDataView);  
 });
 
 
diff --git a/webf/lib/module.dart b/webf/lib/module.dart
index fdce466e60..733e2ec069 100644
--- a/webf/lib/module.dart
+++ b/webf/lib/module.dart
@@ -15,6 +15,3 @@ export 'src/module/history.dart';
 export 'src/module/hybrid_history.dart';
 export 'src/module/navigation.dart';
 export 'src/module/navigator.dart';
-export 'src/module/blob.dart'; 
-export 'src/module/form_data.dart';
-// export 'src/module/arraybuffer.dart';
diff --git a/webf/lib/src/dom/element_registry.dart b/webf/lib/src/dom/element_registry.dart
index c0f364c107..4f403e3c40 100644
--- a/webf/lib/src/dom/element_registry.dart
+++ b/webf/lib/src/dom/element_registry.dart
@@ -5,6 +5,7 @@
 import 'package:webf/dom.dart';
 import 'package:webf/html.dart';
 import 'package:webf/foundation.dart';
+import 'package:webf/src/html/router_link.dart';
 import 'package:webf/svg.dart';
 
 typedef ElementCreator = Element Function(BindingContext? context);
diff --git a/webf/lib/src/module/arraybuffer.dart b/webf/lib/src/module/arraybuffer.dart
deleted file mode 100644
index fbef8b1622..0000000000
--- a/webf/lib/src/module/arraybuffer.dart
+++ /dev/null
@@ -1,58 +0,0 @@
-import 'dart:typed_data';
-
-import 'package:webf/webf.dart';
-/// A simplified version of the Blob class for demonstration purposes.
-class ArrayBufferData extends BaseModule {
-  Uint8List? buffer;
-  static final Map _instanceMap = {};
-  static int _autoId=0; 
-  /// Creates a new FormData instance.
-  ArrayBufferData(ModuleManager? moduleManager) : super(moduleManager);
-  int get length => buffer!.length;
-  
-  @override
-  void dispose() {
-  }
-
-  static String fromBytes(Uint8List buffer){
-     String id=(_autoId++).toString();
-        _instanceMap[id.toString()]={
-          buffer:buffer
-        };
-        return id;
-  }
-  static ArrayBufferData getInstance(String id){
-    return _instanceMap[id.toString()];
-  }
-  
-  @override
-  String invoke(String method, params, InvokeModuleCallback callback) {
-    if(_instanceMap[params[0]]==null){
-      print('Failed to execute \'$method\' on \'fromData\': nullInstance ');
-    }
-    else {
-      switch (method) {
-        case 'init':
-          String id=(_autoId++).toString();
-          _instanceMap[id.toString()]={
-            buffer:Uint8List(params.length==0?0:params[0])
-          };
-          return id;
-        case 'fromBytes':
-          String id=(_autoId++).toString();
-          _instanceMap[id.toString()]={
-            buffer:params[1]
-          };
-          return id;
-      case 'toString':
-        return _instanceMap[params[0]]?.toString()??'';
-      default:
-        print('Failed to execute \'$method\' on \'fromData\': NoSuchMethod ');
-    }
-    }
-    return EMPTY_STRING;
-  }
-  
-  @override
-  String get name => 'ArrayBuffer';
-}
\ No newline at end of file
diff --git a/webf/lib/src/module/blob.dart b/webf/lib/src/module/blob.dart
deleted file mode 100644
index ebca421254..0000000000
--- a/webf/lib/src/module/blob.dart
+++ /dev/null
@@ -1,85 +0,0 @@
-import 'dart:typed_data';
-import 'dart:convert';
-
-import 'package:webf/webf.dart';
-
-
-/// A simplified version of the Blob class for demonstration purposes.
-class Blob extends BaseModule {
-    @override
-  String get name => 'Blob';
-  static final Map _instanceMap = {};
-  static int _autoId=0; 
-  /// Creates a new FormData instance.
-  Blob(ModuleManager? moduleManager) : super(moduleManager);
-
-  Uint8List? _data;
-  final String type='application/octet-stream';
-
-  int get size => _data!.length;
-
-  Uint8List get bytes => _data!;
-
-  String StringResult() {
-    return String.fromCharCodes(_data!);
-  }
-
-  String Base64Result() {
-    return 'data:$type;base64,${base64Encode(_data!)}';
-  }
-
-  // String ArrayBufferResult() {
-  //   ArrayBufferData  ArrayBuffer = moduleManager?.getModule('ArrayBuffer')!;
-  //   return ArrayBuffer.fromBytes(_data!.buffer.asUint8List());
-  // }
-
-  Uint8List slice(int start, int end, [String contentType = '']) {
-    if (start < 0) start = 0;
-    if (end < 0) end = 0;
-    if (end > _data!.length) end = _data!.length;
-    if (start > end) start = end;
-    final slicedData = _data!.sublist(start, end);
-    return slicedData;
-  }
-
-
-  @override
-  String invoke(String method, params, InvokeModuleCallback callback) {
-    if(_instanceMap[params[0]]==null){
-      print('Failed to execute \'$method\' on \'fromData\': nullInstance ');
-    }
-    else {
-      switch (method) {
-        case 'init':
-        String id=(_autoId++).toString();
-        _instanceMap[id.toString()]={
-          _data:params[1],
-          type:params[2]
-        };
-        return id;
-      case 'StringResult':
-        return _instanceMap[params[0]]?.StringResult();
-      case 'Base64Result':
-        return _instanceMap[params[0]]?.Base64Result();
-      case 'ArrayBufferResult':
-        return _instanceMap[params[0]]?.ArrayBufferResult();
-      case 'slice':
-        String id=(_autoId++).toString();
-        Uint8List sliceData=_instanceMap[_autoId.toString()]?.slice(params[1],params[2]);
-        _instanceMap[id.toString()]={
-            _data:sliceData,
-            type:_instanceMap[_autoId.toString()]!.type
-        };
-        return id;
-      case 'toString':
-        return _instanceMap[params[0]]?.toString()??'';
-      default:
-        print('Failed to execute \'$method\' on \'fromData\': NoSuchMethod ');
-    }
-    }
-    return EMPTY_STRING;
-  }
-
-  @override
-  void dispose() {}
-}
\ No newline at end of file
diff --git a/webf/lib/src/module/form_data.dart b/webf/lib/src/module/form_data.dart
deleted file mode 100644
index 521ecee6f8..0000000000
--- a/webf/lib/src/module/form_data.dart
+++ /dev/null
@@ -1,83 +0,0 @@
-import 'dart:convert';
-
-import 'package:webf/webf.dart';
-
-/// A simple implementation of the FormData interface.
-class FormData extends BaseModule {
-  /// Creates a new FormData instance.
-  FormData(ModuleManager? moduleManager) : super(moduleManager);
-  static final Map _instanceMap = {};
-  static int _autoId=0; 
-  /// The list that holds the data.
-  final List> _list = [];
- 
-  /// Adds a key-value pair to the FormData.
-  void append(String name, value) {
-     _list.add([name, value]);
-  }
-
-  /// Returns the first value associated with the given key.
-  dynamic getFirst(String name) {
-    for (var entry in _list) {
-      if (entry[0] == name) {
-        return entry[1];
-      }
-    }
-    return null;
-  }
-
-  /// Returns all values associated with the given key.
-  List getAll(String name) {
-    return _list.where((entry) => entry[0] == name).map((entry) => entry[1]).toList();
-  }
-
-  /// Serializes the FormData into a string.
-  @override
-  String toString() {
-    var entries = _list.map((entry) {
-      if (entry[1] is String) {
-        return '${Uri.encodeComponent(entry[0])}=${Uri.encodeComponent(entry[1] as String)}';
-      } else if (entry[1] is Blob) {
-        // Handle Blob serialization.
-        return '${Uri.encodeComponent(entry[0])}=${Uri.encodeComponent(entry[1].Base64Result())}';
-      } else {
-        return '${Uri.encodeComponent(entry[0])}=${Uri.encodeComponent(jsonEncode(entry[1]))}';
-      }
-    }).join('&');
-
-    return entries;
-  }
-
-  @override
-  String invoke(String method, params, InvokeModuleCallback callback) {
-    if(_instanceMap[params[0]]==null){
-      print('Failed to execute \'$method\' on \'fromData\': nullInstance ');
-    }
-    else {
-      switch (method) {
-        case 'init':
-        String id=(_autoId++).toString();
-        _instanceMap[id.toString()]={};
-        return id;
-      case 'append':
-        _instanceMap[params[0]]?.append(params[1], params[2]);
-        break;
-      case 'getFirst':
-        return jsonEncode(_instanceMap[params[0]]?.getFirst(params[1]));
-      case 'getAll':
-        return jsonEncode(_instanceMap[params[0]]?.getAll(params[1]));
-      case 'toString':
-        return _instanceMap[params[0]]?.toString()??'';
-      default:
-        print('Failed to execute \'$method\' on \'fromData\': NoSuchMethod ');
-    }
-    }
-    return EMPTY_STRING;
-  }
-
-  @override
-  void dispose() {}
-
-  @override
-  String get name => 'FormData';
-}
\ No newline at end of file
diff --git a/webf/lib/src/module/module_manager.dart b/webf/lib/src/module/module_manager.dart
index 38e9eccfd3..2f1b54cf82 100644
--- a/webf/lib/src/module/module_manager.dart
+++ b/webf/lib/src/module/module_manager.dart
@@ -44,9 +44,6 @@ void _defineModuleCreator() {
   _defineModule((ModuleManager? moduleManager) => LocalStorageModule(moduleManager));
   _defineModule((ModuleManager? moduleManager) => SessionStorageModule(moduleManager));
   _defineModule((ModuleManager? moduleManager) => WebSocketModule(moduleManager));
-  _defineModule((ModuleManager? moduleManager) => FormData(moduleManager));
-  _defineModule((ModuleManager? moduleManager) => Blob(moduleManager));
-  // _defineModule((ModuleManager? moduleManager) => ArrayBufferData(moduleManager));
 }
 
 final Map _creatorMap = {};
diff --git a/webf/lib/src/svg/text.dart b/webf/lib/src/svg/text.dart
index 3d0c749dde..d720f25d05 100644
--- a/webf/lib/src/svg/text.dart
+++ b/webf/lib/src/svg/text.dart
@@ -5,6 +5,7 @@
 import 'package:webf/rendering.dart';
 import 'package:webf/svg.dart';
 
+import 'rendering/text.dart';
 
 class SVGTextElement extends SVGTextPositioningElement {
   @override

From 34d052d2bc131b21b1e83d5ec7f6d01d46cda019 Mon Sep 17 00:00:00 2001
From: wahahahaha 
Date: Tue, 13 Aug 2024 20:24:12 +0800
Subject: [PATCH 03/18] =?UTF-8?q?=E6=B8=85=E7=90=86=E4=BB=A3=E7=A0=81?=
 =?UTF-8?q?=EF=BC=8C=E6=92=A4=E5=9B=9E=E4=B8=8D=E5=BF=85=E8=A6=81=E6=94=B9?=
 =?UTF-8?q?=E5=8A=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 integration_tests/lib/bridge/match_snapshots.dart | 2 +-
 integration_tests/lib/bridge/test_input.dart      | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/integration_tests/lib/bridge/match_snapshots.dart b/integration_tests/lib/bridge/match_snapshots.dart
index 6c5edffc6d..abd98e5b19 100644
--- a/integration_tests/lib/bridge/match_snapshots.dart
+++ b/integration_tests/lib/bridge/match_snapshots.dart
@@ -3,9 +3,9 @@
  */
 import 'dart:async';
 import 'dart:io';
+import 'dart:typed_data';
 import 'package:flutter/foundation.dart';
 import 'package:image_compare/image_compare.dart';
-import 'dart:typed_data';
 import 'package:image/image.dart';
 import 'package:path/path.dart' as path;
 
diff --git a/integration_tests/lib/bridge/test_input.dart b/integration_tests/lib/bridge/test_input.dart
index 48e3901345..77afb7b68a 100644
--- a/integration_tests/lib/bridge/test_input.dart
+++ b/integration_tests/lib/bridge/test_input.dart
@@ -6,6 +6,7 @@
 // found in the LICENSE file.
 
 import 'dart:async';
+
 import 'package:flutter/foundation.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';

From 41474739279bf0ee9a45f93df681adbb757cb564 Mon Sep 17 00:00:00 2001
From: wahahahaha 
Date: Tue, 13 Aug 2024 20:04:44 +0800
Subject: [PATCH 04/18] =?UTF-8?q?=E6=B8=85=E7=90=86=E4=BB=A3=E7=A0=81?=
 =?UTF-8?q?=EF=BC=8C=E6=92=A4=E5=9B=9E=E4=B8=8D=E5=BF=85=E8=A6=81=E7=9A=84?=
 =?UTF-8?q?=E6=94=B9=E5=8A=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

清理代码,撤回不必要改动

清理代码,撤回不必要的改动

清理代码,撤回不必要的改动
---
 bridge/bindings/qjs/member_installer.cc | 4 +++-
 bridge/core/api/form_data.d.ts          | 3 ++-
 bridge/core/fileapi/blob_part.h         | 2 ++
 buildAndRunF.sh                         | 4 ++--
 webf/example/assets/bundle.html         | 1 -
 5 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/bridge/bindings/qjs/member_installer.cc b/bridge/bindings/qjs/member_installer.cc
index ac97374d63..995cca3fdf 100644
--- a/bridge/bindings/qjs/member_installer.cc
+++ b/bridge/bindings/qjs/member_installer.cc
@@ -68,7 +68,7 @@ void MemberInstaller::InstallAttributes(ExecutingContext* context,
     }
   }
 }
-// Replace the name placeholder to avoid using C++ keyword **delete**.
+// Defined a placeholder name in FormData to avoid using C++ keyword **delete**.
 const char* fn_form_data_delete="form_data_delete";
 void MemberInstaller::InstallFunctions(ExecutingContext* context,
                                        JSValue root,
@@ -76,6 +76,8 @@ void MemberInstaller::InstallFunctions(ExecutingContext* context,
   JSContext* ctx = context->ctx();
   for (auto& c : config) {
     std::string name = c.name;
+
+    // replace the placeholder name to real one.
     if(c.name==fn_form_data_delete){
       name = "delete";
     }
diff --git a/bridge/core/api/form_data.d.ts b/bridge/core/api/form_data.d.ts
index e7a945e23a..aadefea8bb 100644
--- a/bridge/core/api/form_data.d.ts
+++ b/bridge/core/api/form_data.d.ts
@@ -3,7 +3,8 @@ type FormDataPart={}
 export interface FormData {
     new():FormData;
     append(name: string, value: BlobPart, fileName?: string): void;
-    // this method name will be fixed to **delete** when MemberInstaller::InstallFunctions is called
+    // This method name is a placeholder of **delete** method to avoid using C++ keyword
+    // and will be replaced to **delete** when installing in MemberInstaller::InstallFunctions.
     form_data_delete(name: string): void;
     get(name: string): BlobPart
     getAll(name: string): BlobPart[];
diff --git a/bridge/core/fileapi/blob_part.h b/bridge/core/fileapi/blob_part.h
index f2ff64c2b9..8ad570d692 100644
--- a/bridge/core/fileapi/blob_part.h
+++ b/bridge/core/fileapi/blob_part.h
@@ -17,6 +17,8 @@ class Blob;
 
 class BlobPart {
  public:
+  using ImplType = std::shared_ptr;
+
   enum class ContentType { kArrayBuffer, kArrayBufferView, kBlob, kString };
 
   static std::shared_ptr Create(JSContext* ctx, JSValue value, ExceptionState& exception_state);
diff --git a/buildAndRunF.sh b/buildAndRunF.sh
index 420d6ea81f..4c089fee42 100644
--- a/buildAndRunF.sh
+++ b/buildAndRunF.sh
@@ -1,11 +1,11 @@
 runexample(){
     ret=$PWD
-    cd webf/example && flutter run --release
+    cd webf/example && flutter run -d linux --release
     cd $ret
 }
 runexampled(){
     ret=$PWD
-    cd webf/example && flutter run
+    cd webf/example && flutter run -d linux
     cd $ret
 }
 build-linux(){
diff --git a/webf/example/assets/bundle.html b/webf/example/assets/bundle.html
index 5803758576..431559ebb8 100644
--- a/webf/example/assets/bundle.html
+++ b/webf/example/assets/bundle.html
@@ -47,6 +47,5 @@
       console.log('Welcome to webf!');
     
     
-
   
 

From 2b905f0abac8efa592d22700c7f00b0d51e20126 Mon Sep 17 00:00:00 2001
From: wahahahaha 
Date: Tue, 13 Aug 2024 20:56:13 +0800
Subject: [PATCH 05/18] =?UTF-8?q?=E6=B8=85=E7=90=86=E4=BB=A3=E7=A0=81?=
 =?UTF-8?q?=EF=BC=8C=E6=92=A4=E5=9B=9E=E4=B8=8D=E5=BF=85=E8=A6=81=E7=9A=84?=
 =?UTF-8?q?=E6=94=B9=E5=8A=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 bridge/polyfill/src/index.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/bridge/polyfill/src/index.ts b/bridge/polyfill/src/index.ts
index 9c76040c89..80fd751402 100644
--- a/bridge/polyfill/src/index.ts
+++ b/bridge/polyfill/src/index.ts
@@ -45,6 +45,7 @@ defineGlobalProperty('WebSocket', WebSocket);
 defineGlobalProperty('ResizeObserver', ResizeObserver);
 defineGlobalProperty('AbortSignal', _AbortSignal);
 defineGlobalProperty('AbortController', _AbortController);
+
 function defineGlobalProperty(key: string, value: any, isEnumerable: boolean = true) {
   Object.defineProperty(globalThis, key, {
     value: value,

From e9e3ad19bb6973f44a9668a98ec19c277dc1fc48 Mon Sep 17 00:00:00 2001
From: wahahahaha 
Date: Tue, 13 Aug 2024 21:02:53 +0800
Subject: [PATCH 06/18] =?UTF-8?q?=E6=B8=85=E7=90=86=E4=BB=A3=E7=A0=81?=
 =?UTF-8?q?=EF=BC=8C=E6=92=A4=E5=9B=9E=E4=B8=8D=E5=BF=85=E8=A6=81=E7=9A=84?=
 =?UTF-8?q?=E6=94=B9=E5=8A=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 integration_tests/lib/bridge/match_snapshots.dart | 1 +
 1 file changed, 1 insertion(+)

diff --git a/integration_tests/lib/bridge/match_snapshots.dart b/integration_tests/lib/bridge/match_snapshots.dart
index abd98e5b19..6102acb3a0 100644
--- a/integration_tests/lib/bridge/match_snapshots.dart
+++ b/integration_tests/lib/bridge/match_snapshots.dart
@@ -6,6 +6,7 @@ import 'dart:io';
 import 'dart:typed_data';
 import 'package:flutter/foundation.dart';
 import 'package:image_compare/image_compare.dart';
+
 import 'package:image/image.dart';
 import 'package:path/path.dart' as path;
 

From 899a99900f859c9b7e3ca5bb53ba5a5fedc962b1 Mon Sep 17 00:00:00 2001
From: wahahahaha 
Date: Tue, 13 Aug 2024 21:15:17 +0800
Subject: [PATCH 07/18] =?UTF-8?q?=E6=95=B4=E7=90=86=E4=BB=A3=E7=A0=81?=
 =?UTF-8?q?=EF=BC=8C=E6=92=A4=E5=9B=9E=E4=B8=8D=E5=BF=85=E8=A6=81=E7=9A=84?=
 =?UTF-8?q?=E6=94=B9=E5=8A=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 buildAndRunF.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/buildAndRunF.sh b/buildAndRunF.sh
index 4c089fee42..69436c3612 100644
--- a/buildAndRunF.sh
+++ b/buildAndRunF.sh
@@ -17,5 +17,5 @@ build-linuxd(){
     cp -f bridge/cmake-build-linux/compile_commands.json .
 }
 echo To initialize the build, using build-linux or build-linuxd:
-echo then use runexample for release build, or runexample for debug build
+echo then use runexample for release build, or runexampled for debug build
 

From 9b83d47517aaffb044ea06b21e57a5b355774c3f Mon Sep 17 00:00:00 2001
From: wahahahaha 
Date: Tue, 13 Aug 2024 21:18:40 +0800
Subject: [PATCH 08/18] Add Copyright file header.

---
 bridge/core/api/form_data.cc      | 4 ++++
 bridge/core/api/form_data.d.ts    | 4 ++++
 bridge/core/api/form_data.h       | 3 +++
 bridge/core/api/form_data_part.cc | 4 ++++
 bridge/core/api/form_data_part.h  | 1 -
 5 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/bridge/core/api/form_data.cc b/bridge/core/api/form_data.cc
index dee0ff4a05..40769f6e19 100644
--- a/bridge/core/api/form_data.cc
+++ b/bridge/core/api/form_data.cc
@@ -1,3 +1,7 @@
+/*
+ * Copyright (C) 2022-present The WebF authors. All rights reserved.
+ */
+ 
 #include "form_data.h"
 #include 
 #include "bindings/qjs/atomic_string.h"
diff --git a/bridge/core/api/form_data.d.ts b/bridge/core/api/form_data.d.ts
index aadefea8bb..417d2f9b6c 100644
--- a/bridge/core/api/form_data.d.ts
+++ b/bridge/core/api/form_data.d.ts
@@ -1,3 +1,7 @@
+/*
+ * Copyright (C) 2022-present The WebF authors. All rights reserved.
+ */
+
 // type FormDataEntryValue = File | string;
 type FormDataPart={}
 export interface FormData {
diff --git a/bridge/core/api/form_data.h b/bridge/core/api/form_data.h
index 188d8a51ba..b718ded9ae 100644
--- a/bridge/core/api/form_data.h
+++ b/bridge/core/api/form_data.h
@@ -1,3 +1,6 @@
+/*
+ * Copyright (C) 2022-present The WebF authors. All rights reserved.
+ */
 
 #ifndef WEBF_CORE_API_FORM_DATA_H_
 #define WEBF_CORE_API_FORM_DATA_H_
diff --git a/bridge/core/api/form_data_part.cc b/bridge/core/api/form_data_part.cc
index 9824ee9121..f09de6eacd 100644
--- a/bridge/core/api/form_data_part.cc
+++ b/bridge/core/api/form_data_part.cc
@@ -1,3 +1,7 @@
+/*
+ * Copyright (C) 2022-present The WebF authors. All rights reserved.
+ */
+
 #include "form_data_part.h"
 #include "bindings/qjs/atomic_string.h"
 #include "bindings/qjs/exception_state.h"
diff --git a/bridge/core/api/form_data_part.h b/bridge/core/api/form_data_part.h
index c0470632c2..a89a00c9b1 100644
--- a/bridge/core/api/form_data_part.h
+++ b/bridge/core/api/form_data_part.h
@@ -1,5 +1,4 @@
 /*
- * Copyright (C) 2019-2022 The Kraken authors. All rights reserved.
  * Copyright (C) 2022-present The WebF authors. All rights reserved.
  */
 #ifndef BRIDGE_API_FORM_DATA_PART_H_

From 4b76a7f27cde0ac8e0f86068b9c90b1c4e998a28 Mon Sep 17 00:00:00 2001
From: wahahahaha 
Date: Tue, 13 Aug 2024 22:55:41 +0800
Subject: [PATCH 09/18] =?UTF-8?q?=E6=95=B4=E7=90=86=E4=BB=A3=E7=A0=81?=
 =?UTF-8?q?=EF=BC=8C=E7=A7=BB=E9=99=A4=E4=B8=AD=E6=96=87=E6=B3=A8=E9=87=8A?=
 =?UTF-8?q?=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 bridge/core/api/form_data.cc | 22 +---------------------
 1 file changed, 1 insertion(+), 21 deletions(-)

diff --git a/bridge/core/api/form_data.cc b/bridge/core/api/form_data.cc
index 40769f6e19..7d7c7e4ff9 100644
--- a/bridge/core/api/form_data.cc
+++ b/bridge/core/api/form_data.cc
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2022-present The WebF authors. All rights reserved.
  */
- 
+
 #include "form_data.h"
 #include 
 #include "bindings/qjs/atomic_string.h"
@@ -33,27 +33,22 @@ void FormData::append(const AtomicString& name,
                       const std::shared_ptr& value,
                       const AtomicString& fileName,
                       ExceptionState& exception_state) {
-  // 验证参数有效性
   if (name.IsEmpty()) {
     exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty.");
     return;
   }
 
-  // 创建 FormDataPart 对象
   auto form_data_part = std::make_shared(name.ToStdString(ctx()), value, fileName.ToStdString(ctx()));
 
-  // 添加数据
   _parts.push_back(form_data_part);
 }
 
 void FormData::form_data_delete(const AtomicString& name, ExceptionState& exception_state) {
-  // 验证参数有效性
   if (name.IsEmpty()) {
     exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty.");
     return;
   }
 
-  // 删除数据
   _parts.erase(std::remove_if(_parts.begin(), _parts.end(),
                               [name, this](const std::shared_ptr& part) {
                                 return part->GetName() == name.ToStdString(ctx());
@@ -62,33 +57,28 @@ void FormData::form_data_delete(const AtomicString& name, ExceptionState& except
 }
 
 webf::BlobPart* FormData::get(const AtomicString& name, ExceptionState& exception_state) {
-  // 验证参数有效性
   if (name.IsEmpty()) {
     exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty.");
     return nullptr;
   }
 
-  // 查找数据
   for (const auto& part : _parts) {
     if (part->GetName() == name.ToStdString(ctx())) {
       return &*part->getFirst();
     }
   }
 
-  // 如果没有找到,则返回 nullptr
   return nullptr;
 }
 
 std::vector FormData::getAll(const AtomicString& name, ExceptionState& exception_state) {
   std::vector result;
 
-  // 验证参数有效性
   if (name.IsEmpty()) {
     exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty.");
     return result;
   }
 
-  // 收集数据
   for (const auto& part : _parts) {
     if (part->GetName() == name.ToStdString(ctx())) {
       for (const auto& value : part->GetValues()) {
@@ -101,13 +91,11 @@ std::vector FormData::getAll(const AtomicString& name, Excep
 }
 
 bool FormData::has(const AtomicString& name, ExceptionState& exception_state) {
-  // 验证参数有效性
   if (name.IsEmpty()) {
     exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty.");
     return false;
   }
 
-  // 检查数据
   for (const auto& part : _parts) {
     if (part->GetName() == name.ToStdString(ctx())) {
       return true;
@@ -121,23 +109,19 @@ void FormData::set(const AtomicString& name,
                    const std::shared_ptr& value,
                    const AtomicString& fileName,
                    ExceptionState& exception_state) {
-  // 验证参数有效性
   if (name.IsEmpty()) {
     exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty.");
     return;
   }
 
-  // 移除已存在的相同名称的条目
   _parts.erase(std::remove_if(_parts.begin(), _parts.end(),
                               [name, this](const std::shared_ptr& part) {
                                 return part->GetName() == name.ToStdString(ctx());
                               }),
                _parts.end());
 
-  // 创建 FormDataPart 对象
   auto form_data_part = std::make_shared(name.ToStdString(ctx()), value, fileName.ToStdString(ctx()));
 
-  // 添加新的数据
   _parts.push_back(form_data_part);
 }
 
@@ -157,7 +141,6 @@ void FormData::forEach(const std::shared_ptr& callback,
     // TODO: which parent???
     /*parent*/ args[2] = ScriptValue(ctx(), this->ToQuickJS());
 
-    // 调用回调函数
     ScriptValue result = callback->Invoke(ctx(), thisArg, 3, args);
     if (result.IsException()) {
       exception_state.ThrowException(ctx(), result.QJSValue());
@@ -166,7 +149,6 @@ void FormData::forEach(const std::shared_ptr& callback,
   }
 }
 
-// 实现 keys() 方法
 std::vector FormData::keys(ExceptionState& exception_state) const {
   std::vector keys;
   for (const auto& part : _parts) {
@@ -175,7 +157,6 @@ std::vector FormData::keys(ExceptionState& exception_state)
   return keys;
 }
 
-// 实现 values() 方法
 std::vector> FormData::values(ExceptionState& exception_state) const {
   std::vector> values;
   for (const auto& part : _parts) {
@@ -186,7 +167,6 @@ std::vector> FormData::values(ExceptionState& exceptio
   return values;
 }
 
-// 实现 entries() 方法
 std::vector> FormData::entries(ExceptionState& exception_state) const {
     return std::vector>(_parts.begin(), _parts.end());
 }

From 0794ca7a3857e8187f566f8fb90e589248c3c7d8 Mon Sep 17 00:00:00 2001
From: wahahahaha 
Date: Tue, 13 Aug 2024 23:22:09 +0800
Subject: [PATCH 10/18] =?UTF-8?q?=E6=95=B4=E7=90=86=E4=BB=A3=E7=A0=81?=
 =?UTF-8?q?=EF=BC=8C=E6=8F=90=E4=BE=9B=E6=9B=B4=E6=98=8E=E7=A1=AE=E7=9A=84?=
 =?UTF-8?q?=E6=B3=A8=E9=87=8A=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 bridge/core/api/form_data.d.ts | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/bridge/core/api/form_data.d.ts b/bridge/core/api/form_data.d.ts
index 417d2f9b6c..a604d37804 100644
--- a/bridge/core/api/form_data.d.ts
+++ b/bridge/core/api/form_data.d.ts
@@ -2,8 +2,10 @@
  * Copyright (C) 2022-present The WebF authors. All rights reserved.
  */
 
-// type FormDataEntryValue = File | string;
-type FormDataPart={}
+
+// This file is for generate binding code only.
+
+type FormDataPart={} // dummy code. Real one is introduced in C++ form_data_part.h, also blob_part.h
 export interface FormData {
     new():FormData;
     append(name: string, value: BlobPart, fileName?: string): void;

From 44e348ca3e696e62b639216e790a96d79984864e Mon Sep 17 00:00:00 2001
From: andycall 
Date: Wed, 14 Aug 2024 14:02:44 +0800
Subject: [PATCH 11/18] chore: move form data to core/html/forms

---
 bridge/CMakeLists.txt                         |  4 +-
 bridge/core/api/form_data.d.ts                | 23 -------
 bridge/core/api/form_data.h                   | 61 -----------------
 bridge/core/{api => html/forms}/form_data.cc  |  2 +-
 bridge/core/html/forms/form_data.d.ts         | 22 ++++++
 bridge/core/html/forms/form_data.h            | 68 +++++++++++++++++++
 .../{api => html/forms}/form_data_part.cc     |  0
 .../core/{api => html/forms}/form_data_part.h | 29 ++++----
 bridge/scripts/code_generator/global.d.ts     |  1 +
 9 files changed, 108 insertions(+), 102 deletions(-)
 delete mode 100644 bridge/core/api/form_data.d.ts
 delete mode 100644 bridge/core/api/form_data.h
 rename bridge/core/{api => html/forms}/form_data.cc (99%)
 create mode 100644 bridge/core/html/forms/form_data.d.ts
 create mode 100644 bridge/core/html/forms/form_data.h
 rename bridge/core/{api => html/forms}/form_data_part.cc (100%)
 rename bridge/core/{api => html/forms}/form_data_part.h (69%)

diff --git a/bridge/CMakeLists.txt b/bridge/CMakeLists.txt
index 948ee61f6b..52939c040f 100644
--- a/bridge/CMakeLists.txt
+++ b/bridge/CMakeLists.txt
@@ -266,8 +266,6 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs")
     # Core sources
     webf_bridge.cc
     core/api/api.cc
-    core/api/form_data.cc
-    core/api/form_data_part.cc
     core/executing_context.cc
     core/script_forbidden_scope.cc
     core/script_state.cc
@@ -380,6 +378,8 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs")
     core/html/forms/html_input_element.cc
     core/html/forms/html_form_element.cc
     core/html/forms/html_textarea_element.cc
+    core/html/forms/form_data.cc
+    core/html/forms/form_data_part.cc
 
     # SVG files
     core/svg/svg_element.cc
diff --git a/bridge/core/api/form_data.d.ts b/bridge/core/api/form_data.d.ts
deleted file mode 100644
index a604d37804..0000000000
--- a/bridge/core/api/form_data.d.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2022-present The WebF authors. All rights reserved.
- */
-
-
-// This file is for generate binding code only.
-
-type FormDataPart={} // dummy code. Real one is introduced in C++ form_data_part.h, also blob_part.h
-export interface FormData {
-    new():FormData;
-    append(name: string, value: BlobPart, fileName?: string): void;
-    // This method name is a placeholder of **delete** method to avoid using C++ keyword
-    // and will be replaced to **delete** when installing in MemberInstaller::InstallFunctions.
-    form_data_delete(name: string): void;
-    get(name: string): BlobPart
-    getAll(name: string): BlobPart[];
-    has(name: string): boolean;
-    set(name: string, value: BlobPart, fileName?: string): void;
-    forEach(callbackfn: Function, thisArg?: any): void;
-    keys():string[]
-    values():BlobPart[]
-    entries():FormDataPart[]
-}
diff --git a/bridge/core/api/form_data.h b/bridge/core/api/form_data.h
deleted file mode 100644
index b718ded9ae..0000000000
--- a/bridge/core/api/form_data.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2022-present The WebF authors. All rights reserved.
- */
-
-#ifndef WEBF_CORE_API_FORM_DATA_H_
-#define WEBF_CORE_API_FORM_DATA_H_
-#include 
-#include 
-#include "bindings/qjs/atomic_string.h"
-#include "bindings/qjs/script_wrappable.h"
-#include "core/api/form_data_part.h"
-#include "core/binding_object.h"
-
-#include "core/api/form_data_part.h"
-#include "core/fileapi/blob_part.h"
-
-namespace webf{
-    class FormData :public BindingObject{
-        DEFINE_WRAPPERTYPEINFO();
-
-        public:
-        using ImplType = FormData*;
-        
-
-        FormData() = delete;
-        static FormData* Create(ExecutingContext* context, ExceptionState& exception_state);
-        explicit FormData(JSContext* ctx) : BindingObject(ctx){};
-        explicit FormData(ExecutingContext* context, NativeBindingObject* native_binding_object);
-        NativeValue HandleCallFromDartSide(const AtomicString& method,
-                                     int32_t argc,
-                                     const NativeValue* argv,
-                                     Dart_Handle dart_object) override;
-        bool IsFormData() const override;
-        void append(const AtomicString& name, const std::shared_ptr& value,ExceptionState& exception_state){
-            append(name,value,AtomicString::Empty(),exception_state);
-        }
-        void append(const AtomicString& name, const std::shared_ptr& value, const AtomicString& fileName,ExceptionState& exception_state);
-        void form_data_delete(const AtomicString& name,ExceptionState& exception_state);
-        BlobPart* get(const AtomicString& name,ExceptionState& exception_state);
-        std::vector getAll(const AtomicString& name,ExceptionState& exception_state);
-        bool has(const AtomicString& name,ExceptionState& exception_state);
-        void set(const AtomicString& name, const std::shared_ptr& value,ExceptionState& exception_state){
-            set(name,value,AtomicString::Empty(),exception_state);
-        }
-        void set(const AtomicString& name, const std::shared_ptr& value, const AtomicString& fileName,ExceptionState& exception_state);
-        void forEach(const std::shared_ptr& callback, ExceptionState& exception_state){
-            forEach(callback,webf::ScriptValue::Empty(ctx()),exception_state);
-        }
-        void forEach(const std::shared_ptr& callback, const webf::ScriptValue& thisArg,ExceptionState& exception_state);
-        std::vector keys(ExceptionState& exception_state) const;
-        std::vector> values(ExceptionState& exception_state) const;
-        std::vector> entries(ExceptionState& exception_state) const;
-        private:
-        std::vector> _parts;
-    };
-    template <>
-        struct DowncastTraits {
-        static bool AllowFrom(const BindingObject& binding_object) { return binding_object.IsFormData(); }
-    };
-}
-#endif
\ No newline at end of file
diff --git a/bridge/core/api/form_data.cc b/bridge/core/html/forms/form_data.cc
similarity index 99%
rename from bridge/core/api/form_data.cc
rename to bridge/core/html/forms/form_data.cc
index 7d7c7e4ff9..16c9e2562a 100644
--- a/bridge/core/api/form_data.cc
+++ b/bridge/core/html/forms/form_data.cc
@@ -2,10 +2,10 @@
  * Copyright (C) 2022-present The WebF authors. All rights reserved.
  */
 
+#include "form_data_part.h"
 #include "form_data.h"
 #include 
 #include "bindings/qjs/atomic_string.h"
-#include "core/api/form_data_part.h"
 #include "core/executing_context.h"
 #include "core/fileapi/blob_part.h"
 
diff --git a/bridge/core/html/forms/form_data.d.ts b/bridge/core/html/forms/form_data.d.ts
new file mode 100644
index 0000000000..cb4e565971
--- /dev/null
+++ b/bridge/core/html/forms/form_data.d.ts
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022-present The WebF authors. All rights reserved.
+ */
+
+export interface FormData {
+  new(): FormData;
+  append(name: string, value: BlobPart, fileName?: string): void;
+  append(name: string, value: string, fileName?: string): void;
+  // This method name is a placeholder of **delete** method to avoid using C++ keyword
+  // and will be replaced to **delete** when installing in MemberInstaller::InstallFunctions.
+  form_data_delete(name: string): void;
+  get(name: string): BlobPart
+  getAll(name: string): BlobPart[];
+  has(name: string): boolean;
+  set(name: string, value: BlobPart, fileName?: string): void;
+
+  readonly forEach: JSArrayProtoMethod;
+  readonly keys: JSArrayProtoMethod;
+  readonly entries: JSArrayProtoMethod;
+  readonly values: JSArrayProtoMethod;
+  readonly [Symbol.iterator]: JSArrayProtoMethod;
+}
diff --git a/bridge/core/html/forms/form_data.h b/bridge/core/html/forms/form_data.h
new file mode 100644
index 0000000000..18e89d47de
--- /dev/null
+++ b/bridge/core/html/forms/form_data.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022-present The WebF authors. All rights reserved.
+ */
+
+#ifndef WEBF_CORE_API_FORM_DATA_H_
+#define WEBF_CORE_API_FORM_DATA_H_
+#include 
+#include 
+#include "bindings/qjs/atomic_string.h"
+#include "bindings/qjs/script_wrappable.h"
+#include "core/binding_object.h"
+#include "core/fileapi/blob_part.h"
+
+namespace webf {
+
+class FormData : public BindingObject {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  using ImplType = FormData*;
+
+  FormData() = delete;
+  static FormData* Create(ExecutingContext* context, ExceptionState& exception_state);
+  explicit FormData(JSContext* ctx) : BindingObject(ctx){};
+  explicit FormData(ExecutingContext* context, NativeBindingObject* native_binding_object);
+  NativeValue HandleCallFromDartSide(const AtomicString& method,
+                                     int32_t argc,
+                                     const NativeValue* argv,
+                                     Dart_Handle dart_object) override;
+  bool IsFormData() const override;
+  void append(const AtomicString& name, const std::shared_ptr& value, ExceptionState& exception_state) {
+    append(name, value, AtomicString::Empty(), exception_state);
+  }
+  void append(const AtomicString& name,
+              const std::shared_ptr& value,
+              const AtomicString& fileName,
+              ExceptionState& exception_state);
+  void form_data_delete(const AtomicString& name, ExceptionState& exception_state);
+  BlobPart* get(const AtomicString& name, ExceptionState& exception_state);
+  std::vector getAll(const AtomicString& name, ExceptionState& exception_state);
+  bool has(const AtomicString& name, ExceptionState& exception_state);
+  void set(const AtomicString& name, const std::shared_ptr& value, ExceptionState& exception_state) {
+    set(name, value, AtomicString::Empty(), exception_state);
+  }
+  void set(const AtomicString& name,
+           const std::shared_ptr& value,
+           const AtomicString& fileName,
+           ExceptionState& exception_state);
+  void forEach(const std::shared_ptr& callback, ExceptionState& exception_state) {
+    forEach(callback, webf::ScriptValue::Empty(ctx()), exception_state);
+  }
+  void forEach(const std::shared_ptr& callback,
+               const webf::ScriptValue& thisArg,
+               ExceptionState& exception_state);
+  std::vector keys(ExceptionState& exception_state) const;
+  std::vector> values(ExceptionState& exception_state) const;
+  std::vector> entries(ExceptionState& exception_state) const;
+
+ private:
+  std::vector> _parts;
+};
+template <>
+struct DowncastTraits {
+  static bool AllowFrom(const BindingObject& binding_object) { return binding_object.IsFormData(); }
+};
+
+}  // namespace webf
+#endif
\ No newline at end of file
diff --git a/bridge/core/api/form_data_part.cc b/bridge/core/html/forms/form_data_part.cc
similarity index 100%
rename from bridge/core/api/form_data_part.cc
rename to bridge/core/html/forms/form_data_part.cc
diff --git a/bridge/core/api/form_data_part.h b/bridge/core/html/forms/form_data_part.h
similarity index 69%
rename from bridge/core/api/form_data_part.h
rename to bridge/core/html/forms/form_data_part.h
index a89a00c9b1..5adfd55b34 100644
--- a/bridge/core/api/form_data_part.h
+++ b/bridge/core/html/forms/form_data_part.h
@@ -17,33 +17,32 @@ class BlobPart;
 class FormDataPart {
  public:
   using ImplType = std::shared_ptr;
-  enum class ContentType { kFile,kBlob, kString };
+  enum class ContentType { kFile, kBlob, kString };
 
   static std::shared_ptr Create(JSContext* ctx, JSValue value, ExceptionState& exception_state);
   explicit FormDataPart(std::string name, const std::shared_ptr data) {
-    this->name_=std::move(name);
+    this->name_ = std::move(name);
     AddValue(*data);
   };
-  explicit FormDataPart(){}
-  explicit FormDataPart(std::string name){
-    this->name_=std::move(name);
-  }
-  explicit FormDataPart(std::string name, const std::shared_ptr data,std::string fileName) {
-    this->name_=std::move(name);
+  explicit FormDataPart() {}
+  explicit FormDataPart(std::string name) { this->name_ = std::move(name); }
+  explicit FormDataPart(std::string name, const std::shared_ptr data, std::string fileName) {
+    this->name_ = std::move(name);
     AddValue(*data);
-    //todo: filename is not used.
+    // todo: filename is not used.
   };
   JSValue ToQuickJS(JSContext* ctx) const;
   const std::string& GetName() const { return name_; }
   const std::vector& GetValues() const { return values_; }
   void AddValue(const BlobPart& value);
-std::shared_ptr getFirst() const {
-  if (values_.empty()) {
-    return nullptr;
-  } else {
-    return std::make_shared(values_[0]);
+  std::shared_ptr getFirst() const {
+    if (values_.empty()) {
+      return nullptr;
+    } else {
+      return std::make_shared(values_[0]);
+    }
   }
-}
+
  private:
   std::string name_;
   std::vector values_;
diff --git a/bridge/scripts/code_generator/global.d.ts b/bridge/scripts/code_generator/global.d.ts
index d34b2abc9a..62c081efe1 100644
--- a/bridge/scripts/code_generator/global.d.ts
+++ b/bridge/scripts/code_generator/global.d.ts
@@ -5,6 +5,7 @@ declare type JSArrayProtoMethod = void;
 declare interface Dictionary {}
 
 declare interface BlobPart {}
+declare interface FormDataPart {}
 declare interface BlobPropertyBag {}
 declare function Dictionary() : any;
 declare type JSEventListener = void;

From 28ea9cd6b31a2e4ae0f9fbcb28801d057f957710 Mon Sep 17 00:00:00 2001
From: andycall 
Date: Wed, 14 Aug 2024 15:06:17 +0800
Subject: [PATCH 12/18] feat: add demo for sending byte data from c++ to dart.

---
 bridge/bindings/qjs/converter_impl.h       |  2 +-
 bridge/core/binding_call_methods.json5     |  3 +-
 bridge/core/binding_object.h               |  2 +-
 bridge/core/html/forms/form_data.cc        | 69 ++++++----------------
 bridge/core/html/forms/form_data.d.ts      |  1 -
 bridge/core/html/forms/form_data.h         | 13 +---
 bridge/foundation/native_type.h            |  2 +
 bridge/foundation/native_value.cc          | 12 ++++
 bridge/foundation/native_value.h           |  1 +
 bridge/foundation/native_value_converter.h | 15 +++++
 webf/lib/src/bridge/binding.dart           |  9 ++-
 webf/lib/src/html/forms/form_data.dart     | 18 ++++++
 12 files changed, 81 insertions(+), 66 deletions(-)
 create mode 100644 webf/lib/src/html/forms/form_data.dart

diff --git a/bridge/bindings/qjs/converter_impl.h b/bridge/bindings/qjs/converter_impl.h
index dc8883bc87..0e96d56f98 100644
--- a/bridge/bindings/qjs/converter_impl.h
+++ b/bridge/bindings/qjs/converter_impl.h
@@ -15,7 +15,7 @@
 #include "core/dom/events/event_target.h"
 #include "core/dom/node_list.h"
 #include "core/fileapi/blob_part.h"
-#include "core/api/form_data_part.h"
+#include "core/html/forms/form_data_part.h"
 #include "core/fileapi/blob_property_bag.h"
 #include "core/frame/window.h"
 #include "core/html/html_body_element.h"
diff --git a/bridge/core/binding_call_methods.json5 b/bridge/core/binding_call_methods.json5
index e4f8d8c6a7..1083125eea 100644
--- a/bridge/core/binding_call_methods.json5
+++ b/bridge/core/binding_call_methods.json5
@@ -179,6 +179,7 @@
     "dir",
     "pageXOffset",
     "pageYOffset",
-    "title"
+    "title",
+    "append"
   ]
 }
diff --git a/bridge/core/binding_object.h b/bridge/core/binding_object.h
index c662047068..720ce098e9 100644
--- a/bridge/core/binding_object.h
+++ b/bridge/core/binding_object.h
@@ -70,7 +70,7 @@ enum BindingMethodCallOperations {
   kAsyncAnonymousFunction,
 };
 
-enum CreateBindingObjectType { kCreateDOMMatrix = 0 };
+enum CreateBindingObjectType { kCreateDOMMatrix = 0, kCreateFormData = 1 };
 
 struct BindingObjectPromiseContext : public DartReadable {
   ExecutingContext* context;
diff --git a/bridge/core/html/forms/form_data.cc b/bridge/core/html/forms/form_data.cc
index 16c9e2562a..ab13db0f69 100644
--- a/bridge/core/html/forms/form_data.cc
+++ b/bridge/core/html/forms/form_data.cc
@@ -2,21 +2,26 @@
  * Copyright (C) 2022-present The WebF authors. All rights reserved.
  */
 
-#include "form_data_part.h"
 #include "form_data.h"
 #include 
+#include "binding_call_methods.h"
 #include "bindings/qjs/atomic_string.h"
 #include "core/executing_context.h"
 #include "core/fileapi/blob_part.h"
+#include "form_data_part.h"
+#include "foundation/native_value_converter.h"
 
 namespace webf {
-const char* className= "FormData";
+const char* className = "FormData";
 FormData* FormData::Create(ExecutingContext* context, ExceptionState& exception_state) {
   return MakeGarbageCollected(context->ctx());
 }
 
-FormData::FormData(ExecutingContext* context, NativeBindingObject* native_binding_object)
-    : BindingObject(context->ctx(), native_binding_object) {}
+FormData::FormData(JSContext* ctx) : BindingObject(ctx) {
+  GetExecutingContext()->dartMethodPtr()->createBindingObject(GetExecutingContext()->isDedicated(),
+                                                              GetExecutingContext()->contextId(), bindingObject(),
+                                                              CreateBindingObjectType::kCreateFormData, nullptr, 0);
+}
 
 NativeValue FormData::HandleCallFromDartSide(const AtomicString& method,
                                              int32_t argc,
@@ -41,6 +46,16 @@ void FormData::append(const AtomicString& name,
   auto form_data_part = std::make_shared(name.ToStdString(ctx()), value, fileName.ToStdString(ctx()));
 
   _parts.push_back(form_data_part);
+
+  uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04, 0x05};
+  uint32_t length = 5;
+
+  const NativeValue arguments[]  = {
+      NativeValueConverter::ToNativeValue(buffer, length)
+  };
+
+  NativeValue return_result = InvokeBindingMethod(binding_call_methods::kappend, 1, arguments,
+                                                  FlushUICommandReason::kStandard, exception_state);
 }
 
 void FormData::form_data_delete(const AtomicString& name, ExceptionState& exception_state) {
@@ -124,50 +139,4 @@ void FormData::set(const AtomicString& name,
 
   _parts.push_back(form_data_part);
 }
-
-void FormData::forEach(const std::shared_ptr& callback,
-                       const ScriptValue& thisArg,
-                       ExceptionState& exception_state) {
-  if (!callback || !callback->IsFunction(ctx())) {
-    exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The callback function must be callable.");
-    return;
-  }
-  // callbackFn:(value: BlobPart, key: string, parent: FormData) => void
-  for (const auto& part : _parts) {
-    ScriptValue args[3];
-    /*value*/ args[0] = ScriptValue(ctx(), part->ToQuickJS(ctx()));
-    /*key*/ args[1] = ScriptValue(ctx(), AtomicString(ctx(), part->GetName()));
-
-    // TODO: which parent???
-    /*parent*/ args[2] = ScriptValue(ctx(), this->ToQuickJS());
-
-    ScriptValue result = callback->Invoke(ctx(), thisArg, 3, args);
-    if (result.IsException()) {
-      exception_state.ThrowException(ctx(), result.QJSValue());
-      return;
-    }
-  }
-}
-
-std::vector FormData::keys(ExceptionState& exception_state) const {
-  std::vector keys;
-  for (const auto& part : _parts) {
-    keys.push_back(AtomicString(ctx(), part->GetName()));
-  }
-  return keys;
-}
-
-std::vector> FormData::values(ExceptionState& exception_state) const {
-  std::vector> values;
-  for (const auto& part : _parts) {
-    for (const auto& value : part->GetValues()) {
-      values.push_back(std::make_shared(value));
-    }
-  }
-  return values;
-}
-
-std::vector> FormData::entries(ExceptionState& exception_state) const {
-    return std::vector>(_parts.begin(), _parts.end());
-}
 }  // namespace webf
\ No newline at end of file
diff --git a/bridge/core/html/forms/form_data.d.ts b/bridge/core/html/forms/form_data.d.ts
index cb4e565971..6b3ce7e962 100644
--- a/bridge/core/html/forms/form_data.d.ts
+++ b/bridge/core/html/forms/form_data.d.ts
@@ -5,7 +5,6 @@
 export interface FormData {
   new(): FormData;
   append(name: string, value: BlobPart, fileName?: string): void;
-  append(name: string, value: string, fileName?: string): void;
   // This method name is a placeholder of **delete** method to avoid using C++ keyword
   // and will be replaced to **delete** when installing in MemberInstaller::InstallFunctions.
   form_data_delete(name: string): void;
diff --git a/bridge/core/html/forms/form_data.h b/bridge/core/html/forms/form_data.h
index 18e89d47de..20dfbae0d1 100644
--- a/bridge/core/html/forms/form_data.h
+++ b/bridge/core/html/forms/form_data.h
@@ -10,6 +10,7 @@
 #include "bindings/qjs/script_wrappable.h"
 #include "core/binding_object.h"
 #include "core/fileapi/blob_part.h"
+#include "core/html/forms/form_data_part.h"
 
 namespace webf {
 
@@ -21,8 +22,7 @@ class FormData : public BindingObject {
 
   FormData() = delete;
   static FormData* Create(ExecutingContext* context, ExceptionState& exception_state);
-  explicit FormData(JSContext* ctx) : BindingObject(ctx){};
-  explicit FormData(ExecutingContext* context, NativeBindingObject* native_binding_object);
+  explicit FormData(JSContext* ctx);
   NativeValue HandleCallFromDartSide(const AtomicString& method,
                                      int32_t argc,
                                      const NativeValue* argv,
@@ -46,15 +46,6 @@ class FormData : public BindingObject {
            const std::shared_ptr& value,
            const AtomicString& fileName,
            ExceptionState& exception_state);
-  void forEach(const std::shared_ptr& callback, ExceptionState& exception_state) {
-    forEach(callback, webf::ScriptValue::Empty(ctx()), exception_state);
-  }
-  void forEach(const std::shared_ptr& callback,
-               const webf::ScriptValue& thisArg,
-               ExceptionState& exception_state);
-  std::vector keys(ExceptionState& exception_state) const;
-  std::vector> values(ExceptionState& exception_state) const;
-  std::vector> entries(ExceptionState& exception_state) const;
 
  private:
   std::vector> _parts;
diff --git a/bridge/foundation/native_type.h b/bridge/foundation/native_type.h
index 71862071ab..eb232d084d 100644
--- a/bridge/foundation/native_type.h
+++ b/bridge/foundation/native_type.h
@@ -40,6 +40,8 @@ struct NativeTypeDouble final : public NativeTypeBaseHelper {};
 // JSON
 struct NativeTypeJSON final : public NativeTypeBaseHelper {};
 
+struct NativeTypeBytes final : public NativeTypeBaseHelper {};
+
 // Array
 template 
 struct NativeTypeArray final : public NativeTypeBase {
diff --git a/bridge/foundation/native_value.cc b/bridge/foundation/native_value.cc
index f2ac0a63e3..0ad2dd59a4 100644
--- a/bridge/foundation/native_value.cc
+++ b/bridge/foundation/native_value.cc
@@ -142,6 +142,18 @@ NativeValue Native_NewJSON(JSContext* ctx, const ScriptValue& value, ExceptionSt
 #endif
 }
 
+NativeValue Native_NewBytes(const uint8_t* bytes, uint32_t length) {
+  auto* new_buffer = malloc(sizeof(uint8_t) * length);
+  memcpy(new_buffer, bytes, sizeof(uint8_t) * length);
+
+  NativeValue result = (NativeValue){
+      .u = {.ptr = static_cast(new_buffer)},
+      .uint32 = length,
+      .tag = NativeTag::TAG_UINT8_BYTES,
+  };
+  return result;
+}
+
 JSPointerType GetPointerTypeOfNativePointer(NativeValue native_value) {
   assert(native_value.tag == NativeTag::TAG_POINTER);
   return static_cast(native_value.uint32);
diff --git a/bridge/foundation/native_value.h b/bridge/foundation/native_value.h
index be7ffdd164..7f2984cf04 100644
--- a/bridge/foundation/native_value.h
+++ b/bridge/foundation/native_value.h
@@ -77,6 +77,7 @@ NativeValue Native_NewInt64(int64_t value);
 NativeValue Native_NewList(uint32_t argc, NativeValue* argv);
 NativeValue Native_NewPtr(JSPointerType pointerType, void* ptr);
 NativeValue Native_NewJSON(JSContext* ctx, const ScriptValue& value, ExceptionState& exception_state);
+NativeValue Native_NewBytes(const uint8_t* bytes, uint32_t length);
 
 JSPointerType GetPointerTypeOfNativePointer(NativeValue native_value);
 
diff --git a/bridge/foundation/native_value_converter.h b/bridge/foundation/native_value_converter.h
index af6432d9c1..3b4c5b6c38 100644
--- a/bridge/foundation/native_value_converter.h
+++ b/bridge/foundation/native_value_converter.h
@@ -99,6 +99,21 @@ struct NativeValueConverter : public NativeValueConverterBase<
   }
 };
 
+template <>
+struct NativeValueConverter : public NativeValueConverterBase {
+  static NativeValue ToNativeValue(ImplType value, uint32_t length) { return Native_NewBytes(value, length); }
+
+  static ImplType FromNativeValue(NativeValue value, uint32_t* length) {
+    if (value.tag == NativeTag::TAG_NULL) {
+      return nullptr;
+    }
+
+    assert(value.tag == NativeTag::TAG_UINT8_BYTES);
+    *length = value.uint32;
+    return static_cast(value.u.ptr);
+  }
+};
+
 template <>
 struct NativeValueConverter : public NativeValueConverterBase {
   static NativeValue ToNativeValue(JSContext* ctx, ImplType value, ExceptionState& exception_state) {
diff --git a/webf/lib/src/bridge/binding.dart b/webf/lib/src/bridge/binding.dart
index bb047a1527..4fdde3335c 100644
--- a/webf/lib/src/bridge/binding.dart
+++ b/webf/lib/src/bridge/binding.dart
@@ -14,6 +14,7 @@ import 'package:webf/dom.dart';
 import 'package:webf/geometry.dart';
 import 'package:webf/foundation.dart';
 import 'package:webf/launcher.dart';
+import 'package:webf/src/html/forms/form_data.dart';
 
 // We have some integrated built-in behavior starting with string prefix reuse the callNativeMethod implements.
 enum BindingMethodCallOperations {
@@ -157,7 +158,8 @@ Future _dispatchEventToNative(Event event, bool isCapture) async {
 }
 
 enum CreateBindingObjectType {
-  createDOMMatrix
+  createDOMMatrix,
+  createFormData
 }
 
 abstract class BindingBridge {
@@ -178,6 +180,11 @@ abstract class BindingBridge {
         controller.view.setBindingObject(pointer, domMatrix);
         return;
       }
+      case CreateBindingObjectType.createFormData: {
+        FormData formData = FormData(BindingContext(controller.view, contextId, pointer), arguments);
+        controller.view.setBindingObject(pointer, formData);
+        return;
+      }
     }
   }
 
diff --git a/webf/lib/src/html/forms/form_data.dart b/webf/lib/src/html/forms/form_data.dart
new file mode 100644
index 0000000000..ac946be100
--- /dev/null
+++ b/webf/lib/src/html/forms/form_data.dart
@@ -0,0 +1,18 @@
+import 'dart:typed_data';
+import 'package:webf/foundation.dart';
+
+class FormData extends DynamicBindingObject {
+  FormData(BindingContext context, List domMatrixInit): super(context);
+
+  @override
+  void initializeMethods(Map methods) {
+    methods['append'] = BindingObjectMethodSync(call: (args) {
+      Uint8List bytes = args[0];
+      print('bytes: $bytes');
+    });
+  }
+
+  @override
+  void initializeProperties(Map properties) {
+  }
+}

From aa2070193f1f3ef1e5eca755621e2f5992a79f4b Mon Sep 17 00:00:00 2001
From: andycall 
Date: Thu, 15 Aug 2024 17:09:42 +0800
Subject: [PATCH 13/18] refactor: reimplement form_data.cc

---
 bridge/CMakeLists.txt                         |   2 +-
 bridge/bindings/qjs/converter_impl.h          |  24 ---
 bridge/core/binding_call_methods.json5        |   3 +-
 bridge/core/html/forms/form_data.cc           | 182 ++++++++++--------
 bridge/core/html/forms/form_data.d.ts         |  12 +-
 bridge/core/html/forms/form_data.h            |  66 +++++--
 bridge/core/html/forms/form_data_part.cc      |  55 ------
 bridge/core/html/forms/form_data_part.h       |  53 -----
 bridge/core/html/forms/form_data_test.cc      |  30 +++
 bridge/scripts/code_generator/global.d.ts     |   1 +
 .../code_generator/src/idl/analyzer.ts        |  10 +
 .../code_generator/src/idl/declaration.ts     |   1 +
 .../code_generator/src/idl/generateSource.ts  |   8 +-
 .../templates/idl_templates/interface.cc.tpl  |   4 +-
 .../templates/idl_templates/union.cc.tpl      |   1 +
 bridge/test/test.cmake                        |   1 +
 16 files changed, 216 insertions(+), 237 deletions(-)
 delete mode 100644 bridge/core/html/forms/form_data_part.cc
 delete mode 100644 bridge/core/html/forms/form_data_part.h
 create mode 100644 bridge/core/html/forms/form_data_test.cc

diff --git a/bridge/CMakeLists.txt b/bridge/CMakeLists.txt
index 52939c040f..9437e3e3b4 100644
--- a/bridge/CMakeLists.txt
+++ b/bridge/CMakeLists.txt
@@ -379,7 +379,6 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs")
     core/html/forms/html_form_element.cc
     core/html/forms/html_textarea_element.cc
     core/html/forms/form_data.cc
-    core/html/forms/form_data_part.cc
 
     # SVG files
     core/svg/svg_element.cc
@@ -510,6 +509,7 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs")
     out/qjs_union_dom_string_sequencedouble.cc
     out/qjs_unionhtml_image_elementhtml_canvas_element.cc
     out/qjs_union_dom_stringcanvas_gradient.cc
+    out/qjs_union_dom_stringblob.cc
     out/canvas_types.cc
     out/qjs_html_button_element.cc
     out/qjs_html_input_element.cc
diff --git a/bridge/bindings/qjs/converter_impl.h b/bridge/bindings/qjs/converter_impl.h
index 0e96d56f98..c6d9112377 100644
--- a/bridge/bindings/qjs/converter_impl.h
+++ b/bridge/bindings/qjs/converter_impl.h
@@ -15,7 +15,6 @@
 #include "core/dom/events/event_target.h"
 #include "core/dom/node_list.h"
 #include "core/fileapi/blob_part.h"
-#include "core/html/forms/form_data_part.h"
 #include "core/fileapi/blob_property_bag.h"
 #include "core/frame/window.h"
 #include "core/html/html_body_element.h"
@@ -386,29 +385,6 @@ struct Converter : public ConverterBase {
   }
 };
 
-template <>
-struct Converter : public ConverterBase {
-   using ImplType = FormDataPart::ImplType;
-   static ImplType FromValue(JSContext* ctx, JSValue value, ExceptionState& exception_state) {
-    assert(!JS_IsException(value));
-    return FormDataPart::Create(ctx, value, exception_state);
-   }
-
-  static JSValue ToValue(JSContext* ctx, ImplType data) {
-    if (data == nullptr)
-      return JS_NULL;
-
-    return data->ToQuickJS(ctx);
-  }
-  
-  static JSValue ToValue(JSContext* ctx, FormDataPart* data) {
-    if (data == nullptr)
-      return JS_NULL;
-
-    return data->ToQuickJS(ctx);
-  }
-};
-
 
 template <>
 struct Converter : public ConverterBase {
diff --git a/bridge/core/binding_call_methods.json5 b/bridge/core/binding_call_methods.json5
index 1083125eea..b4a1514e7f 100644
--- a/bridge/core/binding_call_methods.json5
+++ b/bridge/core/binding_call_methods.json5
@@ -180,6 +180,7 @@
     "pageXOffset",
     "pageYOffset",
     "title",
-    "append"
+    "append",
+    "delete"
   ]
 }
diff --git a/bridge/core/html/forms/form_data.cc b/bridge/core/html/forms/form_data.cc
index ab13db0f69..0746679941 100644
--- a/bridge/core/html/forms/form_data.cc
+++ b/bridge/core/html/forms/form_data.cc
@@ -4,15 +4,14 @@
 
 #include "form_data.h"
 #include 
-#include "binding_call_methods.h"
 #include "bindings/qjs/atomic_string.h"
+#include "bindings/qjs/cppgc/gc_visitor.h"
 #include "core/executing_context.h"
 #include "core/fileapi/blob_part.h"
-#include "form_data_part.h"
 #include "foundation/native_value_converter.h"
 
 namespace webf {
-const char* className = "FormData";
+
 FormData* FormData::Create(ExecutingContext* context, ExceptionState& exception_state) {
   return MakeGarbageCollected(context->ctx());
 }
@@ -34,109 +33,138 @@ bool FormData::IsFormData() const {
   return true;
 }
 
+void FormData::append(const webf::AtomicString& name,
+                      const std::shared_ptr& value,
+                      webf::ExceptionState& exception_state) {
+  append(name, value, AtomicString::Empty(), exception_state);
+}
+
 void FormData::append(const AtomicString& name,
-                      const std::shared_ptr& value,
-                      const AtomicString& fileName,
+                      const std::shared_ptr& value,
+                      const AtomicString& file_name,
                       ExceptionState& exception_state) {
-  if (name.IsEmpty()) {
-    exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty.");
-    return;
+  if (value->IsDomString()) {
+    append(name, value->GetAsDomString());
+  } else if (value->IsBlob()) {
+    append(name, value->GetAsBlob(), file_name);
   }
-
-  auto form_data_part = std::make_shared(name.ToStdString(ctx()), value, fileName.ToStdString(ctx()));
-
-  _parts.push_back(form_data_part);
-
-  uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04, 0x05};
-  uint32_t length = 5;
-
-  const NativeValue arguments[]  = {
-      NativeValueConverter::ToNativeValue(buffer, length)
-  };
-
-  NativeValue return_result = InvokeBindingMethod(binding_call_methods::kappend, 1, arguments,
-                                                  FlushUICommandReason::kStandard, exception_state);
 }
 
-void FormData::form_data_delete(const AtomicString& name, ExceptionState& exception_state) {
-  if (name.IsEmpty()) {
-    exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty.");
-    return;
+void FormData::deleteEntry(const AtomicString& name, ExceptionState& exception_state) {
+  size_t i = 0;
+  while (i < entries_.size()) {
+    if (entries_[i]->name() == name) {
+      entries_.erase(entries_.begin() + i);
+    } else {
+      ++i;
+    }
   }
-
-  _parts.erase(std::remove_if(_parts.begin(), _parts.end(),
-                              [name, this](const std::shared_ptr& part) {
-                                return part->GetName() == name.ToStdString(ctx());
-                              }),
-               _parts.end());
 }
 
-webf::BlobPart* FormData::get(const AtomicString& name, ExceptionState& exception_state) {
-  if (name.IsEmpty()) {
-    exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty.");
-    return nullptr;
+std::shared_ptr FormData::get(const AtomicString& name, ExceptionState& exception_state) {
+  for (const auto& entry : Entries()) {
+    if (entry->name() == name) {
+      if (entry->IsString()) {
+        return std::make_shared(entry->Value());
+      } else {
+        assert(entry->isFile());
+        return std::make_shared(entry->GetBlob());
+      }
+    }
   }
+  return nullptr;
+}
 
-  for (const auto& part : _parts) {
-    if (part->GetName() == name.ToStdString(ctx())) {
-      return &*part->getFirst();
+std::vector> FormData::getAll(const AtomicString& name,
+                                                                     ExceptionState& exception_state) {
+  std::vector> results;
+
+  for (const auto& entry : Entries()) {
+    if (entry->name() != name)
+      continue;
+    std::shared_ptr value;
+    if (entry->IsString()) {
+      value = std::make_shared(entry->Value());
+    } else {
+      assert(entry->isFile());
+      value = std::make_shared(entry->GetBlob());
     }
+    results.emplace_back(value);
   }
+  return results;
+}
 
-  return nullptr;
+bool FormData::has(const AtomicString& name, ExceptionState& exception_state) {
+  for (const auto& entry : Entries()) {
+    if (entry->name() == name)
+      return true;
+  }
+  return false;
 }
 
-std::vector FormData::getAll(const AtomicString& name, ExceptionState& exception_state) {
-  std::vector result;
+void FormData::set(const AtomicString& name,
+                   const std::shared_ptr& blob_part,
+                   ExceptionState& exception_state) {
+  if (blob_part->IsBlob()) {
+    SetEntry(std::make_shared(name, blob_part->GetAsBlob(), AtomicString::Empty()));
+  } else if (blob_part->IsDomString()) {
+    SetEntry(std::make_shared(name, blob_part->GetAsDomString()));
+  }
+}
 
-  if (name.IsEmpty()) {
-    exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty.");
-    return result;
+void FormData::set(const AtomicString& name,
+                   const std::shared_ptr& blob_part,
+                   const AtomicString& file_name,
+                   ExceptionState& exception_state) {
+  if (blob_part->IsBlob()) {
+    SetEntry(std::make_shared(name, blob_part->GetAsBlob(), file_name));
+  } else if (blob_part->IsDomString()) {
+    SetEntry(std::make_shared(name, blob_part->GetAsDomString()));
   }
+}
 
-  for (const auto& part : _parts) {
-    if (part->GetName() == name.ToStdString(ctx())) {
-      for (const auto& value : part->GetValues()) {
-        result.push_back(std::make_shared(value));
-      }
+void FormData::SetEntry(std::shared_ptr entry) {
+  assert(entry);
+  bool found = false;
+  size_t i = 0;
+  while (i < entries_.size()) {
+    if (entries_[i]->name() != entry->name()) {
+      ++i;
+    } else if (found) {
+      entries_.erase(entries_.begin() + i);
+    } else {
+      found = true;
+      entries_[i] = entry;
+      ++i;
     }
   }
-
-  return result;
+  if (!found)
+    entries_.emplace_back(entry);
 }
 
-bool FormData::has(const AtomicString& name, ExceptionState& exception_state) {
-  if (name.IsEmpty()) {
-    exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty.");
-    return false;
+void FormData::Trace(webf::GCVisitor* visitor) const {
+  for (auto&& entry : entries_) {
+    entry->Trace(visitor);
   }
+}
 
-  for (const auto& part : _parts) {
-    if (part->GetName() == name.ToStdString(ctx())) {
-      return true;
-    }
-  }
+void FormData::append(const webf::AtomicString& name, const webf::AtomicString& value) {
+  entries_.emplace_back(std::make_shared(name, value));
+}
 
-  return false;
+void FormData::append(const webf::AtomicString& name, webf::Blob* blob, const AtomicString& file_name) {
+  entries_.emplace_back(std::make_shared(name, blob, file_name));
 }
 
-void FormData::set(const AtomicString& name,
-                   const std::shared_ptr& value,
-                   const AtomicString& fileName,
-                   ExceptionState& exception_state) {
-  if (name.IsEmpty()) {
-    exception_state.ThrowException(ctx(), ErrorType::ArgumentError, "The name parameter must not be empty.");
-    return;
-  }
+// ----------------------------------------------------------------
 
-  _parts.erase(std::remove_if(_parts.begin(), _parts.end(),
-                              [name, this](const std::shared_ptr& part) {
-                                return part->GetName() == name.ToStdString(ctx());
-                              }),
-               _parts.end());
+FormData::Entry::Entry(const AtomicString& name, const AtomicString& value) : name_(name), value_(value) {}
 
-  auto form_data_part = std::make_shared(name.ToStdString(ctx()), value, fileName.ToStdString(ctx()));
+FormData::Entry::Entry(const AtomicString& name, Blob* blob, const AtomicString& filename)
+    : name_(name), blob_(blob), filename_(filename) {}
 
-  _parts.push_back(form_data_part);
+void FormData::Entry::Trace(GCVisitor* visitor) const {
+  visitor->TraceMember(blob_);
 }
+
 }  // namespace webf
\ No newline at end of file
diff --git a/bridge/core/html/forms/form_data.d.ts b/bridge/core/html/forms/form_data.d.ts
index 6b3ce7e962..b44785014b 100644
--- a/bridge/core/html/forms/form_data.d.ts
+++ b/bridge/core/html/forms/form_data.d.ts
@@ -4,14 +4,12 @@
 
 export interface FormData {
   new(): FormData;
-  append(name: string, value: BlobPart, fileName?: string): void;
-  // This method name is a placeholder of **delete** method to avoid using C++ keyword
-  // and will be replaced to **delete** when installing in MemberInstaller::InstallFunctions.
-  form_data_delete(name: string): void;
-  get(name: string): BlobPart
-  getAll(name: string): BlobPart[];
+  append(name: string, value: (string | Blob), fileName?: string): void;
+  delete(name: string): ImplementedAs;
+  get(name: string): (string | Blob);
+  getAll(name: string): (string | Blob)[];
   has(name: string): boolean;
-  set(name: string, value: BlobPart, fileName?: string): void;
+  set(name: string, value: string | Blob, fileName?: string): void;
 
   readonly forEach: JSArrayProtoMethod;
   readonly keys: JSArrayProtoMethod;
diff --git a/bridge/core/html/forms/form_data.h b/bridge/core/html/forms/form_data.h
index 20dfbae0d1..8938593902 100644
--- a/bridge/core/html/forms/form_data.h
+++ b/bridge/core/html/forms/form_data.h
@@ -6,11 +6,12 @@
 #define WEBF_CORE_API_FORM_DATA_H_
 #include 
 #include 
+#include "bindings/qjs/cppgc/member.h"
 #include "bindings/qjs/atomic_string.h"
 #include "bindings/qjs/script_wrappable.h"
 #include "core/binding_object.h"
-#include "core/fileapi/blob_part.h"
-#include "core/html/forms/form_data_part.h"
+#include "core/fileapi/blob.h"
+#include "qjs_union_dom_stringblob.h"
 
 namespace webf {
 
@@ -18,6 +19,7 @@ class FormData : public BindingObject {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
+  class Entry;
   using ImplType = FormData*;
 
   FormData() = delete;
@@ -28,28 +30,60 @@ class FormData : public BindingObject {
                                      const NativeValue* argv,
                                      Dart_Handle dart_object) override;
   bool IsFormData() const override;
-  void append(const AtomicString& name, const std::shared_ptr& value, ExceptionState& exception_state) {
-    append(name, value, AtomicString::Empty(), exception_state);
-  }
   void append(const AtomicString& name,
-              const std::shared_ptr& value,
-              const AtomicString& fileName,
+              const std::shared_ptr& value,
               ExceptionState& exception_state);
-  void form_data_delete(const AtomicString& name, ExceptionState& exception_state);
-  BlobPart* get(const AtomicString& name, ExceptionState& exception_state);
-  std::vector getAll(const AtomicString& name, ExceptionState& exception_state);
+  void append(const AtomicString& name,
+              const std::shared_ptr& value,
+              const AtomicString& file_name,
+              ExceptionState& exception_state);
+  void deleteEntry(const AtomicString& name, ExceptionState& exception_state);
+  std::shared_ptr get(const AtomicString& name, ExceptionState& exception_state);
+  std::vector> getAll(const AtomicString& name, ExceptionState& exception_state);
   bool has(const AtomicString& name, ExceptionState& exception_state);
-  void set(const AtomicString& name, const std::shared_ptr& value, ExceptionState& exception_state) {
-    set(name, value, AtomicString::Empty(), exception_state);
-  }
   void set(const AtomicString& name,
-           const std::shared_ptr& value,
-           const AtomicString& fileName,
+           const std::shared_ptr& blob_part,
+           ExceptionState& exception_state);
+  void set(const AtomicString& name,
+           const std::shared_ptr& blob_part,
+           const AtomicString& file_name,
            ExceptionState& exception_state);
 
+  void SetEntry(std::shared_ptr entry);
+  const std::vector>& Entries() const { return entries_; }
+
+  void Trace(webf::GCVisitor *visitor) const override;
+
+ private:
+  void append(const AtomicString&name, const AtomicString& value);
+  void append(const AtomicString&name, Blob* blob, const AtomicString& file_name);
+
+  std::vector> entries_;
+};
+
+// Represents entry, which is a pair of a name and a value.
+// https://xhr.spec.whatwg.org/#concept-formdata-entry
+// Entry objects are immutable.
+class FormData::Entry final {
+ public:
+  Entry(const AtomicString& name, const AtomicString& value);
+  Entry(const AtomicString& name, Blob* blob, const AtomicString& filename);
+  void Trace(GCVisitor*) const;
+
+  bool IsString() const { return !blob_; }
+  bool isFile() const { return blob_ != nullptr; }
+  const AtomicString& name() const { return name_; }
+  const AtomicString& Value() const { return value_; }
+  Blob* GetBlob() const { return blob_.Get(); }
+  const AtomicString& Filename() const { return filename_; }
+
  private:
-  std::vector> _parts;
+  AtomicString name_;
+  AtomicString value_;
+  Member blob_;
+  AtomicString filename_;
 };
+
 template <>
 struct DowncastTraits {
   static bool AllowFrom(const BindingObject& binding_object) { return binding_object.IsFormData(); }
diff --git a/bridge/core/html/forms/form_data_part.cc b/bridge/core/html/forms/form_data_part.cc
deleted file mode 100644
index f09de6eacd..0000000000
--- a/bridge/core/html/forms/form_data_part.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2022-present The WebF authors. All rights reserved.
- */
-
-#include "form_data_part.h"
-#include "bindings/qjs/atomic_string.h"
-#include "bindings/qjs/exception_state.h"
-#include "core/fileapi/blob_part.h"
-#include "qjs_blob.h"
-namespace webf {
-
-std::shared_ptr FormDataPart::Create(JSContext* ctx,
-                                                   JSValue value,
-                                                   ExceptionState& exception_state) {
-  return std::make_shared();
-}
-
-JSValue FormDataPart::ToQuickJS(JSContext* ctx) const {
-  JSValue arr = JS_NewArray(ctx);
-  if (JS_IsNull(arr)) {
-    // Handle error creating array
-    return JS_EXCEPTION;
-  }
-
-  // Convert name to JSValue and add to the array
-  JSValue nameValue = JS_NewString(ctx, name_.c_str());
-  if (JS_IsNull(nameValue)) {
-    // Handle error creating string
-    JS_FreeValue(ctx, arr);
-    return JS_EXCEPTION;
-  }
-  JS_SetPropertyUint32(ctx, arr, 0, nameValue);
-
-  // Assuming there's a way to convert BlobPart to JSValue, which is not shown here.
-  if (!values_.empty()) {
-    JSValue value = values_[0].ToQuickJS(ctx);
-    if (JS_IsNull(value)) {
-      // Handle error converting BlobPart to JSValue
-      JS_FreeValue(ctx, arr);
-      return JS_EXCEPTION;
-    }
-    JS_SetPropertyUint32(ctx, arr, 1, value);
-  } else {
-    // If values_ is empty, set the second element to null
-    JS_SetPropertyUint32(ctx, arr, 1, JS_NULL);
-  }
-
-  return arr;
-}
-
-void FormDataPart::AddValue(const BlobPart& value) {
-  values_.push_back(value);
-}
-
-}  // namespace webf
\ No newline at end of file
diff --git a/bridge/core/html/forms/form_data_part.h b/bridge/core/html/forms/form_data_part.h
deleted file mode 100644
index 5adfd55b34..0000000000
--- a/bridge/core/html/forms/form_data_part.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2022-present The WebF authors. All rights reserved.
- */
-#ifndef BRIDGE_API_FORM_DATA_PART_H_
-#define BRIDGE_API_FORM_DATA_PART_H_
-
-#include 
-#include 
-#include 
-#include 
-#include 
-#include "bindings/qjs/exception_state.h"
-#include "core/fileapi/blob_part.h"
-namespace webf {
-
-class BlobPart;
-class FormDataPart {
- public:
-  using ImplType = std::shared_ptr;
-  enum class ContentType { kFile, kBlob, kString };
-
-  static std::shared_ptr Create(JSContext* ctx, JSValue value, ExceptionState& exception_state);
-  explicit FormDataPart(std::string name, const std::shared_ptr data) {
-    this->name_ = std::move(name);
-    AddValue(*data);
-  };
-  explicit FormDataPart() {}
-  explicit FormDataPart(std::string name) { this->name_ = std::move(name); }
-  explicit FormDataPart(std::string name, const std::shared_ptr data, std::string fileName) {
-    this->name_ = std::move(name);
-    AddValue(*data);
-    // todo: filename is not used.
-  };
-  JSValue ToQuickJS(JSContext* ctx) const;
-  const std::string& GetName() const { return name_; }
-  const std::vector& GetValues() const { return values_; }
-  void AddValue(const BlobPart& value);
-  std::shared_ptr getFirst() const {
-    if (values_.empty()) {
-      return nullptr;
-    } else {
-      return std::make_shared(values_[0]);
-    }
-  }
-
- private:
-  std::string name_;
-  std::vector values_;
-};
-
-}  // namespace webf
-
-#endif  // BRIDGE_CORE_FILEAPI_BLOB_PART_H_
diff --git a/bridge/core/html/forms/form_data_test.cc b/bridge/core/html/forms/form_data_test.cc
new file mode 100644
index 0000000000..4dabb74cdf
--- /dev/null
+++ b/bridge/core/html/forms/form_data_test.cc
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022-present The WebF authors. All rights reserved.
+ */
+
+#include "gtest/gtest.h"
+#include "webf_test_env.h"
+
+using namespace webf;
+
+TEST(FormData, append) {
+  bool static errorCalled = false;
+  bool static logCalled = false;
+  webf::WebFPage::consoleMessageHandler = [](void* ctx, const std::string& message, int logLevel) {
+    logCalled = true;
+  };
+  auto env = TEST_init([](double contextId, const char* errmsg) {
+    WEBF_LOG(VERBOSE) << errmsg;
+    errorCalled = true;
+  });
+  auto context = env->page()->executingContext();
+  std::string code = R"(
+    let formData = new FormData();
+
+    formData.append('name', 1234);
+    formData.append('age', 4567);
+  )";
+  env->page()->evaluateScript(code.c_str(), code.size(), "vm://", 0);
+
+  EXPECT_EQ(errorCalled, false);
+}
\ No newline at end of file
diff --git a/bridge/scripts/code_generator/global.d.ts b/bridge/scripts/code_generator/global.d.ts
index 62c081efe1..c686f2978c 100644
--- a/bridge/scripts/code_generator/global.d.ts
+++ b/bridge/scripts/code_generator/global.d.ts
@@ -15,6 +15,7 @@ declare type LegacyNullToEmptyString = string | null;
 // This property is implemented by Dart side
 type DartImpl = T;
 type StaticMember = T;
+type ImplementedAs = T;
 
 
 type DependentsOnLayout = T;
\ No newline at end of file
diff --git a/bridge/scripts/code_generator/src/idl/analyzer.ts b/bridge/scripts/code_generator/src/idl/analyzer.ts
index 6e0ae6d27a..2796768e58 100644
--- a/bridge/scripts/code_generator/src/idl/analyzer.ts
+++ b/bridge/scripts/code_generator/src/idl/analyzer.ts
@@ -151,6 +151,12 @@ function getParameterBaseType(type: ts.TypeNode, mode?: ParameterMode): Paramete
       return getParameterBaseType(argument);
     } else if (identifier === 'LegacyNullToEmptyString') {
       return FunctionArgumentType.legacy_dom_string;
+    } else if (identifier === 'ImplementedAs') {
+      let secondNameNode: ts.LiteralTypeNode = typeReference.typeArguments![1] as unknown as ts.LiteralTypeNode;
+      if (mode) {
+        mode.secondaryName = secondNameNode.literal['text'] as string;
+      }
+      return getParameterBaseType(typeReference.typeArguments![0] as unknown as ts.TypeNode);
     }
 
     return identifier;
@@ -288,6 +294,10 @@ function walkProgram(blob: IDLBlob, statement: ts.Statement, definedPropertyColl
               let mode = new ParameterMode();
               f.returnType = getParameterType(m.type, unionTypeCollector, mode);
               f.returnTypeMode = mode;
+
+              if (mode.secondaryName) {
+                f.name = mode.secondaryName;
+              }
             }
             break;
           }
diff --git a/bridge/scripts/code_generator/src/idl/declaration.ts b/bridge/scripts/code_generator/src/idl/declaration.ts
index 2bb628d8f8..bcfd92df60 100644
--- a/bridge/scripts/code_generator/src/idl/declaration.ts
+++ b/bridge/scripts/code_generator/src/idl/declaration.ts
@@ -34,6 +34,7 @@ export class ParameterMode {
   dartImpl?: boolean;
   layoutDependent?: boolean;
   static?: boolean;
+  secondaryName?: string;
 }
 
 export class PropsDeclaration {
diff --git a/bridge/scripts/code_generator/src/idl/generateSource.ts b/bridge/scripts/code_generator/src/idl/generateSource.ts
index 128fde764d..c1df3191d6 100644
--- a/bridge/scripts/code_generator/src/idl/generateSource.ts
+++ b/bridge/scripts/code_generator/src/idl/generateSource.ts
@@ -483,9 +483,14 @@ function generateReturnValueInit(blob: IDLBlob, type: ParameterType, options: Ge
 }) {
   if (type.value == FunctionArgumentType.void) return '';
 
+
   if (options.isConstructor) {
     return `${getClassName(blob)}* return_value = nullptr;`
   }
+  if (isUnionType(type) && Array.isArray(type.value)) {
+    return `std::shared_ptr<${generateUnionTypeClassName(type.value)}> return_value = nullptr;`;
+  }
+
   if (isPointerType(type)) {
     if (getPointerType(type) === 'Promise') {
       return 'ScriptPromise return_value;';
@@ -579,7 +584,7 @@ export function generateCppSource(blob: IDLBlob, options: GenerateOptions) {
           } else {
             overloadMethods[method.name] = [method];
             filtedMethods.push(method);
-            options.classPropsInstallList.push(`{"${method.name}", ${method.name}, ${method.args.length}}`)
+            options.classPropsInstallList.push(`{"${method.name}", qjs_${method.name}, ${method.args.length}}`)
           }
         }
 
@@ -712,6 +717,7 @@ export function generateUnionTypeSource(unionType: ParameterType): string {
     generateUnionConstructorImpl,
     generateUnionTypeSetter,
     getUnionTypeName,
+    isPointerType,
     isTypeHaveNull,
     isTypeHaveString
   }).split('\n').filter(str => {
diff --git a/bridge/scripts/code_generator/templates/idl_templates/interface.cc.tpl b/bridge/scripts/code_generator/templates/idl_templates/interface.cc.tpl
index a4f6c9e094..54bb89b356 100644
--- a/bridge/scripts/code_generator/templates/idl_templates/interface.cc.tpl
+++ b/bridge/scripts/code_generator/templates/idl_templates/interface.cc.tpl
@@ -147,12 +147,12 @@ static JSValue <%= overloadMethod.name %>_overload_<%= index %>(JSContext* ctx,
         <%= generateFunctionBody(blob, overloadMethod, {isInstanceMethod: true}) %>
       }
     <% }); %>
-    static JSValue <%= method.name %>(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
+    static JSValue qjs_<%= method.name %>(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
       <%= generateOverLoadSwitchBody(overloadMethods[method.name]) %>
     }
   <% } else { %>
 
-  static JSValue <%= method.name %>(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
+  static JSValue qjs_<%= method.name %>(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) {
     <%= generateFunctionBody(blob, method, {isInstanceMethod: true}) %>
   }
   <% } %>
diff --git a/bridge/scripts/code_generator/templates/idl_templates/union.cc.tpl b/bridge/scripts/code_generator/templates/idl_templates/union.cc.tpl
index 5d01036658..251a892270 100644
--- a/bridge/scripts/code_generator/templates/idl_templates/union.cc.tpl
+++ b/bridge/scripts/code_generator/templates/idl_templates/union.cc.tpl
@@ -8,6 +8,7 @@
 #include "core/html/canvas/canvas_gradient.h"
 #include "<%= generateUnionTypeFileName(unionType) %>.h"
 #include "bindings/qjs/converter_impl.h"
+#include "core/fileapi/blob.h"
 #include "core/html/html_image_element.h"
 #include "core/html/canvas/html_canvas_element.h"
 
diff --git a/bridge/test/test.cmake b/bridge/test/test.cmake
index e2fb3d45d6..4c7ce36741 100644
--- a/bridge/test/test.cmake
+++ b/bridge/test/test.cmake
@@ -27,6 +27,7 @@ list(APPEND WEBF_UNIT_TEST_SOURCEURCE
   ./core/dom/document_test.cc
   ./core/dom/legacy/element_attribute_test.cc
   ./core/dom/node_test.cc
+  ./core/html/forms/form_data_test.cc
   ./core/html/html_collection_test.cc
   ./core/dom/element_test.cc
   ./core/frame/dom_timer_test.cc

From 8c70bdfff48f7bf32f08f94843e80b7b6f0a40b8 Mon Sep 17 00:00:00 2001
From: andycall 
Date: Sat, 17 Aug 2024 01:53:58 +0800
Subject: [PATCH 14/18] feat: add iterator base class for the implementation of
 array like methods.

---
 bridge/CMakeLists.txt                         |  2 +
 bridge/bindings/qjs/array_methods.d.ts        |  8 +++
 bridge/bindings/qjs/binding_initializer.cc    |  2 +
 bridge/bindings/qjs/iterable.cc               |  6 ++
 bridge/bindings/qjs/iterable.h                | 61 +++++++++++++++++++
 bridge/bindings/qjs/sync_iterator.cc          | 31 ++++++++++
 bridge/bindings/qjs/sync_iterator.d.ts        |  7 +++
 bridge/bindings/qjs/sync_iterator.h           | 52 ++++++++++++++++
 bridge/bindings/qjs/wrapper_type_info.h       |  1 +
 bridge/core/html/forms/form_data.cc           | 57 +++++++++++++++++
 bridge/core/html/forms/form_data.d.ts         | 11 ++--
 bridge/core/html/forms/form_data.h            |  9 ++-
 bridge/core/html/forms/form_data_test.cc      |  6 +-
 .../code_generator/bin/code_generator.js      |  3 +-
 bridge/scripts/code_generator/global.d.ts     |  1 -
 .../code_generator/src/idl/analyzer.ts        | 15 +++--
 .../idl_templates/global_function.h.tpl       |  2 +-
 .../templates/idl_templates/interface.h.tpl   |  2 +-
 scripts/tasks.js                              |  2 +-
 19 files changed, 256 insertions(+), 22 deletions(-)
 create mode 100644 bridge/bindings/qjs/array_methods.d.ts
 create mode 100644 bridge/bindings/qjs/iterable.cc
 create mode 100644 bridge/bindings/qjs/iterable.h
 create mode 100644 bridge/bindings/qjs/sync_iterator.cc
 create mode 100644 bridge/bindings/qjs/sync_iterator.d.ts
 create mode 100644 bridge/bindings/qjs/sync_iterator.h

diff --git a/bridge/CMakeLists.txt b/bridge/CMakeLists.txt
index 9437e3e3b4..c308d349bc 100644
--- a/bridge/CMakeLists.txt
+++ b/bridge/CMakeLists.txt
@@ -262,6 +262,7 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs")
     bindings/qjs/exception_state.cc
     bindings/qjs/exception_message.cc
     bindings/qjs/rejected_promises.cc
+    bindings/qjs/sync_iterator.cc
     bindings/qjs/union_base.cc
     # Core sources
     webf_bridge.cc
@@ -557,6 +558,7 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs")
     out/qjs_svg_ellipse_element.cc
     out/qjs_svg_style_element.cc
     out/qjs_svg_line_element.cc
+    out/qjs_sync_iterator.cc
     )
 
 
diff --git a/bridge/bindings/qjs/array_methods.d.ts b/bridge/bindings/qjs/array_methods.d.ts
new file mode 100644
index 0000000000..d9cfaebaf7
--- /dev/null
+++ b/bridge/bindings/qjs/array_methods.d.ts
@@ -0,0 +1,8 @@
+// @ts-ignore
+@Mixin()
+export interface ArrayLikeMethodsMixin {
+  forEach(fn: Function, this_val?: any): void;
+  entries(): SyncIterator;
+  values(): SyncIterator;
+  keys(): SyncIterator;
+}
\ No newline at end of file
diff --git a/bridge/bindings/qjs/binding_initializer.cc b/bridge/bindings/qjs/binding_initializer.cc
index bb75beb41a..46597e4585 100644
--- a/bridge/bindings/qjs/binding_initializer.cc
+++ b/bridge/bindings/qjs/binding_initializer.cc
@@ -99,6 +99,7 @@
 #include "qjs_ui_event.h"
 #include "qjs_widget_element.h"
 #include "qjs_window.h"
+#include "qjs_sync_iterator.h"
 #include "qjs_window_or_worker_global_scope.h"
 
 namespace webf {
@@ -107,6 +108,7 @@ void InstallBindings(ExecutingContext* context) {
   // Must follow the inheritance order when install.
   // Exp: Node extends EventTarget, EventTarget must be install first.
   QJSWindowOrWorkerGlobalScope::Install(context);
+  QJSSyncIterator::Install(context);
   QJSLocation::Install(context);
   QJSModuleManager::Install(context);
   QJSConsole::Install(context);
diff --git a/bridge/bindings/qjs/iterable.cc b/bridge/bindings/qjs/iterable.cc
new file mode 100644
index 0000000000..fe2af0fbc1
--- /dev/null
+++ b/bridge/bindings/qjs/iterable.cc
@@ -0,0 +1,6 @@
+/*
+* Copyright (C) 2019-2022 The Kraken authors. All rights reserved.
+* Copyright (C) 2022-present The WebF authors. All rights reserved.
+*/
+
+#include "iterable.h"
diff --git a/bridge/bindings/qjs/iterable.h b/bridge/bindings/qjs/iterable.h
new file mode 100644
index 0000000000..22cc2b6d16
--- /dev/null
+++ b/bridge/bindings/qjs/iterable.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022-present The WebF authors. All rights reserved.
+ */
+
+#ifndef WEBF_BINDINGS_QJS_ITERABLE_H_
+#define WEBF_BINDINGS_QJS_ITERABLE_H_
+
+#include "bindings/qjs/sync_iterator.h"
+
+namespace webf {
+
+class PairSyncIterationSource {
+ public:
+  virtual ScriptValue Next(ExceptionState& exception_state) = 0;
+
+  virtual ScriptValue Value() = 0;
+  virtual bool Done() = 0;
+
+  virtual void Trace(GCVisitor* visitor) = 0;
+  virtual JSContext* ctx() = 0;
+
+ private:
+};
+
+class PairSyncIterable {
+ public:
+
+  SyncIterator* keys(ExceptionState& exception_state) {
+    std::shared_ptr source = CreateIterationSource(exception_state);
+    if (!source)
+      return nullptr;
+    return MakeGarbageCollected(source->ctx(), source, SyncIterator::Kind::kKey);
+  }
+
+  SyncIterator* values(ExceptionState& exception_state) {
+    std::shared_ptr source = CreateIterationSource(exception_state);
+    if (!source)
+      return nullptr;
+    return MakeGarbageCollected(source->ctx(), source, SyncIterator::Kind::kValue);
+  }
+
+  SyncIterator* entries(ExceptionState& exception_state) {
+    std::shared_ptr source = CreateIterationSource(exception_state);
+    if (!source)
+      return nullptr;
+    return MakeGarbageCollected(source->ctx(), source, SyncIterator::Kind::kKeyValue);
+  }
+
+  void forEach(const std::shared_ptr& callback, ExceptionState& exception_state) {}
+
+  void forEach(const std::shared_ptr& callback,
+               const ScriptValue& this_arg,
+               ExceptionState& exception_state) {}
+
+ private:
+  virtual std::shared_ptr CreateIterationSource(ExceptionState& exception_state) = 0;
+};
+
+}  // namespace webf
+
+#endif  // WEBF_BINDINGS_QJS_ITERABLE_H_
diff --git a/bridge/bindings/qjs/sync_iterator.cc b/bridge/bindings/qjs/sync_iterator.cc
new file mode 100644
index 0000000000..64aa5d95a4
--- /dev/null
+++ b/bridge/bindings/qjs/sync_iterator.cc
@@ -0,0 +1,31 @@
+/*
+* Copyright (C) 2022-present The WebF authors. All rights reserved.
+*/
+
+#include "bindings/qjs/sync_iterator.h"
+#include "bindings/qjs/iterable.h"
+
+namespace webf {
+
+ScriptValue SyncIterator::value() {
+  return iteration_source_->Value();
+}
+
+SyncIterator* SyncIterator::Symbol_iterator() {
+  return this;
+}
+
+bool SyncIterator::done() {
+  return iteration_source_->Done();
+}
+
+void SyncIterator::Trace(GCVisitor* visitor) const {
+  iteration_source_->Trace(visitor);
+  ScriptWrappable::Trace(visitor);
+}
+
+ScriptValue SyncIterator::next(ExceptionState& exception_state) {
+  return iteration_source_->Next(exception_state);
+}
+
+}
\ No newline at end of file
diff --git a/bridge/bindings/qjs/sync_iterator.d.ts b/bridge/bindings/qjs/sync_iterator.d.ts
new file mode 100644
index 0000000000..ee61a162b4
--- /dev/null
+++ b/bridge/bindings/qjs/sync_iterator.d.ts
@@ -0,0 +1,7 @@
+interface SyncIterator {
+  new(): void;
+  next(): any;
+  readonly done: boolean;
+  readonly value: any;
+  readonly [Symbol.iterator]: SyncIterator;
+}
\ No newline at end of file
diff --git a/bridge/bindings/qjs/sync_iterator.h b/bridge/bindings/qjs/sync_iterator.h
new file mode 100644
index 0000000000..a05ee9fade
--- /dev/null
+++ b/bridge/bindings/qjs/sync_iterator.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022-present The WebF authors. All rights reserved.
+ */
+
+#ifndef WEBF_BINDING_SYNC_ITERATOR_BASE
+#define WEBF_BINDING_SYNC_ITERATOR_BASE
+
+#include "bindings/qjs/cppgc/gc_visitor.h"
+#include "bindings/qjs/exception_state.h"
+#include "bindings/qjs/script_wrappable.h"
+#include "core/script_state.h"
+
+namespace webf {
+
+class PairSyncIterationSource;
+
+// SyncIteratorBase is the common base class of all sync iterator classes.
+// Most importantly this class provides a way of type dispatching (e.g.
+// overload resolutions, SFINAE technique, etc.) so that it's possible to
+// distinguish sync iterators from anything else. Also it provides common
+// implementation of sync iterators.
+class SyncIterator : public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+ public:
+  using ImplType = SyncIterator*;
+  // https://webidl.spec.whatwg.org/#default-iterator-object-kind
+  enum class Kind {
+    kKey,
+    kValue,
+    kKeyValue,
+  };
+
+  ~SyncIterator() override = default;
+
+  ScriptValue next(ExceptionState& exception_state);
+  bool done();
+  ScriptValue value();
+  SyncIterator* Symbol_iterator();
+
+  void Trace(GCVisitor* visitor) const override;
+
+  explicit SyncIterator(JSContext* ctx, std::shared_ptr iteration_source, Kind kind)
+      : iteration_source_(std::move(iteration_source)), kind_(kind), ScriptWrappable(ctx) {}
+
+ private:
+  const std::shared_ptr iteration_source_;
+  const Kind kind_;
+};
+
+}  // namespace webf
+
+#endif
\ No newline at end of file
diff --git a/bridge/bindings/qjs/wrapper_type_info.h b/bridge/bindings/qjs/wrapper_type_info.h
index 457dc84139..2bcbe3774c 100644
--- a/bridge/bindings/qjs/wrapper_type_info.h
+++ b/bridge/bindings/qjs/wrapper_type_info.h
@@ -17,6 +17,7 @@ class TouchList;
 // Define all built-in wrapper class id.
 enum {
   JS_CLASS_GC_TRACKER = JS_CLASS_INIT_COUNT + 1,
+  JS_CLASS_SYNC_ITERATOR,
   JS_CLASS_BLOB,
   JS_CLASS_EVENT,
   JS_CLASS_ERROR_EVENT,
diff --git a/bridge/core/html/forms/form_data.cc b/bridge/core/html/forms/form_data.cc
index 0746679941..021c41374f 100644
--- a/bridge/core/html/forms/form_data.cc
+++ b/bridge/core/html/forms/form_data.cc
@@ -6,12 +6,65 @@
 #include 
 #include "bindings/qjs/atomic_string.h"
 #include "bindings/qjs/cppgc/gc_visitor.h"
+#include "bindings/qjs/iterable.h"
 #include "core/executing_context.h"
 #include "core/fileapi/blob_part.h"
 #include "foundation/native_value_converter.h"
 
 namespace webf {
 
+namespace {
+
+class FormDataIterationSource final : public PairSyncIterationSource {
+ public:
+  FormDataIterationSource(FormData* form_data) : form_data_(form_data), current_(0) {}
+
+  ScriptValue Next(webf::ExceptionState& exception_state) override {}
+
+  ScriptValue Value() override {
+    return ScriptValue::Empty(ctx());
+  }
+
+  bool Done() override {
+    return false;
+  }
+
+  void Trace(webf::GCVisitor* visitor) override { visitor->TraceMember(form_data_); }
+
+  JSContext * ctx() override {
+    return form_data_->ctx();
+  }
+
+  //  bool FetchNextItem(ScriptState* script_state,
+  //                     std::string& name,
+  //                     V8FormDataEntryValue*& value,
+  //                     ExceptionState& exception_state) override {
+  //    if (current_ >= form_data_->size())
+  //      return false;
+  //
+  //    const FormData::Entry& entry = *form_data_->Entries()[current_++];
+  //    name = entry.name();
+  //    if (entry.IsString()) {
+  //      value = MakeGarbageCollected(entry.Value());
+  //    } else {
+  //      DCHECK(entry.isFile());
+  //      value = MakeGarbageCollected(entry.GetFile());
+  //    }
+  //    return true;
+  //  }
+  //
+  //  void Trace(Visitor* visitor) const override {
+  //    visitor->Trace(form_data_);
+  //    PairSyncIterable::IterationSource::Trace(visitor);
+  //  }
+
+ private:
+  const Member form_data_;
+  size_t current_;
+};
+
+}  // namespace
+
 FormData* FormData::Create(ExecutingContext* context, ExceptionState& exception_state) {
   return MakeGarbageCollected(context->ctx());
 }
@@ -148,6 +201,10 @@ void FormData::Trace(webf::GCVisitor* visitor) const {
   }
 }
 
+std::shared_ptr FormData::CreateIterationSource(webf::ExceptionState &exception_state) {
+  return std::make_shared(this);
+}
+
 void FormData::append(const webf::AtomicString& name, const webf::AtomicString& value) {
   entries_.emplace_back(std::make_shared(name, value));
 }
diff --git a/bridge/core/html/forms/form_data.d.ts b/bridge/core/html/forms/form_data.d.ts
index b44785014b..492d054cd5 100644
--- a/bridge/core/html/forms/form_data.d.ts
+++ b/bridge/core/html/forms/form_data.d.ts
@@ -2,7 +2,10 @@
  * Copyright (C) 2022-present The WebF authors. All rights reserved.
  */
 
-export interface FormData {
+
+import {ArrayLikeMethodsMixin} from "../../../bindings/qjs/array_methods";
+
+export interface FormData extends ArrayLikeMethodsMixin {
   new(): FormData;
   append(name: string, value: (string | Blob), fileName?: string): void;
   delete(name: string): ImplementedAs;
@@ -10,10 +13,4 @@ export interface FormData {
   getAll(name: string): (string | Blob)[];
   has(name: string): boolean;
   set(name: string, value: string | Blob, fileName?: string): void;
-
-  readonly forEach: JSArrayProtoMethod;
-  readonly keys: JSArrayProtoMethod;
-  readonly entries: JSArrayProtoMethod;
-  readonly values: JSArrayProtoMethod;
-  readonly [Symbol.iterator]: JSArrayProtoMethod;
 }
diff --git a/bridge/core/html/forms/form_data.h b/bridge/core/html/forms/form_data.h
index 8938593902..d896abc06e 100644
--- a/bridge/core/html/forms/form_data.h
+++ b/bridge/core/html/forms/form_data.h
@@ -4,20 +4,23 @@
 
 #ifndef WEBF_CORE_API_FORM_DATA_H_
 #define WEBF_CORE_API_FORM_DATA_H_
+
 #include 
 #include 
 #include "bindings/qjs/cppgc/member.h"
 #include "bindings/qjs/atomic_string.h"
 #include "bindings/qjs/script_wrappable.h"
+#include "bindings/qjs/iterable.h"
 #include "core/binding_object.h"
 #include "core/fileapi/blob.h"
 #include "qjs_union_dom_stringblob.h"
 
 namespace webf {
 
-class FormData : public BindingObject {
-  DEFINE_WRAPPERTYPEINFO();
+class FormData;
 
+class FormData : public BindingObject, public PairSyncIterable {
+  DEFINE_WRAPPERTYPEINFO();
  public:
   class Entry;
   using ImplType = FormData*;
@@ -55,6 +58,8 @@ class FormData : public BindingObject {
   void Trace(webf::GCVisitor *visitor) const override;
 
  private:
+  std::shared_ptr CreateIterationSource(webf::ExceptionState &exception_state) override;
+
   void append(const AtomicString&name, const AtomicString& value);
   void append(const AtomicString&name, Blob* blob, const AtomicString& file_name);
 
diff --git a/bridge/core/html/forms/form_data_test.cc b/bridge/core/html/forms/form_data_test.cc
index 4dabb74cdf..4d90828c7d 100644
--- a/bridge/core/html/forms/form_data_test.cc
+++ b/bridge/core/html/forms/form_data_test.cc
@@ -20,9 +20,9 @@ TEST(FormData, append) {
   auto context = env->page()->executingContext();
   std::string code = R"(
     let formData = new FormData();
-
-    formData.append('name', 1234);
-    formData.append('age', 4567);
+    formData.append('1234', '4567');
+    formData.append('1234', '4567');
+    console.log(formData.keys());
   )";
   env->page()->evaluateScript(code.c_str(), code.size(), "vm://", 0);
 
diff --git a/bridge/scripts/code_generator/bin/code_generator.js b/bridge/scripts/code_generator/bin/code_generator.js
index ab34c528d6..7530aa29ee 100644
--- a/bridge/scripts/code_generator/bin/code_generator.js
+++ b/bridge/scripts/code_generator/bin/code_generator.js
@@ -47,11 +47,12 @@ function genCodeFromTypeDefine() {
   // Generate code from type defines.
   let typeFiles = glob.sync("**/*.d.ts", {
     cwd: source,
+    ignore: ['node_modules/**', 'scripts/**', 'polyfill/**']
   });
 
   let blobs = typeFiles.map(file => {
     let filename = 'qjs_' + file.split('/').slice(-1)[0].replace('.d.ts', '');
-    let implement = file.replace(path.join(__dirname, '../../')).replace('.d.ts', '');
+    let implement = file.replace(path.join(__dirname, '../../../')).replace('.d.ts', '');
     return new IDLBlob(path.join(source, file), dist, filename, implement);
   });
 
diff --git a/bridge/scripts/code_generator/global.d.ts b/bridge/scripts/code_generator/global.d.ts
index c686f2978c..81362de2fe 100644
--- a/bridge/scripts/code_generator/global.d.ts
+++ b/bridge/scripts/code_generator/global.d.ts
@@ -5,7 +5,6 @@ declare type JSArrayProtoMethod = void;
 declare interface Dictionary {}
 
 declare interface BlobPart {}
-declare interface FormDataPart {}
 declare interface BlobPropertyBag {}
 declare function Dictionary() : any;
 declare type JSEventListener = void;
diff --git a/bridge/scripts/code_generator/src/idl/analyzer.ts b/bridge/scripts/code_generator/src/idl/analyzer.ts
index 2796768e58..30b6a32c2a 100644
--- a/bridge/scripts/code_generator/src/idl/analyzer.ts
+++ b/bridge/scripts/code_generator/src/idl/analyzer.ts
@@ -38,15 +38,20 @@ function getInterfaceName(statement: ts.Statement) {
 function getHeritageType(heritage: HeritageClause) {
   let expression = heritage.types[0].expression;
   if (expression.kind === ts.SyntaxKind.Identifier) {
-    return (expression as ts.Identifier).escapedText;
+    let heritageText = (expression as ts.Identifier).escapedText as string;
+    if (heritageText.toLowerCase().indexOf('mixin') >= 0) {
+      return null;
+    }
+    return heritageText;
   }
   return null;
 }
 
-function getMixins(hertage: HeritageClause): string[] | null {
-  if (hertage.types.length <= 1) return null;
+function getMixins(hasParent: boolean, hertage: HeritageClause): string[] | null {
+  const sliceIndex = (hasParent ? 1 : 0);
+  if (hertage.types.length <= sliceIndex) return null;
   let mixins: string[] = [];
-  hertage.types.slice(1).forEach(types => {
+  hertage.types.slice(sliceIndex).forEach(types => {
     let expression = types.expression;
     if (expression.kind === ts.SyntaxKind.Identifier) {
       mixins.push((expression as ts.Identifier).escapedText! as string);
@@ -224,7 +229,7 @@ function walkProgram(blob: IDLBlob, statement: ts.Statement, definedPropertyColl
       if (s.heritageClauses) {
         let heritage = s.heritageClauses[0];
         let heritageType = getHeritageType(heritage);
-        let mixins = getMixins(heritage);
+        let mixins = getMixins(heritageType != null, heritage);
         if (heritageType) obj.parent = heritageType.toString();
         if (mixins) obj.mixinParent = mixins;
       }
diff --git a/bridge/scripts/code_generator/templates/idl_templates/global_function.h.tpl b/bridge/scripts/code_generator/templates/idl_templates/global_function.h.tpl
index 4b38cfe531..046dfb3d94 100644
--- a/bridge/scripts/code_generator/templates/idl_templates/global_function.h.tpl
+++ b/bridge/scripts/code_generator/templates/idl_templates/global_function.h.tpl
@@ -1,4 +1,4 @@
-#include "core/<%= blob.implement %>.h"
+#include "<%= blob.implement %>.h"
 
 namespace webf {
 
diff --git a/bridge/scripts/code_generator/templates/idl_templates/interface.h.tpl b/bridge/scripts/code_generator/templates/idl_templates/interface.h.tpl
index 2703696066..b6934a2023 100644
--- a/bridge/scripts/code_generator/templates/idl_templates/interface.h.tpl
+++ b/bridge/scripts/code_generator/templates/idl_templates/interface.h.tpl
@@ -1,4 +1,4 @@
-#include "core/<%= blob.implement %>.h"
+#include "<%= blob.implement %>.h"
 
 <% if(parentClassName) { %>
 #include "qjs_<%= _.snakeCase(parentClassName) %>.h"
diff --git a/scripts/tasks.js b/scripts/tasks.js
index 7b9d6a66b6..e9b15d0938 100644
--- a/scripts/tasks.js
+++ b/scripts/tasks.js
@@ -558,7 +558,7 @@ task('generate-bindings-code', (done) => {
     return done(buildResult.status);
   }
 
-  let compileResult = spawnSync('node', ['bin/code_generator', '-s', '../../core', '-d', '../../out'], {
+  let compileResult = spawnSync('node', ['bin/code_generator', '-s', '../../', '-d', '../../out'], {
     cwd: paths.codeGen,
     env: {
       ...process.env,

From abbda0d300de1cd20c3cb8c5c563227d314e91ed Mon Sep 17 00:00:00 2001
From: andycall 
Date: Sat, 17 Aug 2024 14:15:41 +0800
Subject: [PATCH 15/18] feat: support iterable of FormData.

---
 bridge/CMakeLists.txt                    |  1 +
 bridge/bindings/qjs/iterable.cc          | 22 +++++++
 bridge/bindings/qjs/iterable.h           | 15 ++---
 bridge/bindings/qjs/sync_iterator.cc     |  9 ++-
 bridge/bindings/qjs/sync_iterator.d.ts   |  4 +-
 bridge/bindings/qjs/sync_iterator.h      |  2 +-
 bridge/core/html/forms/form_data.cc      | 77 ++++++++++++++----------
 bridge/core/html/forms/form_data.h       | 18 ++++--
 bridge/core/html/forms/form_data_test.cc | 18 +++++-
 9 files changed, 112 insertions(+), 54 deletions(-)

diff --git a/bridge/CMakeLists.txt b/bridge/CMakeLists.txt
index c308d349bc..dfe414b2f1 100644
--- a/bridge/CMakeLists.txt
+++ b/bridge/CMakeLists.txt
@@ -263,6 +263,7 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs")
     bindings/qjs/exception_message.cc
     bindings/qjs/rejected_promises.cc
     bindings/qjs/sync_iterator.cc
+    bindings/qjs/iterable.cc
     bindings/qjs/union_base.cc
     # Core sources
     webf_bridge.cc
diff --git a/bridge/bindings/qjs/iterable.cc b/bridge/bindings/qjs/iterable.cc
index fe2af0fbc1..52cdb3944b 100644
--- a/bridge/bindings/qjs/iterable.cc
+++ b/bridge/bindings/qjs/iterable.cc
@@ -4,3 +4,25 @@
 */
 
 #include "iterable.h"
+
+namespace webf {
+
+
+ScriptValue ESCreateIterResultObject(JSContext* ctx, bool done, const ScriptValue& value) {
+  JSValue object = JS_NewObject(ctx);
+  JS_SetPropertyStr(ctx, object, "done", JS_NewBool(ctx, done));
+  JS_SetPropertyStr(ctx, object, "value", JS_DupValue(ctx, value.QJSValue()));
+  return ScriptValue(ctx, object);
+}
+
+ScriptValue ESCreateIterResultObject(JSContext* ctx, bool done, const ScriptValue& value1, const ScriptValue& value2) {
+  JSValue object = JS_NewObject(ctx);
+  JS_SetPropertyStr(ctx, object, "done", JS_NewBool(ctx, done));
+  JSValue array = JS_NewArray(ctx);
+  JS_SetPropertyInt64(ctx, array, 0, JS_DupValue(ctx, value1.QJSValue()));
+  JS_SetPropertyInt64(ctx, array, 1, JS_DupValue(ctx, value2.QJSValue()));
+  JS_SetPropertyStr(ctx, object, "value", array);
+  return ScriptValue(ctx, object);
+}
+
+}
\ No newline at end of file
diff --git a/bridge/bindings/qjs/iterable.h b/bridge/bindings/qjs/iterable.h
index 22cc2b6d16..80c2ac6e9a 100644
--- a/bridge/bindings/qjs/iterable.h
+++ b/bridge/bindings/qjs/iterable.h
@@ -9,9 +9,12 @@
 
 namespace webf {
 
+ScriptValue ESCreateIterResultObject(JSContext* ctx, bool done, const ScriptValue& value);
+ScriptValue ESCreateIterResultObject(JSContext* ctx, bool done, const ScriptValue& value1, const ScriptValue& value2);
+
 class PairSyncIterationSource {
  public:
-  virtual ScriptValue Next(ExceptionState& exception_state) = 0;
+  virtual ScriptValue Next(SyncIterator::Kind, ExceptionState& exception_state) = 0;
 
   virtual ScriptValue Value() = 0;
   virtual bool Done() = 0;
@@ -24,7 +27,6 @@ class PairSyncIterationSource {
 
 class PairSyncIterable {
  public:
-
   SyncIterator* keys(ExceptionState& exception_state) {
     std::shared_ptr source = CreateIterationSource(exception_state);
     if (!source)
@@ -46,11 +48,10 @@ class PairSyncIterable {
     return MakeGarbageCollected(source->ctx(), source, SyncIterator::Kind::kKeyValue);
   }
 
-  void forEach(const std::shared_ptr& callback, ExceptionState& exception_state) {}
-
-  void forEach(const std::shared_ptr& callback,
-               const ScriptValue& this_arg,
-               ExceptionState& exception_state) {}
+  virtual void forEach(const std::shared_ptr& callback, ExceptionState& exception_state) = 0;
+  virtual void forEach(const std::shared_ptr& callback,
+                       const ScriptValue& this_arg,
+                       ExceptionState& exception_state) = 0;
 
  private:
   virtual std::shared_ptr CreateIterationSource(ExceptionState& exception_state) = 0;
diff --git a/bridge/bindings/qjs/sync_iterator.cc b/bridge/bindings/qjs/sync_iterator.cc
index 64aa5d95a4..3678e35ac8 100644
--- a/bridge/bindings/qjs/sync_iterator.cc
+++ b/bridge/bindings/qjs/sync_iterator.cc
@@ -11,8 +11,11 @@ ScriptValue SyncIterator::value() {
   return iteration_source_->Value();
 }
 
-SyncIterator* SyncIterator::Symbol_iterator() {
-  return this;
+ScriptValue SyncIterator::Symbol_iterator() {
+  auto iterator_return_func = [](JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
+    return JS_DupValue(ctx, this_val);
+  };
+  return ScriptValue(ctx(), JS_NewCFunction(ctx(), iterator_return_func, "iterator", 0));
 }
 
 bool SyncIterator::done() {
@@ -25,7 +28,7 @@ void SyncIterator::Trace(GCVisitor* visitor) const {
 }
 
 ScriptValue SyncIterator::next(ExceptionState& exception_state) {
-  return iteration_source_->Next(exception_state);
+  return iteration_source_->Next(kind_, exception_state);
 }
 
 }
\ No newline at end of file
diff --git a/bridge/bindings/qjs/sync_iterator.d.ts b/bridge/bindings/qjs/sync_iterator.d.ts
index ee61a162b4..cb5d8f3e44 100644
--- a/bridge/bindings/qjs/sync_iterator.d.ts
+++ b/bridge/bindings/qjs/sync_iterator.d.ts
@@ -1,7 +1,5 @@
 interface SyncIterator {
   new(): void;
   next(): any;
-  readonly done: boolean;
-  readonly value: any;
-  readonly [Symbol.iterator]: SyncIterator;
+  readonly [Symbol.iterator]: () => SyncIterator;
 }
\ No newline at end of file
diff --git a/bridge/bindings/qjs/sync_iterator.h b/bridge/bindings/qjs/sync_iterator.h
index a05ee9fade..479be21a5d 100644
--- a/bridge/bindings/qjs/sync_iterator.h
+++ b/bridge/bindings/qjs/sync_iterator.h
@@ -35,7 +35,7 @@ class SyncIterator : public ScriptWrappable {
   ScriptValue next(ExceptionState& exception_state);
   bool done();
   ScriptValue value();
-  SyncIterator* Symbol_iterator();
+  ScriptValue Symbol_iterator();
 
   void Trace(GCVisitor* visitor) const override;
 
diff --git a/bridge/core/html/forms/form_data.cc b/bridge/core/html/forms/form_data.cc
index 021c41374f..d8fd092ccc 100644
--- a/bridge/core/html/forms/form_data.cc
+++ b/bridge/core/html/forms/form_data.cc
@@ -19,44 +19,36 @@ class FormDataIterationSource final : public PairSyncIterationSource {
  public:
   FormDataIterationSource(FormData* form_data) : form_data_(form_data), current_(0) {}
 
-  ScriptValue Next(webf::ExceptionState& exception_state) override {}
+  ScriptValue Next(SyncIterator::Kind kind, webf::ExceptionState& exception_state) override {
+    if (current_ + 1 > form_data_->Entries().size()) {
+      if (exception_state.HasException())
+        return {};
+      return ESCreateIterResultObject(ctx(), true, ScriptValue::Undefined(ctx()));
+    }
 
-  ScriptValue Value() override {
-    return ScriptValue::Empty(ctx());
-  }
+    std::shared_ptr current_item = form_data_->Entries()[current_];
+    ScriptValue current_value =
+        current_item->IsString() ? ScriptValue(ctx(), current_item->Value()) : current_item->GetBlob()->ToValue();
 
-  bool Done() override {
-    return false;
+    current_++;
+
+    switch (kind) {
+      case SyncIterator::Kind::kKey:
+        return ESCreateIterResultObject(ctx(), false, ScriptValue(ctx(), current_item->name()));
+      case SyncIterator::Kind::kValue:
+        return ESCreateIterResultObject(ctx(), false, current_value);
+      case SyncIterator::Kind::kKeyValue:
+        return ESCreateIterResultObject(ctx(), false, ScriptValue(ctx(), current_item->name()), current_value);
+    }
   }
 
-  void Trace(webf::GCVisitor* visitor) override { visitor->TraceMember(form_data_); }
+  ScriptValue Value() override { return ScriptValue::Empty(ctx()); }
 
-  JSContext * ctx() override {
-    return form_data_->ctx();
-  }
+  bool Done() override { return false; }
+
+  void Trace(webf::GCVisitor* visitor) override { visitor->TraceMember(form_data_); }
 
-  //  bool FetchNextItem(ScriptState* script_state,
-  //                     std::string& name,
-  //                     V8FormDataEntryValue*& value,
-  //                     ExceptionState& exception_state) override {
-  //    if (current_ >= form_data_->size())
-  //      return false;
-  //
-  //    const FormData::Entry& entry = *form_data_->Entries()[current_++];
-  //    name = entry.name();
-  //    if (entry.IsString()) {
-  //      value = MakeGarbageCollected(entry.Value());
-  //    } else {
-  //      DCHECK(entry.isFile());
-  //      value = MakeGarbageCollected(entry.GetFile());
-  //    }
-  //    return true;
-  //  }
-  //
-  //  void Trace(Visitor* visitor) const override {
-  //    visitor->Trace(form_data_);
-  //    PairSyncIterable::IterationSource::Trace(visitor);
-  //  }
+  JSContext* ctx() override { return form_data_->ctx(); }
 
  private:
   const Member form_data_;
@@ -201,7 +193,26 @@ void FormData::Trace(webf::GCVisitor* visitor) const {
   }
 }
 
-std::shared_ptr FormData::CreateIterationSource(webf::ExceptionState &exception_state) {
+void FormData::forEach(const std::shared_ptr& callback, webf::ExceptionState& exception_state) {
+  forEach(callback, ScriptValue::Empty(ctx()), exception_state);
+}
+
+void FormData::forEach(const std::shared_ptr& callback,
+                       const webf::ScriptValue& this_arg,
+                       webf::ExceptionState& exception_state) {
+  for (auto& entry : entries_) {
+    ScriptValue invoke_this = this_arg.IsEmpty() ? ScriptValue(ctx(), ToQuickJS()) : this_arg;
+    if (entry->IsString()) {
+      ScriptValue arguments[] = {ScriptValue(ctx(), entry->Value()), ScriptValue(ctx(), entry->name())};
+      callback->Invoke(ctx(), invoke_this, 2, arguments);
+    } else {
+      ScriptValue arguments[] = {entry->GetBlob()->ToValue(), ScriptValue(ctx(), entry->name())};
+      callback->Invoke(ctx(), invoke_this, 2, arguments);
+    }
+  }
+}
+
+std::shared_ptr FormData::CreateIterationSource(webf::ExceptionState& exception_state) {
   return std::make_shared(this);
 }
 
diff --git a/bridge/core/html/forms/form_data.h b/bridge/core/html/forms/form_data.h
index d896abc06e..38b9ac6ec2 100644
--- a/bridge/core/html/forms/form_data.h
+++ b/bridge/core/html/forms/form_data.h
@@ -7,10 +7,10 @@
 
 #include 
 #include 
-#include "bindings/qjs/cppgc/member.h"
 #include "bindings/qjs/atomic_string.h"
-#include "bindings/qjs/script_wrappable.h"
+#include "bindings/qjs/cppgc/member.h"
 #include "bindings/qjs/iterable.h"
+#include "bindings/qjs/script_wrappable.h"
 #include "core/binding_object.h"
 #include "core/fileapi/blob.h"
 #include "qjs_union_dom_stringblob.h"
@@ -21,6 +21,7 @@ class FormData;
 
 class FormData : public BindingObject, public PairSyncIterable {
   DEFINE_WRAPPERTYPEINFO();
+
  public:
   class Entry;
   using ImplType = FormData*;
@@ -55,13 +56,18 @@ class FormData : public BindingObject, public PairSyncIterable {
   void SetEntry(std::shared_ptr entry);
   const std::vector>& Entries() const { return entries_; }
 
-  void Trace(webf::GCVisitor *visitor) const override;
+  void Trace(webf::GCVisitor* visitor) const override;
+
+  void forEach(const std::shared_ptr& callback,
+               const webf::ScriptValue& this_arg,
+               webf::ExceptionState& exception_state) override;
+  void forEach(const std::shared_ptr& callback, webf::ExceptionState& exception_state) override;
 
  private:
-  std::shared_ptr CreateIterationSource(webf::ExceptionState &exception_state) override;
+  std::shared_ptr CreateIterationSource(webf::ExceptionState& exception_state) override;
 
-  void append(const AtomicString&name, const AtomicString& value);
-  void append(const AtomicString&name, Blob* blob, const AtomicString& file_name);
+  void append(const AtomicString& name, const AtomicString& value);
+  void append(const AtomicString& name, Blob* blob, const AtomicString& file_name);
 
   std::vector> entries_;
 };
diff --git a/bridge/core/html/forms/form_data_test.cc b/bridge/core/html/forms/form_data_test.cc
index 4d90828c7d..decab71c1d 100644
--- a/bridge/core/html/forms/form_data_test.cc
+++ b/bridge/core/html/forms/form_data_test.cc
@@ -22,7 +22,23 @@ TEST(FormData, append) {
     let formData = new FormData();
     formData.append('1234', '4567');
     formData.append('1234', '4567');
-    console.log(formData.keys());
+    const keys = formData.keys();
+    const values = formData.values();
+    const entries = formData.entries();
+
+    for (let key of keys) {
+      console.log(key);
+    }
+
+    for (let value of values) {
+      console.log(value);
+    }
+
+    for (let entry of entries) {
+      console.log(entry);
+    }
+
+    console.log(keys[Symbol.iterator]() == keys)
   )";
   env->page()->evaluateScript(code.c_str(), code.size(), "vm://", 0);
 

From 4eacd2fa88196ca6362c27e85b0f2e60a31ae18e Mon Sep 17 00:00:00 2001
From: andycall 
Date: Sat, 17 Aug 2024 23:58:42 +0800
Subject: [PATCH 16/18] feat: support formData as body in fetch API.

---
 bridge/CMakeLists.txt                         |  3 +
 bridge/bindings/qjs/generated_code_helper.h   |  1 +
 bridge/core/dart_methods.cc                   |  8 ++-
 bridge/core/dart_methods.h                    |  2 +
 .../core/frame/module_context_coordinator.h   |  1 -
 bridge/core/frame/module_manager.cc           | 26 +++++++
 bridge/core/frame/module_manager.d.ts         |  3 +
 bridge/core/frame/module_manager.h            |  8 +++
 bridge/core/frame/module_manager_options.d.ts |  8 +++
 bridge/core/html/forms/form_data.h            |  5 ++
 .../platform/network/encoded_form_data.cc     |  5 ++
 .../core/platform/network/encoded_form_data.h | 12 ++++
 .../platform/network/form_data_encoder.cc     | 22 ++++++
 .../core/platform/network/form_data_encoder.h | 71 +++++++++++++++++++
 bridge/polyfill/src/bridge.ts                 |  3 +
 bridge/polyfill/src/fetch.ts                  | 54 +++++++++++---
 bridge/polyfill/src/webf.ts                   |  3 +-
 webf/lib/src/bridge/from_native.dart          |  6 ++
 18 files changed, 228 insertions(+), 13 deletions(-)
 create mode 100644 bridge/core/frame/module_manager_options.d.ts
 create mode 100644 bridge/core/platform/network/encoded_form_data.cc
 create mode 100644 bridge/core/platform/network/encoded_form_data.h
 create mode 100644 bridge/core/platform/network/form_data_encoder.cc
 create mode 100644 bridge/core/platform/network/form_data_encoder.h

diff --git a/bridge/CMakeLists.txt b/bridge/CMakeLists.txt
index dfe414b2f1..8b9f63b030 100644
--- a/bridge/CMakeLists.txt
+++ b/bridge/CMakeLists.txt
@@ -382,6 +382,8 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs")
     core/html/forms/html_textarea_element.cc
     core/html/forms/form_data.cc
 
+    core/platform/network/form_data_encoder.cc
+
     # SVG files
     core/svg/svg_element.cc
     core/svg/svg_graphics_element.cc
@@ -500,6 +502,7 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs")
     out/qjs_html_iframe_element.cc
     out/qjs_html_canvas_element.cc
     out/qjs_html_link_element.cc
+    out/qjs_module_manager_options.cc
     out/qjs_image.cc
     out/qjs_widget_element.cc
     out/qjs_canvas_rendering_context_2d.cc
diff --git a/bridge/bindings/qjs/generated_code_helper.h b/bridge/bindings/qjs/generated_code_helper.h
index 50a4aa554e..4b3cddcdfe 100644
--- a/bridge/bindings/qjs/generated_code_helper.h
+++ b/bridge/bindings/qjs/generated_code_helper.h
@@ -9,6 +9,7 @@
 #include "atomic_string.h"
 #include "bindings/qjs/dictionary_base.h"
 #include "bindings/qjs/qjs_interface_bridge.h"
+#include "core/html/forms/form_data.h"
 #include "script_value.h"
 
 #endif
diff --git a/bridge/core/dart_methods.cc b/bridge/core/dart_methods.cc
index 1a8c8e0245..842adce42a 100644
--- a/bridge/core/dart_methods.cc
+++ b/bridge/core/dart_methods.cc
@@ -45,6 +45,7 @@ NativeValue* DartMethodPointer::invokeModule(bool is_dedicated,
                                              SharedNativeString* moduleName,
                                              SharedNativeString* method,
                                              NativeValue* params,
+                                             uint32_t argc,
                                              AsyncModuleCallback callback) {
 #if ENABLE_LOG
   WEBF_LOG(INFO) << "[Dispatcher] DartMethodPointer::invokeModule callSync START";
@@ -52,13 +53,14 @@ NativeValue* DartMethodPointer::invokeModule(bool is_dedicated,
   NativeValue* result = dart_isolate_context_->dispatcher()->PostToDartSync(
       is_dedicated, context_id,
       [&](bool cancel, void* callback_context, double context_id, int64_t profile_link_id,
-          SharedNativeString* moduleName, SharedNativeString* method, NativeValue* params,
+          SharedNativeString* moduleName, SharedNativeString* method, NativeValue* params, uint32_t argc,
           AsyncModuleCallback callback) -> webf::NativeValue* {
         if (cancel)
           return nullptr;
-        return invoke_module_(callback_context, context_id, profile_link_id, moduleName, method, params, callback);
+        return invoke_module_(callback_context, context_id, profile_link_id, moduleName, method, params, argc,
+                              callback);
       },
-      callback_context, context_id, profile_link_id, moduleName, method, params, callback);
+      callback_context, context_id, profile_link_id, moduleName, method, params, argc, callback);
 
 #if ENABLE_LOG
   WEBF_LOG(INFO) << "[Dispatcher] DartMethodPointer::invokeModule callSync END";
diff --git a/bridge/core/dart_methods.h b/bridge/core/dart_methods.h
index bd11aeb4a3..5e4c4a5426 100644
--- a/bridge/core/dart_methods.h
+++ b/bridge/core/dart_methods.h
@@ -43,6 +43,7 @@ typedef NativeValue* (*InvokeModule)(void* callback_context,
                                      SharedNativeString* moduleName,
                                      SharedNativeString* method,
                                      NativeValue* params,
+                                     uint32_t argc,
                                      AsyncModuleCallback callback);
 typedef void (*RequestBatchUpdate)(double context_id);
 typedef void (*ReloadApp)(double context_id);
@@ -146,6 +147,7 @@ class DartMethodPointer {
                             SharedNativeString* moduleName,
                             SharedNativeString* method,
                             NativeValue* params,
+                            uint32_t argc,
                             AsyncModuleCallback callback);
 
   void requestBatchUpdate(bool is_dedicated, double context_id);
diff --git a/bridge/core/frame/module_context_coordinator.h b/bridge/core/frame/module_context_coordinator.h
index 46533cad55..ceb36881aa 100644
--- a/bridge/core/frame/module_context_coordinator.h
+++ b/bridge/core/frame/module_context_coordinator.h
@@ -9,7 +9,6 @@
 // Quickjs's linked-list are more efficient than STL forward_list.
 #include 
 #include "module_callback.h"
-#include "module_manager.h"
 
 namespace webf {
 
diff --git a/bridge/core/frame/module_manager.cc b/bridge/core/frame/module_manager.cc
index 24036f0221..14f559efe5 100644
--- a/bridge/core/frame/module_manager.cc
+++ b/bridge/core/frame/module_manager.cc
@@ -8,6 +8,8 @@
 #include "foundation/native_value.h"
 #include "include/dart_api.h"
 #include "module_callback.h"
+#include "core/html/forms/form_data.h"
+#include "qjs_module_manager_options.h"
 
 namespace webf {
 
@@ -136,7 +138,31 @@ ScriptValue ModuleManager::__webf_invoke_module__(ExecutingContext* context,
                                                   ScriptValue& params_value,
                                                   const std::shared_ptr& callback,
                                                   ExceptionState& exception) {
+  return __webf_invoke_module_with_options__(context, module_name, method, params_value, nullptr, callback,
+                                             exception);
+}
+
+namespace {
+
+struct NativeInvokeModuleOptions {
+  NativeForm* form_data;
+};
+
+};
+
+ScriptValue ModuleManager::__webf_invoke_module_with_options__(ExecutingContext* context,
+                                                               const AtomicString& module_name,
+                                                               const AtomicString& method,
+                                                               ScriptValue& params_value,
+                                                               const std::shared_ptr& options,
+                                                               const std::shared_ptr& callback,
+                                                               ExceptionState& exception) {
   NativeValue params = params_value.ToNative(context->ctx(), exception);
+  if (options != nullptr && options->hasFormData()) {
+    FormData* form_data = options->formData();
+  } else {
+
+  }
 
   if (exception.HasException()) {
     return ScriptValue::Empty(context->ctx());
diff --git a/bridge/core/frame/module_manager.d.ts b/bridge/core/frame/module_manager.d.ts
index c9e568a3c7..1ed23d46dd 100644
--- a/bridge/core/frame/module_manager.d.ts
+++ b/bridge/core/frame/module_manager.d.ts
@@ -1,4 +1,7 @@
+import {ModuleManagerOptions} from "./module_manager_options";
+
 declare const __webf_invoke_module__: (moduleName: string, methodName: string, paramsValue?: any, callback?: Function) => any;
+declare const __webf_invoke_module_with_options__: (moduleName: string, methodName: string, paramsValue: any, options: ModuleManagerOptions, callback: Function) => any;
 declare const __webf_add_module_listener__: (moduleName: string, callback: Function) => void;
 declare const __webf_remove_module_listener__: (moduleName: string) => void;
 declare const __webf_clear_module_listener__: () => void;
diff --git a/bridge/core/frame/module_manager.h b/bridge/core/frame/module_manager.h
index 229fd2f348..b4659afaa5 100644
--- a/bridge/core/frame/module_manager.h
+++ b/bridge/core/frame/module_manager.h
@@ -9,6 +9,7 @@
 #include "bindings/qjs/exception_state.h"
 #include "bindings/qjs/qjs_function.h"
 #include "module_callback.h"
+#include "qjs_module_manager_options.h"
 
 namespace webf {
 
@@ -36,6 +37,13 @@ class ModuleManager {
                                             ScriptValue& params_value,
                                             const std::shared_ptr& callback,
                                             ExceptionState& exception);
+  static ScriptValue __webf_invoke_module_with_options__(ExecutingContext* context,
+                                                         const AtomicString& module_name,
+                                                         const AtomicString& method,
+                                                         ScriptValue& params_value,
+                                                         const std::shared_ptr& options,
+                                                         const std::shared_ptr& callback,
+                                                         ExceptionState& exception);
   static void __webf_add_module_listener__(ExecutingContext* context,
                                            const AtomicString& module_name,
                                            const std::shared_ptr& handler,
diff --git a/bridge/core/frame/module_manager_options.d.ts b/bridge/core/frame/module_manager_options.d.ts
new file mode 100644
index 0000000000..b962656cdf
--- /dev/null
+++ b/bridge/core/frame/module_manager_options.d.ts
@@ -0,0 +1,8 @@
+// @ts-ignore
+import {FormData} from "../html/forms/form_data";
+
+// @ts-ignore
+@Dictionary()
+export interface ModuleManagerOptions {
+  formData?: FormData;
+}
\ No newline at end of file
diff --git a/bridge/core/html/forms/form_data.h b/bridge/core/html/forms/form_data.h
index 38b9ac6ec2..08ed4f8d21 100644
--- a/bridge/core/html/forms/form_data.h
+++ b/bridge/core/html/forms/form_data.h
@@ -19,6 +19,11 @@ namespace webf {
 
 class FormData;
 
+// Exchange data struct designed for reading the bytes data from Dart Side.
+struct NativeFormData : public DartReadable {
+
+};
+
 class FormData : public BindingObject, public PairSyncIterable {
   DEFINE_WRAPPERTYPEINFO();
 
diff --git a/bridge/core/platform/network/encoded_form_data.cc b/bridge/core/platform/network/encoded_form_data.cc
new file mode 100644
index 0000000000..f8991b1901
--- /dev/null
+++ b/bridge/core/platform/network/encoded_form_data.cc
@@ -0,0 +1,5 @@
+/*
+* Copyright (C) 2022-present The WebF authors. All rights reserved.
+*/
+
+#include "encoded_form_data.h"
diff --git a/bridge/core/platform/network/encoded_form_data.h b/bridge/core/platform/network/encoded_form_data.h
new file mode 100644
index 0000000000..d9c0dbd932
--- /dev/null
+++ b/bridge/core/platform/network/encoded_form_data.h
@@ -0,0 +1,12 @@
+/*
+* Copyright (C) 2022-present The WebF authors. All rights reserved.
+*/
+
+#ifndef WEBF_CORE_PLATFORM_NETWORK_ENCODED_FORM_DATA_H_
+#define WEBF_CORE_PLATFORM_NETWORK_ENCODED_FORM_DATA_H_
+
+namespace webf {
+
+}
+
+#endif  // WEBF_CORE_PLATFORM_NETWORK_ENCODED_FORM_DATA_H_
diff --git a/bridge/core/platform/network/form_data_encoder.cc b/bridge/core/platform/network/form_data_encoder.cc
new file mode 100644
index 0000000000..31b4fbda96
--- /dev/null
+++ b/bridge/core/platform/network/form_data_encoder.cc
@@ -0,0 +1,22 @@
+/*
+* Copyright (C) 2008 Torch Mobile Inc. All rights reserved.
+* (http://www.torchmobile.com/)
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Library 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
+* Library General Public License for more details.
+*
+* You should have received a copy of the GNU Library General Public License
+* along with this library; see the file COPYING.LIB.  If not, write to
+* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+* Boston, MA 02110-1301, USA.
+*
+ */
+
+#include "form_data_encoder.h"
diff --git a/bridge/core/platform/network/form_data_encoder.h b/bridge/core/platform/network/form_data_encoder.h
new file mode 100644
index 0000000000..8ab4afcb00
--- /dev/null
+++ b/bridge/core/platform/network/form_data_encoder.h
@@ -0,0 +1,71 @@
+/*
+* Copyright (C) 2008 Torch Mobile Inc. All rights reserved.
+* (http://www.torchmobile.com/)
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Library 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
+* Library General Public License for more details.
+*
+* You should have received a copy of the GNU Library General Public License
+* along with this library; see the file COPYING.LIB.  If not, write to
+* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+* Boston, MA 02110-1301, USA.
+*
+*/
+
+#ifndef WEBF_PLATFORM_NETWORK_FORM_DATA_ENCODER_H_
+#define WEBF_PLATFORM_NETWORK_FORM_DATA_ENCODER_H_
+
+#include "foundation/macros.h"
+
+namespace webf {
+
+class FormDataEncoder {
+ WEBF_STATIC_ONLY(FormDataEncoder);
+
+public:
+ // Specifies how to handle CRs and LFs. When NormalizeCRLF is passed, the
+ // method replaces the following characters with a CRLF pair:
+ // - a CR not followed by an LF
+ // - an LF not preceded by a CR
+ enum Mode { kNormalizeCRLF, kDoNotNormalizeCRLF };
+
+ static WTF::TextEncoding EncodingFromAcceptCharset(
+     const String& accept_charset,
+     const WTF::TextEncoding& fallback_encoding);
+
+ // Helper functions used by HTMLFormElement for multi-part form data
+ static Vector GenerateUniqueBoundaryString();
+ static void BeginMultiPartHeader(Vector&,
+                                  const std::string& boundary,
+                                  const std::string& name);
+ static void AddBoundaryToMultiPartHeader(Vector&,
+                                          const std::string& boundary,
+                                          bool is_last_boundary = false);
+ static void AddFilenameToMultiPartHeader(Vector&,
+                                          const WTF::TextEncoding&,
+                                          const String& filename);
+ static void AddContentTypeToMultiPartHeader(Vector&,
+                                             const String& mime_type);
+ static void FinishMultiPartHeader(Vector&);
+
+ // Helper functions used by HTMLFormElement for non multi-part form data. Mode
+ // argument is not used for TextPlain type.
+ static void AddKeyValuePairAsFormData(
+     Vector&,
+     const std::string& key,
+     const std::string& value,
+     EncodedFormData::EncodingType = EncodedFormData::kFormURLEncoded,
+     Mode = kNormalizeCRLF);
+ static void EncodeStringAsFormData(Vector&, const std::string&, Mode);
+};
+
+}  // namespace blink
+
+#endif  // WEBF_PLATFORM_NETWORK_FORM_DATA_ENCODER_H_
\ No newline at end of file
diff --git a/bridge/polyfill/src/bridge.ts b/bridge/polyfill/src/bridge.ts
index d9c3c4bd75..defc2df420 100644
--- a/bridge/polyfill/src/bridge.ts
+++ b/bridge/polyfill/src/bridge.ts
@@ -6,6 +6,9 @@
 declare const __webf_invoke_module__: (module: string, method: string, params?: any | null, fn?: (err: Error, data: any) => any) => any;
 export const webfInvokeModule = __webf_invoke_module__;
 
+declare const __webf_invoke_module_with_options__: (module: string, method: string, params?: any | null, options?: any, fn?: (err: Error, data: any) => any) => any
+export const webfInvokeModuleWithOptions = __webf_invoke_module_with_options__;
+
 declare const __webf_add_module_listener__: (moduleName: string, fn: (event: Event, extra: any) => any) => void;
 export const addWebfModuleListener = __webf_add_module_listener__;
 
diff --git a/bridge/polyfill/src/fetch.ts b/bridge/polyfill/src/fetch.ts
index abab920f4c..42e7abace6 100644
--- a/bridge/polyfill/src/fetch.ts
+++ b/bridge/polyfill/src/fetch.ts
@@ -82,10 +82,18 @@ export class Headers implements Headers {
   }
 }
 
+function isArrayBuffer(obj: any) {
+  return Object.prototype.toString.call(obj) == '[object ArrayBuffer]'
+}
+
+function isFormData(obj: any) {
+  return FormData.prototype.isPrototypeOf(obj);
+}
+
 class Body {
   // TODO support readableStream
   _bodyInit: any;
-  body: string | null | Blob;
+  body: string | null | Blob | FormData;
   bodyUsed: boolean;
   headers: Headers;
 
@@ -100,8 +108,10 @@ class Body {
       this.body = '';
     } else if (typeof body === 'string') {
       this.body = body;
-    } else if (Object.prototype.toString.call(body) == '[object ArrayBuffer]') {
+    } else if (isArrayBuffer(body)) {
       this.body = new Blob([body as ArrayBuffer]);
+    } else if (isFormData(body)) {
+      this.body = body as FormData;
     } else {
       this.body = body = Object.prototype.toString.call(body);
     }
@@ -118,8 +128,23 @@ class Body {
 
     if (typeof this.body === 'string') {
       return new Blob([this.body]).arrayBuffer();
+    } else if (isArrayBuffer(this.body)) {
+      const isConsumed = consumed(this);
+      if (isConsumed) {
+        return isConsumed
+      } else if (ArrayBuffer.isView(this.body)) {
+        return Promise.resolve(
+          this.body.buffer.slice(
+            this.body.byteOffset,
+            this.body.byteOffset + this.body.byteLength
+          )
+        )
+      } else {
+        return Promise.resolve(this.body as unknown as ArrayBuffer);
+      }
+    } else {
+      return this.blob().then(blob => blob.arrayBuffer());
     }
-    return this.body.arrayBuffer();
   }
 
   async blob(): Promise {
@@ -127,6 +152,8 @@ class Body {
 
     if (typeof this.body === 'string') {
       return new Blob([this.body]);
+    } else if (isFormData(this.body as any)) {
+      throw new Error('could not read FormData body as blob')
     }
     return this.body as Blob;
   }
@@ -144,9 +171,11 @@ class Body {
 
     if (typeof this.body === 'string') {
       return JSON.parse(this.body);
+    } else if (isFormData(this.body)) {
+      throw new Error('could not read FormData body as text');
     }
 
-    const txt = await this.body.text();
+    const txt = await (this.body as Blob).text();
     return JSON.parse(txt);
   }
 
@@ -161,9 +190,11 @@ class Body {
 
     if (typeof this.body === 'string') {
       return this.body || '';
+    } else if (FormData.prototype.isPrototypeOf(this.body as any)) {
+      throw new Error('could not read FormData body as text')
     }
 
-    return this.body.text();
+    return (this.body as Blob).text();
   }
 }
 
@@ -312,10 +343,17 @@ export function fetch(input: Request | string, init?: RequestInit) {
         webf.invokeModule('Fetch', 'abortRequest');
       }
 
-      webf.invokeModule('Fetch', request.url, ({
+      let options: any = {};
+
+      // Tell the bridge the body contents contains FormData
+      if (isFormData(init?.body)) {
+        options.formData = init?.body;
+      }
+
+      webf.invokeModuleWithOptions('Fetch', request.url, ({
         ...init,
-        headers: (headers as Headers).map
-      }), (e, data) => {
+        headers: (headers as Headers).map,
+      }), options,(e, data) => {
         request.signal.removeEventListener('abort', abortRequest);
         if (e) return reject(e);
         let [err, statusCode, body] = data;
diff --git a/bridge/polyfill/src/webf.ts b/bridge/polyfill/src/webf.ts
index d5705aab54..a38a565222 100644
--- a/bridge/polyfill/src/webf.ts
+++ b/bridge/polyfill/src/webf.ts
@@ -3,7 +3,7 @@
 * Copyright (C) 2022-present The WebF authors. All rights reserved.
 */
 
-import { addWebfModuleListener, webfInvokeModule, clearWebfModuleListener, removeWebfModuleListener } from './bridge';
+import { addWebfModuleListener, webfInvokeModule, webfInvokeModuleWithOptions, clearWebfModuleListener, removeWebfModuleListener } from './bridge';
 import { methodChannel, triggerMethodCallHandler } from './method-channel';
 import { hybridHistory } from './hybrid-history';
 
@@ -12,6 +12,7 @@ addWebfModuleListener('MethodChannel', (event, data) => triggerMethodCallHandler
 export const webf = {
   methodChannel,
   invokeModule: webfInvokeModule,
+  invokeModuleWithOptions: webfInvokeModuleWithOptions,
   hybridHistory: hybridHistory,
   addWebfModuleListener: addWebfModuleListener,
   clearWebfModuleListener: clearWebfModuleListener,
diff --git a/webf/lib/src/bridge/from_native.dart b/webf/lib/src/bridge/from_native.dart
index a6ca2d9c64..0241e94f82 100644
--- a/webf/lib/src/bridge/from_native.dart
+++ b/webf/lib/src/bridge/from_native.dart
@@ -94,6 +94,10 @@ typedef DartAsyncModuleCallback = Pointer Function(
 
 typedef NativeHandleInvokeModuleResult = Void Function(Handle context, Pointer result);
 
+class InvokeModuleOptions extends Struct {
+  external Pointer formData;
+}
+
 typedef NativeInvokeModule = Pointer Function(
     Pointer callbackContext,
     Double contextId,
@@ -101,6 +105,7 @@ typedef NativeInvokeModule = Pointer Function(
     Pointer module,
     Pointer method,
     Pointer params,
+    Uint32 argc,
     Pointer>);
 
 class _InvokeModuleResultContext {
@@ -208,6 +213,7 @@ Pointer _invokeModule(
     Pointer module,
     Pointer method,
     Pointer params,
+    int argc,
     Pointer> callback) {
 
   BindingOpItem? currentProfileOp;

From fa95d18f13d1497b012f073e3186e39ff92ada0b Mon Sep 17 00:00:00 2001
From: wahahahaha 
Date: Wed, 14 Aug 2024 18:24:11 +0800
Subject: [PATCH 17/18] delete unnessesary script

---
 buildAndRunF.sh | 21 ---------------------
 1 file changed, 21 deletions(-)
 delete mode 100644 buildAndRunF.sh

diff --git a/buildAndRunF.sh b/buildAndRunF.sh
deleted file mode 100644
index 69436c3612..0000000000
--- a/buildAndRunF.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-runexample(){
-    ret=$PWD
-    cd webf/example && flutter run -d linux --release
-    cd $ret
-}
-runexampled(){
-    ret=$PWD
-    cd webf/example && flutter run -d linux
-    cd $ret
-}
-build-linux(){
-    npm run build:bridge:linux:release
-    cp -f bridge/cmake-build-linux/compile_commands.json .
-}
-build-linuxd(){
-    npm run build:bridge:linux && 
-    cp -f bridge/cmake-build-linux/compile_commands.json .
-}
-echo To initialize the build, using build-linux or build-linuxd:
-echo then use runexample for release build, or runexampled for debug build
-

From c6bac309262aa5c1ab635526814f6f31eb451c44 Mon Sep 17 00:00:00 2001
From: andycall 
Date: Sun, 25 Aug 2024 20:52:34 +0800
Subject: [PATCH 18/18] fix: fix compile.

---
 bridge/CMakeLists.txt               |  2 +-
 bridge/core/frame/module_manager.cc | 14 +++-----------
 2 files changed, 4 insertions(+), 12 deletions(-)

diff --git a/bridge/CMakeLists.txt b/bridge/CMakeLists.txt
index 8b9f63b030..f1f11eb0a4 100644
--- a/bridge/CMakeLists.txt
+++ b/bridge/CMakeLists.txt
@@ -382,7 +382,7 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs")
     core/html/forms/html_textarea_element.cc
     core/html/forms/form_data.cc
 
-    core/platform/network/form_data_encoder.cc
+#    core/platform/network/form_data_encoder.cc
 
     # SVG files
     core/svg/svg_element.cc
diff --git a/bridge/core/frame/module_manager.cc b/bridge/core/frame/module_manager.cc
index 14f559efe5..9a129000b4 100644
--- a/bridge/core/frame/module_manager.cc
+++ b/bridge/core/frame/module_manager.cc
@@ -142,14 +142,6 @@ ScriptValue ModuleManager::__webf_invoke_module__(ExecutingContext* context,
                                              exception);
 }
 
-namespace {
-
-struct NativeInvokeModuleOptions {
-  NativeForm* form_data;
-};
-
-};
-
 ScriptValue ModuleManager::__webf_invoke_module_with_options__(ExecutingContext* context,
                                                                const AtomicString& module_name,
                                                                const AtomicString& method,
@@ -160,8 +152,7 @@ ScriptValue ModuleManager::__webf_invoke_module_with_options__(ExecutingContext*
   NativeValue params = params_value.ToNative(context->ctx(), exception);
   if (options != nullptr && options->hasFormData()) {
     FormData* form_data = options->formData();
-  } else {
-
+    WEBF_LOG(VERBOSE) << "FORM DATA: " << form_data;
   }
 
   if (exception.HasException()) {
@@ -181,11 +172,12 @@ ScriptValue ModuleManager::__webf_invoke_module_with_options__(ExecutingContext*
     result = context->dartMethodPtr()->invokeModule(context->isDedicated(), module_context.get(), context->contextId(),
                                                     context->dartIsolateContext()->profiler()->link_id(),
                                                     module_name_string.get(), method_name_string.get(), ¶ms,
+                                                    1,
                                                     handleInvokeModuleTransientCallbackWrapper);
   } else {
     result = context->dartMethodPtr()->invokeModule(
         context->isDedicated(), nullptr, context->contextId(), context->dartIsolateContext()->profiler()->link_id(),
-        module_name_string.get(), method_name_string.get(), ¶ms, handleInvokeModuleUnexpectedCallback);
+        module_name_string.get(), method_name_string.get(), ¶ms, 1, handleInvokeModuleUnexpectedCallback);
   }
 
   context->dartIsolateContext()->profiler()->FinishTrackLinkSteps();