mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 01:25:53 -04:00
Merge pull request #2310 from navidemad/fix-a22-iscontenteditable
dom: implement HTMLElement.isContentEditable IDL attribute
This commit is contained in:
60
src/browser/tests/element/html/contenteditable.html
Normal file
60
src/browser/tests/element/html/contenteditable.html
Normal file
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../../testing.js"></script>
|
||||
|
||||
<!--
|
||||
Lightpanda has no caret/keyboard editing pipeline, so isContentEditable
|
||||
always returns false regardless of the element's effective contenteditable
|
||||
state. The spec walk still runs internally to log .not_implemented when the
|
||||
spec would have said true; only the return value is asserted here. See
|
||||
Html.zig:getIsContentEditable and PR #2310 for context.
|
||||
-->
|
||||
|
||||
<div id="own-true" contenteditable="true">own true</div>
|
||||
<div id="own-empty" contenteditable="">own empty</div>
|
||||
<div id="own-false" contenteditable="false">own false</div>
|
||||
<div id="own-plaintext" contenteditable="plaintext-only">own plaintext-only</div>
|
||||
|
||||
<div id="ancestor-true" contenteditable="true">
|
||||
<span id="child-of-true">child</span>
|
||||
</div>
|
||||
|
||||
<div id="ancestor-false" contenteditable="false">
|
||||
<div contenteditable="true">
|
||||
<span id="grandchild-rehosted">re-hosted</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="plain"><span id="child-of-plain">no editing context</span></div>
|
||||
|
||||
<script id="always-false">
|
||||
{
|
||||
testing.expectEqual(false, document.getElementById('own-true').isContentEditable);
|
||||
testing.expectEqual(false, document.getElementById('own-empty').isContentEditable);
|
||||
testing.expectEqual(false, document.getElementById('own-false').isContentEditable);
|
||||
testing.expectEqual(false, document.getElementById('own-plaintext').isContentEditable);
|
||||
testing.expectEqual(false, document.getElementById('ancestor-true').isContentEditable);
|
||||
testing.expectEqual(false, document.getElementById('child-of-true').isContentEditable);
|
||||
testing.expectEqual(false, document.getElementById('ancestor-false').isContentEditable);
|
||||
testing.expectEqual(false, document.getElementById('grandchild-rehosted').isContentEditable);
|
||||
testing.expectEqual(false, document.getElementById('plain').isContentEditable);
|
||||
testing.expectEqual(false, document.getElementById('child-of-plain').isContentEditable);
|
||||
testing.expectEqual(false, document.body.isContentEditable);
|
||||
testing.expectEqual(false, document.documentElement.isContentEditable);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="dynamic-attribute">
|
||||
{
|
||||
const el = document.createElement('div');
|
||||
testing.expectEqual(false, el.isContentEditable);
|
||||
|
||||
el.setAttribute('contenteditable', 'true');
|
||||
testing.expectEqual(false, el.isContentEditable);
|
||||
|
||||
el.setAttribute('contenteditable', 'false');
|
||||
testing.expectEqual(false, el.isContentEditable);
|
||||
|
||||
el.removeAttribute('contenteditable');
|
||||
testing.expectEqual(false, el.isContentEditable);
|
||||
}
|
||||
</script>
|
||||
@@ -382,6 +382,31 @@ pub fn setTitle(self: *HtmlElement, value: []const u8, frame: *Frame) !void {
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("title"), .wrap(value), frame);
|
||||
}
|
||||
|
||||
// HTML §7.7.5.2 specifies the IDL attribute as true iff the element's effective
|
||||
// content editable state is "true" or "plaintext-only". Lightpanda has no
|
||||
// caret/keyboard editing pipeline, so a true answer cannot be honored
|
||||
// end-to-end — downstream CDP tools (notably Puppeteer's dispatchKeyEvent
|
||||
// path) would route into an input pipeline that silently no-ops. Always
|
||||
// return false, and log .not_implemented when the spec would have said true
|
||||
// so usage surfaces in telemetry rather than silently depending on an
|
||||
// unsupported value. Spec walk per HTML §7.7.5.2 still applies — the nearest
|
||||
// ancestor with `contenteditable` wins; "false" disables. See PR #2310 for
|
||||
// the routing-vs-fail-loud discussion.
|
||||
//
|
||||
// "contenteditable" is 15 bytes — past the comptime SSO limit — so the
|
||||
// String wrap runs at runtime, mirroring the pattern in interactive.zig.
|
||||
pub fn getIsContentEditable(self: *HtmlElement) bool {
|
||||
var current: ?*Element = self.asElement();
|
||||
while (current) |el| : (current = el.parentElement()) {
|
||||
const raw = el.getAttributeSafe(.wrap("contenteditable")) orelse continue;
|
||||
if (!std.ascii.eqlIgnoreCase(raw, "false")) {
|
||||
log.info(.not_implemented, "IsContentEditable", .{});
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn getAttributeFunction(
|
||||
self: *HtmlElement,
|
||||
listener_type: GlobalEventHandler,
|
||||
@@ -1220,6 +1245,7 @@ pub const JsApi = struct {
|
||||
|
||||
pub const dir = bridge.accessor(HtmlElement.getDir, HtmlElement.setDir, .{});
|
||||
pub const hidden = bridge.accessor(HtmlElement.getHidden, HtmlElement.setHidden, .{});
|
||||
pub const isContentEditable = bridge.accessor(HtmlElement.getIsContentEditable, null, .{});
|
||||
pub const lang = bridge.accessor(HtmlElement.getLang, HtmlElement.setLang, .{});
|
||||
pub const tabIndex = bridge.accessor(HtmlElement.getTabIndex, HtmlElement.setTabIndex, .{});
|
||||
pub const title = bridge.accessor(HtmlElement.getTitle, HtmlElement.setTitle, .{});
|
||||
@@ -1357,3 +1383,6 @@ test "WebApi: HTML.event_listeners" {
|
||||
test "WebApi: HTMLElement.props" {
|
||||
try testing.htmlRunner("element/html/htmlelement-props.html", .{});
|
||||
}
|
||||
test "WebApi: HTMLElement.contenteditable" {
|
||||
try testing.htmlRunner("element/html/contenteditable.html", .{});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user