/* * 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) { static const char *str[DOM_NODE_TYPE_COUNT] = { [DOM_ELEMENT_NODE] = "ELEMENT_NODE", [DOM_ATTRIBUTE_NODE] = "ATTRIBUTE_NODE", [DOM_TEXT_NODE] = "TEXT_NODE", [DOM_CDATA_SECTION_NODE] = "CDATA_SECTION_NODE", [DOM_ENTITY_REFERENCE_NODE] = "ENTITY_REFERENCE_NODE", [DOM_ENTITY_NODE] = "ENTITY_NODE", [DOM_PROCESSING_INSTRUCTION_NODE] = "PROCESSING_INSTRUCTION_NODE", [DOM_COMMENT_NODE] = "COMMENT_NODE", [DOM_DOCUMENT_NODE] = "DOCUMENT_NODE", [DOM_DOCUMENT_TYPE_NODE] = "DOCUMENT_TYPE_NODE", [DOM_DOCUMENT_FRAGMENT_NODE] = "DOCUMENT_FRAGMENT_NODE", [DOM_NOTATION_NODE] = "NOTATION_NODE" }; return str[type]; } /** * 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) { 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(nsl__dom_event_handler, layout, &layout->listener); if (exc != DOM_NO_ERR) { goto fail_listener_create; } exc = dom_event_target_add_event_listener( layout->doc, nsl_dom_str_node_inserted, layout->listener, false); if (exc != DOM_NO_ERR) { goto fail_add_listener_node_inserted; } exc = dom_event_target_add_event_listener( layout->doc, nsl_dom_str_subtree_modified, layout->listener, false); if (exc != DOM_NO_ERR) { goto fail_add_listener_subtree_modified; } exc = dom_event_target_add_event_listener( layout->doc, nsl_dom_str_node_removed, layout->listener, false); if (exc != DOM_NO_ERR) { goto fail_add_listener_node_removed; } exc = dom_event_target_add_event_listener( layout->doc, nsl_dom_str_attr_modified, layout->listener, false); if (exc != DOM_NO_ERR) { goto fail_add_listener_attr_modified; } exc = dom_event_target_add_event_listener( layout->doc, nsl_dom_str_characterdata_modified, layout->listener, false); if (exc != DOM_NO_ERR) { goto fail_add_listener_characterdata_modified; } return NSLAYOUT_OK; fail_add_listener_characterdata_modified: (void) dom_event_target_remove_event_listener( layout->doc, nsl_dom_str_attr_modified, layout->listener, false); fail_add_listener_attr_modified: (void) dom_event_target_remove_event_listener( layout->doc, nsl_dom_str_node_removed, layout->listener, false); fail_add_listener_node_removed: (void) dom_event_target_remove_event_listener( layout->doc, nsl_dom_str_subtree_modified, layout->listener, false); fail_add_listener_subtree_modified: (void) dom_event_target_remove_event_listener( layout->doc, nsl_dom_str_node_inserted, layout->listener, false); fail_add_listener_node_inserted: dom_event_listener_unref(layout->listener); layout->listener = NULL; fail_listener_create: return NSL_DOM_ERR(exc); } /* 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; }