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) { switch (target._type) {
.node => |node| try self.dispatchNode(node, event, &was_handled), .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; const list = self.lookup.getPtr(@intFromPtr(target)) orelse return;
try self.dispatchAll(list, target, event, &was_handled); 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 DocumentFragment = @import("webapi/DocumentFragment.zig");
const ShadowRoot = @import("webapi/ShadowRoot.zig"); const ShadowRoot = @import("webapi/ShadowRoot.zig");
const Performance = @import("webapi/Performance.zig"); const Performance = @import("webapi/Performance.zig");
const Screen = @import("webapi/Screen.zig");
const HtmlScript = @import("webapi/Element.zig").Html.Script; const HtmlScript = @import("webapi/Element.zig").Html.Script;
const MutationObserver = @import("webapi/MutationObserver.zig"); const MutationObserver = @import("webapi/MutationObserver.zig");
const IntersectionObserver = @import("webapi/IntersectionObserver.zig"); const IntersectionObserver = @import("webapi/IntersectionObserver.zig");
@@ -209,12 +210,14 @@ fn reset(self: *Page, comptime initializing: bool) !void {
if (comptime initializing == true) { if (comptime initializing == true) {
const storage_bucket = try self._factory.create(storage.Bucket{}); const storage_bucket = try self._factory.create(storage.Bucket{});
const screen = try Screen.init(self);
self.window = try self._factory.eventTarget(Window{ self.window = try self._factory.eventTarget(Window{
._document = self.document, ._document = self.document,
._storage_bucket = storage_bucket, ._storage_bucket = storage_bucket,
._performance = Performance.init(), ._performance = Performance.init(),
._proto = undefined, ._proto = undefined,
._location = &default_location, ._location = &default_location,
._screen = screen,
}); });
} else { } else {
self.window._document = self.document; self.window._document = self.document;

View File

@@ -17,5 +17,5 @@
<script id=orientation> <script id=orientation>
screen.orientation.addEventListener('change', () => {}) screen.orientation.addEventListener('change', () => {})
// above shouldn't crash (it used to) // above shouldn't crash (it used to)
testing.skip(); testing.expectEqual(true, true);
</script> </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"), message_port: *@import("MessagePort.zig"),
text_track_cue: *@import("media/TextTrackCue.zig"), text_track_cue: *@import("media/TextTrackCue.zig"),
navigation: *@import("navigation/NavigationEventTarget.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 { 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/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const js = @import("../js/js.zig"); 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(); 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 { pub fn getWidth(_: *const Screen) u32 {
return 1920; return 1920;
} }
/// Total height of the screen in pixels
pub fn getHeight(_: *const Screen) u32 { pub fn getHeight(_: *const Screen) u32 {
return 1080; return 1080;
} }
/// Available width (excluding OS UI elements like taskbar)
pub fn getAvailWidth(_: *const Screen) u32 { pub fn getAvailWidth(_: *const Screen) u32 {
return 1920; return 1920;
} }
/// Available height (excluding OS UI elements like taskbar)
pub fn getAvailHeight(_: *const Screen) u32 { pub fn getAvailHeight(_: *const Screen) u32 {
return 1040; // 40px reserved for taskbar/dock return 1040; // 40px reserved for taskbar/dock
} }
/// Color depth in bits per pixel
pub fn getColorDepth(_: *const Screen) u32 { pub fn getColorDepth(_: *const Screen) u32 {
return 24; return 24;
} }
/// Pixel depth in bits per pixel (typically same as colorDepth)
pub fn getPixelDepth(_: *const Screen) u32 { pub fn getPixelDepth(_: *const Screen) u32 {
return 24; 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 JsApi = struct {
pub const bridge = js.Bridge(Screen); pub const bridge = js.Bridge(Screen);
@@ -60,14 +83,48 @@ pub const JsApi = struct {
pub const name = "Screen"; pub const name = "Screen";
pub const prototype_chain = bridge.prototypeChain(); pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined; 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 width = bridge.accessor(Screen.getWidth, null, .{});
pub const height = bridge.accessor(Screen.getHeight, null, .{}); pub const height = bridge.accessor(Screen.getHeight, null, .{});
pub const availWidth = bridge.accessor(Screen.getAvailWidth, null, .{}); pub const availWidth = bridge.accessor(Screen.getAvailWidth, null, .{});
pub const availHeight = bridge.accessor(Screen.getAvailHeight, null, .{}); pub const availHeight = bridge.accessor(Screen.getAvailHeight, null, .{});
pub const colorDepth = bridge.accessor(Screen.getColorDepth, null, .{}); pub const colorDepth = bridge.accessor(Screen.getColorDepth, null, .{});
pub const pixelDepth = bridge.accessor(Screen.getPixelDepth, 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, _crypto: Crypto = .init,
_console: Console = .init, _console: Console = .init,
_navigator: Navigator = .init, _navigator: Navigator = .init,
_screen: Screen = .init, _screen: *Screen,
_performance: Performance, _performance: Performance,
_storage_bucket: *storage.Bucket, _storage_bucket: *storage.Bucket,
_on_load: ?js.Function = null, _on_load: ?js.Function = null,
@@ -88,7 +88,7 @@ pub fn getNavigator(self: *Window) *Navigator {
} }
pub fn getScreen(self: *Window) *Screen { pub fn getScreen(self: *Window) *Screen {
return &self._screen; return self._screen;
} }
pub fn getCrypto(self: *Window) *Crypto { pub fn getCrypto(self: *Window) *Crypto {