Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
_build:
publishResources: false
render: never
list: never

name: "Spec compliant `on<type>` event handlers"
sort_date: "2026-08-14"
enable_flag: "spec_compliant_event_handler_attributes"
disable_flag: "no_spec_compliant_event_handler_attributes"
---

`on<type>` event handler properties, such as `WebSocket.onmessage` and `AbortSignal.onabort`, behave the way the [DOM](https://dom.spec.whatwg.org/#interface-eventtarget) and [HTML](https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-attributes) standards describe them.

Previously, `EventTarget` looked for an `on<type>` property on the object every time an event was dispatched and called it before any listener added with `addEventListener()`. That had two consequences: an `on<type>` handler always ran first rather than in the order it was assigned, and a class that extends `EventTarget` and implements an `on<type>` accessor using `addEventListener()` had its handler called twice per event.

With this flag enabled, `EventTarget` no longer looks for `on<type>` properties. The interfaces that the standards give event handler attributes to (`AbortSignal`, `EventSource`, `MessagePort` and `WebSocket`) implement them as accessors that register an ordinary listener, so:

- The handler runs in the order it was assigned, in sequence with listeners added using `addEventListener()`.
- Assigning a different handler replaces the value without changing its position in the listener list.
- Assigning `null` removes it. Assigning any other non-object is treated as `null`. A non-callable object is stored and returned by the property, but never called.
- `this` inside the handler is the object the handler was assigned to.
- Returning `false` from the handler cancels the event, per the [event handler processing algorithm](https://html.spec.whatwg.org/multipage/webappapis.html#the-event-handler-processing-algorithm). Any other return value is ignored. Without this flag, returning `true` cancels the event and `false` is ignored. That is not standard behavior. The DOM standard ignores the return value of a listener added with `addEventListener()` entirely, and the Workers runtime applies its own `true` cancels rule to those listeners as well. This flag changes only `on<type>` handlers, so listeners added with `addEventListener()` keep the existing behavior.

```js
const controller = new AbortController();
controller.signal.addEventListener("abort", () => console.log("a"));
controller.signal.onabort = () => console.log("b");
controller.signal.addEventListener("abort", () => console.log("c"));
controller.abort();
// Logs "a", "b", "c". Without this flag, it logs "b", "a", "c".
```

Assigning an `on<type>` property for an event type that the interface does not define a handler for has no effect. `EventSource`, for example, defines only `onopen`, `onmessage` and `onerror`, so a named server-sent event has to be observed with `addEventListener()`:

```js
// Has no effect.
eventSource.ontest = (event) => console.log(event.data);

// Use this instead.
eventSource.addEventListener("test", (event) => console.log(event.data));
```

Handlers on the global scope are not affected, so `onfetch`, `onscheduled` and the other global handlers in Service Worker syntax continue to work as before.

Once this flag becomes the default for a compatibility date, add the `no_spec_compliant_event_handler_attributes` compatibility flag to keep the previous behavior.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
_build:
publishResources: false
render: never
list: never

name: "Spec compliant `MessageEvent.origin`"
sort_date: "2026-08-14"
enable_flag: "spec_compliant_message_event_origin"
disable_flag: "no_spec_compliant_message_event_origin"
---

`MessageEvent.origin` reports what the standards say it should. Two things change.

**An absent origin reports an empty string instead of `null`.** A message event's origin is internally nullable, and [the standard's getter](https://html.spec.whatwg.org/multipage/comms.html#dom-messageevent-origin) reports the empty string for the null case. The Workers runtime reported `null` instead. This is what a message delivered through a `MessagePort`, or from a `WebSocketPair` endpoint, now reports, since neither has a URL to take an origin from.

```js
const { port1, port2 } = new MessageChannel();
port2.onmessage = (event) => console.log(event.origin === "");
port1.postMessage("hello");
// Logs true. Without this flag, event.origin is null.
```

**A `WebSocket` opened from a URL reports the origin of that URL.** The [WebSocket standard](https://websockets.spec.whatwg.org/#feedback-from-the-protocol) requires the message event's origin to be the serialized origin of the WebSocket's URL, which the Workers runtime did not do. `EventSource` already reported the origin of its event stream.

```js
const ws = new WebSocket("wss://example.com/chat");
ws.addEventListener("message", (event) => console.log(event.origin));
// Logs "wss://example.com". Without this flag, event.origin is null.
```

Code that compares `origin` against `null` needs updating either way. For the absent case, note that `""` and `null` are both falsy, so a check like `if (!event.origin)` works before and after.

Once this flag becomes the default for a compatibility date, add the `no_spec_compliant_message_event_origin` compatibility flag to keep the previous behavior.