Screen and ScreenOrientation (legacy)

This commit is contained in:
Karl Seguin
2025-12-12 18:21:30 +08:00
parent a6d3a3d0ab
commit 23146f64ab
8 changed files with 97 additions and 182 deletions

View File

@@ -119,7 +119,7 @@ pub fn dispatch(self: *EventManager, target: *EventTarget, event: *Event) !void
switch (target._type) {
.node => |node| try self.dispatchNode(node, event, &was_handled),
.xhr, .window, .abort_signal, .media_query_list, .message_port, .text_track_cue, .navigation => {
.xhr, .window, .abort_signal, .media_query_list, .message_port, .text_track_cue, .navigation, .screen, .screen_orientation => {
const list = self.lookup.getPtr(@intFromPtr(target)) orelse return;
try self.dispatchAll(list, target, event, &was_handled);
},

View File

@@ -52,6 +52,7 @@ const Document = @import("webapi/Document.zig");
const DocumentFragment = @import("webapi/DocumentFragment.zig");
const ShadowRoot = @import("webapi/ShadowRoot.zig");
const Performance = @import("webapi/Performance.zig");
const Screen = @import("webapi/Screen.zig");
const HtmlScript = @import("webapi/Element.zig").Html.Script;
const MutationObserver = @import("webapi/MutationObserver.zig");
const IntersectionObserver = @import("webapi/IntersectionObserver.zig");
@@ -209,12 +210,14 @@ fn reset(self: *Page, comptime initializing: bool) !void {
if (comptime initializing == true) {
const storage_bucket = try self._factory.create(storage.Bucket{});
const screen = try Screen.init(self);
self.window = try self._factory.eventTarget(Window{
._document = self.document,
._storage_bucket = storage_bucket,
._performance = Performance.init(),
._proto = undefined,
._location = &default_location,
._screen = screen,
});
} else {
self.window._document = self.document;

View File

@@ -17,5 +17,5 @@
<script id=orientation>
screen.orientation.addEventListener('change', () => {})
// above shouldn't crash (it used to)
testing.skip();
testing.expectEqual(true, true);
</script>

View File

@@ -1,168 +0,0 @@
<!DOCTYPE html>
<script src="../../testing.js"></script>
<body>
<template id="basic">
<div class="container">
<h1>Hello Template</h1>
<p>This is template content</p>
</div>
</template>
<template id="nested">
<div class="outer">
<span id="inner1">First</span>
<span id="inner2">Second</span>
</div>
</template>
<template id="empty"></template>
<script id=content_property>
{
const template = $('#basic');
const content = template.content;
testing.expectEqual('[object DocumentFragment]', content.toString());
testing.expectEqual(true, content instanceof DocumentFragment);
}
</script>
<script id=content_has_children>
{
const template = $('#basic');
const content = template.content;
const div = content.firstElementChild;
testing.expectTrue(div !== null);
testing.expectEqual('DIV', div.tagName);
testing.expectEqual('container', div.className);
const h1 = div.querySelector('h1');
testing.expectTrue(h1 !== null);
testing.expectEqual('Hello Template', h1.textContent);
const p = div.querySelector('p');
testing.expectTrue(p !== null);
testing.expectEqual('This is template content', p.textContent);
}
</script>
<script id=template_children_not_in_dom>
{
const template = $('#basic');
testing.expectEqual(0, template.children.length);
testing.expectEqual(null, template.firstElementChild);
const h1InDoc = document.querySelector('#basic h1');
testing.expectEqual(null, h1InDoc);
}
</script>
<script id=nested_template_content>
{
const template = $('#nested');
const content = template.content;
const outer = content.firstElementChild;
testing.expectEqual('DIV', outer.tagName);
testing.expectEqual('outer', outer.className);
const inner1 = content.querySelector('#inner1');
testing.expectTrue(inner1 !== null);
testing.expectEqual('First', inner1.textContent);
const inner2 = content.querySelector('#inner2');
testing.expectTrue(inner2 !== null);
testing.expectEqual('Second', inner2.textContent);
}
</script>
<script id=empty_template>
{
const template = $('#empty');
const content = template.content;
testing.expectEqual('[object DocumentFragment]', content.toString());
testing.expectEqual(null, content.firstElementChild);
testing.expectEqual(0, content.childElementCount);
}
</script>
<script id=query_selector_on_content>
{
const template = $('#nested');
const content = template.content;
const spans = content.querySelectorAll('span');
testing.expectEqual(2, spans.length);
testing.expectEqual('inner1', spans[0].id);
testing.expectEqual('inner2', spans[1].id);
}
</script>
<script id=modify_content>
{
const template = $('#basic');
const content = template.content;
testing.expectEqual(1, content.childElementCount);
const newDiv = document.createElement('div');
newDiv.id = 'added';
newDiv.textContent = 'Added element';
content.append(newDiv);
testing.expectEqual(2, content.childElementCount);
}
</script>
<script id=clone_template_content>
{
const template = $('#basic');
const content = template.content;
const clone = content.cloneNode(true);
testing.expectEqual('[object DocumentFragment]', clone.toString());
const div = clone.firstElementChild;
testing.expectTrue(div !== null);
testing.expectEqual('container', div.className);
const h1 = clone.querySelector('h1');
testing.expectEqual('Hello Template', h1.textContent);
}
</script>
<script id=instantiate_template>
{
const template = $('#nested');
const content = template.content;
const originalOuter = content.firstElementChild;
testing.expectEqual(2, originalOuter.childElementCount);
const clone = content.cloneNode(true);
testing.expectEqual(1, clone.childElementCount);
const clonedDiv = clone.firstElementChild;
testing.expectEqual('DIV', clonedDiv.tagName);
testing.expectEqual('outer', clonedDiv.className);
testing.expectEqual(2, clonedDiv.childElementCount);
document.body.appendChild(clone);
testing.expectEqual(0, clone.childElementCount);
const outerInDoc = document.body.querySelector('.outer');
testing.expectTrue(outerInDoc !== null);
testing.expectEqual(clonedDiv, outerInDoc);
testing.expectEqual(2, outerInDoc.childElementCount);
const inner1 = outerInDoc.firstElementChild;
testing.expectEqual('SPAN', inner1.tagName);
testing.expectEqual('inner1', inner1.id);
testing.expectEqual('First', inner1.textContent);
}
</script>

View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<script src="../testing.js"></script>
<script id=screen>
let screen = window.screen;
testing.expectEqual(1920, screen.width);
testing.expectEqual(1080, screen.height);
let orientation = screen.orientation;
testing.expectEqual(0, orientation.angle);
testing.expectEqual('landscape-primary', orientation.type);
// this shouldn't crash (it used to)
screen.addEventListener('change', () => {});
</script>
<script id=orientation>
screen.orientation.addEventListener('change', () => {})
// above shouldn't crash (it used to)
testing.expectEqual(true, true);
</script>

View File

@@ -38,6 +38,8 @@ pub const Type = union(enum) {
message_port: *@import("MessagePort.zig"),
text_track_cue: *@import("media/TextTrackCue.zig"),
navigation: *@import("navigation/NavigationEventTarget.zig"),
screen: *@import("Screen.zig"),
screen_orientation: *@import("Screen.zig").Orientation,
};
pub fn dispatchEvent(self: *EventTarget, event: *Event, page: *Page) !bool {

View File

@@ -17,42 +17,65 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const js = @import("../js/js.zig");
const Page = @import("../Page.zig");
const EventTarget = @import("EventTarget.zig");
pub fn registerTypes() []const type {
return &.{
Screen,
Orientation,
};
}
const Screen = @This();
_pad: bool = false,
pub const init: Screen = .{};
_proto: *EventTarget,
_orientation: ?*Orientation = null,
pub fn init(page: *Page) !*Screen {
return page._factory.eventTarget(Screen{
._proto = undefined,
._orientation = null,
});
}
pub fn asEventTarget(self: *Screen) *EventTarget {
return self._proto;
}
/// Total width of the screen in pixels
pub fn getWidth(_: *const Screen) u32 {
return 1920;
}
/// Total height of the screen in pixels
pub fn getHeight(_: *const Screen) u32 {
return 1080;
}
/// Available width (excluding OS UI elements like taskbar)
pub fn getAvailWidth(_: *const Screen) u32 {
return 1920;
}
/// Available height (excluding OS UI elements like taskbar)
pub fn getAvailHeight(_: *const Screen) u32 {
return 1040; // 40px reserved for taskbar/dock
}
/// Color depth in bits per pixel
pub fn getColorDepth(_: *const Screen) u32 {
return 24;
}
/// Pixel depth in bits per pixel (typically same as colorDepth)
pub fn getPixelDepth(_: *const Screen) u32 {
return 24;
}
pub fn getOrientation(self: *Screen, page: *Page) !*Orientation {
if (self._orientation) |orientation| {
return orientation;
}
const orientation = try Orientation.init(page);
self._orientation = orientation;
return orientation;
}
pub const JsApi = struct {
pub const bridge = js.Bridge(Screen);
@@ -60,14 +83,48 @@ pub const JsApi = struct {
pub const name = "Screen";
pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined;
pub const empty_with_no_proto = true;
};
// Read-only properties
pub const width = bridge.accessor(Screen.getWidth, null, .{});
pub const height = bridge.accessor(Screen.getHeight, null, .{});
pub const availWidth = bridge.accessor(Screen.getAvailWidth, null, .{});
pub const availHeight = bridge.accessor(Screen.getAvailHeight, null, .{});
pub const colorDepth = bridge.accessor(Screen.getColorDepth, null, .{});
pub const pixelDepth = bridge.accessor(Screen.getPixelDepth, null, .{});
pub const orientation = bridge.accessor(Screen.getOrientation, null, .{});
};
pub const Orientation = struct {
_proto: *EventTarget,
pub fn init(page: *Page) !*Orientation {
return page._factory.eventTarget(Orientation{
._proto = undefined,
});
}
pub fn asEventTarget(self: *Orientation) *EventTarget {
return self._proto;
}
pub fn getAngle(_: *const Orientation) u32 {
return 0;
}
pub fn getType(_: *const Orientation) []const u8 {
return "landscape-primary";
}
pub const JsApi = struct {
pub const bridge = js.Bridge(Orientation);
pub const Meta = struct {
pub const name = "ScreenOrientation";
pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined;
};
pub const angle = bridge.accessor(Orientation.getAngle, null, .{});
pub const @"type" = bridge.accessor(Orientation.getType, null, .{});
};
};

View File

@@ -50,7 +50,7 @@ _css: CSS = .init,
_crypto: Crypto = .init,
_console: Console = .init,
_navigator: Navigator = .init,
_screen: Screen = .init,
_screen: *Screen,
_performance: Performance,
_storage_bucket: *storage.Bucket,
_on_load: ?js.Function = null,
@@ -88,7 +88,7 @@ pub fn getNavigator(self: *Window) *Navigator {
}
pub fn getScreen(self: *Window) *Screen {
return &self._screen;
return self._screen;
}
pub fn getCrypto(self: *Window) *Crypto {