Skip to content
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

feat: element.to_blob in rust #706

Merged
merged 3 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion bridge/core/api/element.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,27 @@
*/

#include "plugin_api/element.h"
#include "core/api/exception_state.h"
#include "core/dom/container_node.h"
#include "core/dom/element.h"

namespace webf {} // namespace webf
namespace webf {

void ElementPublicMethods::ToBlob(Element* ptr,
WebFNativeFunctionContext* callback_context,
SharedExceptionState* shared_exception_state) {
auto* element = static_cast<webf::Element*>(ptr);
auto callback_impl = WebFNativeFunction::Create(callback_context, shared_exception_state);
return element->toBlob(callback_impl, shared_exception_state->exception_state);
}

void ElementPublicMethods::ToBlobWithDevicePixelRatio(Element* ptr,
double device_pixel_ratio,
WebFNativeFunctionContext* callback_context,
SharedExceptionState* shared_exception_state) {
auto* element = static_cast<webf::Element*>(ptr);
auto callback_impl = WebFNativeFunction::Create(callback_context, shared_exception_state);
return element->toBlob(device_pixel_ratio, callback_impl, shared_exception_state->exception_state);
}

} // namespace webf
88 changes: 77 additions & 11 deletions bridge/core/dom/element.cc
Original file line number Diff line number Diff line change
Expand Up @@ -385,12 +385,12 @@ Element& Element::CloneWithoutAttributesAndChildren(Document& factory) const {
return *(factory.createElement(local_name_, ASSERT_NO_EXCEPTION()));
}

class ElementSnapshotReader {
class ElementSnapshotPromiseReader {
public:
ElementSnapshotReader(ExecutingContext* context,
Element* element,
std::shared_ptr<ScriptPromiseResolver> resolver,
double device_pixel_ratio)
ElementSnapshotPromiseReader(ExecutingContext* context,
Element* element,
std::shared_ptr<ScriptPromiseResolver> resolver,
double device_pixel_ratio)
: context_(context), element_(element), resolver_(std::move(resolver)), device_pixel_ratio_(device_pixel_ratio) {
Start();
};
Expand All @@ -406,17 +406,17 @@ class ElementSnapshotReader {
double device_pixel_ratio_;
};

void ElementSnapshotReader::Start() {
void ElementSnapshotPromiseReader::Start() {
context_->FlushUICommand(element_,
FlushUICommandReason::kDependentsOnElement | FlushUICommandReason::kDependentsOnLayout);

auto callback = [](void* ptr, double contextId, char* error, uint8_t* bytes, int32_t length) -> void {
auto* reader = static_cast<ElementSnapshotReader*>(ptr);
auto* reader = static_cast<ElementSnapshotPromiseReader*>(ptr);
auto* context = reader->context_;

reader->context_->dartIsolateContext()->dispatcher()->PostToJs(
context->isDedicated(), context->contextId(),
[](ElementSnapshotReader* reader, char* error, uint8_t* bytes, int32_t length) {
[](ElementSnapshotPromiseReader* reader, char* error, uint8_t* bytes, int32_t length) {
if (error != nullptr) {
reader->HandleFailed(error);
dart_free(error);
Expand All @@ -433,15 +433,15 @@ void ElementSnapshotReader::Start() {
element_->bindingObject(), device_pixel_ratio_);
}

void ElementSnapshotReader::HandleSnapshot(uint8_t* bytes, int32_t length) {
void ElementSnapshotPromiseReader::HandleSnapshot(uint8_t* bytes, int32_t length) {
MemberMutationScope mutation_scope{context_};
Blob* blob = Blob::Create(context_);
blob->SetMineType("image/png");
blob->AppendBytes(bytes, length);
resolver_->Resolve<Blob*>(blob);
}

void ElementSnapshotReader::HandleFailed(const char* error) {
void ElementSnapshotPromiseReader::HandleFailed(const char* error) {
MemberMutationScope mutation_scope{context_};
ExceptionState exception_state;
exception_state.ThrowException(context_->ctx(), ErrorType::InternalError, error);
Expand All @@ -450,6 +450,58 @@ void ElementSnapshotReader::HandleFailed(const char* error) {
JS_FreeValue(context_->ctx(), exception_value);
}

class ElementSnapshotNativeFunctionReader {
public:
ElementSnapshotNativeFunctionReader(ExecutingContext* context,
Element* element,
std::shared_ptr<WebFNativeFunction> function,
double device_pixel_ratio)
: context_(context), element_(element), function_(std::move(function)), device_pixel_ratio_(device_pixel_ratio) {
Start();
};

void Start();

private:
ExecutingContext* context_;
Element* element_;
std::shared_ptr<WebFNativeFunction> function_{nullptr};
double device_pixel_ratio_;
};

void ElementSnapshotNativeFunctionReader::Start() {
context_->FlushUICommand(element_,
FlushUICommandReason::kDependentsOnElement | FlushUICommandReason::kDependentsOnLayout);

auto callback = [](void* ptr, double contextId, char* error, uint8_t* bytes, int32_t length) -> void {
auto* reader = static_cast<ElementSnapshotNativeFunctionReader*>(ptr);
auto* context = reader->context_;

reader->context_->dartIsolateContext()->dispatcher()->PostToJs(
context->isDedicated(), context->contextId(),
[](ElementSnapshotNativeFunctionReader* reader, char* error, uint8_t* bytes, int32_t length) {
if (error != nullptr) {
NativeValue error_object = Native_NewCString(error);
reader->function_->Invoke(reader->context_, 1, &error_object);
dart_free(error);
} else {
auto params = new NativeValue[2];
params[0] = Native_NewNull();
params[1] = Native_NewUint8Bytes(length, bytes);
reader->function_->Invoke(reader->context_, 2, params);
dart_free(bytes);
}

reader->context_->RunRustFutureTasks();
delete reader;
},
reader, error, bytes, length);
};

context_->dartMethodPtr()->toBlob(context_->isDedicated(), this, context_->contextId(), callback,
element_->bindingObject(), device_pixel_ratio_);
}

ScriptPromise Element::toBlob(ExceptionState& exception_state) {
Window* window = GetExecutingContext()->window();
double device_pixel_ratio = NativeValueConverter<NativeTypeDouble>::FromNativeValue(window->GetBindingProperty(
Expand All @@ -460,10 +512,24 @@ ScriptPromise Element::toBlob(ExceptionState& exception_state) {

ScriptPromise Element::toBlob(double device_pixel_ratio, ExceptionState& exception_state) {
auto resolver = ScriptPromiseResolver::Create(GetExecutingContext());
new ElementSnapshotReader(GetExecutingContext(), this, resolver, device_pixel_ratio);
new ElementSnapshotPromiseReader(GetExecutingContext(), this, resolver, device_pixel_ratio);
return resolver->Promise();
}

void Element::toBlob(const std::shared_ptr<WebFNativeFunction>& callback, ExceptionState& exception_state) {
Window* window = GetExecutingContext()->window();
double device_pixel_ratio = NativeValueConverter<NativeTypeDouble>::FromNativeValue(window->GetBindingProperty(
binding_call_methods::kdevicePixelRatio,
FlushUICommandReason::kDependentsOnElement | FlushUICommandReason::kDependentsOnLayout, exception_state));
return toBlob(device_pixel_ratio, callback, exception_state);
}

void Element::toBlob(double device_pixel_ratio,
const std::shared_ptr<WebFNativeFunction>& callback,
ExceptionState& exception_state) {
new ElementSnapshotNativeFunctionReader(GetExecutingContext(), this, callback, device_pixel_ratio);
}

ScriptValue Element::___testGlobalToLocal__(double x, double y, webf::ExceptionState& exception_state) {
const NativeValue args[] = {
NativeValueConverter<NativeTypeDouble>::ToNativeValue(x),
Expand Down
5 changes: 5 additions & 0 deletions bridge/core/dom/element.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "bindings/qjs/script_promise.h"
#include "container_node.h"
#include "core/css/inline_css_style_declaration.h"
#include "core/native/native_function.h"
#include "element_data.h"
#include "legacy/bounding_client_rect.h"
#include "legacy/element_attributes.h"
Expand Down Expand Up @@ -80,6 +81,10 @@ class Element : public ContainerNode {

ScriptPromise toBlob(double device_pixel_ratio, ExceptionState& exception_state);
ScriptPromise toBlob(ExceptionState& exception_state);
void toBlob(double device_pixel_ratio,
const std::shared_ptr<WebFNativeFunction>& callback,
ExceptionState& exception_state);
void toBlob(const std::shared_ptr<WebFNativeFunction>& callback, ExceptionState& exception_state);

ScriptValue ___testGlobalToLocal__(double x, double y, ExceptionState& exception_state);

Expand Down
13 changes: 13 additions & 0 deletions bridge/foundation/native_value.cc
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,19 @@ NativeValue Native_NewJSON(JSContext* ctx, const ScriptValue& value, ExceptionSt
#endif
}

NativeValue Native_NewUint8Bytes(uint32_t length, uint8_t* bytes) {
#if _MSC_VER
NativeValue v{};
v.u.ptr = reinterpret_cast<void*>(bytes);
v.uint32 = length;
v.tag = NativeTag::TAG_UINT8_BYTES;
return v;
#else
return (NativeValue){
.u = {.ptr = reinterpret_cast<void*>(bytes)}, .uint32 = length, .tag = NativeTag::TAG_UINT8_BYTES};
#endif
}

JSPointerType GetPointerTypeOfNativePointer(NativeValue native_value) {
assert(native_value.tag == NativeTag::TAG_POINTER);
return static_cast<JSPointerType>(native_value.uint32);
Expand Down
1 change: 1 addition & 0 deletions bridge/foundation/native_value.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,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_NewUint8Bytes(uint32_t length, uint8_t* bytes);

JSPointerType GetPointerTypeOfNativePointer(NativeValue native_value);

Expand Down
15 changes: 15 additions & 0 deletions bridge/include/plugin_api/element.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,25 @@ class SharedExceptionState;
class ExecutingContext;
class Element;
class Document;
typedef struct WebFNativeFunctionContext WebFNativeFunctionContext;

using PublicElementToBlob = void (*)(Element*, WebFNativeFunctionContext*, SharedExceptionState*);
using PublicElementToBlobWithDevicePixelRatio = void (*)(Element*,
double,
WebFNativeFunctionContext*,
SharedExceptionState*);

struct ElementPublicMethods : WebFPublicMethods {
static void ToBlob(Element* element, WebFNativeFunctionContext* context, SharedExceptionState* exception_state);
static void ToBlobWithDevicePixelRatio(Element* element,
double device_pixel_ratio,
WebFNativeFunctionContext* context,
SharedExceptionState* exception_state);

double version{1.0};
ContainerNodePublicMethods container_node;
PublicElementToBlob element_to_blob{ToBlob};
PublicElementToBlobWithDevicePixelRatio element_to_blob_with_device_pixel_ratio{ToBlobWithDevicePixelRatio};
};

} // namespace webf
Expand Down
112 changes: 89 additions & 23 deletions bridge/rusty_webf_sys/src/dom/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,97 @@ use crate::*;
pub struct ElementRustMethods {
pub version: c_double,
pub container_node: ContainerNodeRustMethods,
pub to_blob: extern "C" fn(*const OpaquePtr, *const WebFNativeFunctionContext, *const OpaquePtr) -> c_void,
pub to_blob_with_device_pixel_ratio: extern "C" fn(*const OpaquePtr, c_double, *const WebFNativeFunctionContext, *const OpaquePtr) -> c_void,
}

impl RustMethods for ElementRustMethods {}

#[repr(C)]
enum ElementType {
kHTMLDIVElement,
kHTMLAnchorElement,
kHTMLHeadElement,
kHTMLBodyElement,
kHTMLHTMLElement,
kHTMLImageElement,
kHTMLLinkElement,
kHTMLScriptElement,
kHTMLTemplateElement,
kHTMLUnknownElement,
kHTMLCanvasElement,
kHTMLWidgetElement,
kHTMLButtonElement,
kHTMLFormElement,
kHTMLInputElement,
kHTMLTextAreaElement
}

pub struct Element {
container_node: ContainerNode,
method_pointer: *const ElementRustMethods,
}

impl Element {}
impl Element {
pub fn to_blob(&self, exception_state: &ExceptionState) -> WebFNativeFuture<Vec<u8>> {
let event_target: &EventTarget = &self.container_node.node.event_target;

let future_for_return = WebFNativeFuture::<Vec<u8>>::new();
let future_in_callback = future_for_return.clone();
let general_callback: WebFNativeFunction = Box::new(move |argc, argv| {
if argc == 1 {
let error_string = unsafe { (*argv).clone() };
let error_string = error_string.to_string();
future_in_callback.set_result(Err(error_string));
return NativeValue::new_null();
}
if argc == 2 {
let result = unsafe { (*argv.wrapping_add(1)).clone() };
let value = result.to_u8_bytes();
future_in_callback.set_result(Ok(Some(value)));
return NativeValue::new_null();
}
println!("Invalid argument count for async storage callback");
NativeValue::new_null()
});
let callback_data = Box::new(WebFNativeFunctionContextData {
func: general_callback,
});
let callback_context_data_ptr = Box::into_raw(callback_data);
let callback_context = Box::new(WebFNativeFunctionContext {
callback: invoke_webf_native_function,
free_ptr: release_webf_native_function,
ptr: callback_context_data_ptr,
});
let callback_context_ptr = Box::into_raw(callback_context);
unsafe {
(((*self.method_pointer).to_blob))(event_target.ptr, callback_context_ptr, exception_state.ptr);
}
future_for_return
}

pub trait ElementMethods: ContainerNodeMethods {}
pub fn to_blob_with_device_pixel_ratio(&self, device_pixel_ratio: f64, exception_state: &ExceptionState) -> WebFNativeFuture<Vec<u8>> {
let event_target: &EventTarget = &self.container_node.node.event_target;

let future_for_return = WebFNativeFuture::<Vec<u8>>::new();
let future_in_callback = future_for_return.clone();
let general_callback: WebFNativeFunction = Box::new(move |argc, argv| {
if argc == 1 {
let error_string = unsafe { (*argv).clone() };
let error_string = error_string.to_string();
future_in_callback.set_result(Err(error_string));
return NativeValue::new_null();
}
if argc == 2 {
let result = unsafe { (*argv.wrapping_add(1)).clone() };
let value = result.to_u8_bytes();
future_in_callback.set_result(Ok(Some(value)));
return NativeValue::new_null();
}
println!("Invalid argument count for async storage callback");
NativeValue::new_null()
});
let callback_data = Box::new(WebFNativeFunctionContextData {
func: general_callback,
});
let callback_context_data_ptr = Box::into_raw(callback_data);
let callback_context = Box::new(WebFNativeFunctionContext {
callback: invoke_webf_native_function,
free_ptr: release_webf_native_function,
ptr: callback_context_data_ptr,
});
let callback_context_ptr = Box::into_raw(callback_context);
unsafe {
(((*self.method_pointer).to_blob_with_device_pixel_ratio))(event_target.ptr, device_pixel_ratio, callback_context_ptr, exception_state.ptr);
}
future_for_return
}
}

pub trait ElementMethods: ContainerNodeMethods {
fn to_blob(&self, exception_state: &ExceptionState) -> WebFNativeFuture<Vec<u8>>;
fn to_blob_with_device_pixel_ratio(&self, device_pixel_ratio: f64, exception_state: &ExceptionState) -> WebFNativeFuture<Vec<u8>>;
}

impl ContainerNodeMethods for Element {}

Expand Down Expand Up @@ -97,4 +156,11 @@ impl EventTargetMethods for Element {
}
}

impl ElementMethods for Element {}
impl ElementMethods for Element {
fn to_blob(&self, exception_state: &ExceptionState) -> WebFNativeFuture<Vec<u8>> {
self.to_blob(exception_state)
}
fn to_blob_with_device_pixel_ratio(&self, device_pixel_ratio: f64, exception_state: &ExceptionState) -> WebFNativeFuture<Vec<u8>> {
self.to_blob_with_device_pixel_ratio(device_pixel_ratio, exception_state)
}
}
Loading
Loading