Skip to content

Commit

Permalink
feat: add native function callback support for toBlob
Browse files Browse the repository at this point in the history
  • Loading branch information
LeuisKen committed Jan 2, 2025
1 parent fa77735 commit bfdc09e
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 55 deletions.
37 changes: 29 additions & 8 deletions bridge/core/api/element.cc
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
/*
* Copyright (C) 2022-present The WebF authors. All rights reserved.
*/

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

namespace webf {} // namespace webf
/*
* Copyright (C) 2022-present The WebF authors. All rights reserved.
*/

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

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
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)
}
}
10 changes: 9 additions & 1 deletion bridge/rusty_webf_sys/src/html/html_element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,15 @@ pub struct HTMLElement {

pub trait HTMLElementMethods: ElementMethods {}

impl ElementMethods for HTMLElement {}
impl ElementMethods for HTMLElement {
fn to_blob(&self, exception_state: &ExceptionState) -> WebFNativeFuture<Vec<u8>> {
self.element.to_blob(exception_state)
}

fn to_blob_with_device_pixel_ratio(&self, device_pixel_ratio: f64, exception_state: &ExceptionState) -> WebFNativeFuture<Vec<u8>> {
self.element.to_blob_with_device_pixel_ratio(device_pixel_ratio, exception_state)
}
}

impl ContainerNodeMethods for HTMLElement {}

Expand Down
Loading

0 comments on commit bfdc09e

Please sign in to comment.