/* * This file is part of LibNSLayout * Licensed under the ISC License, http://opensource.org/licenses/ISC * Copyright 2015 Michael Drake */ /** \file src/dom/watcher.c * Implementation of DOM mutation watching. * * TODO: LibDOM mutation event listeners are really slow. * Need to find a better way to get DOM change notifications. * LibDOM probably needs to gain Mutation Observer support, or * gain a LibDOM-specific extension API. */ #include #include #include #include #include #include "libnslayout/nslayout.h" #include "dom/debug.h" #include "dom/watcher.h" #include "util/dom-str.h" #include "util/util.h" /** * A dom watcher object */ struct nsl_dom_watcher { dom_document *document; dom_event_listener *listener; /**< DOM event listener object */ }; /** * LibDOM event handler * * \param[in] evt The LibDOM event object * \param[in] pw Pointer to our dom watcher object */ static void nsl__dom_event_handler(struct dom_event *evt, void *pw) { const struct nsl_dom_watcher *watcher = pw; UNUSED(watcher); nsl_dom_debug_dump_event(evt); /* TODO: Based on event type: * 1. call to do (re)selection: * a. all nodes? * b. just this node? * 2. call to update layout, if needed. */ } /** * Destroy a DOM document's event listener * * \param[in] listener The listener to destroy * \param[in] document The document that the listener was registerd for. * \return NSLAYOUT_OK on success, appropriate error otherwise. */ static nslayout_error nsl__dom_listener_destroy( dom_event_listener *listener, dom_document *document) { dom_exception exc; /* Passing NULL as type, removes listener for all event types. */ exc = dom_event_target_remove_event_listener( document, NULL, listener, false); if (exc != DOM_NO_ERR) { return NSL_DOM_ERR(exc); } dom_event_listener_unref(listener); return NSLAYOUT_OK; } /** * Create a dom event listener. * * \param[out] listener_out Returns a dom listener for watcher's document. * \param[in] watcher DOM watcher object that listener is used for. * \return NSLAYOUT_OK on success, appropriate error otherwise. */ static nslayout_error nsl__dom_listener_create( dom_event_listener **listener_out, const struct nsl_dom_watcher *watcher) { dom_event_listener *listener; dom_exception exc; /* Create listener */ exc = dom_event_listener_create(nsl__dom_event_handler, (void *) watcher, &listener); if (exc != DOM_NO_ERR) { goto error; } /* Set the event types it should listen to */ exc = dom_event_target_add_event_listener(watcher->document, nsl_dom_str_node_inserted, listener, false); if (exc != DOM_NO_ERR) { goto error; } exc = dom_event_target_add_event_listener(watcher->document, nsl_dom_str_subtree_modified, listener, false); if (exc != DOM_NO_ERR) { goto error; } exc = dom_event_target_add_event_listener(watcher->document, nsl_dom_str_node_removed, listener, false); if (exc != DOM_NO_ERR) { goto error; } exc = dom_event_target_add_event_listener(watcher->document, nsl_dom_str_attr_modified, listener, false); if (exc != DOM_NO_ERR) { goto error; } exc = dom_event_target_add_event_listener(watcher->document, nsl_dom_str_characterdata_modified, listener, false); if (exc != DOM_NO_ERR) { goto error; } *listener_out = listener; return NSLAYOUT_OK; error: nsl__dom_listener_destroy(listener, watcher->document); return NSL_DOM_ERR(exc); } /* Exported function, documented in src/dom/watcher.h */ nslayout_error nsl_dom_watcher_create( struct nsl_dom_watcher **watcher_out, dom_document *document) { struct nsl_dom_watcher *watcher; nslayout_error err; watcher = malloc(sizeof(*watcher)); if (watcher == NULL) { return NSLAYOUT_NO_MEM; } watcher->document = document; err = nsl__dom_listener_create(&watcher->listener, watcher); if (err != NSLAYOUT_OK) { free(watcher); return err; } *watcher_out = watcher; return NSLAYOUT_OK; } /* Exported function, documented in src/dom/watcher.h */ nslayout_error nsl_dom_watcher_destroy( struct nsl_dom_watcher *watcher) { nsl__dom_listener_destroy(watcher->listener, watcher->document); free(watcher); return NSLAYOUT_OK; }