1 /* 2 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 package com.sun.org.apache.xerces.internal.dom; 22 23 import com.sun.org.apache.xerces.internal.dom.events.EventImpl; 24 import com.sun.org.apache.xerces.internal.dom.events.MutationEventImpl; 25 import java.io.IOException; 26 import java.io.ObjectInputStream; 27 import java.io.ObjectOutputStream; 28 import java.io.ObjectStreamField; 29 import java.io.Serializable; 30 import java.util.ArrayList; 31 import java.util.HashMap; 32 import java.util.Hashtable; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Vector; 36 import org.w3c.dom.Attr; 37 import org.w3c.dom.DOMException; 38 import org.w3c.dom.DOMImplementation; 39 import org.w3c.dom.DocumentType; 40 import org.w3c.dom.Element; 41 import org.w3c.dom.NamedNodeMap; 42 import org.w3c.dom.Node; 43 import org.w3c.dom.UserDataHandler; 44 import org.w3c.dom.events.DocumentEvent; 45 import org.w3c.dom.events.Event; 46 import org.w3c.dom.events.EventException; 47 import org.w3c.dom.events.EventListener; 48 import org.w3c.dom.events.MutationEvent; 49 import org.w3c.dom.ranges.DocumentRange; 50 import org.w3c.dom.ranges.Range; 51 import org.w3c.dom.traversal.DocumentTraversal; 52 import org.w3c.dom.traversal.NodeFilter; 53 import org.w3c.dom.traversal.NodeIterator; 54 import org.w3c.dom.traversal.TreeWalker; 55 56 57 /** 58 * The Document interface represents the entire HTML or XML document. 59 * Conceptually, it is the root of the document tree, and provides the 60 * primary access to the document's data. 61 * <P> 62 * Since elements, text nodes, comments, processing instructions, 63 * etc. cannot exist outside the context of a Document, the Document 64 * interface also contains the factory methods needed to create these 65 * objects. The Node objects created have a ownerDocument attribute 66 * which associates them with the Document within whose context they 67 * were created. 68 * <p> 69 * The DocumentImpl class also implements the DOM Level 2 DocumentTraversal 70 * interface. This interface is comprised of factory methods needed to 71 * create NodeIterators and TreeWalkers. The process of creating NodeIterator 72 * objects also adds these references to this document. 73 * After finishing with an iterator it is important to remove the object 74 * using the remove methods in this implementation. This allows the release of 75 * the references from the iterator objects to the DOM Nodes. 76 * <p> 77 * <b>Note:</b> When any node in the document is serialized, the 78 * entire document is serialized along with it. 79 * 80 * @xerces.internal 81 * 82 * @author Arnaud Le Hors, IBM 83 * @author Joe Kesselman, IBM 84 * @author Andy Clark, IBM 85 * @author Ralf Pfeiffer, IBM 86 * @since PR-DOM-Level-1-19980818. 87 * @LastModified: Nov 2017 88 */ 89 public class DocumentImpl 90 extends CoreDocumentImpl 91 implements DocumentTraversal, DocumentEvent, DocumentRange { 92 93 // 94 // Constants 95 // 96 97 /** Serialization version. */ 98 static final long serialVersionUID = 515687835542616694L; 99 100 // 101 // Data 102 // 103 104 /** Iterators */ 105 // REVISIT: Should this be transient? -Ac 106 protected List<NodeIterator> iterators; 107 108 /** Ranges */ 109 // REVISIT: Should this be transient? -Ac 110 protected List<Range> ranges; 111 112 /** Table for event listeners registered to this document nodes. */ 113 protected Map<NodeImpl, List<LEntry>> eventListeners; 114 115 /** Bypass mutation events firing. */ 116 protected boolean mutationEvents = false; 117 118 119 /** 120 * @serialField iterators Vector Node iterators 121 * @serialField ranges Vector ranges 122 * @serialField eventListeners Hashtable Event listeners 123 * @serialField mutationEvents boolean Bypass mutation events firing 124 */ 125 private static final ObjectStreamField[] serialPersistentFields = 126 new ObjectStreamField[] { 127 new ObjectStreamField("iterators", Vector.class), 128 new ObjectStreamField("ranges", Vector.class), 129 new ObjectStreamField("eventListeners", Hashtable.class), 130 new ObjectStreamField("mutationEvents", boolean.class), 131 }; 132 133 // 134 // Constructors 135 // 136 137 /** 138 * NON-DOM: Actually creating a Document is outside the DOM's spec, 139 * since it has to operate in terms of a particular implementation. 140 */ 141 public DocumentImpl() { 142 super(); 143 } 144 145 /** Constructor. */ 146 public DocumentImpl(boolean grammarAccess) { 147 super(grammarAccess); 148 } 149 150 /** 151 * For DOM2 support. 152 * The createDocument factory method is in DOMImplementation. 153 */ 154 public DocumentImpl(DocumentType doctype) 155 { 156 super(doctype); 157 } 158 159 /** For DOM2 support. */ 160 public DocumentImpl(DocumentType doctype, boolean grammarAccess) { 161 super(doctype, grammarAccess); 162 } 163 164 // 165 // Node methods 166 // 167 168 /** 169 * Deep-clone a document, including fixing ownerDoc for the cloned 170 * children. Note that this requires bypassing the WRONG_DOCUMENT_ERR 171 * protection. I've chosen to implement it by calling importNode 172 * which is DOM Level 2. 173 * 174 * @return org.w3c.dom.Node 175 * @param deep boolean, iff true replicate children 176 */ 177 public Node cloneNode(boolean deep) { 178 179 DocumentImpl newdoc = new DocumentImpl(); 180 callUserDataHandlers(this, newdoc, UserDataHandler.NODE_CLONED); 181 cloneNode(newdoc, deep); 182 183 // experimental 184 newdoc.mutationEvents = mutationEvents; 185 186 return newdoc; 187 188 } // cloneNode(boolean):Node 189 190 /** 191 * Retrieve information describing the abilities of this particular 192 * DOM implementation. Intended to support applications that may be 193 * using DOMs retrieved from several different sources, potentially 194 * with different underlying representations. 195 */ 196 public DOMImplementation getImplementation() { 197 // Currently implemented as a singleton, since it's hardcoded 198 // information anyway. 199 return DOMImplementationImpl.getDOMImplementation(); 200 } 201 202 // 203 // DocumentTraversal methods 204 // 205 206 /** 207 * NON-DOM extension: 208 * Create and return a NodeIterator. The NodeIterator is 209 * added to a list of NodeIterators so that it can be 210 * removed to free up the DOM Nodes it references. 211 * 212 * @param root The root of the iterator. 213 * @param whatToShow The whatToShow mask. 214 * @param filter The NodeFilter installed. Null means no filter. 215 */ 216 public NodeIterator createNodeIterator(Node root, 217 short whatToShow, 218 NodeFilter filter) 219 { 220 return createNodeIterator(root, whatToShow, filter, true); 221 } 222 223 /** 224 * Create and return a NodeIterator. The NodeIterator is 225 * added to a list of NodeIterators so that it can be 226 * removed to free up the DOM Nodes it references. 227 * 228 * @param root The root of the iterator. 229 * @param whatToShow The whatToShow mask. 230 * @param filter The NodeFilter installed. Null means no filter. 231 * @param entityReferenceExpansion true to expand the contents of 232 * EntityReference nodes 233 * @since WD-DOM-Level-2-19990923 234 */ 235 public NodeIterator createNodeIterator(Node root, 236 int whatToShow, 237 NodeFilter filter, 238 boolean entityReferenceExpansion) 239 { 240 241 if (root == null) { 242 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null); 243 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); 244 } 245 246 NodeIterator iterator = new NodeIteratorImpl(this, 247 root, 248 whatToShow, 249 filter, 250 entityReferenceExpansion); 251 if (iterators == null) { 252 iterators = new ArrayList<>(); 253 } 254 255 iterators.add(iterator); 256 257 return iterator; 258 } 259 260 /** 261 * NON-DOM extension: 262 * Create and return a TreeWalker. 263 * 264 * @param root The root of the iterator. 265 * @param whatToShow The whatToShow mask. 266 * @param filter The NodeFilter installed. Null means no filter. 267 */ 268 public TreeWalker createTreeWalker(Node root, 269 short whatToShow, 270 NodeFilter filter) 271 { 272 return createTreeWalker(root, whatToShow, filter, true); 273 } 274 /** 275 * Create and return a TreeWalker. 276 * 277 * @param root The root of the iterator. 278 * @param whatToShow The whatToShow mask. 279 * @param filter The NodeFilter installed. Null means no filter. 280 * @param entityReferenceExpansion true to expand the contents of 281 * EntityReference nodes 282 * @since WD-DOM-Level-2-19990923 283 */ 284 public TreeWalker createTreeWalker(Node root, 285 int whatToShow, 286 NodeFilter filter, 287 boolean entityReferenceExpansion) 288 { 289 if (root == null) { 290 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null); 291 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); 292 } 293 return new TreeWalkerImpl(root, whatToShow, filter, 294 entityReferenceExpansion); 295 } 296 297 // 298 // Not DOM Level 2. Support DocumentTraversal methods. 299 // 300 301 /** This is not called by the developer client. The 302 * developer client uses the detach() function on the 303 * NodeIterator itself. <p> 304 * 305 * This function is called from the NodeIterator#detach(). 306 */ 307 void removeNodeIterator(NodeIterator nodeIterator) { 308 309 if (nodeIterator == null) return; 310 if (iterators == null) return; 311 312 iterators.remove(nodeIterator); 313 } 314 315 // 316 // DocumentRange methods 317 // 318 /** 319 */ 320 public Range createRange() { 321 322 if (ranges == null) { 323 ranges = new ArrayList<>(); 324 } 325 326 Range range = new RangeImpl(this); 327 ranges.add(range); 328 329 return range; 330 331 } 332 333 /** Not a client function. Called by Range.detach(), 334 * so a Range can remove itself from the list of 335 * Ranges. 336 */ 337 void removeRange(Range range) { 338 339 if (range == null) return; 340 if (ranges == null) return; 341 342 ranges.remove(range); 343 } 344 345 /** 346 * A method to be called when some text was changed in a text node, 347 * so that live objects can be notified. 348 */ 349 void replacedText(NodeImpl node) { 350 // notify ranges 351 if (ranges != null) { 352 int size = ranges.size(); 353 for (int i = 0; i != size; i++) { 354 ((RangeImpl)ranges.get(i)).receiveReplacedText(node); 355 } 356 } 357 } 358 359 /** 360 * A method to be called when some text was deleted from a text node, 361 * so that live objects can be notified. 362 */ 363 void deletedText(NodeImpl node, int offset, int count) { 364 // notify ranges 365 if (ranges != null) { 366 int size = ranges.size(); 367 for (int i = 0; i != size; i++) { 368 ((RangeImpl)ranges.get(i)).receiveDeletedText(node, 369 offset, count); 370 } 371 } 372 } 373 374 /** 375 * A method to be called when some text was inserted into a text node, 376 * so that live objects can be notified. 377 */ 378 void insertedText(NodeImpl node, int offset, int count) { 379 // notify ranges 380 if (ranges != null) { 381 int size = ranges.size(); 382 for (int i = 0; i != size; i++) { 383 ((RangeImpl)ranges.get(i)).receiveInsertedText(node, 384 offset, count); 385 } 386 } 387 } 388 389 /** 390 * A method to be called when a text node has been split, 391 * so that live objects can be notified. 392 */ 393 void splitData(Node node, Node newNode, int offset) { 394 // notify ranges 395 if (ranges != null) { 396 int size = ranges.size(); 397 for (int i = 0; i != size; i++) { 398 ((RangeImpl)ranges.get(i)).receiveSplitData(node, 399 newNode, offset); 400 } 401 } 402 } 403 404 // 405 // DocumentEvent methods 406 // 407 408 /** 409 * Introduced in DOM Level 2. Optional. <p> 410 * Create and return Event objects. 411 * 412 * @param type The eventType parameter specifies the type of Event 413 * interface to be created. If the Event interface specified is supported 414 * by the implementation this method will return a new Event of the 415 * interface type requested. If the Event is to be dispatched via the 416 * dispatchEvent method the appropriate event init method must be called 417 * after creation in order to initialize the Event's values. As an 418 * example, a user wishing to synthesize some kind of Event would call 419 * createEvent with the parameter "Events". The initEvent method could then 420 * be called on the newly created Event to set the specific type of Event 421 * to be dispatched and set its context information. 422 * @return Newly created Event 423 * @exception DOMException NOT_SUPPORTED_ERR: Raised if the implementation 424 * does not support the type of Event interface requested 425 * @since WD-DOM-Level-2-19990923 426 */ 427 public Event createEvent(String type) 428 throws DOMException { 429 if (type.equalsIgnoreCase("Events") || "Event".equals(type)) 430 return new EventImpl(); 431 if (type.equalsIgnoreCase("MutationEvents") || 432 "MutationEvent".equals(type)) 433 return new MutationEventImpl(); 434 else { 435 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_SUPPORTED_ERR", null); 436 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, msg); 437 } 438 } 439 440 /** 441 * Sets whether the DOM implementation generates mutation events 442 * upon operations. 443 */ 444 void setMutationEvents(boolean set) { 445 mutationEvents = set; 446 } 447 448 /** 449 * Returns true if the DOM implementation generates mutation events. 450 */ 451 boolean getMutationEvents() { 452 return mutationEvents; 453 } 454 455 /** 456 * Store event listener registered on a given node 457 * This is another place where we could use weak references! Indeed, the 458 * node here won't be GC'ed as long as some listener is registered on it, 459 * since the eventsListeners table will have a reference to the node. 460 */ 461 protected void setEventListeners(NodeImpl n, List<LEntry> listeners) { 462 if (eventListeners == null) { 463 eventListeners = new HashMap<>(); 464 } 465 if (listeners == null) { 466 eventListeners.remove(n); 467 if (eventListeners.isEmpty()) { 468 // stop firing events when there isn't any listener 469 mutationEvents = false; 470 } 471 } else { 472 eventListeners.put(n, listeners); 473 // turn mutation events on 474 mutationEvents = true; 475 } 476 } 477 478 /** 479 * Retreive event listener registered on a given node 480 */ 481 protected List<LEntry> getEventListeners(NodeImpl n) { 482 if (eventListeners == null) { 483 return null; 484 } 485 return eventListeners.get(n); 486 } 487 488 // 489 // EventTarget support (public and internal) 490 // 491 492 // 493 // Constants 494 // 495 496 /* 497 * NON-DOM INTERNAL: Class LEntry is just a struct used to represent 498 * event listeners registered with this node. Copies of this object 499 * are hung from the nodeListeners Vector. 500 * <p> 501 * I considered using two vectors -- one for capture, 502 * one for bubble -- but decided that since the list of listeners 503 * is probably short in most cases, it might not be worth spending 504 * the space. ***** REVISIT WHEN WE HAVE MORE EXPERIENCE. 505 */ 506 class LEntry implements Serializable { 507 508 private static final long serialVersionUID = -8426757059492421631L; 509 String type; 510 EventListener listener; 511 boolean useCapture; 512 513 /** NON-DOM INTERNAL: Constructor for Listener list Entry 514 * @param type Event name (NOT event group!) to listen for. 515 * @param listener Who gets called when event is dispatched 516 * @param useCaptue True iff listener is registered on 517 * capturing phase rather than at-target or bubbling 518 */ 519 LEntry(String type, EventListener listener, boolean useCapture) 520 { 521 this.type = type; 522 this.listener = listener; 523 this.useCapture = useCapture; 524 } 525 526 } // LEntry 527 528 /** 529 * Introduced in DOM Level 2. <p> Register an event listener with this 530 * Node. A listener may be independently registered as both Capturing and 531 * Bubbling, but may only be registered once per role; redundant 532 * registrations are ignored. 533 * @param node node to add listener to 534 * @param type Event name (NOT event group!) to listen for. 535 * @param listener Who gets called when event is dispatched 536 * @param useCapture True iff listener is registered on 537 * capturing phase rather than at-target or bubbling 538 */ 539 @Override 540 protected void addEventListener(NodeImpl node, String type, 541 EventListener listener, boolean useCapture) 542 { 543 // We can't dispatch to blank type-name, and of course we need 544 // a listener to dispatch to 545 if (type == null || type.isEmpty() || listener == null) 546 return; 547 548 // Each listener may be registered only once per type per phase. 549 // Simplest way to code that is to zap the previous entry, if any. 550 removeEventListener(node, type, listener, useCapture); 551 552 List<LEntry> nodeListeners = getEventListeners(node); 553 if(nodeListeners == null) { 554 nodeListeners = new ArrayList<>(); 555 setEventListeners(node, nodeListeners); 556 } 557 nodeListeners.add(new LEntry(type, listener, useCapture)); 558 559 // Record active listener 560 LCount lc = LCount.lookup(type); 561 if (useCapture) { 562 ++lc.captures; 563 ++lc.total; 564 } 565 else { 566 ++lc.bubbles; 567 ++lc.total; 568 } 569 570 } // addEventListener(NodeImpl,String,EventListener,boolean) :void 571 572 /** 573 * Introduced in DOM Level 2. <p> Deregister an event listener previously 574 * registered with this Node. A listener must be independently removed 575 * from the Capturing and Bubbling roles. Redundant removals (of listeners 576 * not currently registered for this role) are ignored. 577 * @param node node to remove listener from 578 * @param type Event name (NOT event group!) to listen for. 579 * @param listener Who gets called when event is dispatched 580 * @param useCapture True iff listener is registered on 581 * capturing phase rather than at-target or bubbling 582 */ 583 @Override 584 protected void removeEventListener(NodeImpl node, String type, 585 EventListener listener, 586 boolean useCapture) 587 { 588 // If this couldn't be a valid listener registration, ignore request 589 if (type == null || type.isEmpty() || listener == null) 590 return; 591 List<LEntry> nodeListeners = getEventListeners(node); 592 if (nodeListeners == null) 593 return; 594 595 // Note that addListener has previously ensured that 596 // each listener may be registered only once per type per phase. 597 // count-down is OK for deletions! 598 for (int i = nodeListeners.size() - 1; i >= 0; --i) { 599 LEntry le = nodeListeners.get(i); 600 if (le.useCapture == useCapture && le.listener == listener && 601 le.type.equals(type)) { 602 nodeListeners.remove(i); 603 // Storage management: Discard empty listener lists 604 if (nodeListeners.isEmpty()) 605 setEventListeners(node, null); 606 607 // Remove active listener 608 LCount lc = LCount.lookup(type); 609 if (useCapture) { 610 --lc.captures; 611 --lc.total; 612 } 613 else { 614 --lc.bubbles; 615 --lc.total; 616 } 617 618 break; // Found it; no need to loop farther. 619 } 620 } 621 } // removeEventListener(NodeImpl,String,EventListener,boolean) :void 622 623 @Override 624 protected void copyEventListeners(NodeImpl src, NodeImpl tgt) { 625 List<LEntry> nodeListeners = getEventListeners(src); 626 if (nodeListeners == null) { 627 return; 628 } 629 setEventListeners(tgt, new ArrayList<>(nodeListeners)); 630 } 631 632 /** 633 * Introduced in DOM Level 2. <p> 634 * Distribution engine for DOM Level 2 Events. 635 * <p> 636 * Event propagation runs as follows: 637 * <ol> 638 * <li>Event is dispatched to a particular target node, which invokes 639 * this code. Note that the event's stopPropagation flag is 640 * cleared when dispatch begins; thereafter, if it has 641 * been set before processing of a node commences, we instead 642 * immediately advance to the DEFAULT phase. 643 * <li>The node's ancestors are established as destinations for events. 644 * For capture and bubble purposes, node ancestry is determined at 645 * the time dispatch starts. If an event handler alters the document 646 * tree, that does not change which nodes will be informed of the event. 647 * <li>CAPTURING_PHASE: Ancestors are scanned, root to target, for 648 * Capturing listeners. If found, they are invoked (see below). 649 * <li>AT_TARGET: 650 * Event is dispatched to NON-CAPTURING listeners on the 651 * target node. Note that capturing listeners on this node are _not_ 652 * invoked. 653 * <li>BUBBLING_PHASE: Ancestors are scanned, target to root, for 654 * non-capturing listeners. 655 * <li>Default processing: Some DOMs have default behaviors bound to 656 * specific nodes. If this DOM does, and if the event's preventDefault 657 * flag has not been set, we now return to the target node and process 658 * its default handler for this event, if any. 659 * </ol> 660 * <p> 661 * Note that registration of handlers during processing of an event does 662 * not take effect during this phase of this event; they will not be called 663 * until the next time this node is visited by dispatchEvent. On the other 664 * hand, removals take effect immediately. 665 * <p> 666 * If an event handler itself causes events to be dispatched, they are 667 * processed synchronously, before processing resumes 668 * on the event which triggered them. Please be aware that this may 669 * result in events arriving at listeners "out of order" relative 670 * to the actual sequence of requests. 671 * <p> 672 * Note that our implementation resets the event's stop/prevent flags 673 * when dispatch begins. 674 * I believe the DOM's intent is that event objects be redispatchable, 675 * though it isn't stated in those terms. 676 * @param node node to dispatch to 677 * @param event the event object to be dispatched to 678 * registered EventListeners 679 * @return true if the event's <code>preventDefault()</code> 680 * method was invoked by an EventListener; otherwise false. 681 */ 682 @Override 683 @SuppressWarnings({"rawtypes", "unchecked"}) 684 protected boolean dispatchEvent(NodeImpl node, Event event) { 685 if (event == null) return false; 686 687 // Can't use anyone else's implementation, since there's no public 688 // API for setting the event's processing-state fields. 689 EventImpl evt = (EventImpl)event; 690 691 // VALIDATE -- must have been initialized at least once, must have 692 // a non-null non-blank name. 693 if(!evt.initialized || evt.type == null || evt.type.isEmpty()) { 694 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "UNSPECIFIED_EVENT_TYPE_ERR", null); 695 throw new EventException(EventException.UNSPECIFIED_EVENT_TYPE_ERR, msg); 696 } 697 698 // If nobody is listening for this event, discard immediately 699 LCount lc = LCount.lookup(evt.getType()); 700 if (lc.total == 0) 701 return evt.preventDefault; 702 703 // INITIALIZE THE EVENT'S DISPATCH STATUS 704 // (Note that Event objects are reusable in our implementation; 705 // that doesn't seem to be explicitly guaranteed in the DOM, but 706 // I believe it is the intent.) 707 evt.target = node; 708 evt.stopPropagation = false; 709 evt.preventDefault = false; 710 711 // Capture pre-event parentage chain, not including target; 712 // use pre-event-dispatch ancestors even if event handlers mutate 713 // document and change the target's context. 714 // Note that this is parents ONLY; events do not 715 // cross the Attr/Element "blood/brain barrier". 716 // DOMAttrModified. which looks like an exception, 717 // is issued to the Element rather than the Attr 718 // and causes a _second_ DOMSubtreeModified in the Element's 719 // tree. 720 List<Node> pv = new ArrayList<>(10); 721 Node p = node; 722 Node n = p.getParentNode(); 723 while (n != null) { 724 pv.add(n); 725 p = n; 726 n = n.getParentNode(); 727 } 728 729 // CAPTURING_PHASE: 730 if (lc.captures > 0) { 731 evt.eventPhase = Event.CAPTURING_PHASE; 732 // Ancestors are scanned, root to target, for 733 // Capturing listeners. 734 for (int j = pv.size() - 1; j >= 0; --j) { 735 if (evt.stopPropagation) 736 break; // Someone set the flag. Phase ends. 737 738 // Handle all capturing listeners on this node 739 NodeImpl nn = (NodeImpl) pv.get(j); 740 evt.currentTarget = nn; 741 ArrayList<LEntry> nodeListeners = (ArrayList<LEntry>)getEventListeners(nn); 742 if (nodeListeners != null) { 743 List<LEntry> nl = (ArrayList<LEntry>)nodeListeners.clone(); 744 // call listeners in the order in which they got registered 745 int nlsize = nl.size(); 746 for (int i = 0; i < nlsize; i++) { 747 LEntry le = nl.get(i); 748 if (le.useCapture && le.type.equals(evt.type) && 749 nodeListeners.contains(le)) { 750 try { 751 le.listener.handleEvent(evt); 752 } 753 catch (Exception e) { 754 // All exceptions are ignored. 755 } 756 } 757 } 758 } 759 } 760 } 761 762 763 // Both AT_TARGET and BUBBLE use non-capturing listeners. 764 if (lc.bubbles > 0) { 765 // AT_TARGET PHASE: Event is dispatched to NON-CAPTURING listeners 766 // on the target node. Note that capturing listeners on the target 767 // node are _not_ invoked, even during the capture phase. 768 evt.eventPhase = Event.AT_TARGET; 769 evt.currentTarget = node; 770 ArrayList<LEntry> nodeListeners = (ArrayList<LEntry>)getEventListeners(node); 771 if (!evt.stopPropagation && nodeListeners != null) { 772 List<LEntry> nl = (ArrayList<LEntry>)nodeListeners.clone(); 773 // call listeners in the order in which they got registered 774 int nlsize = nl.size(); 775 for (int i = 0; i < nlsize; i++) { 776 LEntry le = nl.get(i); 777 if (!le.useCapture && le.type.equals(evt.type) && 778 nodeListeners.contains(le)) { 779 try { 780 le.listener.handleEvent(evt); 781 } 782 catch (Exception e) { 783 // All exceptions are ignored. 784 } 785 } 786 } 787 } 788 // BUBBLING_PHASE: Ancestors are scanned, target to root, for 789 // non-capturing listeners. If the event's preventBubbling flag 790 // has been set before processing of a node commences, we 791 // instead immediately advance to the default phase. 792 // Note that not all events bubble. 793 if (evt.bubbles) { 794 evt.eventPhase = Event.BUBBLING_PHASE; 795 int pvsize = pv.size(); 796 for (int j = 0; j < pvsize; j++) { 797 if (evt.stopPropagation) 798 break; // Someone set the flag. Phase ends. 799 800 // Handle all bubbling listeners on this node 801 NodeImpl nn = (NodeImpl) pv.get(j); 802 evt.currentTarget = nn; 803 nodeListeners = (ArrayList<LEntry>)getEventListeners(nn); 804 if (nodeListeners != null) { 805 List<LEntry> nl = (ArrayList<LEntry>)nodeListeners.clone(); 806 // call listeners in the order in which they got 807 // registered 808 int nlsize = nl.size(); 809 for (int i = 0; i < nlsize; i++) { 810 LEntry le = nl.get(i); 811 if (!le.useCapture && le.type.equals(evt.type) && 812 nodeListeners.contains(le)) { 813 try { 814 le.listener.handleEvent(evt); 815 } 816 catch (Exception e) { 817 // All exceptions are ignored. 818 } 819 } 820 } 821 } 822 } 823 } 824 } 825 826 // DEFAULT PHASE: Some DOMs have default behaviors bound to specific 827 // nodes. If this DOM does, and if the event's preventDefault flag has 828 // not been set, we now return to the target node and process its 829 // default handler for this event, if any. 830 // No specific phase value defined, since this is DOM-internal 831 if (lc.defaults > 0 && (!evt.cancelable || !evt.preventDefault)) { 832 // evt.eventPhase = Event.DEFAULT_PHASE; 833 // evt.currentTarget = node; 834 // DO_DEFAULT_OPERATION 835 } 836 837 return evt.preventDefault; 838 } // dispatchEvent(NodeImpl,Event) :boolean 839 840 /** 841 * NON-DOM INTERNAL: DOMNodeInsertedIntoDocument and ...RemovedFrom... 842 * are dispatched to an entire subtree. This is the distribution code 843 * therefor. They DO NOT bubble, thanks be, but may be captured. 844 * <p> 845 * Similar to code in dispatchingEventToSubtree however this method 846 * is only used on the target node and does not start a dispatching chain 847 * on the sibling of the target node as this is not part of the subtree 848 * ***** At the moment I'm being sloppy and using the normal 849 * capture dispatcher on every node. This could be optimized hugely 850 * by writing a capture engine that tracks our position in the tree to 851 * update the capture chain without repeated chases up to root. 852 * @param n target node (that was directly inserted or removed) 853 * @param e event to be sent to that node and its subtree 854 */ 855 protected void dispatchEventToSubtree(Node n, Event e) { 856 857 ((NodeImpl) n).dispatchEvent(e); 858 if (n.getNodeType() == Node.ELEMENT_NODE) { 859 NamedNodeMap a = n.getAttributes(); 860 for (int i = a.getLength() - 1; i >= 0; --i) 861 dispatchingEventToSubtree(a.item(i), e); 862 } 863 dispatchingEventToSubtree(n.getFirstChild(), e); 864 865 } // dispatchEventToSubtree(NodeImpl,Node,Event) :void 866 867 868 /** 869 * Dispatches event to the target node's descendents recursively 870 * 871 * @param n node to dispatch to 872 * @param e event to be sent to that node and its subtree 873 */ 874 protected void dispatchingEventToSubtree(Node n, Event e) { 875 if (n==null) 876 return; 877 878 // ***** Recursive implementation. This is excessively expensive, 879 // and should be replaced in conjunction with optimization 880 // mentioned above. 881 ((NodeImpl) n).dispatchEvent(e); 882 if (n.getNodeType() == Node.ELEMENT_NODE) { 883 NamedNodeMap a = n.getAttributes(); 884 for (int i = a.getLength() - 1; i >= 0; --i) 885 dispatchingEventToSubtree(a.item(i), e); 886 } 887 dispatchingEventToSubtree(n.getFirstChild(), e); 888 dispatchingEventToSubtree(n.getNextSibling(), e); 889 } 890 891 /** 892 * NON-DOM INTERNAL: Return object for getEnclosingAttr. Carries 893 * (two values, the Attr node affected (if any) and its previous 894 * string value. Simple struct, no methods. 895 */ 896 class EnclosingAttr implements Serializable { 897 private static final long serialVersionUID = 5208387723391647216L; 898 AttrImpl node; 899 String oldvalue; 900 } 901 902 EnclosingAttr savedEnclosingAttr; 903 904 /** 905 * NON-DOM INTERNAL: Convenience wrapper for calling 906 * dispatchAggregateEvents when the context was established 907 * by <code>savedEnclosingAttr</code>. 908 * @param node node to dispatch to 909 * @param ea description of Attr affected by current operation 910 */ 911 protected void dispatchAggregateEvents(NodeImpl node, EnclosingAttr ea) { 912 if (ea != null) 913 dispatchAggregateEvents(node, ea.node, ea.oldvalue, 914 MutationEvent.MODIFICATION); 915 else 916 dispatchAggregateEvents(node, null, null, (short) 0); 917 918 } // dispatchAggregateEvents(NodeImpl,EnclosingAttr) :void 919 920 /** 921 * NON-DOM INTERNAL: Generate the "aggregated" post-mutation events 922 * DOMAttrModified and DOMSubtreeModified. 923 * Both of these should be issued only once for each user-requested 924 * mutation operation, even if that involves multiple changes to 925 * the DOM. 926 * For example, if a DOM operation makes multiple changes to a single 927 * Attr before returning, it would be nice to generate only one 928 * DOMAttrModified, and multiple changes over larger scope but within 929 * a recognizable single subtree might want to generate only one 930 * DOMSubtreeModified, sent to their lowest common ancestor. 931 * <p> 932 * To manage this, use the "internal" versions of insert and remove 933 * with MUTATION_LOCAL, then make an explicit call to this routine 934 * at the higher level. Some examples now exist in our code. 935 * 936 * @param node The node to dispatch to 937 * @param enclosingAttr The Attr node (if any) whose value has been changed 938 * as a result of the DOM operation. Null if none such. 939 * @param oldValue The String value previously held by the 940 * enclosingAttr. Ignored if none such. 941 * @param change Type of modification to the attr. See 942 * MutationEvent.attrChange 943 */ 944 protected void dispatchAggregateEvents(NodeImpl node, 945 AttrImpl enclosingAttr, 946 String oldvalue, short change) { 947 // We have to send DOMAttrModified. 948 NodeImpl owner = null; 949 if (enclosingAttr != null) { 950 LCount lc = LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED); 951 owner = (NodeImpl) enclosingAttr.getOwnerElement(); 952 if (lc.total > 0) { 953 if (owner != null) { 954 MutationEventImpl me = new MutationEventImpl(); 955 me.initMutationEvent(MutationEventImpl.DOM_ATTR_MODIFIED, 956 true, false, enclosingAttr, 957 oldvalue, 958 enclosingAttr.getNodeValue(), 959 enclosingAttr.getNodeName(), 960 change); 961 owner.dispatchEvent(me); 962 } 963 } 964 } 965 // DOMSubtreeModified gets sent to the lowest common root of a 966 // set of changes. 967 // "This event is dispatched after all other events caused by the 968 // mutation have been fired." 969 LCount lc = LCount.lookup(MutationEventImpl.DOM_SUBTREE_MODIFIED); 970 if (lc.total > 0) { 971 MutationEvent me = new MutationEventImpl(); 972 me.initMutationEvent(MutationEventImpl.DOM_SUBTREE_MODIFIED, 973 true, false, null, null, 974 null, null, (short) 0); 975 976 // If we're within an Attr, DStM gets sent to the Attr 977 // and to its owningElement. Otherwise we dispatch it 978 // locally. 979 if (enclosingAttr != null) { 980 dispatchEvent(enclosingAttr, me); 981 if (owner != null) 982 dispatchEvent(owner, me); 983 } 984 else 985 dispatchEvent(node, me); 986 } 987 } // dispatchAggregateEvents(NodeImpl, AttrImpl,String) :void 988 989 /** 990 * NON-DOM INTERNAL: Pre-mutation context check, in 991 * preparation for later generating DOMAttrModified events. 992 * Determines whether this node is within an Attr 993 * @param node node to get enclosing attribute for 994 * @return either a description of that Attr, or null if none such. 995 */ 996 protected void saveEnclosingAttr(NodeImpl node) { 997 savedEnclosingAttr = null; 998 // MUTATION PREPROCESSING AND PRE-EVENTS: 999 // If we're within the scope of an Attr and DOMAttrModified 1000 // was requested, we need to preserve its previous value for 1001 // that event. 1002 LCount lc = LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED); 1003 if (lc.total > 0) { 1004 NodeImpl eventAncestor = node; 1005 while (true) { 1006 if (eventAncestor == null) 1007 return; 1008 int type = eventAncestor.getNodeType(); 1009 if (type == Node.ATTRIBUTE_NODE) { 1010 EnclosingAttr retval = new EnclosingAttr(); 1011 retval.node = (AttrImpl) eventAncestor; 1012 retval.oldvalue = retval.node.getNodeValue(); 1013 savedEnclosingAttr = retval; 1014 return; 1015 } 1016 else if (type == Node.ENTITY_REFERENCE_NODE) 1017 eventAncestor = eventAncestor.parentNode(); 1018 else if (type == Node.TEXT_NODE) 1019 eventAncestor = eventAncestor.parentNode(); 1020 else 1021 return; 1022 // Any other parent means we're not in an Attr 1023 } 1024 } 1025 } // saveEnclosingAttr(NodeImpl) :void 1026 1027 /** 1028 * A method to be called when a character data node has been modified 1029 */ 1030 void modifyingCharacterData(NodeImpl node, boolean replace) { 1031 if (mutationEvents) { 1032 if (!replace) { 1033 saveEnclosingAttr(node); 1034 } 1035 } 1036 } 1037 1038 /** 1039 * A method to be called when a character data node has been modified 1040 */ 1041 void modifiedCharacterData(NodeImpl node, String oldvalue, String value, boolean replace) { 1042 if (mutationEvents) { 1043 if (!replace) { 1044 // MUTATION POST-EVENTS: 1045 LCount lc = 1046 LCount.lookup(MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED); 1047 if (lc.total > 0) { 1048 MutationEvent me = new MutationEventImpl(); 1049 me.initMutationEvent( 1050 MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED, 1051 true, false, null, 1052 oldvalue, value, null, (short) 0); 1053 dispatchEvent(node, me); 1054 } 1055 1056 // Subroutine: Transmit DOMAttrModified and DOMSubtreeModified, 1057 // if required. (Common to most kinds of mutation) 1058 dispatchAggregateEvents(node, savedEnclosingAttr); 1059 } // End mutation postprocessing 1060 } 1061 } 1062 1063 /** 1064 * A method to be called when a character data node has been replaced 1065 */ 1066 void replacedCharacterData(NodeImpl node, String oldvalue, String value) { 1067 //now that we have finished replacing data, we need to perform the same actions 1068 //that are required after a character data node has been modified 1069 //send the value of false for replace parameter so that mutation 1070 //events if appropriate will be initiated 1071 modifiedCharacterData(node, oldvalue, value, false); 1072 } 1073 1074 1075 1076 /** 1077 * A method to be called when a node is about to be inserted in the tree. 1078 */ 1079 void insertingNode(NodeImpl node, boolean replace) { 1080 if (mutationEvents) { 1081 if (!replace) { 1082 saveEnclosingAttr(node); 1083 } 1084 } 1085 } 1086 1087 /** 1088 * A method to be called when a node has been inserted in the tree. 1089 */ 1090 void insertedNode(NodeImpl node, NodeImpl newInternal, boolean replace) { 1091 if (mutationEvents) { 1092 // MUTATION POST-EVENTS: 1093 // "Local" events (non-aggregated) 1094 // New child is told it was inserted, and where 1095 LCount lc = LCount.lookup(MutationEventImpl.DOM_NODE_INSERTED); 1096 if (lc.total > 0) { 1097 MutationEventImpl me = new MutationEventImpl(); 1098 me.initMutationEvent(MutationEventImpl.DOM_NODE_INSERTED, 1099 true, false, node, 1100 null, null, null, (short) 0); 1101 dispatchEvent(newInternal, me); 1102 } 1103 1104 // If within the Document, tell the subtree it's been added 1105 // to the Doc. 1106 lc = LCount.lookup( 1107 MutationEventImpl.DOM_NODE_INSERTED_INTO_DOCUMENT); 1108 if (lc.total > 0) { 1109 NodeImpl eventAncestor = node; 1110 if (savedEnclosingAttr != null) 1111 eventAncestor = (NodeImpl) 1112 savedEnclosingAttr.node.getOwnerElement(); 1113 if (eventAncestor != null) { // Might have been orphan Attr 1114 NodeImpl p = eventAncestor; 1115 while (p != null) { 1116 eventAncestor = p; // Last non-null ancestor 1117 // In this context, ancestry includes 1118 // walking back from Attr to Element 1119 if (p.getNodeType() == ATTRIBUTE_NODE) { 1120 p = (NodeImpl) ((AttrImpl)p).getOwnerElement(); 1121 } 1122 else { 1123 p = p.parentNode(); 1124 } 1125 } 1126 if (eventAncestor.getNodeType() == Node.DOCUMENT_NODE){ 1127 MutationEventImpl me = new MutationEventImpl(); 1128 me.initMutationEvent(MutationEventImpl 1129 .DOM_NODE_INSERTED_INTO_DOCUMENT, 1130 false,false,null,null, 1131 null,null,(short)0); 1132 dispatchEventToSubtree(newInternal, me); 1133 } 1134 } 1135 } 1136 if (!replace) { 1137 // Subroutine: Transmit DOMAttrModified and DOMSubtreeModified 1138 // (Common to most kinds of mutation) 1139 dispatchAggregateEvents(node, savedEnclosingAttr); 1140 } 1141 } 1142 1143 // notify the range of insertions 1144 if (ranges != null) { 1145 int size = ranges.size(); 1146 for (int i = 0; i != size; i++) { 1147 ((RangeImpl)ranges.get(i)).insertedNodeFromDOM(newInternal); 1148 } 1149 } 1150 } 1151 1152 /** 1153 * A method to be called when a node is about to be removed from the tree. 1154 */ 1155 void removingNode(NodeImpl node, NodeImpl oldChild, boolean replace) { 1156 1157 // notify iterators 1158 if (iterators != null) { 1159 int size = iterators.size(); 1160 for (int i = 0; i != size; i++) { 1161 ((NodeIteratorImpl)iterators.get(i)).removeNode(oldChild); 1162 } 1163 } 1164 1165 // notify ranges 1166 if (ranges != null) { 1167 int size = ranges.size(); 1168 for (int i = 0; i != size; i++) { 1169 ((RangeImpl)ranges.get(i)).removeNode(oldChild); 1170 } 1171 } 1172 1173 // mutation events 1174 if (mutationEvents) { 1175 // MUTATION PREPROCESSING AND PRE-EVENTS: 1176 // If we're within the scope of an Attr and DOMAttrModified 1177 // was requested, we need to preserve its previous value for 1178 // that event. 1179 if (!replace) { 1180 saveEnclosingAttr(node); 1181 } 1182 // Child is told that it is about to be removed 1183 LCount lc = LCount.lookup(MutationEventImpl.DOM_NODE_REMOVED); 1184 if (lc.total > 0) { 1185 MutationEventImpl me= new MutationEventImpl(); 1186 me.initMutationEvent(MutationEventImpl.DOM_NODE_REMOVED, 1187 true, false, node, null, 1188 null, null, (short) 0); 1189 dispatchEvent(oldChild, me); 1190 } 1191 1192 // If within Document, child's subtree is informed that it's 1193 // losing that status 1194 lc = LCount.lookup( 1195 MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT); 1196 if (lc.total > 0) { 1197 NodeImpl eventAncestor = this; 1198 if(savedEnclosingAttr != null) 1199 eventAncestor = (NodeImpl) 1200 savedEnclosingAttr.node.getOwnerElement(); 1201 if (eventAncestor != null) { // Might have been orphan Attr 1202 for (NodeImpl p = eventAncestor.parentNode(); 1203 p != null; p = p.parentNode()) { 1204 eventAncestor = p; // Last non-null ancestor 1205 } 1206 if (eventAncestor.getNodeType() == Node.DOCUMENT_NODE){ 1207 MutationEventImpl me = new MutationEventImpl(); 1208 me.initMutationEvent( 1209 MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT, 1210 false, false, null, 1211 null, null, null, (short) 0); 1212 dispatchEventToSubtree(oldChild, me); 1213 } 1214 } 1215 } 1216 } // End mutation preprocessing 1217 } 1218 1219 /** 1220 * A method to be called when a node has been removed from the tree. 1221 */ 1222 void removedNode(NodeImpl node, boolean replace) { 1223 if (mutationEvents) { 1224 // MUTATION POST-EVENTS: 1225 // Subroutine: Transmit DOMAttrModified and DOMSubtreeModified, 1226 // if required. (Common to most kinds of mutation) 1227 if (!replace) { 1228 dispatchAggregateEvents(node, savedEnclosingAttr); 1229 } 1230 } // End mutation postprocessing 1231 } 1232 1233 /** 1234 * A method to be called when a node is about to be replaced in the tree. 1235 */ 1236 void replacingNode(NodeImpl node) { 1237 if (mutationEvents) { 1238 saveEnclosingAttr(node); 1239 } 1240 } 1241 1242 /** 1243 * A method to be called when character data is about to be replaced in the tree. 1244 */ 1245 void replacingData (NodeImpl node) { 1246 if (mutationEvents) { 1247 saveEnclosingAttr(node); 1248 } 1249 } 1250 1251 /** 1252 * A method to be called when a node has been replaced in the tree. 1253 */ 1254 void replacedNode(NodeImpl node) { 1255 if (mutationEvents) { 1256 dispatchAggregateEvents(node, savedEnclosingAttr); 1257 } 1258 } 1259 1260 /** 1261 * A method to be called when an attribute value has been modified 1262 */ 1263 void modifiedAttrValue(AttrImpl attr, String oldvalue) { 1264 if (mutationEvents) { 1265 // MUTATION POST-EVENTS: 1266 dispatchAggregateEvents(attr, attr, oldvalue, 1267 MutationEvent.MODIFICATION); 1268 } 1269 } 1270 1271 /** 1272 * A method to be called when an attribute node has been set 1273 */ 1274 void setAttrNode(AttrImpl attr, AttrImpl previous) { 1275 if (mutationEvents) { 1276 // MUTATION POST-EVENTS: 1277 if (previous == null) { 1278 dispatchAggregateEvents(attr.ownerNode, attr, null, 1279 MutationEvent.ADDITION); 1280 } 1281 else { 1282 dispatchAggregateEvents(attr.ownerNode, attr, 1283 previous.getNodeValue(), 1284 MutationEvent.MODIFICATION); 1285 } 1286 } 1287 } 1288 1289 /** 1290 * A method to be called when an attribute node has been removed 1291 */ 1292 void removedAttrNode(AttrImpl attr, NodeImpl oldOwner, String name) { 1293 // We can't use the standard dispatchAggregate, since it assumes 1294 // that the Attr is still attached to an owner. This code is 1295 // similar but dispatches to the previous owner, "element". 1296 if (mutationEvents) { 1297 // If we have to send DOMAttrModified (determined earlier), 1298 // do so. 1299 LCount lc = LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED); 1300 if (lc.total > 0) { 1301 MutationEventImpl me= new MutationEventImpl(); 1302 me.initMutationEvent(MutationEventImpl.DOM_ATTR_MODIFIED, 1303 true, false, attr, 1304 attr.getNodeValue(), null, name, 1305 MutationEvent.REMOVAL); 1306 dispatchEvent(oldOwner, me); 1307 } 1308 1309 // We can hand off to process DOMSubtreeModified, though. 1310 // Note that only the Element needs to be informed; the 1311 // Attr's subtree has not been changed by this operation. 1312 dispatchAggregateEvents(oldOwner, null, null, (short) 0); 1313 } 1314 } 1315 1316 1317 /** 1318 * A method to be called when an attribute node has been renamed 1319 */ 1320 void renamedAttrNode(Attr oldAt, Attr newAt) { 1321 // REVISIT: To be implemented!!! 1322 } 1323 1324 /** 1325 * A method to be called when an element has been renamed 1326 */ 1327 void renamedElement(Element oldEl, Element newEl) { 1328 // REVISIT: To be implemented!!! 1329 } 1330 1331 1332 /** 1333 * @serialData Serialized fields. Convert Maps to Hashtables and Lists 1334 * to Vectors for backward compatibility. 1335 */ 1336 private void writeObject(ObjectOutputStream out) throws IOException { 1337 // Convert Maps to Hashtables, Lists to Vectors 1338 Vector<NodeIterator> it = (iterators == null)? null : new Vector<>(iterators); 1339 Vector<Range> r = (ranges == null)? null : new Vector<>(ranges); 1340 1341 Hashtable<NodeImpl, Vector<LEntry>> el = null; 1342 if (eventListeners != null) { 1343 el = new Hashtable<>(); 1344 for (Map.Entry<NodeImpl, List<LEntry>> e : eventListeners.entrySet()) { 1345 el.put(e.getKey(), new Vector<>(e.getValue())); 1346 } 1347 } 1348 1349 // Write serialized fields 1350 ObjectOutputStream.PutField pf = out.putFields(); 1351 pf.put("iterators", it); 1352 pf.put("ranges", r); 1353 pf.put("eventListeners", el); 1354 pf.put("mutationEvents", mutationEvents); 1355 out.writeFields(); 1356 } 1357 1358 @SuppressWarnings("unchecked") 1359 private void readObject(ObjectInputStream in) 1360 throws IOException, ClassNotFoundException { 1361 // We have to read serialized fields first. 1362 ObjectInputStream.GetField gf = in.readFields(); 1363 Vector<NodeIterator> it = (Vector<NodeIterator>)gf.get("iterators", null); 1364 Vector<Range> r = (Vector<Range>)gf.get("ranges", null); 1365 Hashtable<NodeImpl, Vector<LEntry>> el = 1366 (Hashtable<NodeImpl, Vector<LEntry>>)gf.get("eventListeners", null); 1367 1368 mutationEvents = gf.get("mutationEvents", false); 1369 1370 //convert Hashtables back to HashMaps and Vectors to Lists 1371 if (it != null) iterators = new ArrayList<>(it); 1372 if (r != null) ranges = new ArrayList<>(r); 1373 if (el != null) { 1374 eventListeners = new HashMap<>(); 1375 for (Map.Entry<NodeImpl, Vector<LEntry>> e : el.entrySet()) { 1376 eventListeners.put(e.getKey(), new ArrayList<>(e.getValue())); 1377 } 1378 } 1379 } 1380 } // class DocumentImpl