-
-
Notifications
You must be signed in to change notification settings - Fork 117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add FormData Support #164
Comments
Hi there, I'm porting an vue app to webf however http errors stop me. Seems like FormData is not implemented currently, do we have a plan
|
We do not have a plan to support this from now on Being a sponsor for this project can enhance your desired features compared to other projects we are currently working on. |
Fine, thank you for you reply. I decide to implement formData in webF by my side. Due to the poor code skill of mine on Dart ( the first meet between Dart and I ), I made it hard with much help of AI suggestion. I wish I could issue a pull request to webF if I can get some advice/help from you webF authors about how to follow your archetectures or rules. arraybuffer.dart import 'dart:typed_data';
class ArrayBufferData {
Uint8List buffer;
ArrayBufferData(this.buffer);
factory ArrayBufferData.fromBytes(Uint8List bytes) {
return ArrayBufferData(bytes);
}
int get length => buffer.length;
} blob.dart import 'dart:typed_data';
import 'dart:convert';
import 'arraybuffer.dart';
/// A simplified version of the Blob class for demonstration purposes.
class Blob {
Blob(this._data, [this.type = 'application/octet-stream']);
final Uint8List _data;
final String type;
int get size => _data.length;
Uint8List get bytes => _data;
String StringResult() {
return String.fromCharCodes(_data);
}
String Base64Result() {
return 'data:$type;base64,${base64Encode(_data)}';
}
ArrayBufferData ArrayBufferResult() {
return ArrayBufferData(_data.buffer.asUint8List());
}
Blob 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 Blob(slicedData, contentType);
}
} formdata.dart import 'dart:convert';
import 'package:webf/webf.dart';
import 'blob.dart';
/// A simple implementation of the FormData interface.
class FormData extends BaseModule {
/// Creates a new FormData instance.
FormData(ModuleManager? moduleManager) : super(moduleManager);
/// The list that holds the data.
final List<List<dynamic>> _list = [];
/// Adds a key-value pair to the FormData.
void append(String name, value) {
if (value is Blob) {
// Handle Blob type.
_list.add([name, value]);
} else {
// Handle other types.
_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<dynamic> 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) {
switch (method) {
case 'append':
append(params[0], params[1]);
break;
case 'getFirst':
return jsonEncode(getFirst(params[0]));
case 'getAll':
return jsonEncode(getAll(params[0]));
case 'toString':
return toString();
default:
print('Failed to execute \'$method\' on \'fromData\': NoSuchMethod ');
}
return EMPTY_STRING;
}
@override
void dispose() {}
@override
String get name => 'FormData';
} module_manager.dart: one line change. void _defineModuleCreator() {
if (_isDefined) return;
_isDefined = true;
[...]
_defineModule((ModuleManager? moduleManager) => FormData(moduleManager));
} |
After add code listed above, still say 'FormData' is not defined. I'm no idea about how a js object registered in webF. |
Adding a FormData API in JavaScript must be done with C++ bindings. The Blob JavaScript API is a similar case to the FormData API. You need to store the underlying byte data of FormData in C++ and send it to Dart via Dart FFI. Then, append this byte data into the networking request, as described in the GitHub repository. |
我尝试添加了formData和arrayBuffer,并且编译通过。 现在在测试arrayBuffer,但是用js测试new出来的对象似乎类型不对。 ts这样写的:
测试代码这样写的:
错误是这样的:
我将本地的提交生成了一个patch文件 0001-WIP-Implement-fromdata-and-arraybuffer.zip 请指导一下。 |
arrayBuffer 是 ecma 标准支持的对象,quickjs 也支持了,不需要单独支持 |
而且通过 C++ 实现的话,是不需要在 polyfill 里新增任何代码 |
确实没有很清晰使用了polyfill,C++,又混合dart的时候,对象实现是如何注册到qjs里面。 |
那如果实现FormData,必须要实现一个qjs_form_data是吗? |
写一个 TypeScript Typings,比如 blob.d.ts,生成器会生成与 QuickJS 交互的胶水代码,然后实现 form_data.h 和 form_data.cc,需要继承 BindingObject,这样才可以创建一个用于在 JavaScript 环境内使用的 C++ 对象,还能在 Dart 那边同步访问,最后别忘了在这里注册:binding_initializer.cc 搞定上面一切后,JS 的全局环境就会出现你添加的类,直接在 JS 中调用即可。然后在 JS 里调用了 FormData 返回的 JS 对象可以传递到 JS 写的 Fetch API,并体现在 invokeModule 的参数内,这时候在 C++ 那边提取参数时,就能找到 FormData 创建的 JS 对象。由于它是 BindingObject,可以直接转成 Pointer 发送到 Dart 那边。 然后你需要写一个 Dart 版本的 FormData 对象,记得继承 DynamicBindingObject。 其他的一些参考,比如 CanvasGradient 对象,以及 [Dart 那边的对应实]现](https://github.com/openwebf/webf/blob/main/webf/lib/src/html/canvas/canvas_context.dart#L138)
这是生成器生成的,不需要手动写 |
生成器是借鉴了 Chrome 的 WebIDL 思路,使用 TypeScript Typing 定义和 TypeScript API 定制的,代码在这里: https://github.com/openwebf/webf/tree/main/bridge/scripts/code_generator |
好的。Fetch API和xmlHttpRequest,在webf中是共享的实现代码吗?好像没有看到xmlHttpRequest。 |
好的。那如果添加了FormData, 估计xhr这里也要适配一下。 |
我尝试在formData中使用BlobParts, // type FormDataEntryValue = File | string;
export interface FormData {
new():FormData;
append(name: string, value: BlobPart, fileName?: string): void;
del(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: (value: BlobPart, key: string, parent: FormData) => void, thisArg?: any): void;
} 但是由于它的ImplType不是BlobPart*,而是一个std_shared_ptr, class BlobPart {
public:
using ImplType = std::shared_ptr<BlobPart>; 那么默认的ConverterImpl在绑定getAll的时候就转换不过去 template <>
struct Converter<BlobPart> : public ConverterBase<BlobPart> {
using ImplType = BlobPart::ImplType;
static ImplType FromValue(JSContext* ctx, JSValue value, ExceptionState& exception_state) {
assert(!JS_IsException(value));
return BlobPart::Create(ctx, value, exception_state);
}
static JSValue ToValue(JSContext* ctx, BlobPart* data) {
if (data == nullptr)
return JS_NULL;
return data->ToQuickJS(ctx);
}
}; 那遇到这样的情况,我认为可以修改converter来支持,也可以创建一个类似blobPart的类型且它的ImplType是其本身指针来解决,不知道这样理解是否正确,有更好的方案吗? |
template <>
struct Converter<BlobPart> : public ConverterBase<BlobPart> {
using ImplType = BlobPart::ImplType;
static ImplType FromValue(JSContext* ctx, JSValue value, ExceptionState& exception_state) {
assert(!JS_IsException(value));
return BlobPart::Create(ctx, value, exception_state);
}
static JSValue ToValue(JSContext* ctx, BlobPart* data) {
if (data == nullptr)
return JS_NULL;
return data->ToQuickJS(ctx);
}
}; 你的想法没错,这里的实现有问题,应该把 ToValue 的参数改成: static JSValue ToValue(JSContext* ctx, std::shared_ptr<BlobPart> data) {
if (data == nullptr)
return JS_NULL;
return data->ToQuickJS(ctx);
} |
回调函数在d.ts里面用什么表示,NativeTypeFunction? |
declare const setInterval: (callback: Function, timeout?: double) => int64; |
测试需要在 macOS 平台运行。 可以加到这里 https://github.com/openwebf/webf/tree/main/integration_tests/specs/xhr |
目前只是测试FormData的api,FFI我还不会,不知道怎么弄到fetchModule里面。 我想是否能在FormData上面添加一个getBytes(), 在xhr.ts里面调用一下,data就自动变成一个dart里面的Uint8List? |
看了下,底层的 FFI 通信现在还只支持从 Dart 发送 Uint8List 到 C++,反过来还没实现,你先把 PR 提交一下,我在你的分支上补充这块 |
Pull request created: #645 |
@linsmod done,看下 PR,通信的例子已经补充上了 |
Feature type
DOM API
The spec link.
https://kapeli.com/dash_share?docset_file=JavaScript&docset_name=JavaScript&path=developer.mozilla.org/en-US/docs/Web/API/FormData.html&platform=javascript&repo=Main&source=developer.mozilla.org/en-US/docs/Web/API/FormData
How much important for you
No response
The text was updated successfully, but these errors were encountered: