summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn-Mark Bell <jmb@netsurf-browser.org>2024-01-21 18:26:43 +0000
committerJohn-Mark Bell <jmb@netsurf-browser.org>2024-01-21 18:26:43 +0000
commitff40e58792f3bf64c838d3f81b1447c8b82bfec3 (patch)
tree578c589ca1b07b5041895ef598d26d169d95484d
parent6419d42dca75df1e7868bc244d4f0a18f9c1c398 (diff)
downloadnetsurf-ff40e58792f3bf64c838d3f81b1447c8b82bfec3.tar.gz
netsurf-ff40e58792f3bf64c838d3f81b1447c8b82bfec3.tar.bz2
HTML: Attributes are special snowflakes
These result in SubtreeModified being fired at the Element they are attached to but, for layout purposes, that means that all the subsequent siblings (and subtrees) of the Element are also affected so we really want to consider the subtree of the Element's parent in this case (rather than just the subtree of the Element itself)
-rw-r--r--content/handlers/html/box/manager.c106
1 files changed, 88 insertions, 18 deletions
diff --git a/content/handlers/html/box/manager.c b/content/handlers/html/box/manager.c
index 2da6bc285..111571b1f 100644
--- a/content/handlers/html/box/manager.c
+++ b/content/handlers/html/box/manager.c
@@ -141,11 +141,8 @@ box_manager_process_events(void *pw)
}
static void
-box_manager_capture_event(box_manager *mgr, dom_event *evt)
+box_manager_capture_event_target(box_manager *mgr, dom_event_target *target)
{
- dom_exception exc;
- dom_event_target *target = NULL;
-
if (mgr->pending_queue_idx == mgr->pending_queue_slots) {
size_t slots;
dom_node **tmp;
@@ -166,12 +163,6 @@ box_manager_capture_event(box_manager *mgr, dom_event *evt)
mgr->pending_queue_slots = slots;
}
- exc = dom_event_get_target(evt, &target);
- if (exc != DOM_NO_ERR || target == NULL) {
- /* DOM error, but no way to deal with it? */
- return;
- }
-
mgr->pending_queue[mgr->pending_queue_idx++] = (dom_node *) target;
}
@@ -179,6 +170,9 @@ static void
box_manager_handle_dom_event(dom_event *evt, void *pw)
{
box_manager *mgr = pw;
+ dom_event_target *target = NULL;
+ dom_string *evt_type = NULL, *computed_type = NULL;
+ dom_exception exc;
/* 0. During DOM construction, ignore events until we have all
* known stylesheets */
@@ -186,14 +180,64 @@ box_manager_handle_dom_event(dom_event *evt, void *pw)
return;
}
- /* 1. Ignore non subtree modified events; ignore events for non-Element
- * targets (these should be a no-op, by construction) */
+ /* 1. Ignore unknown events */
+ exc = dom_event_get_type(evt, &evt_type);
+ if (exc == DOM_NO_ERR) {
+ /* Select corestring from event type */
+ if (dom_string_isequal(evt_type,
+ corestring_dom_DOMSubtreeModified)) {
+ computed_type = corestring_dom_DOMSubtreeModified;
+ } else if (dom_string_isequal(evt_type,
+ corestring_dom_DOMAttrModified)) {
+ computed_type = corestring_dom_DOMAttrModified;
+ }
+ }
+ dom_string_unref(evt_type);
+ if (exc != DOM_NO_ERR || computed_type == NULL) {
+ return;
+ }
- /* 2. Capture event target (ensuring we keep a ref) into list of
- * pending events */
- box_manager_capture_event(mgr, evt);
+ /* 2. Ignore events without targets */
+ exc = dom_event_get_target(evt, &target);
+ if (exc != DOM_NO_ERR || target == NULL) {
+ return;
+ }
- /* 3. Schedule a callback to process the list (if no callback already
+ /* 3. Capture event target (ensuring we keep a ref) into list of
+ * pending events. */
+ if (computed_type == corestring_dom_DOMAttrModified) {
+ /* Attribute case: capture target's parent */
+ dom_node_type node_type;
+ dom_element *parent = NULL;
+
+ /* The event target must be an Element node */
+ exc = dom_node_get_node_type(target, &node_type);
+ if (exc != DOM_NO_ERR || node_type != DOM_ELEMENT_NODE) {
+ dom_node_unref(target);
+ return;
+ }
+
+ /* Find parent element (if any) */
+ exc = dom_element_parent_node((dom_element *) target, &parent);
+ if (exc != DOM_NO_ERR || parent == NULL) {
+ dom_node_unref(target);
+ return;
+ }
+ dom_node_unref(target);
+
+ /* Capture parent, retaining the ref we hold */
+ box_manager_capture_event_target(mgr,
+ (dom_event_target *) parent);
+ } else if (computed_type == corestring_dom_DOMSubtreeModified) {
+ /* Subtree case: capture event target, retaining the ref we
+ * hold */
+ box_manager_capture_event_target(mgr, target);
+ } else {
+ /* Can't happen */
+ assert(0);
+ }
+
+ /* 4. Schedule a callback to process the list (if no callback already
* scheduled) */
if (mgr->callback_pending == false) {
// XXX: 20ms ~= 50Hz -- enough?
@@ -213,6 +257,21 @@ box_manager_create(dom_document *doc, box_manager **manager)
return NSERROR_NOMEM;
}
+ /* Register event listener.
+ *
+ * We listen for AttrModified and SubtreeModified events as we
+ * need to be able to disambiguate between a SubtreeModified generated
+ * by an attribute change vs a structural tree change.
+ *
+ * In the case of an attribute change, the affected subtree is rooted
+ * at the element's *parent* element, and not rooted at the element
+ * itself (which it will be in the case of a structural tree change).
+ *
+ * Note that, as a change to an attribute will also generate a
+ * SubtreeModified event for the element the attribute is attached to,
+ * we will record both the element (on receipt of SubtreeModified) and
+ * its parent element (on AttrModified).
+ */
exc = dom_event_listener_create(box_manager_handle_dom_event, mgr,
&mgr->listener);
if (exc != DOM_NO_ERR) {
@@ -220,8 +279,6 @@ box_manager_create(dom_document *doc, box_manager **manager)
return NSERROR_DOM;
}
- //XXX: do we need to care about other mutation events?
- //XXX: or need to do this via the default handlers?
exc = dom_event_target_add_event_listener(doc,
corestring_dom_DOMSubtreeModified,
mgr->listener,
@@ -232,6 +289,19 @@ box_manager_create(dom_document *doc, box_manager **manager)
return NSERROR_DOM;
}
+ exc = dom_event_target_add_event_listener(doc,
+ corestring_dom_DOMAttrModified,
+ mgr->listener,
+ true);
+ if (exc != DOM_NO_ERR) {
+ /* Remove all instances of this listener */
+ dom_event_target_remove_event_listener(doc, NULL,
+ mgr->listener, true);
+ dom_event_listener_unref(mgr->listener);
+ free(mgr);
+ return NSERROR_DOM;
+ }
+
mgr->doc = (dom_document *) dom_node_ref(doc);
mgr->have_styles = false;
mgr->callback_pending = false;