diff --git a/bridge/bindings/qjs/qjs_function.h b/bridge/bindings/qjs/qjs_function.h index 18eb339627..f1c0a07e86 100644 --- a/bridge/bindings/qjs/qjs_function.h +++ b/bridge/bindings/qjs/qjs_function.h @@ -5,6 +5,8 @@ #ifndef BRIDGE_QJS_FUNCTION_H #define BRIDGE_QJS_FUNCTION_H +#include "foundation/casting.h" +#include "foundation/function.h" #include "script_value.h" namespace webf { @@ -17,7 +19,7 @@ using QJSFunctionCallback = ScriptValue (*)(JSContext* ctx, // https://webidl.spec.whatwg.org/#dfn-callback-interface // QJSFunction memory are auto managed by std::shared_ptr. -class QJSFunction { +class QJSFunction : public Function { public: static std::shared_ptr Create(JSContext* ctx, JSValue function) { return std::make_shared(ctx, function); @@ -46,6 +48,8 @@ class QJSFunction { bool IsFunction(JSContext* ctx); + bool IsQJSFunction() const override { return true; } + JSValue ToQuickJS() { return JS_DupValue(ctx_, function_); }; JSValue ToQuickJSUnsafe() { return function_; } @@ -66,6 +70,11 @@ class QJSFunction { JSValue this_val_{JS_NULL}; }; +template <> +struct DowncastTraits { + static bool AllowFrom(const Function& function) { return function.IsQJSFunction(); } +}; + } // namespace webf #endif // BRIDGE_QJS_FUNCTION_H diff --git a/bridge/core/api/event_target.cc b/bridge/core/api/event_target.cc index 2888dc5b78..20238fa3d0 100644 --- a/bridge/core/api/event_target.cc +++ b/bridge/core/api/event_target.cc @@ -20,50 +20,10 @@ #include "core/html/html_image_element.h" #include "html_element_type_helper.h" #include "plugin_api/add_event_listener_options.h" +#include "plugin_api/webf_event_listener.h" namespace webf { -class WebFPublicPluginEventListener : public EventListener { - public: - WebFPublicPluginEventListener(WebFEventListenerContext* callback_context, - SharedExceptionState* shared_exception_state) - : callback_context_(callback_context), shared_exception_state_(shared_exception_state) {} - - ~WebFPublicPluginEventListener() { - callback_context_->free_ptr(callback_context_); - delete callback_context_; - } - - static const std::shared_ptr Create(WebFEventListenerContext* WebF_event_listener, - SharedExceptionState* shared_exception_state) { - return std::make_shared(WebF_event_listener, shared_exception_state); - }; - - [[nodiscard]] bool IsPublicPluginEventHandler() const override { return true; } - - void Invoke(ExecutingContext* context, Event* event, ExceptionState& exception_state) override { - WebFValueStatus* status_block = event->KeepAlive(); - callback_context_->callback(callback_context_, event, event->eventPublicMethods(), status_block, - shared_exception_state_); - } - - [[nodiscard]] bool Matches(const EventListener& other) const override { - const auto* other_listener = DynamicTo(other); - return other_listener && other_listener->callback_context_ && - other_listener->callback_context_->callback == callback_context_->callback; - } - - void Trace(GCVisitor* visitor) const override {} - - WebFEventListenerContext* callback_context_; - SharedExceptionState* shared_exception_state_; -}; - -template <> -struct DowncastTraits { - static bool AllowFrom(const EventListener& event_listener) { return event_listener.IsPublicPluginEventHandler(); } -}; - void EventTargetPublicMethods::AddEventListener(EventTarget* event_target, const char* event_name_str, WebFEventListenerContext* callback_context, diff --git a/bridge/core/api/executing_context.cc b/bridge/core/api/executing_context.cc index 8acf4d3301..36416e2382 100644 --- a/bridge/core/api/executing_context.cc +++ b/bridge/core/api/executing_context.cc @@ -8,6 +8,7 @@ #include "core/dom/document.h" #include "core/executing_context.h" #include "core/frame/window.h" +#include "core/frame/window_or_worker_global_scope.h" namespace webf { @@ -31,4 +32,34 @@ void ExecutingContextWebFMethods::FinishRecordingUIOperations(webf::ExecutingCon context->uiCommandBuffer()->AddCommand(UICommand::kFinishRecordingCommand, nullptr, nullptr, nullptr, false); } +int32_t ExecutingContextWebFMethods::SetTimeout(ExecutingContext* context, + WebFNativeFunctionContext* callback_context, + int32_t timeout, + SharedExceptionState* shared_exception_state) { + auto callback_impl = WebFNativeFunction::Create(callback_context, shared_exception_state); + + return WindowOrWorkerGlobalScope::setTimeout(context, callback_impl, timeout, + shared_exception_state->exception_state); +} + +int32_t ExecutingContextWebFMethods::SetInterval(ExecutingContext* context, + WebFNativeFunctionContext* callback_context, + int32_t timeout, + SharedExceptionState* shared_exception_state) { + auto callback_impl = WebFNativeFunction::Create(callback_context, shared_exception_state); + + return WindowOrWorkerGlobalScope::setInterval(context, callback_impl, timeout, + shared_exception_state->exception_state); +} + +void ExecutingContextWebFMethods::ClearTimeout(ExecutingContext* context, int32_t timeout_id, + SharedExceptionState* shared_exception_state) { + WindowOrWorkerGlobalScope::clearTimeout(context, timeout_id, shared_exception_state->exception_state); +} + +void ExecutingContextWebFMethods::ClearInterval(ExecutingContext* context, int32_t interval_id, + SharedExceptionState* shared_exception_state) { + WindowOrWorkerGlobalScope::clearInterval(context, interval_id, shared_exception_state->exception_state); +} + } // namespace webf diff --git a/bridge/core/frame/dom_timer.cc b/bridge/core/frame/dom_timer.cc index 6eee1b97c4..256e4fdb49 100644 --- a/bridge/core/frame/dom_timer.cc +++ b/bridge/core/frame/dom_timer.cc @@ -16,25 +16,29 @@ namespace webf { std::shared_ptr DOMTimer::create(ExecutingContext* context, - const std::shared_ptr& callback, + const std::shared_ptr& callback, TimerKind timer_kind) { return std::make_shared(context, callback, timer_kind); } -DOMTimer::DOMTimer(ExecutingContext* context, std::shared_ptr callback, TimerKind timer_kind) +DOMTimer::DOMTimer(ExecutingContext* context, std::shared_ptr callback, TimerKind timer_kind) : context_(context), callback_(std::move(callback)), status_(TimerStatus::kPending), kind_(timer_kind) {} void DOMTimer::Fire() { if (status_ == TimerStatus::kTerminated) return; - if (!callback_->IsFunction(context_->ctx())) - return; + if (auto* callback = DynamicTo(callback_.get())) { + if (!callback->IsFunction(context_->ctx())) + return; - ScriptValue returnValue = callback_->Invoke(context_->ctx(), ScriptValue::Empty(context_->ctx()), 0, nullptr); + ScriptValue returnValue = callback->Invoke(context_->ctx(), ScriptValue::Empty(context_->ctx()), 0, nullptr); - if (returnValue.IsException()) { - context_->HandleException(&returnValue); + if (returnValue.IsException()) { + context_->HandleException(&returnValue); + } + } else if (auto* callback = DynamicTo(callback_.get())) { + callback->Invoke(context_, 0, nullptr); } } diff --git a/bridge/core/frame/dom_timer.h b/bridge/core/frame/dom_timer.h index f85e2a9c4c..1bd1560cec 100644 --- a/bridge/core/frame/dom_timer.h +++ b/bridge/core/frame/dom_timer.h @@ -17,9 +17,9 @@ class DOMTimer { enum TimerStatus { kPending, kExecuting, kFinished, kCanceled, kTerminated }; static std::shared_ptr create(ExecutingContext* context, - const std::shared_ptr& callback, + const std::shared_ptr& callback, TimerKind timer_kind); - DOMTimer(ExecutingContext* context, std::shared_ptr callback, TimerKind timer_kind); + DOMTimer(ExecutingContext* context, std::shared_ptr callback, TimerKind timer_kind); // Trigger timer callback. void Fire(); @@ -42,7 +42,7 @@ class DOMTimer { ExecutingContext* context_{nullptr}; int32_t timer_id_{-1}; TimerStatus status_; - std::shared_ptr callback_; + std::shared_ptr callback_; }; } // namespace webf diff --git a/bridge/core/frame/window_or_worker_global_scope.cc b/bridge/core/frame/window_or_worker_global_scope.cc index de1d8e52e3..b6f3ba3d51 100644 --- a/bridge/core/frame/window_or_worker_global_scope.cc +++ b/bridge/core/frame/window_or_worker_global_scope.cc @@ -105,13 +105,13 @@ static void handlePersistentCallbackWrapper(void* ptr, double contextId, char* e } int WindowOrWorkerGlobalScope::setTimeout(ExecutingContext* context, - const std::shared_ptr& handler, + const std::shared_ptr& handler, ExceptionState& exception) { return setTimeout(context, handler, 0.0, exception); } int WindowOrWorkerGlobalScope::setTimeout(ExecutingContext* context, - const std::shared_ptr& handler, + const std::shared_ptr& handler, int32_t timeout, ExceptionState& exception) { if (handler == nullptr) { @@ -133,13 +133,13 @@ int WindowOrWorkerGlobalScope::setTimeout(ExecutingContext* context, } int WindowOrWorkerGlobalScope::setInterval(ExecutingContext* context, - std::shared_ptr handler, + std::shared_ptr handler, ExceptionState& exception) { return setInterval(context, handler, 0.0, exception); } int WindowOrWorkerGlobalScope::setInterval(ExecutingContext* context, - const std::shared_ptr& handler, + const std::shared_ptr& handler, int32_t timeout, ExceptionState& exception) { if (handler == nullptr) { diff --git a/bridge/core/frame/window_or_worker_global_scope.h b/bridge/core/frame/window_or_worker_global_scope.h index 8d95a5560c..69ffeff787 100644 --- a/bridge/core/frame/window_or_worker_global_scope.h +++ b/bridge/core/frame/window_or_worker_global_scope.h @@ -8,23 +8,22 @@ #include "bindings/qjs/exception_state.h" #include "bindings/qjs/qjs_function.h" #include "core/executing_context.h" +#include "foundation/function.h" namespace webf { class WindowOrWorkerGlobalScope { public: static int setTimeout(ExecutingContext* context, - const std::shared_ptr& handler, + const std::shared_ptr& handler, int32_t timeout, ExceptionState& exception); - static int setTimeout(ExecutingContext* context, - const std::shared_ptr& handler, - ExceptionState& exception); + static int setTimeout(ExecutingContext* context, const std::shared_ptr& handler, ExceptionState& exception); static int setInterval(ExecutingContext* context, - const std::shared_ptr& handler, + const std::shared_ptr& handler, int32_t timeout, ExceptionState& exception); - static int setInterval(ExecutingContext* context, std::shared_ptr handler, ExceptionState& exception); + static int setInterval(ExecutingContext* context, std::shared_ptr handler, ExceptionState& exception); static void clearTimeout(ExecutingContext* context, int32_t timerId, ExceptionState& exception); static void clearInterval(ExecutingContext* context, int32_t timerId, ExceptionState& exception); static void __gc__(ExecutingContext* context, ExceptionState& exception); diff --git a/bridge/core/native/native_function.h b/bridge/core/native/native_function.h new file mode 100644 index 0000000000..a108d81332 --- /dev/null +++ b/bridge/core/native/native_function.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#ifndef WEBF_CORE_NATIVE_NATIVE_FUNCTION_H_ +#define WEBF_CORE_NATIVE_NATIVE_FUNCTION_H_ + +#include "foundation/casting.h" +#include "foundation/function.h" +#include "foundation/native_value.h" +#include "plugin_api/rust_readable.h" + +namespace webf { + +class SharedExceptionState; +typedef struct WebFNativeFunctionContext WebFNativeFunctionContext; + +using WebFNativeFunctionCallback = void (*)(WebFNativeFunctionContext* callback_context, + int32_t argc, + NativeValue* argv, + SharedExceptionState* shared_exception_state); +using WebFNativeFunctionFreePtrFn = void (*)(WebFNativeFunctionContext* callback_context); + +struct WebFNativeFunctionContext : public RustReadable { + WebFNativeFunctionCallback callback; + WebFNativeFunctionFreePtrFn free_ptr; + void* ptr; +}; + +class WebFNativeFunction : public Function { + public: + WebFNativeFunction(WebFNativeFunctionContext* callback_context, SharedExceptionState* shared_exception_state) + : callback_context_(callback_context), shared_exception_state_(shared_exception_state) {} + + static const std::shared_ptr Create(WebFNativeFunctionContext* callback_context, + SharedExceptionState* shared_exception_state) { + return std::make_shared(callback_context, shared_exception_state); + } + + ~WebFNativeFunction() { + callback_context_->free_ptr(callback_context_); + delete callback_context_; + } + + bool IsWebFNativeFunction() const override { return true; } + + void Invoke(ExecutingContext* context, int32_t argc, NativeValue* argv) { + callback_context_->callback(callback_context_, argc, argv, shared_exception_state_); + } + + private: + WebFNativeFunctionContext* callback_context_; + SharedExceptionState* shared_exception_state_; +}; + +template <> +struct DowncastTraits { + static bool AllowFrom(const Function& function) { return function.IsWebFNativeFunction(); } +}; + +} // namespace webf + +#endif // WEBF_CORE_NATIVE_NATIVE_FUNCTION_H_ diff --git a/bridge/foundation/function.h b/bridge/foundation/function.h new file mode 100644 index 0000000000..7737575b92 --- /dev/null +++ b/bridge/foundation/function.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019-2022 The Kraken authors. All rights reserved. + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#ifndef BRIDGE_FOUNDATION_FUNCTION_H_ +#define BRIDGE_FOUNDATION_FUNCTION_H_ + +namespace webf { + +class Function { + public: + virtual bool IsQJSFunction() const { return false; } + virtual bool IsWebFNativeFunction() const { return false; } +}; + +} // namespace webf + +#endif // BRIDGE_FOUNDATION_FUNCTION_H_ diff --git a/bridge/include/plugin_api/event_target.h b/bridge/include/plugin_api/event_target.h index e380b8ea38..37d6694049 100644 --- a/bridge/include/plugin_api/event_target.h +++ b/bridge/include/plugin_api/event_target.h @@ -18,13 +18,6 @@ class Event; typedef struct EventPublicMethods EventPublicMethods; typedef struct WebFEventListenerContext WebFEventListenerContext; -using WebFImplEventCallback = void (*)(WebFEventListenerContext* callback_context, - Event* event, - const EventPublicMethods* event_methods, - WebFValueStatus* status, - SharedExceptionState* shared_exception_state); -using FreePtrFn = void (*)(WebFEventListenerContext* callback_context); - enum class EventTargetType { kEventTarget = 0, kNode = 1, @@ -42,12 +35,6 @@ enum class EventTargetType { kComment = 13, }; -struct WebFEventListenerContext : public RustReadable { - WebFImplEventCallback callback; - FreePtrFn free_ptr; - void* ptr; -}; - using PublicEventTargetAddEventListener = void (*)(EventTarget* event_target, const char*, WebFEventListenerContext* callback_context, diff --git a/bridge/include/plugin_api/executing_context.h b/bridge/include/plugin_api/executing_context.h index b2f317b233..53e9617648 100644 --- a/bridge/include/plugin_api/executing_context.h +++ b/bridge/include/plugin_api/executing_context.h @@ -5,6 +5,7 @@ #ifndef WEBF_CORE_RUST_API_EXECUTING_CONTEXT_H_ #define WEBF_CORE_RUST_API_EXECUTING_CONTEXT_H_ +#include "core/native/native_function.h" #include "document.h" #include "exception_state.h" #include "window.h" @@ -19,6 +20,16 @@ using PublicContextGetDocument = WebFValue (*)( using PublicContextGetWindow = WebFValue (*)(ExecutingContext*); using PublicContextGetExceptionState = WebFValue (*)(); using PublicFinishRecordingUIOperations = void (*)(ExecutingContext* context); +using PublicContextSetTimeout = int32_t (*)(ExecutingContext*, + WebFNativeFunctionContext*, + int32_t, + SharedExceptionState*); +using PublicContextSetInterval = int32_t (*)(ExecutingContext*, + WebFNativeFunctionContext*, + int32_t, + SharedExceptionState*); +using PublicContextClearTimeout = void (*)(ExecutingContext*, int32_t, SharedExceptionState*); +using PublicContextClearInterval = void (*)(ExecutingContext*, int32_t, SharedExceptionState*); // Memory aligned and readable from WebF side. // Only C type member can be included in this class, any C++ type and classes can is not allowed to use here. @@ -27,12 +38,26 @@ struct ExecutingContextWebFMethods { static WebFValue window(ExecutingContext* context); static WebFValue CreateExceptionState(); static void FinishRecordingUIOperations(ExecutingContext* context); + static int32_t SetTimeout(ExecutingContext* context, + WebFNativeFunctionContext* callback_context, + int32_t timeout, + SharedExceptionState* shared_exception_state); + static int32_t SetInterval(ExecutingContext* context, + WebFNativeFunctionContext* callback_context, + int32_t timeout, + SharedExceptionState* shared_exception_state); + static void ClearTimeout(ExecutingContext* context, int32_t timeout_id, SharedExceptionState* shared_exception_state); + static void ClearInterval(ExecutingContext* context, int32_t interval_id, SharedExceptionState* shared_exception_state); double version{1.0}; - PublicContextGetDocument rust_context_get_document_{document}; - PublicContextGetWindow rust_context_get_window_{window}; - PublicContextGetExceptionState rust_context_get_exception_state_{CreateExceptionState}; - PublicFinishRecordingUIOperations finish_recording_ui_operations{FinishRecordingUIOperations}; + PublicContextGetDocument context_get_document{document}; + PublicContextGetWindow context_get_window{window}; + PublicContextGetExceptionState context_get_exception_state{CreateExceptionState}; + PublicFinishRecordingUIOperations context_finish_recording_ui_operations{FinishRecordingUIOperations}; + PublicContextSetTimeout context_set_timeout{SetTimeout}; + PublicContextSetInterval context_set_interval{SetInterval}; + PublicContextClearTimeout context_clear_timeout{ClearTimeout}; + PublicContextClearInterval context_clear_interval{ClearInterval}; }; } // namespace webf diff --git a/bridge/include/plugin_api/webf_event_listener.h b/bridge/include/plugin_api/webf_event_listener.h new file mode 100644 index 0000000000..f4c5faa4b5 --- /dev/null +++ b/bridge/include/plugin_api/webf_event_listener.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#ifndef WEBF_CORE_WEBF_API_PLUGIN_API_WEB_EVENT_LISTENER_H_ +#define WEBF_CORE_WEBF_API_PLUGIN_API_WEB_EVENT_LISTENER_H_ + +#include "foundation/casting.h" +#include "rust_readable.h" +#include "webf_value.h" + +namespace webf { + +class SharedExceptionState; +class Event; +typedef struct EventPublicMethods EventPublicMethods; +typedef struct WebFEventListenerContext WebFEventListenerContext; + +using WebFImplEventCallback = void (*)(WebFEventListenerContext* callback_context, + Event* event, + const EventPublicMethods* event_methods, + WebFValueStatus* status, + SharedExceptionState* shared_exception_state); +using FreePtrFn = void (*)(WebFEventListenerContext* callback_context); + +struct WebFEventListenerContext : public RustReadable { + WebFImplEventCallback callback; + FreePtrFn free_ptr; + void* ptr; +}; + +class WebFPublicPluginEventListener : public EventListener { + public: + WebFPublicPluginEventListener(WebFEventListenerContext* callback_context, + SharedExceptionState* shared_exception_state) + : callback_context_(callback_context), shared_exception_state_(shared_exception_state) {} + + ~WebFPublicPluginEventListener() { + callback_context_->free_ptr(callback_context_); + delete callback_context_; + } + + static const std::shared_ptr Create(WebFEventListenerContext* WebF_event_listener, + SharedExceptionState* shared_exception_state) { + return std::make_shared(WebF_event_listener, shared_exception_state); + }; + + [[nodiscard]] bool IsPublicPluginEventHandler() const override { return true; } + + void Invoke(ExecutingContext* context, Event* event, ExceptionState& exception_state) override { + WebFValueStatus* status_block = event->KeepAlive(); + callback_context_->callback(callback_context_, event, event->eventPublicMethods(), status_block, + shared_exception_state_); + } + + [[nodiscard]] bool Matches(const EventListener& other) const override { + const auto* other_listener = DynamicTo(other); + return other_listener && other_listener->callback_context_ && + other_listener->callback_context_->callback == callback_context_->callback; + } + + void Trace(GCVisitor* visitor) const override {} + + WebFEventListenerContext* callback_context_; + SharedExceptionState* shared_exception_state_; +}; + +template <> +struct DowncastTraits { + static bool AllowFrom(const EventListener& event_listener) { return event_listener.IsPublicPluginEventHandler(); } +}; + +} // namespace webf + +#endif // WEBF_CORE_WEBF_API_PLUGIN_API_WEB_EVENT_LISTENER_H_ diff --git a/bridge/rusty_webf_sys/src/event_target.rs b/bridge/rusty_webf_sys/src/event_target.rs index d5d1ab24d8..2f70de7538 100644 --- a/bridge/rusty_webf_sys/src/event_target.rs +++ b/bridge/rusty_webf_sys/src/event_target.rs @@ -2,42 +2,13 @@ * Copyright (C) 2022-present The WebF authors. All rights reserved. */ -use std::ffi::{c_double, c_void, CString, c_char}; -use crate::add_event_listener_options::AddEventListenerOptions; -use crate::element::{Element, ElementRustMethods}; -use crate::event::{Event, EventRustMethods}; -use crate::exception_state::ExceptionState; -use crate::executing_context::{ExecutingContext, ExecutingContextRustMethods}; -use crate::{executing_context, OpaquePtr, RustValue, RustValueStatus}; -use crate::container_node::{ContainerNode, ContainerNodeRustMethods}; -use crate::document::{Document, DocumentRustMethods}; -use crate::html_element::{HTMLElement, HTMLElementRustMethods}; -use crate::node::{Node, NodeRustMethods}; -use crate::window::{Window, WindowRustMethods}; - -#[repr(C)] -struct EventCallbackContext { - pub callback: extern "C" fn(event_callback_context: *const OpaquePtr, - event: *const OpaquePtr, - event_method_pointer: *const EventRustMethods, - status: *const RustValueStatus, - exception_state: *const OpaquePtr) -> *const c_void, - pub free_ptr: extern "C" fn(event_callback_context_ptr: *const OpaquePtr) -> *const c_void, - pub ptr: *const EventCallbackContextData, -} - -struct EventCallbackContextData { - executing_context_ptr: *const OpaquePtr, - executing_context_method_pointer: *const ExecutingContextRustMethods, - executing_context_status: *const RustValueStatus, - func: EventListenerCallback, -} +use std::ffi::*; +use crate::*; pub trait RustMethods {} - #[repr(C)] -enum EventTargetType { +pub enum EventTargetType { EventTarget = 0, Node = 1, ContainerNode = 2, @@ -86,43 +57,6 @@ pub struct EventTarget { method_pointer: *const EventTargetRustMethods, } -pub type EventListenerCallback = Box; - -// Define the callback function -extern "C" fn handle_event_listener_callback( - event_callback_context_ptr: *const OpaquePtr, - event_ptr: *const OpaquePtr, - event_method_pointer: *const EventRustMethods, - status: *const RustValueStatus, - exception_state: *const OpaquePtr, -) -> *const c_void { - // Reconstruct the Box and drop it to free the memory - let event_callback_context = unsafe { - &(*(event_callback_context_ptr as *mut EventCallbackContext)) - }; - let callback_context_data = unsafe { - &(*(event_callback_context.ptr as *mut EventCallbackContextData)) - }; - - unsafe { - let func = &(*callback_context_data).func; - let callback_data = &(*callback_context_data); - let executing_context = ExecutingContext::initialize(callback_data.executing_context_ptr, callback_data.executing_context_method_pointer, callback_data.executing_context_status); - let event = Event::initialize(event_ptr, &executing_context, event_method_pointer, status); - func(&event); - } - - std::ptr::null() -} - -extern "C" fn handle_callback_data_free(event_callback_context_ptr: *const OpaquePtr) -> *const c_void { - unsafe { - let event_callback_context = &(*(event_callback_context_ptr as *mut EventCallbackContext)); - let _ = Box::from_raw(event_callback_context.ptr as *mut EventCallbackContextData); - } - std::ptr::null() -} - impl EventTarget { fn ptr(&self) -> *const OpaquePtr { self.ptr @@ -147,7 +81,11 @@ impl EventTarget { func: callback, }); let callback_context_data_ptr = Box::into_raw(callback_context_data); - let callback_context = Box::new(EventCallbackContext { callback: handle_event_listener_callback, free_ptr: handle_callback_data_free, ptr: callback_context_data_ptr }); + let callback_context = Box::new(EventCallbackContext { + callback: invoke_event_listener_callback, + free_ptr: release_event_listener_callback, + ptr: callback_context_data_ptr + }); let callback_context_ptr = Box::into_raw(callback_context); let c_event_name = CString::new(event_name).unwrap(); unsafe { @@ -178,7 +116,11 @@ impl EventTarget { func: callback, }); let callback_context_data_ptr = Box::into_raw(callback_context_data); - let callback_context = Box::new(EventCallbackContext { callback: handle_event_listener_callback, free_ptr: handle_callback_data_free, ptr: callback_context_data_ptr }); + let callback_context = Box::new(EventCallbackContext { + callback: invoke_event_listener_callback, + free_ptr: release_event_listener_callback, + ptr: callback_context_data_ptr + }); let callback_context_ptr = Box::into_raw(callback_context); let c_event_name = CString::new(event_name).unwrap(); unsafe { diff --git a/bridge/rusty_webf_sys/src/executing_context.rs b/bridge/rusty_webf_sys/src/executing_context.rs index c9e6d96188..82253e7ca8 100644 --- a/bridge/rusty_webf_sys/src/executing_context.rs +++ b/bridge/rusty_webf_sys/src/executing_context.rs @@ -2,16 +2,8 @@ * Copyright (C) 2022-present The WebF authors. All rights reserved. */ -use std::ffi::{c_char, c_double, c_void}; -use std::ptr; -use libc; -use libc::c_uint; -use crate::document::{Document, DocumentRustMethods}; -use crate::event_target::EventTargetMethods; -use crate::exception_state::{ExceptionState, ExceptionStateRustMethods}; -use crate::{OpaquePtr, RustValue, RustValueStatus}; -use crate::custom_event::{CustomEvent, CustomEventRustMethods}; -use crate::window::{Window, WindowRustMethods}; +use std::ffi::*; +use crate::*; #[repr(C)] pub struct ExecutingContextRustMethods { @@ -20,8 +12,15 @@ pub struct ExecutingContextRustMethods { pub get_window: extern "C" fn(*const OpaquePtr) -> RustValue, pub create_exception_state: extern "C" fn() -> RustValue, pub finish_recording_ui_operations: extern "C" fn(executing_context: *const OpaquePtr) -> c_void, + pub set_timeout: extern "C" fn(*const OpaquePtr, *const WebFNativeFunctionContext, c_int, *const OpaquePtr) -> c_int, + pub set_interval: extern "C" fn(*const OpaquePtr, *const WebFNativeFunctionContext, c_int, *const OpaquePtr) -> c_int, + pub clear_timeout: extern "C" fn(*const OpaquePtr, c_int, *const OpaquePtr), + pub clear_interval: extern "C" fn(*const OpaquePtr, c_int, *const OpaquePtr), } +pub type TimeoutCallback = Box; +pub type IntervalCallback = Box; + /// An environment contains all the necessary running states of a web page. /// /// For Flutter apps, there could be many web pages running in the same Dart environment, @@ -81,6 +80,97 @@ impl ExecutingContext { }; ExceptionState::initialize(result.value, result.method_pointer) } + + pub fn set_timeout_with_callback(&self, callback: TimeoutCallback, exception_state: &ExceptionState) -> Result { + self.set_timeout_with_callback_and_timeout(callback, 0, exception_state) + } + + pub fn set_timeout_with_callback_and_timeout(&self, callback: TimeoutCallback, timeout: i32, exception_state: &ExceptionState) -> Result { + let general_callback: WebFNativeFunction = Box::new(move |argc, argv| { + if argc != 0 { + println!("Invalid argument count for timeout callback"); + return; + } + callback(); + }); + + 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); + + let result = unsafe { + ((*self.method_pointer).set_timeout)(self.ptr, callback_context_ptr, timeout, exception_state.ptr) + }; + + if exception_state.has_exception() { + unsafe { + let _ = Box::from_raw(callback_context_ptr); + let _ = Box::from_raw(callback_context_data_ptr); + } + return Err(exception_state.stringify(self)); + } + + Ok(result) + } + + pub fn set_interval_with_callback(&self, callback: IntervalCallback, exception_state: &ExceptionState) -> Result { + self.set_interval_with_callback_and_timeout(callback, 0, exception_state) + } + + pub fn set_interval_with_callback_and_timeout(&self, callback: IntervalCallback, interval: i32, exception_state: &ExceptionState) -> Result { + let general_callback: WebFNativeFunction = Box::new(move |argc, argv| { + if argc != 0 { + println!("Invalid argument count for interval callback"); + return; + } + callback(); + }); + + 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); + + let result = unsafe { + ((*self.method_pointer).set_interval)(self.ptr, callback_context_ptr, interval, exception_state.ptr) + }; + + if exception_state.has_exception() { + unsafe { + let _ = Box::from_raw(callback_context_ptr); + let _ = Box::from_raw(callback_context_data_ptr); + } + return Err(exception_state.stringify(self)); + } + + Ok(result) + } + + pub fn clear_timeout(&self, timeout_id: i32, exception_state: &ExceptionState) { + unsafe { + ((*self.method_pointer).clear_timeout)(self.ptr, timeout_id, exception_state.ptr) + } + } + + pub fn clear_interval(&self, interval_id: i32, exception_state: &ExceptionState) { + unsafe { + ((*self.method_pointer).clear_interval)(self.ptr, interval_id, exception_state.ptr) + } + } + } impl Drop for ExecutingContext { diff --git a/bridge/rusty_webf_sys/src/lib.rs b/bridge/rusty_webf_sys/src/lib.rs index 826698fea0..ca4ac50867 100644 --- a/bridge/rusty_webf_sys/src/lib.rs +++ b/bridge/rusty_webf_sys/src/lib.rs @@ -49,6 +49,8 @@ pub mod scroll_to_options; pub mod touch_init; pub mod transition_event_init; pub mod ui_event_init; +pub mod webf_event_listener; +pub mod webf_function; mod memory_utils; pub use executing_context::*; @@ -72,6 +74,7 @@ pub use transition_event::*; pub use ui_event::*; pub use container_node::*; pub use exception_state::*; +pub use executing_context::*; pub use text::*; pub use comment::*; pub use character_data::*; @@ -96,6 +99,8 @@ pub use scroll_to_options::*; pub use touch_init::*; pub use transition_event_init::*; pub use ui_event_init::*; +pub use webf_event_listener::*; +pub use webf_function::*; #[repr(C)] pub struct OpaquePtr; diff --git a/bridge/rusty_webf_sys/src/webf_event_listener.rs b/bridge/rusty_webf_sys/src/webf_event_listener.rs new file mode 100644 index 0000000000..ca27aca677 --- /dev/null +++ b/bridge/rusty_webf_sys/src/webf_event_listener.rs @@ -0,0 +1,66 @@ +/* +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ +use std::ffi::*; +use crate::*; + +pub type EventListenerCallback = Box; + +pub struct EventCallbackContextData { + pub executing_context_ptr: *const OpaquePtr, + pub executing_context_method_pointer: *const ExecutingContextRustMethods, + pub executing_context_status: *const RustValueStatus, + pub func: EventListenerCallback, +} + +impl Drop for EventCallbackContextData { + fn drop(&mut self) { + println!("Drop webf event callback context data"); + } +} + +#[repr(C)] +pub struct EventCallbackContext { + pub callback: extern "C" fn(event_callback_context: *const OpaquePtr, + event: *const OpaquePtr, + event_method_pointer: *const EventRustMethods, + status: *const RustValueStatus, + exception_state: *const OpaquePtr) -> *const c_void, + pub free_ptr: extern "C" fn(event_callback_context_ptr: *const OpaquePtr) -> *const c_void, + pub ptr: *const EventCallbackContextData, +} + +// Define the callback function +pub extern "C" fn invoke_event_listener_callback( + event_callback_context_ptr: *const OpaquePtr, + event_ptr: *const OpaquePtr, + event_method_pointer: *const EventRustMethods, + status: *const RustValueStatus, + exception_state: *const OpaquePtr, +) -> *const c_void { + // Reconstruct the Box and drop it to free the memory + let event_callback_context = unsafe { + &(*(event_callback_context_ptr as *mut EventCallbackContext)) + }; + let callback_context_data = unsafe { + &(*(event_callback_context.ptr as *mut EventCallbackContextData)) + }; + + unsafe { + let func = &(*callback_context_data).func; + let callback_data = &(*callback_context_data); + let executing_context = ExecutingContext::initialize(callback_data.executing_context_ptr, callback_data.executing_context_method_pointer, callback_data.executing_context_status); + let event = Event::initialize(event_ptr, &executing_context, event_method_pointer, status); + func(&event); + } + + std::ptr::null() +} + +pub extern "C" fn release_event_listener_callback(event_callback_context_ptr: *const OpaquePtr) -> *const c_void { + unsafe { + let event_callback_context = &(*(event_callback_context_ptr as *mut EventCallbackContext)); + let _ = Box::from_raw(event_callback_context.ptr as *mut EventCallbackContextData); + } + std::ptr::null() +} diff --git a/bridge/rusty_webf_sys/src/webf_function.rs b/bridge/rusty_webf_sys/src/webf_function.rs new file mode 100644 index 0000000000..be8ea330aa --- /dev/null +++ b/bridge/rusty_webf_sys/src/webf_function.rs @@ -0,0 +1,56 @@ +/* +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ +use std::ffi::*; +use crate::*; + +pub type WebFNativeFunction = Box; + +pub struct WebFNativeFunctionContextData { + pub func: WebFNativeFunction, +} + +impl Drop for WebFNativeFunctionContextData { + fn drop(&mut self) { + println!("Drop webf native function context data"); + } +} + +#[repr(C)] +pub struct WebFNativeFunctionContext { + pub callback: extern "C" fn(callback_context: *const OpaquePtr, + argc: c_int, + argv: *const OpaquePtr, + exception_state: *const OpaquePtr) -> *const c_void, + pub free_ptr: extern "C" fn(callback_context: *const OpaquePtr) -> *const c_void, + pub ptr: *const WebFNativeFunctionContextData, +} + +pub extern "C" fn invoke_webf_native_function( + callback_context_ptr: *const OpaquePtr, + argc: c_int, + argv: *const OpaquePtr, + exception_state: *const OpaquePtr, +) -> *const c_void { + let callback_context = unsafe { + &(*(callback_context_ptr as *mut WebFNativeFunctionContext)) + }; + let callback_context_data = unsafe { + &(*(callback_context.ptr as *mut WebFNativeFunctionContextData)) + }; + + unsafe { + let func = &(*callback_context_data).func; + func(argc, argv); + } + + std::ptr::null() +} + +pub extern "C" fn release_webf_native_function(callback_context_ptr: *const OpaquePtr) -> *const c_void { + unsafe { + let callback_context = &(*(callback_context_ptr as *mut WebFNativeFunctionContext)); + let _ = Box::from_raw(callback_context.ptr as *mut WebFNativeFunctionContextData); + } + std::ptr::null() +} diff --git a/webf/example/rust_builder/rust/src/lib.rs b/webf/example/rust_builder/rust/src/lib.rs index f2063ac115..5fd63f752d 100644 --- a/webf/example/rust_builder/rust/src/lib.rs +++ b/webf/example/rust_builder/rust/src/lib.rs @@ -12,6 +12,12 @@ pub extern "C" fn init_webf_app(handle: RustValue) let exception_state = context.create_exception_state(); let document = context.document(); + let timer_callback = Box::new(move || { + println!("Timer Callback"); + }); + + context.set_interval_with_callback_and_timeout(timer_callback, 1000, &exception_state).unwrap(); + let click_event = document.create_event("custom_click", &exception_state).unwrap(); document.dispatch_event(&click_event, &exception_state); @@ -93,6 +99,5 @@ pub extern "C" fn init_webf_app(handle: RustValue) event_cleaner_element.add_event_listener("click", event_cleaner_handler, &event_listener_options, &exception_state).unwrap(); document.body().append_child(&event_cleaner_element.as_node(), &exception_state).unwrap(); - std::ptr::null_mut() }