mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-16 08:18:59 +00:00
Window.scrollX/Y, postMessage, more custom element edge cases
This commit is contained in:
@@ -550,6 +550,7 @@ pub const JsApis = flattenTypes(&.{
|
|||||||
@import("../webapi/Event.zig"),
|
@import("../webapi/Event.zig"),
|
||||||
@import("../webapi/event/CustomEvent.zig"),
|
@import("../webapi/event/CustomEvent.zig"),
|
||||||
@import("../webapi/event/ErrorEvent.zig"),
|
@import("../webapi/event/ErrorEvent.zig"),
|
||||||
|
@import("../webapi/event/MessageEvent.zig"),
|
||||||
@import("../webapi/event/ProgressEvent.zig"),
|
@import("../webapi/event/ProgressEvent.zig"),
|
||||||
@import("../webapi/EventTarget.zig"),
|
@import("../webapi/EventTarget.zig"),
|
||||||
@import("../webapi/Location.zig"),
|
@import("../webapi/Location.zig"),
|
||||||
|
|||||||
@@ -122,6 +122,10 @@
|
|||||||
testing.expectEqual(0, callbackCalls.length);
|
testing.expectEqual(0, callbackCalls.length);
|
||||||
|
|
||||||
customElements.define('upgrade-attr-element', UpgradeAttrElement);
|
customElements.define('upgrade-attr-element', UpgradeAttrElement);
|
||||||
|
testing.expectEqual(0, callbackCalls.length);
|
||||||
|
|
||||||
|
document.body.appendChild(el);
|
||||||
|
|
||||||
testing.expectEqual(1, callbackCalls.length);
|
testing.expectEqual(1, callbackCalls.length);
|
||||||
testing.expectEqual('existing', callbackCalls[0].name);
|
testing.expectEqual('existing', callbackCalls[0].name);
|
||||||
testing.expectEqual(null, callbackCalls[0].oldValue);
|
testing.expectEqual(null, callbackCalls[0].oldValue);
|
||||||
|
|||||||
@@ -181,4 +181,175 @@
|
|||||||
testing.expectEqual(null, attributeChangedCalls[1].oldValue);
|
testing.expectEqual(null, attributeChangedCalls[1].oldValue);
|
||||||
testing.expectEqual('world', attributeChangedCalls[1].newValue);
|
testing.expectEqual('world', attributeChangedCalls[1].newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let attributeChangedCalls = [];
|
||||||
|
let connectedCalls = 0;
|
||||||
|
|
||||||
|
class DetachedWithAttrs extends HTMLElement {
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ['foo'];
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
attributeChangedCalls.push({ name, oldValue, newValue });
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
connectedCalls++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.innerHTML = '<detached-with-attrs foo="bar"></detached-with-attrs>';
|
||||||
|
|
||||||
|
testing.expectEqual(0, attributeChangedCalls.length);
|
||||||
|
|
||||||
|
customElements.define('detached-with-attrs', DetachedWithAttrs);
|
||||||
|
|
||||||
|
testing.expectEqual(0, attributeChangedCalls.length);
|
||||||
|
testing.expectEqual(0, connectedCalls);
|
||||||
|
|
||||||
|
document.body.appendChild(container);
|
||||||
|
|
||||||
|
testing.expectEqual(1, attributeChangedCalls.length);
|
||||||
|
testing.expectEqual('foo', attributeChangedCalls[0].name);
|
||||||
|
testing.expectEqual(null, attributeChangedCalls[0].oldValue);
|
||||||
|
testing.expectEqual('bar', attributeChangedCalls[0].newValue);
|
||||||
|
testing.expectEqual(1, connectedCalls);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let attributeChangedCalls = [];
|
||||||
|
let constructorCalled = 0;
|
||||||
|
|
||||||
|
class ManualUpgradeWithAttrs extends HTMLElement {
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ['x', 'y'];
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
constructorCalled++;
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
attributeChangedCalls.push({ name, oldValue, newValue });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('manual-upgrade-with-attrs', ManualUpgradeWithAttrs);
|
||||||
|
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.innerHTML = '<manual-upgrade-with-attrs x="1" y="2"></manual-upgrade-with-attrs>';
|
||||||
|
|
||||||
|
testing.expectEqual(1, constructorCalled);
|
||||||
|
testing.expectEqual(2, attributeChangedCalls.length);
|
||||||
|
|
||||||
|
const elem = container.querySelector('manual-upgrade-with-attrs');
|
||||||
|
elem.setAttribute('z', '3');
|
||||||
|
|
||||||
|
customElements.upgrade(container);
|
||||||
|
|
||||||
|
testing.expectEqual(1, constructorCalled);
|
||||||
|
testing.expectEqual(2, attributeChangedCalls.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let attributeChangedCalls = [];
|
||||||
|
|
||||||
|
class MixedAttrs extends HTMLElement {
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ['watched'];
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
attributeChangedCalls.push({ name, oldValue, newValue });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.innerHTML = '<mixed-attrs watched="yes" ignored="no" also-ignored="maybe"></mixed-attrs>';
|
||||||
|
document.body.appendChild(container);
|
||||||
|
|
||||||
|
testing.expectEqual(0, attributeChangedCalls.length);
|
||||||
|
|
||||||
|
customElements.define('mixed-attrs', MixedAttrs);
|
||||||
|
|
||||||
|
testing.expectEqual(1, attributeChangedCalls.length);
|
||||||
|
testing.expectEqual('watched', attributeChangedCalls[0].name);
|
||||||
|
testing.expectEqual('yes', attributeChangedCalls[0].newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let attributeChangedCalls = [];
|
||||||
|
|
||||||
|
class EmptyAttr extends HTMLElement {
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ['empty', 'non-empty'];
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
attributeChangedCalls.push({ name, oldValue, newValue });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.innerHTML = '<empty-attr empty="" non-empty="value"></empty-attr>';
|
||||||
|
document.body.appendChild(container);
|
||||||
|
|
||||||
|
customElements.define('empty-attr', EmptyAttr);
|
||||||
|
|
||||||
|
testing.expectEqual(2, attributeChangedCalls.length);
|
||||||
|
testing.expectEqual('empty', attributeChangedCalls[0].name);
|
||||||
|
testing.expectEqual('', attributeChangedCalls[0].newValue);
|
||||||
|
testing.expectEqual('non-empty', attributeChangedCalls[1].name);
|
||||||
|
testing.expectEqual('value', attributeChangedCalls[1].newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let parentCalls = [];
|
||||||
|
let childCalls = [];
|
||||||
|
|
||||||
|
class NestedParent extends HTMLElement {
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ['parent-attr'];
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
parentCalls.push({ name, oldValue, newValue });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NestedChild extends HTMLElement {
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ['child-attr'];
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
childCalls.push({ name, oldValue, newValue });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.innerHTML = '<nested-parent parent-attr="p"><nested-child child-attr="c"></nested-child></nested-parent>';
|
||||||
|
document.body.appendChild(container);
|
||||||
|
|
||||||
|
testing.expectEqual(0, parentCalls.length);
|
||||||
|
testing.expectEqual(0, childCalls.length);
|
||||||
|
|
||||||
|
customElements.define('nested-parent', NestedParent);
|
||||||
|
|
||||||
|
testing.expectEqual(1, parentCalls.length);
|
||||||
|
testing.expectEqual('parent-attr', parentCalls[0].name);
|
||||||
|
testing.expectEqual('p', parentCalls[0].newValue);
|
||||||
|
testing.expectEqual(0, childCalls.length);
|
||||||
|
|
||||||
|
customElements.define('nested-child', NestedChild);
|
||||||
|
|
||||||
|
testing.expectEqual(1, parentCalls.length);
|
||||||
|
testing.expectEqual(1, childCalls.length);
|
||||||
|
testing.expectEqual('child-attr', childCalls[0].name);
|
||||||
|
testing.expectEqual('c', childCalls[0].newValue);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
170
src/browser/tests/event/message.html
Normal file
170
src/browser/tests/event/message.html
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<script src="../testing.js"></script>
|
||||||
|
|
||||||
|
<script id=messageEventConstructor>
|
||||||
|
{
|
||||||
|
const evt = new MessageEvent('message', {
|
||||||
|
data: { foo: 'bar', count: 42 },
|
||||||
|
origin: 'https://example.com',
|
||||||
|
source: window
|
||||||
|
});
|
||||||
|
|
||||||
|
testing.expectEqual('message', evt.type);
|
||||||
|
testing.expectEqual({ foo: 'bar', count: 42 }, evt.data);
|
||||||
|
testing.expectEqual('https://example.com', evt.origin);
|
||||||
|
testing.expectEqual(window, evt.source);
|
||||||
|
testing.expectEqual(false, evt.bubbles);
|
||||||
|
testing.expectEqual(false, evt.cancelable);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=messageEventWithString>
|
||||||
|
{
|
||||||
|
const evt2 = new MessageEvent('message', {
|
||||||
|
data: 'Hello, World!',
|
||||||
|
origin: 'https://test.com'
|
||||||
|
});
|
||||||
|
|
||||||
|
testing.expectEqual('Hello, World!', evt2.data);
|
||||||
|
testing.expectEqual('https://test.com', evt2.origin);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=messageEventDefaults>
|
||||||
|
{
|
||||||
|
const evt3 = new MessageEvent('message');
|
||||||
|
|
||||||
|
testing.expectEqual('', evt3.origin);
|
||||||
|
testing.expectEqual(false, evt3.bubbles);
|
||||||
|
testing.expectEqual(false, evt3.cancelable);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=messageEventInheritance>
|
||||||
|
{
|
||||||
|
const evt4 = new MessageEvent('custom');
|
||||||
|
|
||||||
|
testing.expectEqual('custom', evt4.type);
|
||||||
|
testing.expectEqual(Event.NONE, evt4.eventPhase);
|
||||||
|
testing.expectEqual(false, evt4.defaultPrevented);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=postMessageBasic>
|
||||||
|
{
|
||||||
|
let receivedEvent = null;
|
||||||
|
|
||||||
|
const handler = (e) => {
|
||||||
|
receivedEvent = e;
|
||||||
|
};
|
||||||
|
window.addEventListener('message', handler, { once: true });
|
||||||
|
|
||||||
|
window.postMessage('test data', '*');
|
||||||
|
|
||||||
|
testing.eventually(() => {
|
||||||
|
testing.expectEqual('test data', receivedEvent.data);
|
||||||
|
testing.expectEqual(window, receivedEvent.source);
|
||||||
|
testing.expectEqual('message', receivedEvent.type);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=postMessageWithObject>
|
||||||
|
{
|
||||||
|
let receivedData = null;
|
||||||
|
|
||||||
|
const handler = (e) => {
|
||||||
|
receivedData = e.data;
|
||||||
|
};
|
||||||
|
window.addEventListener('message', handler, { once: true });
|
||||||
|
|
||||||
|
const testObj = { type: 'test', value: 123, nested: { key: 'value' } };
|
||||||
|
window.postMessage(testObj, '*');
|
||||||
|
|
||||||
|
testing.eventually(() => {
|
||||||
|
testing.expectEqual(testObj, receivedData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=messageEventWithBubbles>
|
||||||
|
{
|
||||||
|
const evt = new MessageEvent('message', {
|
||||||
|
data: 'test',
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
testing.expectEqual(true, evt.bubbles);
|
||||||
|
testing.expectEqual(true, evt.cancelable);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=postMessageWithNumber>
|
||||||
|
{
|
||||||
|
let received = null;
|
||||||
|
|
||||||
|
const handler = (e) => {
|
||||||
|
received = e.data;
|
||||||
|
};
|
||||||
|
window.addEventListener('message', handler, { once: true });
|
||||||
|
|
||||||
|
window.postMessage(42, '*');
|
||||||
|
|
||||||
|
testing.eventually(() => {
|
||||||
|
testing.expectEqual(42, received);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=postMessageWithArray>
|
||||||
|
{
|
||||||
|
let received = null;
|
||||||
|
|
||||||
|
const handler = (e) => {
|
||||||
|
received = e.data;
|
||||||
|
};
|
||||||
|
window.addEventListener('message', handler, { once: true });
|
||||||
|
|
||||||
|
const arr = [1, 2, 3, 'test'];
|
||||||
|
window.postMessage(arr, '*');
|
||||||
|
|
||||||
|
testing.eventually(() => {
|
||||||
|
testing.expectEqual(arr, received);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=postMessageWithNull>
|
||||||
|
{
|
||||||
|
let received = undefined;
|
||||||
|
|
||||||
|
const handler = (e) => {
|
||||||
|
received = e.data;
|
||||||
|
};
|
||||||
|
window.addEventListener('message', handler, { once: true });
|
||||||
|
|
||||||
|
window.postMessage(null, '*');
|
||||||
|
|
||||||
|
testing.eventually(() => {
|
||||||
|
testing.expectEqual(null, received);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=messageEventOriginFromLocation>
|
||||||
|
{
|
||||||
|
let receivedOrigin = null;
|
||||||
|
|
||||||
|
const handler = (e) => {
|
||||||
|
receivedOrigin = e.origin;
|
||||||
|
};
|
||||||
|
window.addEventListener('message', handler, { once: true });
|
||||||
|
|
||||||
|
window.postMessage('test', '*');
|
||||||
|
|
||||||
|
testing.eventually(() => {
|
||||||
|
testing.expectEqual('http://127.0.0.1:9582', receivedOrigin);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
19
src/browser/tests/event/message_multiple_listeners.html
Normal file
19
src/browser/tests/event/message_multiple_listeners.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<script src="../testing.js"></script>
|
||||||
|
|
||||||
|
<script id=postMessageMultipleListeners>
|
||||||
|
{
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
const handler1 = () => { count++; };
|
||||||
|
const handler2 = () => { count++; };
|
||||||
|
window.addEventListener('message', handler1, { once: true });
|
||||||
|
window.addEventListener('message', handler2, { once: true });
|
||||||
|
|
||||||
|
window.postMessage('trigger', '*');
|
||||||
|
|
||||||
|
testing.eventually(() => {
|
||||||
|
testing.expectEqual(2, count);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -92,6 +92,11 @@ pub fn define(self: *CustomElementRegistry, name: []const u8, constructor: js.Fu
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!custom.asElement().asNode().isConnected()) {
|
||||||
|
idx += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
upgradeCustomElement(custom, definition, page) catch {
|
upgradeCustomElement(custom, definition, page) catch {
|
||||||
_ = page._undefined_custom_elements.swapRemove(idx);
|
_ = page._undefined_custom_elements.swapRemove(idx);
|
||||||
continue;
|
continue;
|
||||||
@@ -134,7 +139,7 @@ fn upgradeElement(self: *CustomElementRegistry, element: *Element, page: *Page)
|
|||||||
try upgradeCustomElement(custom, definition, page);
|
try upgradeCustomElement(custom, definition, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upgradeCustomElement(custom: *Custom, definition: *CustomElementDefinition, page: *Page) !void {
|
pub fn upgradeCustomElement(custom: *Custom, definition: *CustomElementDefinition, page: *Page) !void {
|
||||||
custom._definition = definition;
|
custom._definition = definition;
|
||||||
|
|
||||||
// Reset callback flags since this is a fresh upgrade
|
// Reset callback flags since this is a fresh upgrade
|
||||||
|
|||||||
@@ -49,9 +49,10 @@ pub const EventPhase = enum(u8) {
|
|||||||
|
|
||||||
pub const Type = union(enum) {
|
pub const Type = union(enum) {
|
||||||
generic,
|
generic,
|
||||||
progress_event: *@import("event/ProgressEvent.zig"),
|
|
||||||
error_event: *@import("event/ErrorEvent.zig"),
|
error_event: *@import("event/ErrorEvent.zig"),
|
||||||
custom_event: *@import("event/CustomEvent.zig"),
|
custom_event: *@import("event/CustomEvent.zig"),
|
||||||
|
message_event: *@import("event/MessageEvent.zig"),
|
||||||
|
progress_event: *@import("event/ProgressEvent.zig"),
|
||||||
};
|
};
|
||||||
|
|
||||||
const Options = struct {
|
const Options = struct {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ const Location = @import("Location.zig");
|
|||||||
const Fetch = @import("net/Fetch.zig");
|
const Fetch = @import("net/Fetch.zig");
|
||||||
const EventTarget = @import("EventTarget.zig");
|
const EventTarget = @import("EventTarget.zig");
|
||||||
const ErrorEvent = @import("event/ErrorEvent.zig");
|
const ErrorEvent = @import("event/ErrorEvent.zig");
|
||||||
|
const MessageEvent = @import("event/MessageEvent.zig");
|
||||||
const MediaQueryList = @import("css/MediaQueryList.zig");
|
const MediaQueryList = @import("css/MediaQueryList.zig");
|
||||||
const storage = @import("storage/storage.zig");
|
const storage = @import("storage/storage.zig");
|
||||||
const Element = @import("Element.zig");
|
const Element = @import("Element.zig");
|
||||||
@@ -255,6 +256,28 @@ pub fn getComputedStyle(_: *const Window, _: *Element, page: *Page) !*CSSStyleDe
|
|||||||
return CSSStyleDeclaration.init(null, page);
|
return CSSStyleDeclaration.init(null, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn postMessage(self: *Window, message: js.Object, target_origin: ?[]const u8, page: *Page) !void {
|
||||||
|
// For now, we ignore targetOrigin checking and just dispatch the message
|
||||||
|
// In a full implementation, we would validate the origin
|
||||||
|
_ = target_origin;
|
||||||
|
|
||||||
|
// postMessage queues a task (not a microtask), so use the scheduler
|
||||||
|
const origin = try self._location.getOrigin(page);
|
||||||
|
const callback = try page._factory.create(PostMessageCallback{
|
||||||
|
.window = self,
|
||||||
|
.message = try message.persist() ,
|
||||||
|
.origin = try page.arena.dupe(u8, origin),
|
||||||
|
.page = page,
|
||||||
|
});
|
||||||
|
errdefer page._factory.destroy(callback);
|
||||||
|
|
||||||
|
|
||||||
|
try page.scheduler.add(callback, PostMessageCallback.run, 0, .{
|
||||||
|
.name = "postMessage",
|
||||||
|
.low_priority = false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn btoa(_: *const Window, input: []const u8, page: *Page) ![]const u8 {
|
pub fn btoa(_: *const Window, input: []const u8, page: *Page) ![]const u8 {
|
||||||
const encoded_len = std.base64.standard.Encoder.calcSize(input.len);
|
const encoded_len = std.base64.standard.Encoder.calcSize(input.len);
|
||||||
const encoded = try page.call_arena.alloc(u8, encoded_len);
|
const encoded = try page.call_arena.alloc(u8, encoded_len);
|
||||||
@@ -268,6 +291,26 @@ pub fn atob(_: *const Window, input: []const u8, page: *Page) ![]const u8 {
|
|||||||
return decoded;
|
return decoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getLength(_: *const Window) u32 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getInnerWidth(_: *const Window) u32 {
|
||||||
|
return 1920;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getInnerHeight(_: *const Window) u32 {
|
||||||
|
return 1080;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getScrollX(_: *const Window) u32 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getScrollY(_: *const Window) u32 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const ScheduleOpts = struct {
|
const ScheduleOpts = struct {
|
||||||
repeat: bool,
|
repeat: bool,
|
||||||
params: []js.Object,
|
params: []js.Object,
|
||||||
@@ -376,6 +419,35 @@ const ScheduleCallback = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const PostMessageCallback = struct {
|
||||||
|
window: *Window,
|
||||||
|
message: js.Object,
|
||||||
|
origin: []const u8,
|
||||||
|
page: *Page,
|
||||||
|
|
||||||
|
fn deinit(self: *PostMessageCallback) void {
|
||||||
|
self.page._factory.destroy(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(ctx: *anyopaque) !?u32 {
|
||||||
|
const self: *PostMessageCallback = @ptrCast(@alignCast(ctx));
|
||||||
|
defer self.deinit();
|
||||||
|
|
||||||
|
const message_event = try MessageEvent.init("message", .{
|
||||||
|
.data = self.message,
|
||||||
|
.origin = self.origin,
|
||||||
|
.source = self.window,
|
||||||
|
.bubbles = false,
|
||||||
|
.cancelable = false,
|
||||||
|
}, self.page);
|
||||||
|
|
||||||
|
const event = message_event.asEvent();
|
||||||
|
try self.page._event_manager.dispatch(self.window.asEventTarget(), event);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const JsApi = struct {
|
pub const JsApi = struct {
|
||||||
pub const bridge = js.Bridge(Window);
|
pub const bridge = js.Bridge(Window);
|
||||||
|
|
||||||
@@ -415,27 +487,19 @@ pub const JsApi = struct {
|
|||||||
pub const requestAnimationFrame = bridge.function(Window.requestAnimationFrame, .{});
|
pub const requestAnimationFrame = bridge.function(Window.requestAnimationFrame, .{});
|
||||||
pub const cancelAnimationFrame = bridge.function(Window.cancelAnimationFrame, .{});
|
pub const cancelAnimationFrame = bridge.function(Window.cancelAnimationFrame, .{});
|
||||||
pub const matchMedia = bridge.function(Window.matchMedia, .{});
|
pub const matchMedia = bridge.function(Window.matchMedia, .{});
|
||||||
|
pub const postMessage = bridge.function(Window.postMessage, .{});
|
||||||
pub const btoa = bridge.function(Window.btoa, .{});
|
pub const btoa = bridge.function(Window.btoa, .{});
|
||||||
pub const atob = bridge.function(Window.atob, .{});
|
pub const atob = bridge.function(Window.atob, .{});
|
||||||
pub const reportError = bridge.function(Window.reportError, .{});
|
pub const reportError = bridge.function(Window.reportError, .{});
|
||||||
pub const frames = bridge.accessor(Window.getWindow, null, .{ .cache = "frames" });
|
pub const frames = bridge.accessor(Window.getWindow, null, .{ .cache = "frames" });
|
||||||
pub const length = bridge.accessor(struct {
|
|
||||||
fn wrap(_: *const Window) u32 {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}.wrap, null, .{ .cache = "length" });
|
|
||||||
|
|
||||||
pub const innerWidth = bridge.accessor(struct {
|
|
||||||
fn wrap(_: *const Window) u32 {
|
|
||||||
return 1920;
|
|
||||||
}
|
|
||||||
}.wrap, null, .{ .cache = "innerWidth" });
|
|
||||||
pub const innerHeight = bridge.accessor(struct {
|
|
||||||
fn wrap(_: *const Window) u32 {
|
|
||||||
return 1080;
|
|
||||||
}
|
|
||||||
}.wrap, null, .{ .cache = "innerHeight" });
|
|
||||||
pub const getComputedStyle = bridge.function(Window.getComputedStyle, .{});
|
pub const getComputedStyle = bridge.function(Window.getComputedStyle, .{});
|
||||||
|
pub const length = bridge.accessor(Window.getLength, null, .{ .cache = "length" });
|
||||||
|
pub const innerWidth = bridge.accessor(Window.getInnerWidth, null, .{ .cache = "innerWidth" });
|
||||||
|
pub const innerHeight = bridge.accessor(Window.getInnerHeight, null, .{ .cache = "innerHeight" });
|
||||||
|
pub const scrollX = bridge.accessor(Window.getScrollX, null, .{ .cache = "scrollX" });
|
||||||
|
pub const scrollY = bridge.accessor(Window.getScrollY, null, .{ .cache = "scrollY" });
|
||||||
|
pub const pageXOffset = bridge.accessor(Window.getScrollX, null, .{ .cache = "pageXOffset" });
|
||||||
|
pub const pageYOffset = bridge.accessor(Window.getScrollY, null, .{ .cache = "pageYOffset" });
|
||||||
};
|
};
|
||||||
|
|
||||||
const testing = @import("../../testing.zig");
|
const testing = @import("../../testing.zig");
|
||||||
|
|||||||
@@ -73,6 +73,16 @@ pub fn invokeAttributeChangedCallback(self: *Custom, name: []const u8, old_value
|
|||||||
pub fn invokeConnectedCallbackOnElement(comptime from_parser: bool, element: *Element, page: *Page) !void {
|
pub fn invokeConnectedCallbackOnElement(comptime from_parser: bool, element: *Element, page: *Page) !void {
|
||||||
// Autonomous custom element
|
// Autonomous custom element
|
||||||
if (element.is(Custom)) |custom| {
|
if (element.is(Custom)) |custom| {
|
||||||
|
// If the element is undefined, check if a definition now exists and upgrade
|
||||||
|
if (custom._definition == null) {
|
||||||
|
const name = custom._tag_name.str();
|
||||||
|
if (page.window._custom_elements._definitions.get(name)) |definition| {
|
||||||
|
const CustomElementRegistry = @import("../../CustomElementRegistry.zig");
|
||||||
|
CustomElementRegistry.upgradeCustomElement(custom, definition, page) catch {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (comptime from_parser) {
|
if (comptime from_parser) {
|
||||||
// From parser, we know the element is brand new
|
// From parser, we know the element is brand new
|
||||||
custom._connected_callback_invoked = true;
|
custom._connected_callback_invoked = true;
|
||||||
|
|||||||
90
src/browser/webapi/event/MessageEvent.zig
Normal file
90
src/browser/webapi/event/MessageEvent.zig
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
||||||
|
//
|
||||||
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const js = @import("../../js/js.zig");
|
||||||
|
|
||||||
|
const Page = @import("../../Page.zig");
|
||||||
|
const Event = @import("../Event.zig");
|
||||||
|
const Window = @import("../Window.zig");
|
||||||
|
|
||||||
|
const MessageEvent = @This();
|
||||||
|
|
||||||
|
_proto: *Event,
|
||||||
|
_data: ?js.Object = null,
|
||||||
|
_origin: []const u8 = "",
|
||||||
|
_source: ?*Window = null,
|
||||||
|
|
||||||
|
pub const InitOptions = struct {
|
||||||
|
data: ?js.Object = null,
|
||||||
|
origin: ?[]const u8 = null,
|
||||||
|
source: ?*Window = null,
|
||||||
|
bubbles: bool = false,
|
||||||
|
cancelable: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init(typ: []const u8, opts_: ?InitOptions, page: *Page) !*MessageEvent {
|
||||||
|
const opts = opts_ orelse InitOptions{};
|
||||||
|
|
||||||
|
const event = try page._factory.event(typ, MessageEvent{
|
||||||
|
._proto = undefined,
|
||||||
|
._data = if (opts.data) |d| try d.persist() else null,
|
||||||
|
._origin = if (opts.origin) |str| try page.arena.dupe(u8, str) else "",
|
||||||
|
._source = opts.source,
|
||||||
|
});
|
||||||
|
|
||||||
|
event._proto._bubbles = opts.bubbles;
|
||||||
|
event._proto._cancelable = opts.cancelable;
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn asEvent(self: *MessageEvent) *Event {
|
||||||
|
return self._proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getData(self: *const MessageEvent) ?js.Object {
|
||||||
|
return self._data;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getOrigin(self: *const MessageEvent) []const u8 {
|
||||||
|
return self._origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getSource(self: *const MessageEvent) ?*Window {
|
||||||
|
return self._source;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const JsApi = struct {
|
||||||
|
pub const bridge = js.Bridge(MessageEvent);
|
||||||
|
|
||||||
|
pub const Meta = struct {
|
||||||
|
pub const name = "MessageEvent";
|
||||||
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const constructor = bridge.constructor(MessageEvent.init, .{});
|
||||||
|
pub const data = bridge.accessor(MessageEvent.getData, null, .{});
|
||||||
|
pub const origin = bridge.accessor(MessageEvent.getOrigin, null, .{});
|
||||||
|
pub const source = bridge.accessor(MessageEvent.getSource, null, .{});
|
||||||
|
};
|
||||||
|
|
||||||
|
const testing = @import("../../../testing.zig");
|
||||||
|
test "WebApi: MessageEvent" {
|
||||||
|
try testing.htmlRunner("event/message.html", .{});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user