Skip to content

Commit

Permalink
feat: add Event.preventDefault support.
Browse files Browse the repository at this point in the history
  • Loading branch information
andycall committed Feb 10, 2025
1 parent e0f595f commit 6687142
Show file tree
Hide file tree
Showing 17 changed files with 85 additions and 17 deletions.
3 changes: 2 additions & 1 deletion bridge/core/dom/events/event_target.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ namespace webf {
struct EventDispatchResult : public DartReadable {
bool canceled{false};
bool propagationStopped{false};
bool preventDefaulted{false};
};

struct DartEventListenerOptions : public DartReadable {
Expand Down Expand Up @@ -443,7 +444,7 @@ NativeValue EventTarget::HandleDispatchEventFromDart(int32_t argc, const NativeV
GetExecutingContext()->dartIsolateContext()->profiler()->FinishTrackSteps();

auto* result = new EventDispatchResult{.canceled = dispatch_result == DispatchEventResult::kCanceledByEventHandler,
.propagationStopped = event->propagationStopped()};
.propagationStopped = event->propagationStopped(), .preventDefaulted = event->defaultPrevented()};
return NativeValueConverter<NativeTypePointer<EventDispatchResult>>::ToNativeValue(result);
}

Expand Down
4 changes: 2 additions & 2 deletions bridge/core/events/mouse_event.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ MouseEvent::MouseEvent(ExecutingContext* context,
MouseEvent::MouseEvent(ExecutingContext* context, const AtomicString& type, NativeMouseEvent* native_mouse_event)
: UIEvent(context, type, &native_mouse_event->native_event),
// alt_key_(native_mouse_event->altKey),
// button_(native_mouse_event->button),
// buttons_(native_mouse_event->buttons),
button_(native_mouse_event->button),
// buttons_(native_mouse_event->buttons),
client_x_(native_mouse_event->clientX),
client_y_(native_mouse_event->clientY),
// ctrl_key_(native_mouse_event->ctrlKey),
Expand Down
2 changes: 1 addition & 1 deletion bridge/core/events/mouse_event.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {MouseEventInit} from "./mouse_event_init";
/** Events that occur due to the user interacting with a pointing device (such as a mouse). Common events using this interface include click, dblclick, mouseup, mousedown. */
interface MouseEvent extends UIEvent {
// readonly altKey: boolean;
// readonly button: number;
readonly button: number;
// readonly buttons: number;
readonly clientX: number;
readonly clientY: number;
Expand Down
4 changes: 2 additions & 2 deletions bridge/core/events/mouse_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ class MouseEvent : public UIEvent {

private:
bool alt_key_;
double button_;
double buttons_;
double button_{0};
double buttons_{0};
double client_x_;
double client_y_;
bool ctrl_key_;
Expand Down
3 changes: 2 additions & 1 deletion bridge/core/html/html_anchor_element.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Element} from "../dom/element";
import {HTMLElement} from "./html_element";

interface HTMLAnchorElement extends Element {
interface HTMLAnchorElement extends HTMLElement {
target: DartImpl<string>;
accessKey: DartImpl<string>;
download: DartImpl<string>;
Expand Down
3 changes: 3 additions & 0 deletions bridge/include/plugin_api/mouse_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,20 @@ class SharedExceptionState;
class ExecutingContext;
class MouseEvent;
typedef struct ScriptValueRef ScriptValueRef;
using PublicMouseEventGetButton = double (*)(MouseEvent*);
using PublicMouseEventGetClientX = double (*)(MouseEvent*);
using PublicMouseEventGetClientY = double (*)(MouseEvent*);
using PublicMouseEventGetOffsetX = double (*)(MouseEvent*);
using PublicMouseEventGetOffsetY = double (*)(MouseEvent*);
struct MouseEventPublicMethods : public WebFPublicMethods {
static double Button(MouseEvent* mouse_event);
static double ClientX(MouseEvent* mouse_event);
static double ClientY(MouseEvent* mouse_event);
static double OffsetX(MouseEvent* mouse_event);
static double OffsetY(MouseEvent* mouse_event);
double version{1.0};
UIEventPublicMethods ui_event;
PublicMouseEventGetButton mouse_event_get_button{Button};
PublicMouseEventGetClientX mouse_event_get_client_x{ClientX};
PublicMouseEventGetClientY mouse_event_get_client_y{ClientY};
PublicMouseEventGetOffsetX mouse_event_get_offset_x{OffsetX};
Expand Down
11 changes: 11 additions & 0 deletions bridge/rusty_webf_sys/src/events/mouse_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::*;
pub struct MouseEventRustMethods {
pub version: c_double,
pub ui_event: UIEventRustMethods,
pub button: extern "C" fn(ptr: *const OpaquePtr) -> c_double,
pub client_x: extern "C" fn(ptr: *const OpaquePtr) -> c_double,
pub client_y: extern "C" fn(ptr: *const OpaquePtr) -> c_double,
pub offset_x: extern "C" fn(ptr: *const OpaquePtr) -> c_double,
Expand Down Expand Up @@ -38,6 +39,12 @@ impl MouseEvent {
pub fn context<'a>(&self) -> &'a ExecutingContext {
self.ui_event.context()
}
pub fn button(&self) -> f64 {
let value = unsafe {
((*self.method_pointer).button)(self.ptr())
};
value
}
pub fn client_x(&self) -> f64 {
let value = unsafe {
((*self.method_pointer).client_x)(self.ptr())
Expand All @@ -64,13 +71,17 @@ impl MouseEvent {
}
}
pub trait MouseEventMethods: UIEventMethods {
fn button(&self) -> f64;
fn client_x(&self) -> f64;
fn client_y(&self) -> f64;
fn offset_x(&self) -> f64;
fn offset_y(&self) -> f64;
fn as_mouse_event(&self) -> &MouseEvent;
}
impl MouseEventMethods for MouseEvent {
fn button(&self) -> f64 {
self.button()
}
fn client_x(&self) -> f64 {
self.client_x()
}
Expand Down
3 changes: 3 additions & 0 deletions bridge/rusty_webf_sys/src/events/pointer_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ impl PointerEventMethods for PointerEvent {
}
}
impl MouseEventMethods for PointerEvent {
fn button(&self) -> f64 {
self.mouse_event.button()
}
fn client_x(&self) -> f64 {
self.mouse_event.client_x()
}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions integration_tests/specs/dom/events/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -786,4 +786,42 @@ describe('Event', () => {
el.style.width = '102px';
el2.style.width = '102px';
});

it('should works with preventDefault in `<a /> element', async (done) => {
const anchorElement = createElement('a', {}, [createText('')]);
BODY.append(anchorElement);

anchorElement.addEventListener('click', async (e) => {
e.preventDefault();

BODY.append(createText('Nothing happened'));

await snapshot();
done();
});

anchorElement.click();
});

it('should satisfy react-router event check', (done) => {
function isModifiedEvent(event: MouseEvent) {
return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
}
function shouldProcessLinkClick(event: MouseEvent) {
return event.button === 0 &&
// Let browser handle "target=_blank" etc.
!isModifiedEvent(event) // Ignore clicks with modifier keys
;
}

const anchorElement = createElement('a', {}, []);
BODY.append(anchorElement);

anchorElement.addEventListener('click', async (e) => {
expect(shouldProcessLinkClick(e));
done();
});

anchorElement.click();
});
});
5 changes: 3 additions & 2 deletions webf/lib/src/bridge/binding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ void _handleDispatchResult(Object contextHandle, Pointer<NativeValue> returnValu
Event event = context.event;
event.cancelable = dispatchResult.ref.canceled;
event.propagationStopped = dispatchResult.ref.propagationStopped;
event.defaultPrevented = dispatchResult.ref.preventDefaulted;
event.sharedJSProps = Pointer.fromAddress(context.rawEvent.ref.bytes.elementAt(8).value);
event.propLen = context.rawEvent.ref.bytes.elementAt(9).value;
event.allocateLen = context.rawEvent.ref.bytes.elementAt(10).value;
Expand Down Expand Up @@ -103,9 +104,9 @@ class _DispatchEventResultContext {
Future<void> _dispatchEventToNative(Event event, bool isCapture) async {
Pointer<NativeBindingObject>? pointer = event.currentTarget?.pointer;
double? contextId = event.target?.contextId;
WebFController controller = WebFController.getControllerOfJSContextId(contextId)!;
WebFController? controller = WebFController.getControllerOfJSContextId(contextId);

if (controller.view.disposed) return;
if (controller == null || controller.view.disposed) return;

if (contextId != null &&
pointer != null &&
Expand Down
3 changes: 3 additions & 0 deletions webf/lib/src/bridge/native_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ class EventDispatchResult extends Struct {

@Bool()
external bool propagationStopped;

@Bool()
external bool preventDefaulted;
}

class AddEventListenerOptions extends Struct {
Expand Down
4 changes: 3 additions & 1 deletion webf/lib/src/dom/event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ class FocusEvent extends UIEvent {

/// reference: https://developer.mozilla.org/zh-CN/docs/Web/API/MouseEvent
class MouseEvent extends UIEvent {
double button = 0;
double clientX;
double clientY;
double offsetX;
Expand All @@ -403,10 +404,11 @@ class MouseEvent extends UIEvent {
@override
Pointer toRaw([int extraLength = 0, bool isCustomEvent = false]) {
List<int> methods = [
doubleToUint64(button),
doubleToUint64(clientX),
doubleToUint64(clientY),
doubleToUint64(offsetX),
doubleToUint64(offsetY)
doubleToUint64(offsetY),
];

Pointer<RawEvent> rawEvent = super.toRaw(methods.length + extraLength).cast<RawEvent>();
Expand Down
8 changes: 6 additions & 2 deletions webf/lib/src/dom/event_target.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ abstract class EventTarget extends DynamicBindingObject {
// To avoid concurrent exception while prev handler modify the original handler list, causing list iteration
// with error, copy the handlers here.
try {
for (EventHandler handler in [...existHandler]) {
List<EventHandler> handlers = [...existHandler];
for (int i = handlers.length - 1; i >= 0; i --) {
final handler = handlers[i];
await handler(event);
}
} catch (e, stack) {
Expand All @@ -105,7 +107,9 @@ abstract class EventTarget extends DynamicBindingObject {
// To avoid concurrent exception while prev handler modify the original handler list, causing list iteration
// with error, copy the handlers here.
try {
for (EventHandler handler in [...existHandler]) {
List<EventHandler> handlers = [...existHandler];
for (int i = handlers.length - 1; i >= 0; i --) {
final handler = handlers[i];
await handler(event);
}
} catch (e, stack) {
Expand Down
4 changes: 2 additions & 2 deletions webf/lib/src/dom/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class Window extends EventTarget {
}

@override
void addEventListener(String eventType, EventHandler handler, {EventListenerOptions? addEventListenerOptions}) {
void addEventListener(String eventType, EventHandler handler, {EventListenerOptions? addEventListenerOptions, bool builtInCallback = false}) {
super.addEventListener(eventType, handler, addEventListenerOptions: addEventListenerOptions);
switch (eventType) {
case EVENT_SCROLL:
Expand All @@ -143,7 +143,7 @@ class Window extends EventTarget {
}

@override
void removeEventListener(String eventType, EventHandler handler, {bool isCapture = false}) {
void removeEventListener(String eventType, EventHandler handler, {bool isCapture = false, bool builtInCallback = false}) {
super.removeEventListener(eventType, handler, isCapture: isCapture);
switch (eventType) {
case EVENT_SCROLL:
Expand Down
5 changes: 3 additions & 2 deletions webf/lib/src/html/a.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class HTMLAnchorElement extends Element {
}

Future<void> _handleClick(Event event) async {
if (event.defaultPrevented) return;
String? href = attributes['href'];
if (href != null && href.isNotEmpty) {
String baseUrl = ownerDocument.controller.url;
Expand All @@ -23,9 +24,9 @@ class HTMLAnchorElement extends Element {
if (href.trim().startsWith('#')) {
HistoryModule historyModule = ownerDocument.controller.module.moduleManager.getModule('History')!;
historyModule.pushState(null, url: href);
ownerView.window.dispatchEvent(HashChangeEvent(newUrl: resolvedUri.toString(), oldUrl: baseUrl));
await ownerView.window.dispatchEvent(HashChangeEvent(newUrl: resolvedUri.toString(), oldUrl: baseUrl));
} else {
ownerDocument.controller.view
await ownerDocument.controller.view
.handleNavigationAction(baseUrl, resolvedUri.toString(), _getNavigationType(resolvedUri.scheme));
}
}
Expand Down
2 changes: 1 addition & 1 deletion webf/lib/src/launcher/controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ class WebFViewController implements WidgetsBindingObserver {
String oldUrl = rootController.url;
HistoryModule historyModule = rootController.module.moduleManager.getModule('History')!;
historyModule.pushState(null, url: targetPath);
window.dispatchEvent(HashChangeEvent(newUrl: targetPath, oldUrl: oldUrl));
await window.dispatchEvent(HashChangeEvent(newUrl: targetPath, oldUrl: oldUrl));
return;
}

Expand Down

0 comments on commit 6687142

Please sign in to comment.