mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-15 15:58:57 +00:00
customElements.upgrade
This commit is contained in:
@@ -92,4 +92,63 @@
|
||||
document.body.appendChild(container);
|
||||
testing.expectEqual(1, connectedCalled);
|
||||
}
|
||||
|
||||
{
|
||||
let constructorCalled = 0;
|
||||
let connectedCalled = 0;
|
||||
|
||||
class ManualUpgrade extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
constructorCalled++;
|
||||
this.manuallyUpgraded = true;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
connectedCalled++;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('manual-upgrade', ManualUpgrade);
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.innerHTML = '<manual-upgrade id="m1"><manual-upgrade id="m2"></manual-upgrade></manual-upgrade>';
|
||||
|
||||
testing.expectEqual(2, constructorCalled);
|
||||
testing.expectEqual(0, connectedCalled);
|
||||
|
||||
customElements.upgrade(container);
|
||||
|
||||
testing.expectEqual(2, constructorCalled);
|
||||
testing.expectEqual(0, connectedCalled);
|
||||
|
||||
const m1 = container.querySelector('#m1');
|
||||
const m2 = container.querySelector('#m2');
|
||||
testing.expectEqual(true, m1.manuallyUpgraded);
|
||||
testing.expectEqual(true, m2.manuallyUpgraded);
|
||||
|
||||
document.body.appendChild(container);
|
||||
testing.expectEqual(2, connectedCalled);
|
||||
}
|
||||
|
||||
{
|
||||
let alreadyUpgradedCalled = 0;
|
||||
|
||||
class AlreadyUpgraded extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
alreadyUpgradedCalled++;
|
||||
}
|
||||
}
|
||||
|
||||
const elem = document.createElement('div');
|
||||
elem.innerHTML = '<already-upgraded></already-upgraded>';
|
||||
document.body.appendChild(elem);
|
||||
|
||||
customElements.define('already-upgraded', AlreadyUpgraded);
|
||||
testing.expectEqual(1, alreadyUpgradedCalled);
|
||||
|
||||
customElements.upgrade(elem);
|
||||
testing.expectEqual(1, alreadyUpgradedCalled);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -107,30 +107,24 @@
|
||||
{
|
||||
const el1 = $('#attr1');
|
||||
|
||||
// Toggle non-existent attribute (should add it and return true)
|
||||
testing.expectEqual(false, el1.hasAttribute('toggle-test'));
|
||||
testing.expectEqual(true, el1.toggleAttribute('toggle-test'));
|
||||
testing.expectEqual(true, el1.hasAttribute('toggle-test'));
|
||||
testing.expectEqual('', el1.getAttribute('toggle-test'));
|
||||
|
||||
// Toggle existing attribute (should remove it and return false)
|
||||
testing.expectEqual(false, el1.toggleAttribute('toggle-test'));
|
||||
testing.expectEqual(false, el1.hasAttribute('toggle-test'));
|
||||
|
||||
// Toggle with force=true when attribute doesn't exist (should add and return true)
|
||||
testing.expectEqual(false, el1.hasAttribute('toggle-test'));
|
||||
testing.expectEqual(true, el1.toggleAttribute('toggle-test', true));
|
||||
testing.expectEqual(true, el1.hasAttribute('toggle-test'));
|
||||
|
||||
// Toggle with force=true when attribute exists (should keep and return true)
|
||||
testing.expectEqual(true, el1.toggleAttribute('toggle-test', true));
|
||||
testing.expectEqual(true, el1.hasAttribute('toggle-test'));
|
||||
|
||||
// Toggle with force=false when attribute exists (should remove and return false)
|
||||
testing.expectEqual(false, el1.toggleAttribute('toggle-test', false));
|
||||
testing.expectEqual(false, el1.hasAttribute('toggle-test'));
|
||||
|
||||
// Toggle with force=false when attribute doesn't exist (should keep removed and return false)
|
||||
testing.expectEqual(false, el1.toggleAttribute('toggle-test', false));
|
||||
testing.expectEqual(false, el1.hasAttribute('toggle-test'));
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ const log = @import("../../log.zig");
|
||||
const js = @import("../js/js.zig");
|
||||
const Page = @import("../Page.zig");
|
||||
const Element = @import("Element.zig");
|
||||
const Node = @import("Node.zig");
|
||||
const CustomElementDefinition = @import("CustomElementDefinition.zig");
|
||||
|
||||
const CustomElementRegistry = @This();
|
||||
@@ -88,24 +89,11 @@ pub fn define(self: *CustomElementRegistry, name: []const u8, constructor: js.Fu
|
||||
continue;
|
||||
}
|
||||
|
||||
custom._definition = definition;
|
||||
|
||||
const node = custom.asNode();
|
||||
const prev_upgrading = page._upgrading_element;
|
||||
page._upgrading_element = node;
|
||||
defer page._upgrading_element = prev_upgrading;
|
||||
|
||||
var result: js.Function.Result = undefined;
|
||||
_ = definition.constructor.newInstance(&result) catch |err| {
|
||||
log.warn(.js, "custom element upgrade", .{ .name = name, .err = err });
|
||||
upgradeCustomElement(custom, definition, page) catch {
|
||||
_ = page._undefined_custom_elements.swapRemove(idx);
|
||||
continue;
|
||||
};
|
||||
|
||||
if (node.isConnected()) {
|
||||
custom.invokeConnectedCallback(page);
|
||||
}
|
||||
|
||||
_ = page._undefined_custom_elements.swapRemove(idx);
|
||||
}
|
||||
}
|
||||
@@ -115,6 +103,55 @@ pub fn get(self: *CustomElementRegistry, name: []const u8) ?js.Function {
|
||||
return definition.constructor;
|
||||
}
|
||||
|
||||
pub fn upgrade(self: *CustomElementRegistry, root: *Node, page: *Page) !void {
|
||||
try upgradeNode(self, root, page);
|
||||
}
|
||||
|
||||
fn upgradeNode(self: *CustomElementRegistry, node: *Node, page: *Page) !void {
|
||||
if (node.is(Element)) |element| {
|
||||
try upgradeElement(self, element, page);
|
||||
}
|
||||
|
||||
var it = node.childrenIterator();
|
||||
while (it.next()) |child| {
|
||||
try upgradeNode(self, child, page);
|
||||
}
|
||||
}
|
||||
|
||||
fn upgradeElement(self: *CustomElementRegistry, element: *Element, page: *Page) !void {
|
||||
const Custom = @import("element/html/Custom.zig");
|
||||
|
||||
const custom = element.is(Custom) orelse {
|
||||
return Custom.checkAndAttachBuiltIn(element, page);
|
||||
};
|
||||
|
||||
if (custom._definition != null) return;
|
||||
|
||||
const name = custom._tag_name.str();
|
||||
const definition = self._definitions.get(name) orelse return;
|
||||
|
||||
try upgradeCustomElement(custom, definition, page);
|
||||
}
|
||||
|
||||
fn upgradeCustomElement(custom: *@import("element/html/Custom.zig"), definition: *CustomElementDefinition, page: *Page) !void {
|
||||
custom._definition = definition;
|
||||
|
||||
const node = custom.asNode();
|
||||
const prev_upgrading = page._upgrading_element;
|
||||
page._upgrading_element = node;
|
||||
defer page._upgrading_element = prev_upgrading;
|
||||
|
||||
var result: js.Function.Result = undefined;
|
||||
_ = definition.constructor.newInstance(&result) catch |err| {
|
||||
log.warn(.js, "custom element upgrade", .{ .name = definition.name, .err = err });
|
||||
return error.CustomElementUpgradeFailed;
|
||||
};
|
||||
|
||||
if (node.isConnected()) {
|
||||
custom.invokeConnectedCallback(page);
|
||||
}
|
||||
}
|
||||
|
||||
fn validateName(name: []const u8) !void {
|
||||
if (name.len == 0) {
|
||||
return error.InvalidCustomElementName;
|
||||
@@ -166,6 +203,7 @@ pub const JsApi = struct {
|
||||
|
||||
pub const define = bridge.function(CustomElementRegistry.define, .{ .dom_exception = true });
|
||||
pub const get = bridge.function(CustomElementRegistry.get, .{ .null_as_undefined = true });
|
||||
pub const upgrade = bridge.function(CustomElementRegistry.upgrade, .{});
|
||||
};
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
|
||||
Reference in New Issue
Block a user