diff options
author | John-Mark Bell <jmb@netsurf-browser.org> | 2024-01-06 14:32:34 +0000 |
---|---|---|
committer | John-Mark Bell <jmb@netsurf-browser.org> | 2024-01-06 14:33:43 +0000 |
commit | 4edb140c98c8f8918f43c2b670d1a89badf92277 (patch) | |
tree | 1b0ddd2a0a1627a53f961f2dccc79b211d597606 /content/handlers/html/box/manager.c | |
parent | 0b28c94f4f8d14c09fcd5396698c791c506286ad (diff) | |
download | netsurf-4edb140c98c8f8918f43c2b670d1a89badf92277.tar.gz netsurf-4edb140c98c8f8918f43c2b670d1a89badf92277.tar.bz2 |
HTML: skeletal box tree manager
Diffstat (limited to 'content/handlers/html/box/manager.c')
-rw-r--r-- | content/handlers/html/box/manager.c | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/content/handlers/html/box/manager.c b/content/handlers/html/box/manager.c new file mode 100644 index 000000000..9fe5269d6 --- /dev/null +++ b/content/handlers/html/box/manager.c @@ -0,0 +1,175 @@ +#include <stdlib.h> + +#include "desktop/gui_internal.h" +#include "html/box/manager.h" +#include "netsurf/misc.h" +#include "utils/corestrings.h" + +struct box_manager +{ + dom_document *doc; + dom_event_listener *listener; + + /* Whether we have been told that stylesheets are available */ + bool have_styles; + /* Whether there is an outstanding callback pending */ + bool callback_pending; + + dom_node **pending_queue; + size_t pending_queue_idx; + size_t pending_queue_slots; +}; + +static void +box_manager_process_events(void *pw) +{ + box_manager *mgr = pw; + + mgr->callback_pending = false; + + //XXX: implement + + /* Just release references for now */ + while (mgr->pending_queue_idx > 0) { + mgr->pending_queue_idx--; + dom_node_unref(mgr->pending_queue[mgr->pending_queue_idx]); + } + + // 1. Eliminate nodes which are no longer in our document, or in a detached tree (we expect to have received a corresponding event for their parent) + // 2. Eliminate duplicate nodes from the list + // 3. Eliminate nodes which are descendants of other nodes in the list + // 4. For every remaining node: (re)build the structural box tree segment rooted at each of node's children + // 5. For each modified box tree segment: + // a. find the immediate containing block(s) for the segment + // (there may be more than one if posititioned/out-of-flow elements are in play) + // b. emit (a) "box tree modified" event(s) referencing the containing block(s) + // + // The box tree must always be in normal form so it may be necessary to insert (or remove?) elements between the segment root and its parent box + // Layout calculators will receive the "box tree modified" events and deal with them + + // XXX: need an API for stylesheet change notifications + // On receipt, mark entire tree invalid (synthesise a subtree modified event? -- not fired at DOM, for obvious reasons, but could be thrown at our event handler) +} + +static void +box_manager_capture_event(box_manager *mgr, dom_event *evt) +{ + dom_exception exc; + dom_event_target *target = NULL; + + if (mgr->pending_queue_idx == mgr->pending_queue_slots) { + dom_node **tmp; + if (mgr->pending_queue == NULL) { + tmp = malloc(sizeof(*tmp) * 32); + } else { + tmp = realloc(mgr->pending_queue, + sizeof(*tmp) * (mgr->pending_queue_slots + 32)); + } + if (tmp == NULL) { + /* ENOMEM, but no way to deal with it? */ + return; + } + + mgr->pending_queue = tmp; + mgr->pending_queue_slots += 32; + } + + 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; +} + +static void +box_manager_handle_dom_event(dom_event *evt, void *pw) +{ + box_manager *mgr = pw; + + /* 0. During DOM construction, ignore events until we have all + * known stylesheets */ + if (mgr->have_styles == false) { + return; + } + + /* 1. Ignore non subtree modified events; ignore events for non-Element + * targets (these should be a no-op, by construction) */ + + /* 2. Capture event target (ensuring we keep a ref) into list of + * pending events */ + box_manager_capture_event(mgr, evt); + + /* 3. Schedule a callback to process the list (if no callback already + * scheduled) */ + if (mgr->callback_pending == false) { + // XXX: 20ms ~= 50Hz -- enough? + guit->misc->schedule(20, box_manager_process_events, mgr); + mgr->callback_pending = true; + } +} + +nserror +box_manager_create(dom_document *doc, box_manager **manager) +{ + box_manager *mgr; + dom_exception exc; + + mgr = malloc(sizeof(*mgr)); + if (mgr == NULL) { + return NSERROR_NOMEM; + } + + exc = dom_event_listener_create(box_manager_handle_dom_event, mgr, + &mgr->listener); + if (exc != DOM_NO_ERR) { + free(mgr); + 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, + true); + if (exc != DOM_NO_ERR) { + 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; + mgr->pending_queue = NULL; + mgr->pending_queue_idx = mgr->pending_queue_slots = 0; + + *manager = mgr; + + return NSERROR_OK; +} + +nserror +box_manager_destroy(box_manager *manager) +{ + if (manager->callback_pending) { + guit->misc->schedule(-1, box_manager_process_events, manager); + } + while (manager->pending_queue_idx > 0) { + manager->pending_queue_idx--; + dom_node_unref( + manager->pending_queue[manager->pending_queue_idx]); + } + free(manager->pending_queue); + (void) dom_event_target_remove_event_listener(manager->doc, + corestring_dom_DOMSubtreeModified, + manager->listener, + true); + dom_event_listener_unref(manager->listener); + dom_node_unref(manager->doc); + free(manager); + + return NSERROR_OK; +} |