mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-16 08:18:59 +00:00
customElements.upgrade
This commit is contained in:
@@ -92,4 +92,63 @@
|
|||||||
document.body.appendChild(container);
|
document.body.appendChild(container);
|
||||||
testing.expectEqual(1, connectedCalled);
|
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>
|
</script>
|
||||||
|
|||||||
@@ -107,30 +107,24 @@
|
|||||||
{
|
{
|
||||||
const el1 = $('#attr1');
|
const el1 = $('#attr1');
|
||||||
|
|
||||||
// Toggle non-existent attribute (should add it and return true)
|
|
||||||
testing.expectEqual(false, el1.hasAttribute('toggle-test'));
|
testing.expectEqual(false, el1.hasAttribute('toggle-test'));
|
||||||
testing.expectEqual(true, el1.toggleAttribute('toggle-test'));
|
testing.expectEqual(true, el1.toggleAttribute('toggle-test'));
|
||||||
testing.expectEqual(true, el1.hasAttribute('toggle-test'));
|
testing.expectEqual(true, el1.hasAttribute('toggle-test'));
|
||||||
testing.expectEqual('', el1.getAttribute('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.toggleAttribute('toggle-test'));
|
||||||
testing.expectEqual(false, el1.hasAttribute('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(false, el1.hasAttribute('toggle-test'));
|
||||||
testing.expectEqual(true, el1.toggleAttribute('toggle-test', true));
|
testing.expectEqual(true, el1.toggleAttribute('toggle-test', true));
|
||||||
testing.expectEqual(true, el1.hasAttribute('toggle-test'));
|
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.toggleAttribute('toggle-test', true));
|
||||||
testing.expectEqual(true, el1.hasAttribute('toggle-test'));
|
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.toggleAttribute('toggle-test', false));
|
||||||
testing.expectEqual(false, el1.hasAttribute('toggle-test'));
|
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.toggleAttribute('toggle-test', false));
|
||||||
testing.expectEqual(false, el1.hasAttribute('toggle-test'));
|
testing.expectEqual(false, el1.hasAttribute('toggle-test'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ const log = @import("../../log.zig");
|
|||||||
const js = @import("../js/js.zig");
|
const js = @import("../js/js.zig");
|
||||||
const Page = @import("../Page.zig");
|
const Page = @import("../Page.zig");
|
||||||
const Element = @import("Element.zig");
|
const Element = @import("Element.zig");
|
||||||
|
const Node = @import("Node.zig");
|
||||||
const CustomElementDefinition = @import("CustomElementDefinition.zig");
|
const CustomElementDefinition = @import("CustomElementDefinition.zig");
|
||||||
|
|
||||||
const CustomElementRegistry = @This();
|
const CustomElementRegistry = @This();
|
||||||
@@ -88,24 +89,11 @@ pub fn define(self: *CustomElementRegistry, name: []const u8, constructor: js.Fu
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
custom._definition = definition;
|
upgradeCustomElement(custom, definition, page) catch {
|
||||||
|
|
||||||
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 });
|
|
||||||
_ = page._undefined_custom_elements.swapRemove(idx);
|
_ = page._undefined_custom_elements.swapRemove(idx);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (node.isConnected()) {
|
|
||||||
custom.invokeConnectedCallback(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = page._undefined_custom_elements.swapRemove(idx);
|
_ = page._undefined_custom_elements.swapRemove(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,6 +103,55 @@ pub fn get(self: *CustomElementRegistry, name: []const u8) ?js.Function {
|
|||||||
return definition.constructor;
|
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 {
|
fn validateName(name: []const u8) !void {
|
||||||
if (name.len == 0) {
|
if (name.len == 0) {
|
||||||
return error.InvalidCustomElementName;
|
return error.InvalidCustomElementName;
|
||||||
@@ -166,6 +203,7 @@ pub const JsApi = struct {
|
|||||||
|
|
||||||
pub const define = bridge.function(CustomElementRegistry.define, .{ .dom_exception = true });
|
pub const define = bridge.function(CustomElementRegistry.define, .{ .dom_exception = true });
|
||||||
pub const get = bridge.function(CustomElementRegistry.get, .{ .null_as_undefined = true });
|
pub const get = bridge.function(CustomElementRegistry.get, .{ .null_as_undefined = true });
|
||||||
|
pub const upgrade = bridge.function(CustomElementRegistry.upgrade, .{});
|
||||||
};
|
};
|
||||||
|
|
||||||
const testing = @import("../../testing.zig");
|
const testing = @import("../../testing.zig");
|
||||||
|
|||||||
Reference in New Issue
Block a user