/* * 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 * DOM mutation handling */ #include #include #include #include #include "layout.h" #include "dom/watcher.h" #include "util/dom-str.h" #include "util/util.h" /** * Convert a dom node type to a string * * \param[in] type DOM node type * \return appropriate string. */ static const char *nsl__dom_node_type_to_string(dom_node_type type) { const char *str[] = { "ELEMENT_NODE", "ATTRIBUTE_NODE", "TEXT_NODE", "CDATA_SECTION_NODE", "ENTITY_REFERENCE_NODE", "ENTITY_NODE", "PROCESSING_INSTRUCTION_NODE", "COMMENT_NODE", "DOCUMENT_NODE", "DOCUMENT_TYPE_NODE", "DOCUMENT_FRAGMENT_NODE", "NOTATION_NODE" }; assert(DOM_NODE_TYPE_COUNT == 12); return str[type - 1]; } /** * LibDOM event handler * * \param[in] evt The LibDOM event object * \param[in] pw Pointer to our nslayout_layout object */ static void nsl__dom_event_handler(struct dom_event *evt, void *pw) { dom_event_target *node = NULL; dom_node_type node_type; dom_string *name = NULL; dom_string *type = NULL; dom_exception exc; UNUSED(pw); printf(" DOM Event: "); /* Ugly test to see what events come out */ exc = dom_event_get_target(evt, &node); if ((exc != DOM_NO_ERR) || (node == NULL)) { printf("FAILED to get target node!\n"); goto fail; } exc = dom_node_get_node_type(node, &node_type); if (exc != DOM_NO_ERR) { printf("FAILED to get target node type!\n"); goto fail; } if (node_type == DOM_ELEMENT_NODE) { exc = dom_node_get_node_name(node, &name); if ((exc != DOM_NO_ERR) || (name == NULL)) { printf("FAILED to get target node name!\n"); goto fail; } } exc = dom_event_get_type(evt, &type); if ((exc != DOM_NO_ERR) || (type == NULL)) { printf("FAILED to get event type!\n"); goto fail; } if (node_type == DOM_ELEMENT_NODE) { printf("<%s> %s", dom_string_data(name), dom_string_data(type)); } else { printf("%s %s", nsl__dom_node_type_to_string(node_type), dom_string_data(type)); } /* 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. */ fail: if (type != NULL) dom_string_unref(type); if (name != NULL) dom_string_unref(name); if (node != NULL) dom_node_unref(node); printf("\n"); } /* Exported function, documented in src/dom/event.h */ nslayout_error nsl_dom_watcher_add_for_layout(nslayout_layout *layout) { nslayout_error err; dom_exception exc; /* TODO: LibDOM event listeners are really slow. Need to find a better * way to get DOM change notifications. */ exc = dom_event_listener_create(layout->doc, nsl__dom_event_handler, layout, &layout->listener); if (exc != DOM_NO_ERR) { return NSL_DOM_ERR(exc); } exc = dom_event_target_add_event_listener( layout->doc, nsl_dom_str_node_inserted, layout->listener, false); if (exc != DOM_NO_ERR) { err = NSL_DOM_ERR(exc); goto fail; } exc = dom_event_target_add_event_listener( layout->doc, nsl_dom_str_subtree_modified, layout->listener, false); if (exc != DOM_NO_ERR) { (void) dom_event_target_remove_event_listener( layout->doc, nsl_dom_str_node_inserted, layout->listener, false); err = NSL_DOM_ERR(exc); goto fail; } return NSLAYOUT_OK; fail: dom_event_listener_unref(layout->listener); layout->listener = NULL; return err; } /* Exported function, documented in src/dom/event.h */ nslayout_error nsl_dom_watcher_remove_for_layout(nslayout_layout *layout) { dom_exception exc1, exc2; exc1 = dom_event_target_remove_event_listener( layout->doc, nsl_dom_str_node_inserted, layout->listener, false); exc2 = dom_event_target_remove_event_listener( layout->doc, nsl_dom_str_subtree_modified, layout->listener, false); if (exc1 != DOM_NO_ERR) { return NSL_DOM_ERR(exc1); } if (exc2 != DOM_NO_ERR) { return NSL_DOM_ERR(exc2); } dom_event_listener_unref(layout->listener); layout->listener = NULL; return NSLAYOUT_OK; }