summaryrefslogtreecommitdiff
path: root/content/handlers/html/box/manager.c
diff options
context:
space:
mode:
authorJohn-Mark Bell <jmb@netsurf-browser.org>2024-01-06 14:32:34 +0000
committerJohn-Mark Bell <jmb@netsurf-browser.org>2024-01-06 14:33:43 +0000
commit4edb140c98c8f8918f43c2b670d1a89badf92277 (patch)
tree1b0ddd2a0a1627a53f961f2dccc79b211d597606 /content/handlers/html/box/manager.c
parent0b28c94f4f8d14c09fcd5396698c791c506286ad (diff)
downloadnetsurf-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.c175
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;
+}