1 | /* |
2 | * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
3 | * (C) 1999 Antti Koivisto (koivisto@kde.org) |
4 | * (C) 2001 Dirk Mueller (mueller@kde.org) |
5 | * Copyright (C) 2004-2017 Apple Inc. All rights reserved. |
6 | * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) |
7 | * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) |
8 | * |
9 | * This library is free software; you can redistribute it and/or |
10 | * modify it under the terms of the GNU Library General Public |
11 | * License as published by the Free Software Foundation; either |
12 | * version 2 of the License, or (at your option) any later version. |
13 | * |
14 | * This library is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | * Library General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU Library General Public License |
20 | * along with this library; see the file COPYING.LIB. If not, write to |
21 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
22 | * Boston, MA 02110-1301, USA. |
23 | */ |
24 | |
25 | #include "config.h" |
26 | #include "Node.h" |
27 | |
28 | #include "AXObjectCache.h" |
29 | #include "Attr.h" |
30 | #include "BeforeLoadEvent.h" |
31 | #include "ChildListMutationScope.h" |
32 | #include "CommonVM.h" |
33 | #include "ComposedTreeAncestorIterator.h" |
34 | #include "ContainerNodeAlgorithms.h" |
35 | #include "ContextMenuController.h" |
36 | #include "DOMWindow.h" |
37 | #include "DataTransfer.h" |
38 | #include "DocumentType.h" |
39 | #include "ElementIterator.h" |
40 | #include "ElementRareData.h" |
41 | #include "ElementTraversal.h" |
42 | #include "EventDispatcher.h" |
43 | #include "EventHandler.h" |
44 | #include "FrameView.h" |
45 | #include "HTMLAreaElement.h" |
46 | #include "HTMLBodyElement.h" |
47 | #include "HTMLCollection.h" |
48 | #include "HTMLElement.h" |
49 | #include "HTMLImageElement.h" |
50 | #include "HTMLSlotElement.h" |
51 | #include "HTMLStyleElement.h" |
52 | #include "InputEvent.h" |
53 | #include "InspectorController.h" |
54 | #include "KeyboardEvent.h" |
55 | #include "Logging.h" |
56 | #include "MutationEvent.h" |
57 | #include "NodeRenderStyle.h" |
58 | #include "ProcessingInstruction.h" |
59 | #include "ProgressEvent.h" |
60 | #include "Range.h" |
61 | #include "RenderBlock.h" |
62 | #include "RenderBox.h" |
63 | #include "RenderTextControl.h" |
64 | #include "RenderView.h" |
65 | #include "SVGElement.h" |
66 | #include "ScopedEventQueue.h" |
67 | #include "ScriptDisallowedScope.h" |
68 | #include "StorageEvent.h" |
69 | #include "StyleResolver.h" |
70 | #include "StyleSheetContents.h" |
71 | #include "TemplateContentDocumentFragment.h" |
72 | #include "TextEvent.h" |
73 | #include "TouchEvent.h" |
74 | #include "WheelEvent.h" |
75 | #include "XMLNSNames.h" |
76 | #include "XMLNames.h" |
77 | #include <wtf/IsoMallocInlines.h> |
78 | #include <wtf/RefCountedLeakCounter.h> |
79 | #include <wtf/SHA1.h> |
80 | #include <wtf/Variant.h> |
81 | #include <wtf/text/CString.h> |
82 | #include <wtf/text/StringBuilder.h> |
83 | |
84 | namespace WebCore { |
85 | |
86 | WTF_MAKE_ISO_ALLOCATED_IMPL(Node); |
87 | |
88 | using namespace HTMLNames; |
89 | |
90 | #if DUMP_NODE_STATISTICS |
91 | static HashSet<Node*>& liveNodeSet() |
92 | { |
93 | static NeverDestroyed<HashSet<Node*>> liveNodes; |
94 | return liveNodes; |
95 | } |
96 | |
97 | static const char* stringForRareDataUseType(NodeRareData::UseType useType) |
98 | { |
99 | switch (useType) { |
100 | case NodeRareData::UseType::ConnectedFrameCount: |
101 | return "ConnectedFrameCount" ; |
102 | case NodeRareData::UseType::NodeList: |
103 | return "NodeList" ; |
104 | case NodeRareData::UseType::MutationObserver: |
105 | return "MutationObserver" ; |
106 | case NodeRareData::UseType::TabIndex: |
107 | return "TabIndex" ; |
108 | case NodeRareData::UseType::StyleFlags: |
109 | return "StyleFlags" ; |
110 | case NodeRareData::UseType::MinimumSize: |
111 | return "MinimumSize" ; |
112 | case NodeRareData::UseType::ScrollingPosition: |
113 | return "ScrollingPosition" ; |
114 | case NodeRareData::UseType::ComputedStyle: |
115 | return "ComputedStyle" ; |
116 | case NodeRareData::UseType::Dataset: |
117 | return "Dataset" ; |
118 | case NodeRareData::UseType::ClassList: |
119 | return "ClassList" ; |
120 | case NodeRareData::UseType::ShadowRoot: |
121 | return "ShadowRoot" ; |
122 | case NodeRareData::UseType::CustomElementQueue: |
123 | return "CustomElementQueue" ; |
124 | case NodeRareData::UseType::AttributeMap: |
125 | return "AttributeMap" ; |
126 | case NodeRareData::UseType::InteractionObserver: |
127 | return "InteractionObserver" ; |
128 | case NodeRareData::UseType::PseudoElements: |
129 | return "PseudoElements" ; |
130 | } |
131 | return nullptr; |
132 | } |
133 | |
134 | #endif |
135 | |
136 | void Node::dumpStatistics() |
137 | { |
138 | #if DUMP_NODE_STATISTICS |
139 | size_t nodesWithRareData = 0; |
140 | |
141 | size_t elementNodes = 0; |
142 | size_t attrNodes = 0; |
143 | size_t textNodes = 0; |
144 | size_t cdataNodes = 0; |
145 | size_t commentNodes = 0; |
146 | size_t piNodes = 0; |
147 | size_t documentNodes = 0; |
148 | size_t docTypeNodes = 0; |
149 | size_t fragmentNodes = 0; |
150 | size_t shadowRootNodes = 0; |
151 | |
152 | HashMap<String, size_t> perTagCount; |
153 | |
154 | size_t attributes = 0; |
155 | size_t attributesWithAttr = 0; |
156 | size_t elementsWithAttributeStorage = 0; |
157 | size_t elementsWithRareData = 0; |
158 | size_t elementsWithNamedNodeMap = 0; |
159 | |
160 | HashMap<uint16_t, size_t> rareDataSingleUseTypeCounts; |
161 | size_t mixedRareDataUseCount = 0; |
162 | |
163 | for (auto* node : liveNodeSet()) { |
164 | if (node->hasRareData()) { |
165 | ++nodesWithRareData; |
166 | if (is<Element>(*node)) { |
167 | ++elementsWithRareData; |
168 | if (downcast<Element>(*node).hasNamedNodeMap()) |
169 | ++elementsWithNamedNodeMap; |
170 | } |
171 | auto* rareData = node->rareData(); |
172 | auto useTypes = is<Element>(node) ? static_cast<ElementRareData*>(rareData)->useTypes() : rareData->useTypes(); |
173 | unsigned useTypeCount = 0; |
174 | for (auto type : useTypes) { |
175 | UNUSED_PARAM(type); |
176 | useTypeCount++; |
177 | } |
178 | if (useTypeCount == 1) { |
179 | auto result = rareDataSingleUseTypeCounts.add(static_cast<uint16_t>(*useTypes.begin()), 0); |
180 | result.iterator->value++; |
181 | } else |
182 | mixedRareDataUseCount++; |
183 | } |
184 | |
185 | switch (node->nodeType()) { |
186 | case ELEMENT_NODE: { |
187 | ++elementNodes; |
188 | |
189 | // Tag stats |
190 | Element& element = downcast<Element>(*node); |
191 | HashMap<String, size_t>::AddResult result = perTagCount.add(element.tagName(), 1); |
192 | if (!result.isNewEntry) |
193 | result.iterator->value++; |
194 | |
195 | if (const ElementData* elementData = element.elementData()) { |
196 | unsigned length = elementData->length(); |
197 | attributes += length; |
198 | ++elementsWithAttributeStorage; |
199 | for (unsigned i = 0; i < length; ++i) { |
200 | const Attribute& attr = elementData->attributeAt(i); |
201 | if (element.attrIfExists(attr.name())) |
202 | ++attributesWithAttr; |
203 | } |
204 | } |
205 | break; |
206 | } |
207 | case ATTRIBUTE_NODE: { |
208 | ++attrNodes; |
209 | break; |
210 | } |
211 | case TEXT_NODE: { |
212 | ++textNodes; |
213 | break; |
214 | } |
215 | case CDATA_SECTION_NODE: { |
216 | ++cdataNodes; |
217 | break; |
218 | } |
219 | case PROCESSING_INSTRUCTION_NODE: { |
220 | ++piNodes; |
221 | break; |
222 | } |
223 | case COMMENT_NODE: { |
224 | ++commentNodes; |
225 | break; |
226 | } |
227 | case DOCUMENT_NODE: { |
228 | ++documentNodes; |
229 | break; |
230 | } |
231 | case DOCUMENT_TYPE_NODE: { |
232 | ++docTypeNodes; |
233 | break; |
234 | } |
235 | case DOCUMENT_FRAGMENT_NODE: { |
236 | if (node->isShadowRoot()) |
237 | ++shadowRootNodes; |
238 | else |
239 | ++fragmentNodes; |
240 | break; |
241 | } |
242 | } |
243 | } |
244 | |
245 | printf("Number of Nodes: %d\n\n" , liveNodeSet().size()); |
246 | printf("Number of Nodes with RareData: %zu\n" , nodesWithRareData); |
247 | printf(" Mixed use: %zu\n" , mixedRareDataUseCount); |
248 | for (auto it : rareDataSingleUseTypeCounts) |
249 | printf(" %s: %zu\n" , stringForRareDataUseType(static_cast<NodeRareData::UseType>(it.key)), it.value); |
250 | printf("\n" ); |
251 | |
252 | |
253 | printf("NodeType distribution:\n" ); |
254 | printf(" Number of Element nodes: %zu\n" , elementNodes); |
255 | printf(" Number of Attribute nodes: %zu\n" , attrNodes); |
256 | printf(" Number of Text nodes: %zu\n" , textNodes); |
257 | printf(" Number of CDATASection nodes: %zu\n" , cdataNodes); |
258 | printf(" Number of Comment nodes: %zu\n" , commentNodes); |
259 | printf(" Number of ProcessingInstruction nodes: %zu\n" , piNodes); |
260 | printf(" Number of Document nodes: %zu\n" , documentNodes); |
261 | printf(" Number of DocumentType nodes: %zu\n" , docTypeNodes); |
262 | printf(" Number of DocumentFragment nodes: %zu\n" , fragmentNodes); |
263 | printf(" Number of ShadowRoot nodes: %zu\n" , shadowRootNodes); |
264 | |
265 | printf("Element tag name distibution:\n" ); |
266 | for (auto& stringSizePair : perTagCount) |
267 | printf(" Number of <%s> tags: %zu\n" , stringSizePair.key.utf8().data(), stringSizePair.value); |
268 | |
269 | printf("Attributes:\n" ); |
270 | printf(" Number of Attributes (non-Node and Node): %zu [%zu]\n" , attributes, sizeof(Attribute)); |
271 | printf(" Number of Attributes with an Attr: %zu\n" , attributesWithAttr); |
272 | printf(" Number of Elements with attribute storage: %zu [%zu]\n" , elementsWithAttributeStorage, sizeof(ElementData)); |
273 | printf(" Number of Elements with RareData: %zu\n" , elementsWithRareData); |
274 | printf(" Number of Elements with NamedNodeMap: %zu [%zu]\n" , elementsWithNamedNodeMap, sizeof(NamedNodeMap)); |
275 | #endif |
276 | } |
277 | |
278 | DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, nodeCounter, ("WebCoreNode" )); |
279 | |
280 | #ifndef NDEBUG |
281 | static bool shouldIgnoreLeaks = false; |
282 | |
283 | static HashSet<Node*>& ignoreSet() |
284 | { |
285 | static NeverDestroyed<HashSet<Node*>> ignore; |
286 | |
287 | return ignore; |
288 | } |
289 | |
290 | #endif |
291 | |
292 | void Node::startIgnoringLeaks() |
293 | { |
294 | #ifndef NDEBUG |
295 | shouldIgnoreLeaks = true; |
296 | #endif |
297 | } |
298 | |
299 | void Node::stopIgnoringLeaks() |
300 | { |
301 | #ifndef NDEBUG |
302 | shouldIgnoreLeaks = false; |
303 | #endif |
304 | } |
305 | |
306 | void Node::trackForDebugging() |
307 | { |
308 | #ifndef NDEBUG |
309 | if (shouldIgnoreLeaks) |
310 | ignoreSet().add(this); |
311 | else |
312 | nodeCounter.increment(); |
313 | #endif |
314 | |
315 | #if DUMP_NODE_STATISTICS |
316 | liveNodeSet().add(this); |
317 | #endif |
318 | } |
319 | |
320 | Node::Node(Document& document, ConstructionType type) |
321 | : m_nodeFlags(type) |
322 | , m_treeScope(&document) |
323 | { |
324 | ASSERT(isMainThread()); |
325 | |
326 | document.incrementReferencingNodeCount(); |
327 | |
328 | #if !defined(NDEBUG) || (defined(DUMP_NODE_STATISTICS) && DUMP_NODE_STATISTICS) |
329 | trackForDebugging(); |
330 | #endif |
331 | } |
332 | |
333 | Node::~Node() |
334 | { |
335 | ASSERT(isMainThread()); |
336 | ASSERT(m_refCountAndParentBit == s_refCountIncrement); |
337 | ASSERT(m_deletionHasBegun); |
338 | ASSERT(!m_adoptionIsRequired); |
339 | |
340 | #ifndef NDEBUG |
341 | if (!ignoreSet().remove(this)) |
342 | nodeCounter.decrement(); |
343 | #endif |
344 | |
345 | #if DUMP_NODE_STATISTICS |
346 | liveNodeSet().remove(this); |
347 | #endif |
348 | |
349 | RELEASE_ASSERT(!renderer()); |
350 | ASSERT(!parentNode()); |
351 | ASSERT(!m_previous); |
352 | ASSERT(!m_next); |
353 | |
354 | if (hasRareData()) |
355 | clearRareData(); |
356 | |
357 | if (!isContainerNode()) |
358 | willBeDeletedFrom(document()); |
359 | |
360 | if (hasEventTargetData()) |
361 | clearEventTargetData(); |
362 | |
363 | document().decrementReferencingNodeCount(); |
364 | |
365 | #if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS_FAMILY) && (!ASSERT_DISABLED || ENABLE(SECURITY_ASSERTIONS)) |
366 | for (auto* document : Document::allDocuments()) { |
367 | ASSERT_WITH_SECURITY_IMPLICATION(!document->touchEventListenersContain(*this)); |
368 | ASSERT_WITH_SECURITY_IMPLICATION(!document->touchEventHandlersContain(*this)); |
369 | ASSERT_WITH_SECURITY_IMPLICATION(!document->touchEventTargetsContain(*this)); |
370 | } |
371 | #endif |
372 | } |
373 | |
374 | void Node::willBeDeletedFrom(Document& document) |
375 | { |
376 | if (hasEventTargetData()) { |
377 | document.didRemoveWheelEventHandler(*this, EventHandlerRemoval::All); |
378 | #if ENABLE(TOUCH_EVENTS) |
379 | #if PLATFORM(IOS_FAMILY) |
380 | document.removeTouchEventListener(*this, EventHandlerRemoval::All); |
381 | #endif |
382 | document.didRemoveTouchEventHandler(*this, EventHandlerRemoval::All); |
383 | #endif |
384 | } |
385 | |
386 | #if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS_FAMILY) |
387 | document.removeTouchEventHandler(*this, EventHandlerRemoval::All); |
388 | #endif |
389 | |
390 | if (auto* cache = document.existingAXObjectCache()) |
391 | cache->remove(*this); |
392 | } |
393 | |
394 | void Node::materializeRareData() |
395 | { |
396 | NodeRareData* data; |
397 | if (is<Element>(*this)) |
398 | data = std::make_unique<ElementRareData>(downcast<RenderElement>(m_data.m_renderer)).release(); |
399 | else |
400 | data = std::make_unique<NodeRareData>(m_data.m_renderer).release(); |
401 | ASSERT(data); |
402 | |
403 | m_data.m_rareData = data; |
404 | setFlag(HasRareDataFlag); |
405 | } |
406 | |
407 | void Node::clearRareData() |
408 | { |
409 | ASSERT(hasRareData()); |
410 | ASSERT(!transientMutationObserverRegistry() || transientMutationObserverRegistry()->isEmpty()); |
411 | |
412 | RenderObject* renderer = m_data.m_rareData->renderer(); |
413 | if (isElementNode()) |
414 | delete static_cast<ElementRareData*>(m_data.m_rareData); |
415 | else |
416 | delete static_cast<NodeRareData*>(m_data.m_rareData); |
417 | m_data.m_renderer = renderer; |
418 | clearFlag(HasRareDataFlag); |
419 | } |
420 | |
421 | bool Node::isNode() const |
422 | { |
423 | return true; |
424 | } |
425 | |
426 | String Node::nodeValue() const |
427 | { |
428 | return String(); |
429 | } |
430 | |
431 | ExceptionOr<void> Node::setNodeValue(const String&) |
432 | { |
433 | // By default, setting nodeValue has no effect. |
434 | return { }; |
435 | } |
436 | |
437 | RefPtr<NodeList> Node::childNodes() |
438 | { |
439 | if (is<ContainerNode>(*this)) |
440 | return ensureRareData().ensureNodeLists().ensureChildNodeList(downcast<ContainerNode>(*this)); |
441 | return ensureRareData().ensureNodeLists().ensureEmptyChildNodeList(*this); |
442 | } |
443 | |
444 | Node *Node::lastDescendant() const |
445 | { |
446 | Node *n = const_cast<Node *>(this); |
447 | while (n && n->lastChild()) |
448 | n = n->lastChild(); |
449 | return n; |
450 | } |
451 | |
452 | Node* Node::firstDescendant() const |
453 | { |
454 | Node *n = const_cast<Node *>(this); |
455 | while (n && n->firstChild()) |
456 | n = n->firstChild(); |
457 | return n; |
458 | } |
459 | |
460 | Element* Node::previousElementSibling() const |
461 | { |
462 | return ElementTraversal::previousSibling(*this); |
463 | } |
464 | |
465 | Element* Node::nextElementSibling() const |
466 | { |
467 | return ElementTraversal::nextSibling(*this); |
468 | } |
469 | |
470 | ExceptionOr<void> Node::insertBefore(Node& newChild, Node* refChild) |
471 | { |
472 | if (!is<ContainerNode>(*this)) |
473 | return Exception { HierarchyRequestError }; |
474 | return downcast<ContainerNode>(*this).insertBefore(newChild, refChild); |
475 | } |
476 | |
477 | ExceptionOr<void> Node::replaceChild(Node& newChild, Node& oldChild) |
478 | { |
479 | if (!is<ContainerNode>(*this)) |
480 | return Exception { HierarchyRequestError }; |
481 | return downcast<ContainerNode>(*this).replaceChild(newChild, oldChild); |
482 | } |
483 | |
484 | ExceptionOr<void> Node::removeChild(Node& oldChild) |
485 | { |
486 | if (!is<ContainerNode>(*this)) |
487 | return Exception { NotFoundError }; |
488 | return downcast<ContainerNode>(*this).removeChild(oldChild); |
489 | } |
490 | |
491 | ExceptionOr<void> Node::appendChild(Node& newChild) |
492 | { |
493 | if (!is<ContainerNode>(*this)) |
494 | return Exception { HierarchyRequestError }; |
495 | return downcast<ContainerNode>(*this).appendChild(newChild); |
496 | } |
497 | |
498 | static HashSet<RefPtr<Node>> nodeSetPreTransformedFromNodeOrStringVector(const Vector<NodeOrString>& vector) |
499 | { |
500 | HashSet<RefPtr<Node>> nodeSet; |
501 | for (const auto& variant : vector) { |
502 | WTF::switchOn(variant, |
503 | [&] (const RefPtr<Node>& node) { nodeSet.add(const_cast<Node*>(node.get())); }, |
504 | [] (const String&) { } |
505 | ); |
506 | } |
507 | return nodeSet; |
508 | } |
509 | |
510 | static RefPtr<Node> firstPrecedingSiblingNotInNodeSet(Node& context, const HashSet<RefPtr<Node>>& nodeSet) |
511 | { |
512 | for (auto* sibling = context.previousSibling(); sibling; sibling = sibling->previousSibling()) { |
513 | if (!nodeSet.contains(sibling)) |
514 | return sibling; |
515 | } |
516 | return nullptr; |
517 | } |
518 | |
519 | static RefPtr<Node> firstFollowingSiblingNotInNodeSet(Node& context, const HashSet<RefPtr<Node>>& nodeSet) |
520 | { |
521 | for (auto* sibling = context.nextSibling(); sibling; sibling = sibling->nextSibling()) { |
522 | if (!nodeSet.contains(sibling)) |
523 | return sibling; |
524 | } |
525 | return nullptr; |
526 | } |
527 | |
528 | ExceptionOr<RefPtr<Node>> Node::convertNodesOrStringsIntoNode(Vector<NodeOrString>&& nodeOrStringVector) |
529 | { |
530 | if (nodeOrStringVector.isEmpty()) |
531 | return nullptr; |
532 | |
533 | Vector<Ref<Node>> nodes; |
534 | nodes.reserveInitialCapacity(nodeOrStringVector.size()); |
535 | for (auto& variant : nodeOrStringVector) { |
536 | WTF::switchOn(variant, |
537 | [&](RefPtr<Node>& node) { nodes.uncheckedAppend(*node.get()); }, |
538 | [&](String& string) { nodes.uncheckedAppend(Text::create(document(), string)); } |
539 | ); |
540 | } |
541 | |
542 | if (nodes.size() == 1) |
543 | return RefPtr<Node> { WTFMove(nodes.first()) }; |
544 | |
545 | auto nodeToReturn = DocumentFragment::create(document()); |
546 | for (auto& node : nodes) { |
547 | auto appendResult = nodeToReturn->appendChild(node); |
548 | if (appendResult.hasException()) |
549 | return appendResult.releaseException(); |
550 | } |
551 | return RefPtr<Node> { WTFMove(nodeToReturn) }; |
552 | } |
553 | |
554 | ExceptionOr<void> Node::before(Vector<NodeOrString>&& nodeOrStringVector) |
555 | { |
556 | RefPtr<ContainerNode> parent = parentNode(); |
557 | if (!parent) |
558 | return { }; |
559 | |
560 | auto nodeSet = nodeSetPreTransformedFromNodeOrStringVector(nodeOrStringVector); |
561 | auto viablePreviousSibling = firstPrecedingSiblingNotInNodeSet(*this, nodeSet); |
562 | |
563 | auto result = convertNodesOrStringsIntoNode(WTFMove(nodeOrStringVector)); |
564 | if (result.hasException()) |
565 | return result.releaseException(); |
566 | auto node = result.releaseReturnValue(); |
567 | if (!node) |
568 | return { }; |
569 | |
570 | if (viablePreviousSibling) |
571 | viablePreviousSibling = viablePreviousSibling->nextSibling(); |
572 | else |
573 | viablePreviousSibling = parent->firstChild(); |
574 | |
575 | return parent->insertBefore(*node, viablePreviousSibling.get()); |
576 | } |
577 | |
578 | ExceptionOr<void> Node::after(Vector<NodeOrString>&& nodeOrStringVector) |
579 | { |
580 | RefPtr<ContainerNode> parent = parentNode(); |
581 | if (!parent) |
582 | return { }; |
583 | |
584 | auto nodeSet = nodeSetPreTransformedFromNodeOrStringVector(nodeOrStringVector); |
585 | auto viableNextSibling = firstFollowingSiblingNotInNodeSet(*this, nodeSet); |
586 | |
587 | auto result = convertNodesOrStringsIntoNode(WTFMove(nodeOrStringVector)); |
588 | if (result.hasException()) |
589 | return result.releaseException(); |
590 | auto node = result.releaseReturnValue(); |
591 | if (!node) |
592 | return { }; |
593 | |
594 | return parent->insertBefore(*node, viableNextSibling.get()); |
595 | } |
596 | |
597 | ExceptionOr<void> Node::replaceWith(Vector<NodeOrString>&& nodeOrStringVector) |
598 | { |
599 | RefPtr<ContainerNode> parent = parentNode(); |
600 | if (!parent) |
601 | return { }; |
602 | |
603 | auto nodeSet = nodeSetPreTransformedFromNodeOrStringVector(nodeOrStringVector); |
604 | auto viableNextSibling = firstFollowingSiblingNotInNodeSet(*this, nodeSet); |
605 | |
606 | auto result = convertNodesOrStringsIntoNode(WTFMove(nodeOrStringVector)); |
607 | if (result.hasException()) |
608 | return result.releaseException(); |
609 | |
610 | if (parentNode() == parent) { |
611 | if (auto node = result.releaseReturnValue()) |
612 | return parent->replaceChild(*node, *this); |
613 | return parent->removeChild(*this); |
614 | } |
615 | |
616 | if (auto node = result.releaseReturnValue()) |
617 | return parent->insertBefore(*node, viableNextSibling.get()); |
618 | return { }; |
619 | } |
620 | |
621 | ExceptionOr<void> Node::remove() |
622 | { |
623 | auto* parent = parentNode(); |
624 | if (!parent) |
625 | return { }; |
626 | return parent->removeChild(*this); |
627 | } |
628 | |
629 | void Node::normalize() |
630 | { |
631 | // Go through the subtree beneath us, normalizing all nodes. This means that |
632 | // any two adjacent text nodes are merged and any empty text nodes are removed. |
633 | |
634 | RefPtr<Node> node = this; |
635 | while (Node* firstChild = node->firstChild()) |
636 | node = firstChild; |
637 | while (node) { |
638 | NodeType type = node->nodeType(); |
639 | if (type == ELEMENT_NODE) |
640 | downcast<Element>(*node).normalizeAttributes(); |
641 | |
642 | if (node == this) |
643 | break; |
644 | |
645 | if (type != TEXT_NODE) { |
646 | node = NodeTraversal::nextPostOrder(*node); |
647 | continue; |
648 | } |
649 | |
650 | RefPtr<Text> text = downcast<Text>(node.get()); |
651 | |
652 | // Remove empty text nodes. |
653 | if (!text->length()) { |
654 | // Care must be taken to get the next node before removing the current node. |
655 | node = NodeTraversal::nextPostOrder(*node); |
656 | text->remove(); |
657 | continue; |
658 | } |
659 | |
660 | // Merge text nodes. |
661 | while (Node* nextSibling = node->nextSibling()) { |
662 | if (nextSibling->nodeType() != TEXT_NODE) |
663 | break; |
664 | Ref<Text> nextText = downcast<Text>(*nextSibling); |
665 | |
666 | // Remove empty text nodes. |
667 | if (!nextText->length()) { |
668 | nextText->remove(); |
669 | continue; |
670 | } |
671 | |
672 | // Both non-empty text nodes. Merge them. |
673 | unsigned offset = text->length(); |
674 | text->appendData(nextText->data()); |
675 | document().textNodesMerged(nextText, offset); |
676 | nextText->remove(); |
677 | } |
678 | |
679 | node = NodeTraversal::nextPostOrder(*node); |
680 | } |
681 | } |
682 | |
683 | ExceptionOr<Ref<Node>> Node::cloneNodeForBindings(bool deep) |
684 | { |
685 | if (UNLIKELY(isShadowRoot())) |
686 | return Exception { NotSupportedError }; |
687 | return cloneNode(deep); |
688 | } |
689 | |
690 | const AtomicString& Node::prefix() const |
691 | { |
692 | // For nodes other than elements and attributes, the prefix is always null |
693 | return nullAtom(); |
694 | } |
695 | |
696 | ExceptionOr<void> Node::setPrefix(const AtomicString&) |
697 | { |
698 | // The spec says that for nodes other than elements and attributes, prefix is always null. |
699 | // It does not say what to do when the user tries to set the prefix on another type of |
700 | // node, however Mozilla throws a NamespaceError exception. |
701 | return Exception { NamespaceError }; |
702 | } |
703 | |
704 | const AtomicString& Node::localName() const |
705 | { |
706 | return nullAtom(); |
707 | } |
708 | |
709 | const AtomicString& Node::namespaceURI() const |
710 | { |
711 | return nullAtom(); |
712 | } |
713 | |
714 | bool Node::isContentEditable() |
715 | { |
716 | return computeEditability(UserSelectAllDoesNotAffectEditability, ShouldUpdateStyle::Update) != Editability::ReadOnly; |
717 | } |
718 | |
719 | bool Node::isContentRichlyEditable() |
720 | { |
721 | return computeEditability(UserSelectAllIsAlwaysNonEditable, ShouldUpdateStyle::Update) == Editability::CanEditRichly; |
722 | } |
723 | |
724 | void Node::inspect() |
725 | { |
726 | if (document().page()) |
727 | document().page()->inspectorController().inspect(this); |
728 | } |
729 | |
730 | static Node::Editability computeEditabilityFromComputedStyle(const Node& startNode, Node::UserSelectAllTreatment treatment) |
731 | { |
732 | // Ideally we'd call ASSERT(!needsStyleRecalc()) here, but |
733 | // ContainerNode::setFocus() calls invalidateStyleForSubtree(), so the assertion |
734 | // would fire in the middle of Document::setFocusedElement(). |
735 | |
736 | for (const Node* node = &startNode; node; node = node->parentNode()) { |
737 | auto* style = node->isDocumentNode() ? node->renderStyle() : const_cast<Node*>(node)->computedStyle(); |
738 | if (!style) |
739 | continue; |
740 | if (style->display() == DisplayType::None) |
741 | continue; |
742 | #if ENABLE(USERSELECT_ALL) |
743 | // Elements with user-select: all style are considered atomic |
744 | // therefore non editable. |
745 | if (treatment == Node::UserSelectAllIsAlwaysNonEditable && style->userSelect() == UserSelect::All) |
746 | return Node::Editability::ReadOnly; |
747 | #else |
748 | UNUSED_PARAM(treatment); |
749 | #endif |
750 | switch (style->userModify()) { |
751 | case UserModify::ReadOnly: |
752 | return Node::Editability::ReadOnly; |
753 | case UserModify::ReadWrite: |
754 | return Node::Editability::CanEditRichly; |
755 | case UserModify::ReadWritePlaintextOnly: |
756 | return Node::Editability::CanEditPlainText; |
757 | } |
758 | ASSERT_NOT_REACHED(); |
759 | return Node::Editability::ReadOnly; |
760 | } |
761 | return Node::Editability::ReadOnly; |
762 | } |
763 | |
764 | Node::Editability Node::computeEditability(UserSelectAllTreatment treatment, ShouldUpdateStyle shouldUpdateStyle) const |
765 | { |
766 | if (!document().hasLivingRenderTree() || isPseudoElement()) |
767 | return Editability::ReadOnly; |
768 | |
769 | if (isInShadowTree()) |
770 | return HTMLElement::editabilityFromContentEditableAttr(*this); |
771 | |
772 | if (document().frame() && document().frame()->page() && document().frame()->page()->isEditable()) |
773 | return Editability::CanEditRichly; |
774 | |
775 | if (shouldUpdateStyle == ShouldUpdateStyle::Update && document().needsStyleRecalc()) { |
776 | if (!document().usesStyleBasedEditability()) |
777 | return HTMLElement::editabilityFromContentEditableAttr(*this); |
778 | document().updateStyleIfNeeded(); |
779 | } |
780 | return computeEditabilityFromComputedStyle(*this, treatment); |
781 | } |
782 | |
783 | RenderBox* Node::renderBox() const |
784 | { |
785 | RenderObject* renderer = this->renderer(); |
786 | return is<RenderBox>(renderer) ? downcast<RenderBox>(renderer) : nullptr; |
787 | } |
788 | |
789 | RenderBoxModelObject* Node::renderBoxModelObject() const |
790 | { |
791 | RenderObject* renderer = this->renderer(); |
792 | return is<RenderBoxModelObject>(renderer) ? downcast<RenderBoxModelObject>(renderer) : nullptr; |
793 | } |
794 | |
795 | LayoutRect Node::renderRect(bool* isReplaced) |
796 | { |
797 | RenderObject* hitRenderer = this->renderer(); |
798 | if (!hitRenderer && is<HTMLAreaElement>(*this)) { |
799 | auto& area = downcast<HTMLAreaElement>(*this); |
800 | if (auto* imageElement = area.imageElement()) |
801 | hitRenderer = imageElement->renderer(); |
802 | } |
803 | RenderObject* renderer = hitRenderer; |
804 | while (renderer && !renderer->isBody() && !renderer->isDocumentElementRenderer()) { |
805 | if (renderer->isRenderBlock() || renderer->isInlineBlockOrInlineTable() || renderer->isReplaced()) { |
806 | *isReplaced = renderer->isReplaced(); |
807 | return renderer->absoluteBoundingBoxRect(); |
808 | } |
809 | renderer = renderer->parent(); |
810 | } |
811 | return LayoutRect(); |
812 | } |
813 | |
814 | void Node::refEventTarget() |
815 | { |
816 | ref(); |
817 | } |
818 | |
819 | void Node::derefEventTarget() |
820 | { |
821 | deref(); |
822 | } |
823 | |
824 | void Node::adjustStyleValidity(Style::Validity validity, Style::InvalidationMode mode) |
825 | { |
826 | if (validity > styleValidity()) { |
827 | m_nodeFlags &= ~StyleValidityMask; |
828 | m_nodeFlags |= static_cast<unsigned>(validity) << StyleValidityShift; |
829 | } |
830 | if (mode == Style::InvalidationMode::RecompositeLayer) |
831 | setFlag(StyleResolutionShouldRecompositeLayerFlag); |
832 | } |
833 | |
834 | inline void Node::updateAncestorsForStyleRecalc() |
835 | { |
836 | auto composedAncestors = composedTreeAncestors(*this); |
837 | auto it = composedAncestors.begin(); |
838 | auto end = composedAncestors.end(); |
839 | if (it != end) { |
840 | it->setDirectChildNeedsStyleRecalc(); |
841 | |
842 | for (; it != end; ++it) { |
843 | // Iterator skips over shadow roots. |
844 | if (auto* shadowRoot = it->shadowRoot()) |
845 | shadowRoot->setChildNeedsStyleRecalc(); |
846 | if (it->childNeedsStyleRecalc()) |
847 | break; |
848 | it->setChildNeedsStyleRecalc(); |
849 | } |
850 | } |
851 | |
852 | auto* documentElement = document().documentElement(); |
853 | if (!documentElement) |
854 | return; |
855 | if (!documentElement->childNeedsStyleRecalc() && !documentElement->needsStyleRecalc()) |
856 | return; |
857 | document().setChildNeedsStyleRecalc(); |
858 | document().scheduleStyleRecalc(); |
859 | } |
860 | |
861 | void Node::invalidateStyle(Style::Validity validity, Style::InvalidationMode mode) |
862 | { |
863 | ASSERT(validity != Style::Validity::Valid); |
864 | if (!inRenderedDocument()) |
865 | return; |
866 | |
867 | // FIXME: This should eventually be an ASSERT. |
868 | if (document().inRenderTreeUpdate()) |
869 | return; |
870 | |
871 | // FIXME: Why the second condition? |
872 | bool markAncestors = styleValidity() == Style::Validity::Valid || validity == Style::Validity::SubtreeAndRenderersInvalid; |
873 | |
874 | adjustStyleValidity(validity, mode); |
875 | |
876 | if (markAncestors) |
877 | updateAncestorsForStyleRecalc(); |
878 | } |
879 | |
880 | unsigned Node::computeNodeIndex() const |
881 | { |
882 | unsigned count = 0; |
883 | for (Node* sibling = previousSibling(); sibling; sibling = sibling->previousSibling()) |
884 | ++count; |
885 | return count; |
886 | } |
887 | |
888 | template<unsigned type> |
889 | bool shouldInvalidateNodeListCachesForAttr(const unsigned nodeListCounts[], const QualifiedName& attrName) |
890 | { |
891 | if (nodeListCounts[type] && shouldInvalidateTypeOnAttributeChange(static_cast<NodeListInvalidationType>(type), attrName)) |
892 | return true; |
893 | return shouldInvalidateNodeListCachesForAttr<type + 1>(nodeListCounts, attrName); |
894 | } |
895 | |
896 | template<> |
897 | bool shouldInvalidateNodeListCachesForAttr<numNodeListInvalidationTypes>(const unsigned[], const QualifiedName&) |
898 | { |
899 | return false; |
900 | } |
901 | |
902 | inline bool Document::shouldInvalidateNodeListAndCollectionCaches() const |
903 | { |
904 | for (int type = 0; type < numNodeListInvalidationTypes; ++type) { |
905 | if (m_nodeListAndCollectionCounts[type]) |
906 | return true; |
907 | } |
908 | return false; |
909 | } |
910 | |
911 | inline bool Document::shouldInvalidateNodeListAndCollectionCachesForAttribute(const QualifiedName& attrName) const |
912 | { |
913 | return shouldInvalidateNodeListCachesForAttr<DoNotInvalidateOnAttributeChanges + 1>(m_nodeListAndCollectionCounts, attrName); |
914 | } |
915 | |
916 | template <typename InvalidationFunction> |
917 | void Document::invalidateNodeListAndCollectionCaches(InvalidationFunction invalidate) |
918 | { |
919 | for (auto* list : copyToVectorSpecialization<Vector<LiveNodeList*, 8>>(m_listsInvalidatedAtDocument)) |
920 | invalidate(*list); |
921 | |
922 | for (auto* collection : copyToVectorSpecialization<Vector<HTMLCollection*, 8>>(m_collectionsInvalidatedAtDocument)) |
923 | invalidate(*collection); |
924 | } |
925 | |
926 | void Node::invalidateNodeListAndCollectionCachesInAncestors() |
927 | { |
928 | if (hasRareData()) { |
929 | if (auto* lists = rareData()->nodeLists()) |
930 | lists->clearChildNodeListCache(); |
931 | } |
932 | |
933 | if (!document().shouldInvalidateNodeListAndCollectionCaches()) |
934 | return; |
935 | |
936 | document().invalidateNodeListAndCollectionCaches([](auto& list) { |
937 | list.invalidateCache(); |
938 | }); |
939 | |
940 | for (auto* node = this; node; node = node->parentNode()) { |
941 | if (!node->hasRareData()) |
942 | continue; |
943 | |
944 | if (auto* lists = node->rareData()->nodeLists()) |
945 | lists->invalidateCaches(); |
946 | } |
947 | } |
948 | |
949 | void Node::invalidateNodeListAndCollectionCachesInAncestorsForAttribute(const QualifiedName& attrName) |
950 | { |
951 | ASSERT(is<Element>(*this)); |
952 | |
953 | if (!document().shouldInvalidateNodeListAndCollectionCachesForAttribute(attrName)) |
954 | return; |
955 | |
956 | document().invalidateNodeListAndCollectionCaches([&attrName](auto& list) { |
957 | list.invalidateCacheForAttribute(attrName); |
958 | }); |
959 | |
960 | for (auto* node = this; node; node = node->parentNode()) { |
961 | if (!node->hasRareData()) |
962 | continue; |
963 | |
964 | if (auto* lists = node->rareData()->nodeLists()) |
965 | lists->invalidateCachesForAttribute(attrName); |
966 | } |
967 | } |
968 | |
969 | NodeListsNodeData* Node::nodeLists() |
970 | { |
971 | return hasRareData() ? rareData()->nodeLists() : nullptr; |
972 | } |
973 | |
974 | void Node::clearNodeLists() |
975 | { |
976 | rareData()->clearNodeLists(); |
977 | } |
978 | |
979 | ExceptionOr<void> Node::checkSetPrefix(const AtomicString& prefix) |
980 | { |
981 | // Perform error checking as required by spec for setting Node.prefix. Used by |
982 | // Element::setPrefix() and Attr::setPrefix() |
983 | |
984 | if (!prefix.isEmpty() && !Document::isValidName(prefix)) |
985 | return Exception { InvalidCharacterError }; |
986 | |
987 | // FIXME: Raise NamespaceError if prefix is malformed per the Namespaces in XML specification. |
988 | |
989 | auto& namespaceURI = this->namespaceURI(); |
990 | if (namespaceURI.isEmpty() && !prefix.isEmpty()) |
991 | return Exception { NamespaceError }; |
992 | if (prefix == xmlAtom() && namespaceURI != XMLNames::xmlNamespaceURI) |
993 | return Exception { NamespaceError }; |
994 | |
995 | // Attribute-specific checks are in Attr::setPrefix(). |
996 | |
997 | return { }; |
998 | } |
999 | |
1000 | bool Node::isDescendantOf(const Node& other) const |
1001 | { |
1002 | // Return true if other is an ancestor of this, otherwise false |
1003 | if (!other.hasChildNodes() || isConnected() != other.isConnected()) |
1004 | return false; |
1005 | if (other.isDocumentNode()) |
1006 | return &document() == &other && !isDocumentNode() && isConnected(); |
1007 | for (const auto* ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode()) { |
1008 | if (ancestor == &other) |
1009 | return true; |
1010 | } |
1011 | return false; |
1012 | } |
1013 | |
1014 | bool Node::isDescendantOrShadowDescendantOf(const Node* other) const |
1015 | { |
1016 | // FIXME: This element's shadow tree's host could be inside another shadow tree. |
1017 | // This function doesn't handle that case correctly. Maybe share code with |
1018 | // the containsIncludingShadowDOM function? |
1019 | return other && (isDescendantOf(*other) || other->contains(shadowHost())); |
1020 | } |
1021 | |
1022 | bool Node::contains(const Node* node) const |
1023 | { |
1024 | return this == node || (node && node->isDescendantOf(*this)); |
1025 | } |
1026 | |
1027 | bool Node::containsIncludingShadowDOM(const Node* node) const |
1028 | { |
1029 | for (; node; node = node->parentOrShadowHostNode()) { |
1030 | if (node == this) |
1031 | return true; |
1032 | } |
1033 | return false; |
1034 | } |
1035 | |
1036 | Node* Node::pseudoAwarePreviousSibling() const |
1037 | { |
1038 | Element* parentOrHost = is<PseudoElement>(*this) ? downcast<PseudoElement>(*this).hostElement() : parentElement(); |
1039 | if (parentOrHost && !previousSibling()) { |
1040 | if (isAfterPseudoElement() && parentOrHost->lastChild()) |
1041 | return parentOrHost->lastChild(); |
1042 | if (!isBeforePseudoElement()) |
1043 | return parentOrHost->beforePseudoElement(); |
1044 | } |
1045 | return previousSibling(); |
1046 | } |
1047 | |
1048 | Node* Node::pseudoAwareNextSibling() const |
1049 | { |
1050 | Element* parentOrHost = is<PseudoElement>(*this) ? downcast<PseudoElement>(*this).hostElement() : parentElement(); |
1051 | if (parentOrHost && !nextSibling()) { |
1052 | if (isBeforePseudoElement() && parentOrHost->firstChild()) |
1053 | return parentOrHost->firstChild(); |
1054 | if (!isAfterPseudoElement()) |
1055 | return parentOrHost->afterPseudoElement(); |
1056 | } |
1057 | return nextSibling(); |
1058 | } |
1059 | |
1060 | Node* Node::pseudoAwareFirstChild() const |
1061 | { |
1062 | if (is<Element>(*this)) { |
1063 | const Element& currentElement = downcast<Element>(*this); |
1064 | Node* first = currentElement.beforePseudoElement(); |
1065 | if (first) |
1066 | return first; |
1067 | first = currentElement.firstChild(); |
1068 | if (!first) |
1069 | first = currentElement.afterPseudoElement(); |
1070 | return first; |
1071 | } |
1072 | return firstChild(); |
1073 | } |
1074 | |
1075 | Node* Node::pseudoAwareLastChild() const |
1076 | { |
1077 | if (is<Element>(*this)) { |
1078 | const Element& currentElement = downcast<Element>(*this); |
1079 | Node* last = currentElement.afterPseudoElement(); |
1080 | if (last) |
1081 | return last; |
1082 | last = currentElement.lastChild(); |
1083 | if (!last) |
1084 | last = currentElement.beforePseudoElement(); |
1085 | return last; |
1086 | } |
1087 | return lastChild(); |
1088 | } |
1089 | |
1090 | const RenderStyle* Node::computedStyle(PseudoId pseudoElementSpecifier) |
1091 | { |
1092 | auto* composedParent = composedTreeAncestors(*this).first(); |
1093 | if (!composedParent) |
1094 | return nullptr; |
1095 | return composedParent->computedStyle(pseudoElementSpecifier); |
1096 | } |
1097 | |
1098 | int Node::maxCharacterOffset() const |
1099 | { |
1100 | ASSERT_NOT_REACHED(); |
1101 | return 0; |
1102 | } |
1103 | |
1104 | // FIXME: Shouldn't these functions be in the editing code? Code that asks questions about HTML in the core DOM class |
1105 | // is obviously misplaced. |
1106 | bool Node::canStartSelection() const |
1107 | { |
1108 | if (hasEditableStyle()) |
1109 | return true; |
1110 | |
1111 | if (renderer()) { |
1112 | const RenderStyle& style = renderer()->style(); |
1113 | // We allow selections to begin within an element that has -webkit-user-select: none set, |
1114 | // but if the element is draggable then dragging should take priority over selection. |
1115 | if (style.userDrag() == UserDrag::Element && style.userSelect() == UserSelect::None) |
1116 | return false; |
1117 | } |
1118 | return parentOrShadowHostNode() ? parentOrShadowHostNode()->canStartSelection() : true; |
1119 | } |
1120 | |
1121 | Element* Node::shadowHost() const |
1122 | { |
1123 | if (ShadowRoot* root = containingShadowRoot()) |
1124 | return root->host(); |
1125 | return nullptr; |
1126 | } |
1127 | |
1128 | ShadowRoot* Node::containingShadowRoot() const |
1129 | { |
1130 | ContainerNode& root = treeScope().rootNode(); |
1131 | return is<ShadowRoot>(root) ? downcast<ShadowRoot>(&root) : nullptr; |
1132 | } |
1133 | |
1134 | #if !ASSERT_DISABLED |
1135 | // https://dom.spec.whatwg.org/#concept-closed-shadow-hidden |
1136 | static bool isClosedShadowHiddenUsingSpecDefinition(const Node& A, const Node& B) |
1137 | { |
1138 | return A.isInShadowTree() |
1139 | && !A.rootNode().containsIncludingShadowDOM(&B) |
1140 | && (A.containingShadowRoot()->mode() != ShadowRootMode::Open || isClosedShadowHiddenUsingSpecDefinition(*A.shadowHost(), B)); |
1141 | } |
1142 | #endif |
1143 | |
1144 | // http://w3c.github.io/webcomponents/spec/shadow/#dfn-unclosed-node |
1145 | bool Node::isClosedShadowHidden(const Node& otherNode) const |
1146 | { |
1147 | // Use Vector instead of HashSet since we expect the number of ancestor tree scopes to be small. |
1148 | Vector<TreeScope*, 8> ancestorScopesOfThisNode; |
1149 | |
1150 | for (auto* scope = &treeScope(); scope; scope = scope->parentTreeScope()) |
1151 | ancestorScopesOfThisNode.append(scope); |
1152 | |
1153 | for (auto* treeScopeThatCanAccessOtherNode = &otherNode.treeScope(); treeScopeThatCanAccessOtherNode; treeScopeThatCanAccessOtherNode = treeScopeThatCanAccessOtherNode->parentTreeScope()) { |
1154 | for (auto* scope : ancestorScopesOfThisNode) { |
1155 | if (scope == treeScopeThatCanAccessOtherNode) { |
1156 | ASSERT(!isClosedShadowHiddenUsingSpecDefinition(otherNode, *this)); |
1157 | return false; // treeScopeThatCanAccessOtherNode is a shadow-including inclusive ancestor of this node. |
1158 | } |
1159 | } |
1160 | auto& root = treeScopeThatCanAccessOtherNode->rootNode(); |
1161 | if (is<ShadowRoot>(root) && downcast<ShadowRoot>(root).mode() != ShadowRootMode::Open) |
1162 | break; |
1163 | } |
1164 | |
1165 | ASSERT(isClosedShadowHiddenUsingSpecDefinition(otherNode, *this)); |
1166 | return true; |
1167 | } |
1168 | |
1169 | static inline ShadowRoot* parentShadowRoot(const Node& node) |
1170 | { |
1171 | if (auto* parent = node.parentElement()) |
1172 | return parent->shadowRoot(); |
1173 | return nullptr; |
1174 | } |
1175 | |
1176 | HTMLSlotElement* Node::assignedSlot() const |
1177 | { |
1178 | if (auto* shadowRoot = parentShadowRoot(*this)) |
1179 | return shadowRoot->findAssignedSlot(*this); |
1180 | return nullptr; |
1181 | } |
1182 | |
1183 | HTMLSlotElement* Node::assignedSlotForBindings() const |
1184 | { |
1185 | auto* shadowRoot = parentShadowRoot(*this); |
1186 | if (shadowRoot && shadowRoot->mode() == ShadowRootMode::Open) |
1187 | return shadowRoot->findAssignedSlot(*this); |
1188 | return nullptr; |
1189 | } |
1190 | |
1191 | ContainerNode* Node::parentInComposedTree() const |
1192 | { |
1193 | ASSERT(isMainThreadOrGCThread()); |
1194 | if (auto* slot = assignedSlot()) |
1195 | return slot; |
1196 | if (is<ShadowRoot>(*this)) |
1197 | return downcast<ShadowRoot>(*this).host(); |
1198 | return parentNode(); |
1199 | } |
1200 | |
1201 | Element* Node::parentElementInComposedTree() const |
1202 | { |
1203 | if (auto* slot = assignedSlot()) |
1204 | return slot; |
1205 | if (is<PseudoElement>(*this)) |
1206 | return downcast<PseudoElement>(*this).hostElement(); |
1207 | if (auto* parent = parentNode()) { |
1208 | if (is<ShadowRoot>(*parent)) |
1209 | return downcast<ShadowRoot>(*parent).host(); |
1210 | if (is<Element>(*parent)) |
1211 | return downcast<Element>(parent); |
1212 | } |
1213 | return nullptr; |
1214 | } |
1215 | |
1216 | bool Node::isInUserAgentShadowTree() const |
1217 | { |
1218 | auto* shadowRoot = containingShadowRoot(); |
1219 | return shadowRoot && shadowRoot->mode() == ShadowRootMode::UserAgent; |
1220 | } |
1221 | |
1222 | Node* Node::nonBoundaryShadowTreeRootNode() |
1223 | { |
1224 | ASSERT(!isShadowRoot()); |
1225 | Node* root = this; |
1226 | while (root) { |
1227 | if (root->isShadowRoot()) |
1228 | return root; |
1229 | Node* parent = root->parentNodeGuaranteedHostFree(); |
1230 | if (parent && parent->isShadowRoot()) |
1231 | return root; |
1232 | root = parent; |
1233 | } |
1234 | return 0; |
1235 | } |
1236 | |
1237 | ContainerNode* Node::nonShadowBoundaryParentNode() const |
1238 | { |
1239 | ContainerNode* parent = parentNode(); |
1240 | return parent && !parent->isShadowRoot() ? parent : nullptr; |
1241 | } |
1242 | |
1243 | Element* Node::parentOrShadowHostElement() const |
1244 | { |
1245 | ContainerNode* parent = parentOrShadowHostNode(); |
1246 | if (!parent) |
1247 | return nullptr; |
1248 | |
1249 | if (is<ShadowRoot>(*parent)) |
1250 | return downcast<ShadowRoot>(*parent).host(); |
1251 | |
1252 | if (!is<Element>(*parent)) |
1253 | return nullptr; |
1254 | |
1255 | return downcast<Element>(parent); |
1256 | } |
1257 | |
1258 | Node& Node::traverseToRootNode() const |
1259 | { |
1260 | Node* node = const_cast<Node*>(this); |
1261 | Node* highest = node; |
1262 | for (; node; node = node->parentNode()) |
1263 | highest = node; |
1264 | return *highest; |
1265 | } |
1266 | |
1267 | // https://dom.spec.whatwg.org/#concept-shadow-including-root |
1268 | Node& Node::shadowIncludingRoot() const |
1269 | { |
1270 | auto& root = rootNode(); |
1271 | if (!is<ShadowRoot>(root)) |
1272 | return root; |
1273 | auto* host = downcast<ShadowRoot>(root).host(); |
1274 | return host ? host->shadowIncludingRoot() : root; |
1275 | } |
1276 | |
1277 | Node& Node::getRootNode(const GetRootNodeOptions& options) const |
1278 | { |
1279 | return options.composed ? shadowIncludingRoot() : rootNode(); |
1280 | } |
1281 | |
1282 | Node::InsertedIntoAncestorResult Node::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree) |
1283 | { |
1284 | if (insertionType.connectedToDocument) |
1285 | setFlag(IsConnectedFlag); |
1286 | if (parentOfInsertedTree.isInShadowTree()) |
1287 | setFlag(IsInShadowTreeFlag); |
1288 | |
1289 | invalidateStyle(Style::Validity::SubtreeAndRenderersInvalid); |
1290 | |
1291 | return InsertedIntoAncestorResult::Done; |
1292 | } |
1293 | |
1294 | void Node::removedFromAncestor(RemovalType removalType, ContainerNode&) |
1295 | { |
1296 | if (removalType.disconnectedFromDocument) |
1297 | clearFlag(IsConnectedFlag); |
1298 | if (isInShadowTree() && !treeScope().rootNode().isShadowRoot()) |
1299 | clearFlag(IsInShadowTreeFlag); |
1300 | } |
1301 | |
1302 | bool Node::isRootEditableElement() const |
1303 | { |
1304 | return hasEditableStyle() && isElementNode() && (!parentNode() || !parentNode()->hasEditableStyle() |
1305 | || !parentNode()->isElementNode() || hasTagName(bodyTag)); |
1306 | } |
1307 | |
1308 | Element* Node::rootEditableElement() const |
1309 | { |
1310 | Element* result = nullptr; |
1311 | for (Node* node = const_cast<Node*>(this); node && node->hasEditableStyle(); node = node->parentNode()) { |
1312 | if (is<Element>(*node)) |
1313 | result = downcast<Element>(node); |
1314 | if (is<HTMLBodyElement>(*node)) |
1315 | break; |
1316 | } |
1317 | return result; |
1318 | } |
1319 | |
1320 | // FIXME: End of obviously misplaced HTML editing functions. Try to move these out of Node. |
1321 | |
1322 | Document* Node::ownerDocument() const |
1323 | { |
1324 | Document* document = &this->document(); |
1325 | return document == this ? nullptr : document; |
1326 | } |
1327 | |
1328 | const URL& Node::baseURI() const |
1329 | { |
1330 | auto& url = document().baseURL(); |
1331 | return url.isNull() ? WTF::blankURL() : url; |
1332 | } |
1333 | |
1334 | bool Node::isEqualNode(Node* other) const |
1335 | { |
1336 | if (!other) |
1337 | return false; |
1338 | |
1339 | NodeType nodeType = this->nodeType(); |
1340 | if (nodeType != other->nodeType()) |
1341 | return false; |
1342 | |
1343 | switch (nodeType) { |
1344 | case Node::DOCUMENT_TYPE_NODE: { |
1345 | auto& thisDocType = downcast<DocumentType>(*this); |
1346 | auto& otherDocType = downcast<DocumentType>(*other); |
1347 | if (thisDocType.name() != otherDocType.name()) |
1348 | return false; |
1349 | if (thisDocType.publicId() != otherDocType.publicId()) |
1350 | return false; |
1351 | if (thisDocType.systemId() != otherDocType.systemId()) |
1352 | return false; |
1353 | break; |
1354 | } |
1355 | case Node::ELEMENT_NODE: { |
1356 | auto& thisElement = downcast<Element>(*this); |
1357 | auto& otherElement = downcast<Element>(*other); |
1358 | if (thisElement.tagQName() != otherElement.tagQName()) |
1359 | return false; |
1360 | if (!thisElement.hasEquivalentAttributes(otherElement)) |
1361 | return false; |
1362 | break; |
1363 | } |
1364 | case Node::PROCESSING_INSTRUCTION_NODE: { |
1365 | auto& thisProcessingInstruction = downcast<ProcessingInstruction>(*this); |
1366 | auto& otherProcessingInstruction = downcast<ProcessingInstruction>(*other); |
1367 | if (thisProcessingInstruction.target() != otherProcessingInstruction.target()) |
1368 | return false; |
1369 | if (thisProcessingInstruction.data() != otherProcessingInstruction.data()) |
1370 | return false; |
1371 | break; |
1372 | } |
1373 | case Node::CDATA_SECTION_NODE: |
1374 | case Node::TEXT_NODE: |
1375 | case Node::COMMENT_NODE: { |
1376 | auto& thisCharacterData = downcast<CharacterData>(*this); |
1377 | auto& otherCharacterData = downcast<CharacterData>(*other); |
1378 | if (thisCharacterData.data() != otherCharacterData.data()) |
1379 | return false; |
1380 | break; |
1381 | } |
1382 | case Node::ATTRIBUTE_NODE: { |
1383 | auto& thisAttribute = downcast<Attr>(*this); |
1384 | auto& otherAttribute = downcast<Attr>(*other); |
1385 | if (thisAttribute.qualifiedName() != otherAttribute.qualifiedName()) |
1386 | return false; |
1387 | if (thisAttribute.value() != otherAttribute.value()) |
1388 | return false; |
1389 | break; |
1390 | } |
1391 | case Node::DOCUMENT_NODE: |
1392 | case Node::DOCUMENT_FRAGMENT_NODE: |
1393 | break; |
1394 | } |
1395 | |
1396 | Node* child = firstChild(); |
1397 | Node* otherChild = other->firstChild(); |
1398 | |
1399 | while (child) { |
1400 | if (!child->isEqualNode(otherChild)) |
1401 | return false; |
1402 | |
1403 | child = child->nextSibling(); |
1404 | otherChild = otherChild->nextSibling(); |
1405 | } |
1406 | |
1407 | if (otherChild) |
1408 | return false; |
1409 | |
1410 | return true; |
1411 | } |
1412 | |
1413 | // https://dom.spec.whatwg.org/#locate-a-namespace |
1414 | static const AtomicString& locateDefaultNamespace(const Node& node, const AtomicString& prefix) |
1415 | { |
1416 | switch (node.nodeType()) { |
1417 | case Node::ELEMENT_NODE: { |
1418 | auto& element = downcast<Element>(node); |
1419 | auto& namespaceURI = element.namespaceURI(); |
1420 | if (!namespaceURI.isNull() && element.prefix() == prefix) |
1421 | return namespaceURI; |
1422 | |
1423 | if (element.hasAttributes()) { |
1424 | for (auto& attribute : element.attributesIterator()) { |
1425 | if (attribute.namespaceURI() != XMLNSNames::xmlnsNamespaceURI) |
1426 | continue; |
1427 | |
1428 | if ((prefix.isNull() && attribute.prefix().isNull() && attribute.localName() == xmlnsAtom()) || (attribute.prefix() == xmlnsAtom() && attribute.localName() == prefix)) { |
1429 | auto& result = attribute.value(); |
1430 | return result.isEmpty() ? nullAtom() : result; |
1431 | } |
1432 | } |
1433 | } |
1434 | auto* parent = node.parentElement(); |
1435 | return parent ? locateDefaultNamespace(*parent, prefix) : nullAtom(); |
1436 | } |
1437 | case Node::DOCUMENT_NODE: |
1438 | if (auto* documentElement = downcast<Document>(node).documentElement()) |
1439 | return locateDefaultNamespace(*documentElement, prefix); |
1440 | return nullAtom(); |
1441 | case Node::DOCUMENT_TYPE_NODE: |
1442 | case Node::DOCUMENT_FRAGMENT_NODE: |
1443 | return nullAtom(); |
1444 | case Node::ATTRIBUTE_NODE: |
1445 | if (auto* ownerElement = downcast<Attr>(node).ownerElement()) |
1446 | return locateDefaultNamespace(*ownerElement, prefix); |
1447 | return nullAtom(); |
1448 | default: |
1449 | if (auto* parent = node.parentElement()) |
1450 | return locateDefaultNamespace(*parent, prefix); |
1451 | return nullAtom(); |
1452 | } |
1453 | } |
1454 | |
1455 | // https://dom.spec.whatwg.org/#dom-node-isdefaultnamespace |
1456 | bool Node::isDefaultNamespace(const AtomicString& potentiallyEmptyNamespace) const |
1457 | { |
1458 | const AtomicString& namespaceURI = potentiallyEmptyNamespace.isEmpty() ? nullAtom() : potentiallyEmptyNamespace; |
1459 | return locateDefaultNamespace(*this, nullAtom()) == namespaceURI; |
1460 | } |
1461 | |
1462 | // https://dom.spec.whatwg.org/#dom-node-lookupnamespaceuri |
1463 | const AtomicString& Node::lookupNamespaceURI(const AtomicString& potentiallyEmptyPrefix) const |
1464 | { |
1465 | const AtomicString& prefix = potentiallyEmptyPrefix.isEmpty() ? nullAtom() : potentiallyEmptyPrefix; |
1466 | return locateDefaultNamespace(*this, prefix); |
1467 | } |
1468 | |
1469 | // https://dom.spec.whatwg.org/#locate-a-namespace-prefix |
1470 | static const AtomicString& locateNamespacePrefix(const Element& element, const AtomicString& namespaceURI) |
1471 | { |
1472 | if (element.namespaceURI() == namespaceURI) |
1473 | return element.prefix(); |
1474 | |
1475 | if (element.hasAttributes()) { |
1476 | for (auto& attribute : element.attributesIterator()) { |
1477 | if (attribute.prefix() == xmlnsAtom() && attribute.value() == namespaceURI) |
1478 | return attribute.localName(); |
1479 | } |
1480 | } |
1481 | auto* parent = element.parentElement(); |
1482 | return parent ? locateNamespacePrefix(*parent, namespaceURI) : nullAtom(); |
1483 | } |
1484 | |
1485 | // https://dom.spec.whatwg.org/#dom-node-lookupprefix |
1486 | const AtomicString& Node::lookupPrefix(const AtomicString& namespaceURI) const |
1487 | { |
1488 | if (namespaceURI.isEmpty()) |
1489 | return nullAtom(); |
1490 | |
1491 | switch (nodeType()) { |
1492 | case ELEMENT_NODE: |
1493 | return locateNamespacePrefix(downcast<Element>(*this), namespaceURI); |
1494 | case DOCUMENT_NODE: |
1495 | if (auto* documentElement = downcast<Document>(*this).documentElement()) |
1496 | return locateNamespacePrefix(*documentElement, namespaceURI); |
1497 | return nullAtom(); |
1498 | case DOCUMENT_FRAGMENT_NODE: |
1499 | case DOCUMENT_TYPE_NODE: |
1500 | return nullAtom(); |
1501 | case ATTRIBUTE_NODE: |
1502 | if (auto* ownerElement = downcast<Attr>(*this).ownerElement()) |
1503 | return locateNamespacePrefix(*ownerElement, namespaceURI); |
1504 | return nullAtom(); |
1505 | default: |
1506 | if (auto* parent = parentElement()) |
1507 | return locateNamespacePrefix(*parent, namespaceURI); |
1508 | return nullAtom(); |
1509 | } |
1510 | } |
1511 | |
1512 | static void appendTextContent(const Node* node, bool convertBRsToNewlines, bool& isNullString, StringBuilder& content) |
1513 | { |
1514 | switch (node->nodeType()) { |
1515 | case Node::TEXT_NODE: |
1516 | case Node::CDATA_SECTION_NODE: |
1517 | case Node::COMMENT_NODE: |
1518 | isNullString = false; |
1519 | content.append(downcast<CharacterData>(*node).data()); |
1520 | break; |
1521 | |
1522 | case Node::PROCESSING_INSTRUCTION_NODE: |
1523 | isNullString = false; |
1524 | content.append(downcast<ProcessingInstruction>(*node).data()); |
1525 | break; |
1526 | |
1527 | case Node::ATTRIBUTE_NODE: |
1528 | isNullString = false; |
1529 | content.append(downcast<Attr>(*node).value()); |
1530 | break; |
1531 | |
1532 | case Node::ELEMENT_NODE: |
1533 | if (node->hasTagName(brTag) && convertBRsToNewlines) { |
1534 | isNullString = false; |
1535 | content.append('\n'); |
1536 | break; |
1537 | } |
1538 | FALLTHROUGH; |
1539 | case Node::DOCUMENT_FRAGMENT_NODE: |
1540 | isNullString = false; |
1541 | for (Node* child = node->firstChild(); child; child = child->nextSibling()) { |
1542 | if (child->nodeType() == Node::COMMENT_NODE || child->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) |
1543 | continue; |
1544 | appendTextContent(child, convertBRsToNewlines, isNullString, content); |
1545 | } |
1546 | break; |
1547 | |
1548 | case Node::DOCUMENT_NODE: |
1549 | case Node::DOCUMENT_TYPE_NODE: |
1550 | break; |
1551 | } |
1552 | } |
1553 | |
1554 | String Node::textContent(bool convertBRsToNewlines) const |
1555 | { |
1556 | StringBuilder content; |
1557 | bool isNullString = true; |
1558 | appendTextContent(this, convertBRsToNewlines, isNullString, content); |
1559 | return isNullString ? String() : content.toString(); |
1560 | } |
1561 | |
1562 | ExceptionOr<void> Node::setTextContent(const String& text) |
1563 | { |
1564 | switch (nodeType()) { |
1565 | case ATTRIBUTE_NODE: |
1566 | case TEXT_NODE: |
1567 | case CDATA_SECTION_NODE: |
1568 | case COMMENT_NODE: |
1569 | case PROCESSING_INSTRUCTION_NODE: |
1570 | return setNodeValue(text); |
1571 | case ELEMENT_NODE: |
1572 | case DOCUMENT_FRAGMENT_NODE: { |
1573 | auto& container = downcast<ContainerNode>(*this); |
1574 | if (text.isEmpty()) |
1575 | container.replaceAllChildren(nullptr); |
1576 | else |
1577 | container.replaceAllChildren(document().createTextNode(text)); |
1578 | return { }; |
1579 | } |
1580 | case DOCUMENT_NODE: |
1581 | case DOCUMENT_TYPE_NODE: |
1582 | // Do nothing. |
1583 | return { }; |
1584 | } |
1585 | ASSERT_NOT_REACHED(); |
1586 | return { }; |
1587 | } |
1588 | |
1589 | static SHA1::Digest hashPointer(void* pointer) |
1590 | { |
1591 | SHA1 sha1; |
1592 | sha1.addBytes(reinterpret_cast<const uint8_t*>(&pointer), sizeof(pointer)); |
1593 | SHA1::Digest digest; |
1594 | sha1.computeHash(digest); |
1595 | return digest; |
1596 | } |
1597 | |
1598 | static inline unsigned short compareDetachedElementsPosition(Node& firstNode, Node& secondNode) |
1599 | { |
1600 | // If the 2 nodes are not in the same tree, return the result of adding DOCUMENT_POSITION_DISCONNECTED, |
1601 | // DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC, and either DOCUMENT_POSITION_PRECEDING or |
1602 | // DOCUMENT_POSITION_FOLLOWING, with the constraint that this is to be consistent. Whether to return |
1603 | // DOCUMENT_POSITION_PRECEDING or DOCUMENT_POSITION_FOLLOWING is implemented by comparing cryptographic |
1604 | // hashes of Node pointers. |
1605 | // See step 3 in https://dom.spec.whatwg.org/#dom-node-comparedocumentposition |
1606 | SHA1::Digest firstHash = hashPointer(&firstNode); |
1607 | SHA1::Digest secondHash = hashPointer(&secondNode); |
1608 | |
1609 | unsigned short direction = memcmp(firstHash.data(), secondHash.data(), SHA1::hashSize) > 0 ? Node::DOCUMENT_POSITION_PRECEDING : Node::DOCUMENT_POSITION_FOLLOWING; |
1610 | |
1611 | return Node::DOCUMENT_POSITION_DISCONNECTED | Node::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | direction; |
1612 | } |
1613 | |
1614 | unsigned short Node::compareDocumentPosition(Node& otherNode) |
1615 | { |
1616 | if (&otherNode == this) |
1617 | return DOCUMENT_POSITION_EQUIVALENT; |
1618 | |
1619 | Attr* attr1 = is<Attr>(*this) ? downcast<Attr>(this) : nullptr; |
1620 | Attr* attr2 = is<Attr>(otherNode) ? &downcast<Attr>(otherNode) : nullptr; |
1621 | |
1622 | Node* start1 = attr1 ? attr1->ownerElement() : this; |
1623 | Node* start2 = attr2 ? attr2->ownerElement() : &otherNode; |
1624 | |
1625 | // If either of start1 or start2 is null, then we are disconnected, since one of the nodes is |
1626 | // an orphaned attribute node. |
1627 | if (!start1 || !start2) |
1628 | return compareDetachedElementsPosition(*this, otherNode); |
1629 | |
1630 | Vector<Node*, 16> chain1; |
1631 | Vector<Node*, 16> chain2; |
1632 | if (attr1) |
1633 | chain1.append(attr1); |
1634 | if (attr2) |
1635 | chain2.append(attr2); |
1636 | |
1637 | if (attr1 && attr2 && start1 == start2 && start1) { |
1638 | // We are comparing two attributes on the same node. Crawl our attribute map and see which one we hit first. |
1639 | Element* owner1 = attr1->ownerElement(); |
1640 | owner1->synchronizeAllAttributes(); |
1641 | for (const Attribute& attribute : owner1->attributesIterator()) { |
1642 | // If neither of the two determining nodes is a child node and nodeType is the same for both determining nodes, then an |
1643 | // implementation-dependent order between the determining nodes is returned. This order is stable as long as no nodes of |
1644 | // the same nodeType are inserted into or removed from the direct container. This would be the case, for example, |
1645 | // when comparing two attributes of the same element, and inserting or removing additional attributes might change |
1646 | // the order between existing attributes. |
1647 | if (attr1->qualifiedName() == attribute.name()) |
1648 | return DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | DOCUMENT_POSITION_FOLLOWING; |
1649 | if (attr2->qualifiedName() == attribute.name()) |
1650 | return DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | DOCUMENT_POSITION_PRECEDING; |
1651 | } |
1652 | |
1653 | ASSERT_NOT_REACHED(); |
1654 | return DOCUMENT_POSITION_DISCONNECTED; |
1655 | } |
1656 | |
1657 | // If one node is in the document and the other is not, we must be disconnected. |
1658 | // If the nodes have different owning documents, they must be disconnected. Note that we avoid |
1659 | // comparing Attr nodes here, since they return false from isConnected() all the time (which seems like a bug). |
1660 | if (start1->isConnected() != start2->isConnected() || &start1->treeScope() != &start2->treeScope()) |
1661 | return compareDetachedElementsPosition(*this, otherNode); |
1662 | |
1663 | // We need to find a common ancestor container, and then compare the indices of the two immediate children. |
1664 | Node* current; |
1665 | for (current = start1; current; current = current->parentNode()) |
1666 | chain1.append(current); |
1667 | for (current = start2; current; current = current->parentNode()) |
1668 | chain2.append(current); |
1669 | |
1670 | unsigned index1 = chain1.size(); |
1671 | unsigned index2 = chain2.size(); |
1672 | |
1673 | // If the two elements don't have a common root, they're not in the same tree. |
1674 | if (chain1[index1 - 1] != chain2[index2 - 1]) |
1675 | return compareDetachedElementsPosition(*this, otherNode); |
1676 | |
1677 | // Walk the two chains backwards and look for the first difference. |
1678 | for (unsigned i = std::min(index1, index2); i; --i) { |
1679 | Node* child1 = chain1[--index1]; |
1680 | Node* child2 = chain2[--index2]; |
1681 | if (child1 != child2) { |
1682 | // If one of the children is an attribute, it wins. |
1683 | if (child1->nodeType() == ATTRIBUTE_NODE) |
1684 | return DOCUMENT_POSITION_FOLLOWING; |
1685 | if (child2->nodeType() == ATTRIBUTE_NODE) |
1686 | return DOCUMENT_POSITION_PRECEDING; |
1687 | |
1688 | if (!child2->nextSibling()) |
1689 | return DOCUMENT_POSITION_FOLLOWING; |
1690 | if (!child1->nextSibling()) |
1691 | return DOCUMENT_POSITION_PRECEDING; |
1692 | |
1693 | // Otherwise we need to see which node occurs first. Crawl backwards from child2 looking for child1. |
1694 | for (Node* child = child2->previousSibling(); child; child = child->previousSibling()) { |
1695 | if (child == child1) |
1696 | return DOCUMENT_POSITION_FOLLOWING; |
1697 | } |
1698 | return DOCUMENT_POSITION_PRECEDING; |
1699 | } |
1700 | } |
1701 | |
1702 | // There was no difference between the two parent chains, i.e., one was a subset of the other. The shorter |
1703 | // chain is the ancestor. |
1704 | return index1 < index2 ? |
1705 | DOCUMENT_POSITION_FOLLOWING | DOCUMENT_POSITION_CONTAINED_BY : |
1706 | DOCUMENT_POSITION_PRECEDING | DOCUMENT_POSITION_CONTAINS; |
1707 | } |
1708 | |
1709 | FloatPoint Node::convertToPage(const FloatPoint& p) const |
1710 | { |
1711 | // If there is a renderer, just ask it to do the conversion |
1712 | if (renderer()) |
1713 | return renderer()->localToAbsolute(p, UseTransforms); |
1714 | |
1715 | // Otherwise go up the tree looking for a renderer |
1716 | if (auto* parent = parentElement()) |
1717 | return parent->convertToPage(p); |
1718 | |
1719 | // No parent - no conversion needed |
1720 | return p; |
1721 | } |
1722 | |
1723 | FloatPoint Node::convertFromPage(const FloatPoint& p) const |
1724 | { |
1725 | // If there is a renderer, just ask it to do the conversion |
1726 | if (renderer()) |
1727 | return renderer()->absoluteToLocal(p, UseTransforms); |
1728 | |
1729 | // Otherwise go up the tree looking for a renderer |
1730 | if (auto* parent = parentElement()) |
1731 | return parent->convertFromPage(p); |
1732 | |
1733 | // No parent - no conversion needed |
1734 | return p; |
1735 | } |
1736 | |
1737 | #if ENABLE(TREE_DEBUGGING) |
1738 | |
1739 | static void appendAttributeDesc(const Node* node, StringBuilder& stringBuilder, const QualifiedName& name, const char* attrDesc) |
1740 | { |
1741 | if (!is<Element>(*node)) |
1742 | return; |
1743 | |
1744 | const AtomicString& attr = downcast<Element>(*node).getAttribute(name); |
1745 | if (attr.isEmpty()) |
1746 | return; |
1747 | |
1748 | stringBuilder.append(attrDesc); |
1749 | stringBuilder.append(attr); |
1750 | } |
1751 | |
1752 | void Node::showNode(const char* prefix) const |
1753 | { |
1754 | if (!prefix) |
1755 | prefix = "" ; |
1756 | if (isTextNode()) { |
1757 | String value = nodeValue(); |
1758 | value.replaceWithLiteral('\\', "\\\\" ); |
1759 | value.replaceWithLiteral('\n', "\\n" ); |
1760 | fprintf(stderr, "%s%s\t%p \"%s\"\n" , prefix, nodeName().utf8().data(), this, value.utf8().data()); |
1761 | } else { |
1762 | StringBuilder attrs; |
1763 | appendAttributeDesc(this, attrs, classAttr, " CLASS=" ); |
1764 | appendAttributeDesc(this, attrs, styleAttr, " STYLE=" ); |
1765 | fprintf(stderr, "%s%s\t%p (renderer %p) %s%s%s\n" , prefix, nodeName().utf8().data(), this, renderer(), attrs.toString().utf8().data(), needsStyleRecalc() ? " (needs style recalc)" : "" , childNeedsStyleRecalc() ? " (child needs style recalc)" : "" ); |
1766 | } |
1767 | } |
1768 | |
1769 | void Node::showTreeForThis() const |
1770 | { |
1771 | showTreeAndMark(this, "*" ); |
1772 | } |
1773 | |
1774 | void Node::showNodePathForThis() const |
1775 | { |
1776 | Vector<const Node*, 16> chain; |
1777 | const Node* node = this; |
1778 | while (node->parentOrShadowHostNode()) { |
1779 | chain.append(node); |
1780 | node = node->parentOrShadowHostNode(); |
1781 | } |
1782 | for (unsigned index = chain.size(); index > 0; --index) { |
1783 | const Node* node = chain[index - 1]; |
1784 | if (is<ShadowRoot>(*node)) { |
1785 | int count = 0; |
1786 | for (const ShadowRoot* shadowRoot = downcast<ShadowRoot>(node); shadowRoot && shadowRoot != node; shadowRoot = shadowRoot->shadowRoot()) |
1787 | ++count; |
1788 | fprintf(stderr, "/#shadow-root[%d]" , count); |
1789 | continue; |
1790 | } |
1791 | |
1792 | switch (node->nodeType()) { |
1793 | case ELEMENT_NODE: { |
1794 | fprintf(stderr, "/%s" , node->nodeName().utf8().data()); |
1795 | |
1796 | const Element& element = downcast<Element>(*node); |
1797 | const AtomicString& idattr = element.getIdAttribute(); |
1798 | bool hasIdAttr = !idattr.isNull() && !idattr.isEmpty(); |
1799 | if (node->previousSibling() || node->nextSibling()) { |
1800 | int count = 0; |
1801 | for (Node* previous = node->previousSibling(); previous; previous = previous->previousSibling()) |
1802 | if (previous->nodeName() == node->nodeName()) |
1803 | ++count; |
1804 | if (hasIdAttr) |
1805 | fprintf(stderr, "[@id=\"%s\" and position()=%d]" , idattr.string().utf8().data(), count); |
1806 | else |
1807 | fprintf(stderr, "[%d]" , count); |
1808 | } else if (hasIdAttr) |
1809 | fprintf(stderr, "[@id=\"%s\"]" , idattr.string().utf8().data()); |
1810 | break; |
1811 | } |
1812 | case TEXT_NODE: |
1813 | fprintf(stderr, "/text()" ); |
1814 | break; |
1815 | case ATTRIBUTE_NODE: |
1816 | fprintf(stderr, "/@%s" , node->nodeName().utf8().data()); |
1817 | break; |
1818 | default: |
1819 | break; |
1820 | } |
1821 | } |
1822 | fprintf(stderr, "\n" ); |
1823 | } |
1824 | |
1825 | static void traverseTreeAndMark(const String& baseIndent, const Node* rootNode, const Node* markedNode1, const char* markedLabel1, const Node* markedNode2, const char* markedLabel2) |
1826 | { |
1827 | for (const Node* node = rootNode; node; node = NodeTraversal::next(*node)) { |
1828 | if (node == markedNode1) |
1829 | fprintf(stderr, "%s" , markedLabel1); |
1830 | if (node == markedNode2) |
1831 | fprintf(stderr, "%s" , markedLabel2); |
1832 | |
1833 | StringBuilder indent; |
1834 | indent.append(baseIndent); |
1835 | for (const Node* tmpNode = node; tmpNode && tmpNode != rootNode; tmpNode = tmpNode->parentOrShadowHostNode()) |
1836 | indent.append('\t'); |
1837 | fprintf(stderr, "%s" , indent.toString().utf8().data()); |
1838 | node->showNode(); |
1839 | indent.append('\t'); |
1840 | if (!node->isShadowRoot()) { |
1841 | if (ShadowRoot* shadowRoot = node->shadowRoot()) |
1842 | traverseTreeAndMark(indent.toString(), shadowRoot, markedNode1, markedLabel1, markedNode2, markedLabel2); |
1843 | } |
1844 | } |
1845 | } |
1846 | |
1847 | void Node::showTreeAndMark(const Node* markedNode1, const char* markedLabel1, const Node* markedNode2, const char* markedLabel2) const |
1848 | { |
1849 | const Node* rootNode; |
1850 | const Node* node = this; |
1851 | while (node->parentOrShadowHostNode() && !node->hasTagName(bodyTag)) |
1852 | node = node->parentOrShadowHostNode(); |
1853 | rootNode = node; |
1854 | |
1855 | String startingIndent; |
1856 | traverseTreeAndMark(startingIndent, rootNode, markedNode1, markedLabel1, markedNode2, markedLabel2); |
1857 | } |
1858 | |
1859 | void Node::formatForDebugger(char* buffer, unsigned length) const |
1860 | { |
1861 | String result; |
1862 | String s; |
1863 | |
1864 | s = nodeName(); |
1865 | if (s.isEmpty()) |
1866 | result = "<none>" ; |
1867 | else |
1868 | result = s; |
1869 | |
1870 | strncpy(buffer, result.utf8().data(), length - 1); |
1871 | } |
1872 | |
1873 | static ContainerNode* parentOrShadowHostOrFrameOwner(const Node* node) |
1874 | { |
1875 | ContainerNode* parent = node->parentOrShadowHostNode(); |
1876 | if (!parent && node->document().frame()) |
1877 | parent = node->document().frame()->ownerElement(); |
1878 | return parent; |
1879 | } |
1880 | |
1881 | static void showSubTreeAcrossFrame(const Node* node, const Node* markedNode, const String& indent) |
1882 | { |
1883 | if (node == markedNode) |
1884 | fputs("*" , stderr); |
1885 | fputs(indent.utf8().data(), stderr); |
1886 | node->showNode(); |
1887 | if (!node->isShadowRoot()) { |
1888 | if (node->isFrameOwnerElement()) |
1889 | showSubTreeAcrossFrame(static_cast<const HTMLFrameOwnerElement*>(node)->contentDocument(), markedNode, indent + "\t" ); |
1890 | if (ShadowRoot* shadowRoot = node->shadowRoot()) |
1891 | showSubTreeAcrossFrame(shadowRoot, markedNode, indent + "\t" ); |
1892 | } |
1893 | for (Node* child = node->firstChild(); child; child = child->nextSibling()) |
1894 | showSubTreeAcrossFrame(child, markedNode, indent + "\t" ); |
1895 | } |
1896 | |
1897 | void Node::showTreeForThisAcrossFrame() const |
1898 | { |
1899 | Node* rootNode = const_cast<Node*>(this); |
1900 | while (parentOrShadowHostOrFrameOwner(rootNode)) |
1901 | rootNode = parentOrShadowHostOrFrameOwner(rootNode); |
1902 | showSubTreeAcrossFrame(rootNode, this, "" ); |
1903 | } |
1904 | |
1905 | #endif // ENABLE(TREE_DEBUGGING) |
1906 | |
1907 | // -------- |
1908 | |
1909 | void NodeListsNodeData::invalidateCaches() |
1910 | { |
1911 | for (auto& atomicName : m_atomicNameCaches) |
1912 | atomicName.value->invalidateCache(); |
1913 | |
1914 | for (auto& collection : m_cachedCollections) |
1915 | collection.value->invalidateCache(); |
1916 | |
1917 | for (auto& tagCollection : m_tagCollectionNSCache) |
1918 | tagCollection.value->invalidateCache(); |
1919 | } |
1920 | |
1921 | void NodeListsNodeData::invalidateCachesForAttribute(const QualifiedName& attrName) |
1922 | { |
1923 | for (auto& atomicName : m_atomicNameCaches) |
1924 | atomicName.value->invalidateCacheForAttribute(attrName); |
1925 | |
1926 | for (auto& collection : m_cachedCollections) |
1927 | collection.value->invalidateCacheForAttribute(attrName); |
1928 | } |
1929 | |
1930 | void Node::getSubresourceURLs(ListHashSet<URL>& urls) const |
1931 | { |
1932 | addSubresourceAttributeURLs(urls); |
1933 | } |
1934 | |
1935 | Element* Node::enclosingLinkEventParentOrSelf() |
1936 | { |
1937 | for (Node* node = this; node; node = node->parentInComposedTree()) { |
1938 | // For imagemaps, the enclosing link element is the associated area element not the image itself. |
1939 | // So we don't let images be the enclosing link element, even though isLink sometimes returns |
1940 | // true for them. |
1941 | if (node->isLink() && !is<HTMLImageElement>(*node)) |
1942 | return downcast<Element>(node); |
1943 | } |
1944 | |
1945 | return nullptr; |
1946 | } |
1947 | |
1948 | EventTargetInterface Node::eventTargetInterface() const |
1949 | { |
1950 | return NodeEventTargetInterfaceType; |
1951 | } |
1952 | |
1953 | template <typename MoveNodeFunction, typename MoveShadowRootFunction> |
1954 | static void traverseSubtreeToUpdateTreeScope(Node& root, MoveNodeFunction moveNode, MoveShadowRootFunction moveShadowRoot) |
1955 | { |
1956 | for (Node* node = &root; node; node = NodeTraversal::next(*node, &root)) { |
1957 | moveNode(*node); |
1958 | |
1959 | if (!is<Element>(*node)) |
1960 | continue; |
1961 | Element& element = downcast<Element>(*node); |
1962 | |
1963 | if (element.hasSyntheticAttrChildNodes()) { |
1964 | for (auto& attr : element.attrNodeList()) |
1965 | moveNode(*attr); |
1966 | } |
1967 | |
1968 | if (auto* shadow = element.shadowRoot()) |
1969 | moveShadowRoot(*shadow); |
1970 | } |
1971 | } |
1972 | |
1973 | inline void Node::moveShadowTreeToNewDocument(ShadowRoot& shadowRoot, Document& oldDocument, Document& newDocument) |
1974 | { |
1975 | traverseSubtreeToUpdateTreeScope(shadowRoot, [&oldDocument, &newDocument](Node& node) { |
1976 | node.moveNodeToNewDocument(oldDocument, newDocument); |
1977 | }, [&oldDocument, &newDocument](ShadowRoot& innerShadowRoot) { |
1978 | RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(&innerShadowRoot.document() == &oldDocument); |
1979 | innerShadowRoot.moveShadowRootToNewDocument(newDocument); |
1980 | moveShadowTreeToNewDocument(innerShadowRoot, oldDocument, newDocument); |
1981 | }); |
1982 | } |
1983 | |
1984 | void Node::moveTreeToNewScope(Node& root, TreeScope& oldScope, TreeScope& newScope) |
1985 | { |
1986 | ASSERT(&oldScope != &newScope); |
1987 | |
1988 | Document& oldDocument = oldScope.documentScope(); |
1989 | Document& newDocument = newScope.documentScope(); |
1990 | if (&oldDocument != &newDocument) { |
1991 | oldDocument.incrementReferencingNodeCount(); |
1992 | traverseSubtreeToUpdateTreeScope(root, [&](Node& node) { |
1993 | ASSERT(!node.isTreeScope()); |
1994 | RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(&node.treeScope() == &oldScope); |
1995 | node.setTreeScope(newScope); |
1996 | node.moveNodeToNewDocument(oldDocument, newDocument); |
1997 | }, [&](ShadowRoot& shadowRoot) { |
1998 | ASSERT_WITH_SECURITY_IMPLICATION(&shadowRoot.document() == &oldDocument); |
1999 | shadowRoot.moveShadowRootToNewParentScope(newScope, newDocument); |
2000 | moveShadowTreeToNewDocument(shadowRoot, oldDocument, newDocument); |
2001 | }); |
2002 | RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(&oldScope.documentScope() == &oldDocument && &newScope.documentScope() == &newDocument); |
2003 | oldDocument.decrementReferencingNodeCount(); |
2004 | } else { |
2005 | traverseSubtreeToUpdateTreeScope(root, [&](Node& node) { |
2006 | ASSERT(!node.isTreeScope()); |
2007 | RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(&node.treeScope() == &oldScope); |
2008 | node.setTreeScope(newScope); |
2009 | if (UNLIKELY(!node.hasRareData())) |
2010 | return; |
2011 | if (auto* nodeLists = node.rareData()->nodeLists()) |
2012 | nodeLists->adoptTreeScope(); |
2013 | }, [&newScope](ShadowRoot& shadowRoot) { |
2014 | shadowRoot.setParentTreeScope(newScope); |
2015 | }); |
2016 | } |
2017 | } |
2018 | |
2019 | void Node::moveNodeToNewDocument(Document& oldDocument, Document& newDocument) |
2020 | { |
2021 | newDocument.incrementReferencingNodeCount(); |
2022 | oldDocument.decrementReferencingNodeCount(); |
2023 | |
2024 | if (hasRareData()) { |
2025 | if (auto* nodeLists = rareData()->nodeLists()) |
2026 | nodeLists->adoptDocument(oldDocument, newDocument); |
2027 | if (auto* registry = mutationObserverRegistry()) { |
2028 | for (auto& registration : *registry) |
2029 | newDocument.addMutationObserverTypes(registration->mutationTypes()); |
2030 | } |
2031 | if (auto* transientRegistry = transientMutationObserverRegistry()) { |
2032 | for (auto& registration : *transientRegistry) |
2033 | newDocument.addMutationObserverTypes(registration->mutationTypes()); |
2034 | } |
2035 | } else { |
2036 | ASSERT(!mutationObserverRegistry()); |
2037 | ASSERT(!transientMutationObserverRegistry()); |
2038 | } |
2039 | |
2040 | oldDocument.moveNodeIteratorsToNewDocument(*this, newDocument); |
2041 | |
2042 | if (AXObjectCache::accessibilityEnabled()) { |
2043 | if (auto* cache = oldDocument.existingAXObjectCache()) |
2044 | cache->remove(*this); |
2045 | } |
2046 | |
2047 | if (auto* eventTargetData = this->eventTargetData()) { |
2048 | if (!eventTargetData->eventListenerMap.isEmpty()) { |
2049 | for (auto& type : eventTargetData->eventListenerMap.eventTypes()) |
2050 | newDocument.addListenerTypeIfNeeded(type); |
2051 | } |
2052 | |
2053 | unsigned numWheelEventHandlers = eventListeners(eventNames().mousewheelEvent).size() + eventListeners(eventNames().wheelEvent).size(); |
2054 | for (unsigned i = 0; i < numWheelEventHandlers; ++i) { |
2055 | oldDocument.didRemoveWheelEventHandler(*this); |
2056 | newDocument.didAddWheelEventHandler(*this); |
2057 | } |
2058 | |
2059 | unsigned numTouchEventListeners = 0; |
2060 | #if ENABLE(TOUCH_EVENTS) |
2061 | if (newDocument.quirks().shouldDispatchSimulatedMouseEvents() || RuntimeEnabledFeatures::sharedFeatures().mouseEventsSimulationEnabled()) { |
2062 | for (auto& name : eventNames().extendedTouchRelatedEventNames()) |
2063 | numTouchEventListeners += eventListeners(name).size(); |
2064 | } else { |
2065 | #endif |
2066 | for (auto& name : eventNames().touchRelatedEventNames()) |
2067 | numTouchEventListeners += eventListeners(name).size(); |
2068 | #if ENABLE(TOUCH_EVENTS) |
2069 | } |
2070 | #endif |
2071 | |
2072 | for (unsigned i = 0; i < numTouchEventListeners; ++i) { |
2073 | oldDocument.didRemoveTouchEventHandler(*this); |
2074 | newDocument.didAddTouchEventHandler(*this); |
2075 | #if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS_FAMILY) |
2076 | oldDocument.removeTouchEventListener(*this); |
2077 | newDocument.addTouchEventListener(*this); |
2078 | #endif |
2079 | } |
2080 | |
2081 | #if ENABLE(TOUCH_EVENTS) && ENABLE(IOS_GESTURE_EVENTS) |
2082 | unsigned numGestureEventListeners = 0; |
2083 | for (auto& name : eventNames().gestureEventNames()) |
2084 | numGestureEventListeners += eventListeners(name).size(); |
2085 | |
2086 | for (unsigned i = 0; i < numGestureEventListeners; ++i) { |
2087 | oldDocument.removeTouchEventHandler(*this); |
2088 | newDocument.addTouchEventHandler(*this); |
2089 | } |
2090 | #endif |
2091 | } |
2092 | |
2093 | #if !ASSERT_DISABLED || ENABLE(SECURITY_ASSERTIONS) |
2094 | #if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS_FAMILY) |
2095 | ASSERT_WITH_SECURITY_IMPLICATION(!oldDocument.touchEventListenersContain(*this)); |
2096 | ASSERT_WITH_SECURITY_IMPLICATION(!oldDocument.touchEventHandlersContain(*this)); |
2097 | #endif |
2098 | #if ENABLE(TOUCH_EVENTS) && ENABLE(IOS_GESTURE_EVENTS) |
2099 | ASSERT_WITH_SECURITY_IMPLICATION(!oldDocument.touchEventTargetsContain(*this)); |
2100 | #endif |
2101 | #endif |
2102 | |
2103 | if (is<Element>(*this)) |
2104 | downcast<Element>(*this).didMoveToNewDocument(oldDocument, newDocument); |
2105 | } |
2106 | |
2107 | static inline bool tryAddEventListener(Node* targetNode, const AtomicString& eventType, Ref<EventListener>&& listener, const EventTarget::AddEventListenerOptions& options) |
2108 | { |
2109 | if (!targetNode->EventTarget::addEventListener(eventType, listener.copyRef(), options)) |
2110 | return false; |
2111 | |
2112 | targetNode->document().addListenerTypeIfNeeded(eventType); |
2113 | if (eventNames().isWheelEventType(eventType)) |
2114 | targetNode->document().didAddWheelEventHandler(*targetNode); |
2115 | else if (eventNames().isTouchRelatedEventType(targetNode->document(), eventType)) |
2116 | targetNode->document().didAddTouchEventHandler(*targetNode); |
2117 | |
2118 | #if PLATFORM(IOS_FAMILY) |
2119 | if (targetNode == &targetNode->document() && eventType == eventNames().scrollEvent) |
2120 | targetNode->document().domWindow()->incrementScrollEventListenersCount(); |
2121 | |
2122 | #if ENABLE(TOUCH_EVENTS) |
2123 | if (eventNames().isTouchRelatedEventType(targetNode->document(), eventType)) |
2124 | targetNode->document().addTouchEventListener(*targetNode); |
2125 | #endif |
2126 | #endif // PLATFORM(IOS_FAMILY) |
2127 | |
2128 | #if ENABLE(IOS_GESTURE_EVENTS) && ENABLE(TOUCH_EVENTS) |
2129 | if (eventNames().isGestureEventType(eventType)) |
2130 | targetNode->document().addTouchEventHandler(*targetNode); |
2131 | #endif |
2132 | |
2133 | return true; |
2134 | } |
2135 | |
2136 | bool Node::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options) |
2137 | { |
2138 | return tryAddEventListener(this, eventType, WTFMove(listener), options); |
2139 | } |
2140 | |
2141 | static inline bool tryRemoveEventListener(Node* targetNode, const AtomicString& eventType, EventListener& listener, const EventTarget::ListenerOptions& options) |
2142 | { |
2143 | if (!targetNode->EventTarget::removeEventListener(eventType, listener, options)) |
2144 | return false; |
2145 | |
2146 | // FIXME: Notify Document that the listener has vanished. We need to keep track of a number of |
2147 | // listeners for each type, not just a bool - see https://bugs.webkit.org/show_bug.cgi?id=33861 |
2148 | if (eventNames().isWheelEventType(eventType)) |
2149 | targetNode->document().didRemoveWheelEventHandler(*targetNode); |
2150 | else if (eventNames().isTouchRelatedEventType(targetNode->document(), eventType)) |
2151 | targetNode->document().didRemoveTouchEventHandler(*targetNode); |
2152 | |
2153 | #if PLATFORM(IOS_FAMILY) |
2154 | if (targetNode == &targetNode->document() && eventType == eventNames().scrollEvent) |
2155 | targetNode->document().domWindow()->decrementScrollEventListenersCount(); |
2156 | |
2157 | #if ENABLE(TOUCH_EVENTS) |
2158 | if (eventNames().isTouchRelatedEventType(targetNode->document(), eventType)) |
2159 | targetNode->document().removeTouchEventListener(*targetNode); |
2160 | #endif |
2161 | #endif // PLATFORM(IOS_FAMILY) |
2162 | |
2163 | #if ENABLE(IOS_GESTURE_EVENTS) && ENABLE(TOUCH_EVENTS) |
2164 | if (eventNames().isGestureEventType(eventType)) |
2165 | targetNode->document().removeTouchEventHandler(*targetNode); |
2166 | #endif |
2167 | |
2168 | return true; |
2169 | } |
2170 | |
2171 | bool Node::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options) |
2172 | { |
2173 | return tryRemoveEventListener(this, eventType, listener, options); |
2174 | } |
2175 | |
2176 | typedef HashMap<Node*, std::unique_ptr<EventTargetData>> EventTargetDataMap; |
2177 | |
2178 | static EventTargetDataMap& eventTargetDataMap() |
2179 | { |
2180 | static NeverDestroyed<EventTargetDataMap> map; |
2181 | |
2182 | return map; |
2183 | } |
2184 | |
2185 | static Lock s_eventTargetDataMapLock; |
2186 | |
2187 | EventTargetData* Node::eventTargetData() |
2188 | { |
2189 | return hasEventTargetData() ? eventTargetDataMap().get(this) : nullptr; |
2190 | } |
2191 | |
2192 | EventTargetData* Node::eventTargetDataConcurrently() |
2193 | { |
2194 | // Not holding the lock when the world is stopped accelerates parallel constraint solving, which |
2195 | // calls this function from many threads. Parallel constraint solving can happen with the world |
2196 | // running or stopped, but if we do it with a running world, then we're usually mixing constraint |
2197 | // solving with other work. Therefore, the most likely time for contention on this lock is when the |
2198 | // world is stopped. We don't have to hold the lock when the world is stopped, because a stopped world |
2199 | // means that we will never mutate the event target data map. |
2200 | JSC::VM* vm = commonVMOrNull(); |
2201 | auto locker = holdLockIf(s_eventTargetDataMapLock, vm && vm->heap.worldIsRunning()); |
2202 | return hasEventTargetData() ? eventTargetDataMap().get(this) : nullptr; |
2203 | } |
2204 | |
2205 | EventTargetData& Node::ensureEventTargetData() |
2206 | { |
2207 | if (hasEventTargetData()) |
2208 | return *eventTargetDataMap().get(this); |
2209 | |
2210 | JSC::VM* vm = commonVMOrNull(); |
2211 | RELEASE_ASSERT(!vm || vm->heap.worldIsRunning()); |
2212 | |
2213 | auto locker = holdLock(s_eventTargetDataMapLock); |
2214 | setHasEventTargetData(true); |
2215 | return *eventTargetDataMap().add(this, std::make_unique<EventTargetData>()).iterator->value; |
2216 | } |
2217 | |
2218 | void Node::clearEventTargetData() |
2219 | { |
2220 | JSC::VM* vm = commonVMOrNull(); |
2221 | RELEASE_ASSERT(!vm || vm->heap.worldIsRunning()); |
2222 | auto locker = holdLock(s_eventTargetDataMapLock); |
2223 | eventTargetDataMap().remove(this); |
2224 | } |
2225 | |
2226 | Vector<std::unique_ptr<MutationObserverRegistration>>* Node::mutationObserverRegistry() |
2227 | { |
2228 | if (!hasRareData()) |
2229 | return nullptr; |
2230 | auto* data = rareData()->mutationObserverData(); |
2231 | if (!data) |
2232 | return nullptr; |
2233 | return &data->registry; |
2234 | } |
2235 | |
2236 | HashSet<MutationObserverRegistration*>* Node::transientMutationObserverRegistry() |
2237 | { |
2238 | if (!hasRareData()) |
2239 | return nullptr; |
2240 | auto* data = rareData()->mutationObserverData(); |
2241 | if (!data) |
2242 | return nullptr; |
2243 | return &data->transientRegistry; |
2244 | } |
2245 | |
2246 | template<typename Registry> static inline void collectMatchingObserversForMutation(HashMap<Ref<MutationObserver>, MutationRecordDeliveryOptions>& observers, Registry* registry, Node& target, MutationObserver::MutationType type, const QualifiedName* attributeName) |
2247 | { |
2248 | if (!registry) |
2249 | return; |
2250 | |
2251 | for (auto& registration : *registry) { |
2252 | if (registration->shouldReceiveMutationFrom(target, type, attributeName)) { |
2253 | auto deliveryOptions = registration->deliveryOptions(); |
2254 | auto result = observers.add(registration->observer(), deliveryOptions); |
2255 | if (!result.isNewEntry) |
2256 | result.iterator->value |= deliveryOptions; |
2257 | } |
2258 | } |
2259 | } |
2260 | |
2261 | HashMap<Ref<MutationObserver>, MutationRecordDeliveryOptions> Node::registeredMutationObservers(MutationObserver::MutationType type, const QualifiedName* attributeName) |
2262 | { |
2263 | HashMap<Ref<MutationObserver>, MutationRecordDeliveryOptions> result; |
2264 | ASSERT((type == MutationObserver::Attributes && attributeName) || !attributeName); |
2265 | collectMatchingObserversForMutation(result, mutationObserverRegistry(), *this, type, attributeName); |
2266 | collectMatchingObserversForMutation(result, transientMutationObserverRegistry(), *this, type, attributeName); |
2267 | for (Node* node = parentNode(); node; node = node->parentNode()) { |
2268 | collectMatchingObserversForMutation(result, node->mutationObserverRegistry(), *this, type, attributeName); |
2269 | collectMatchingObserversForMutation(result, node->transientMutationObserverRegistry(), *this, type, attributeName); |
2270 | } |
2271 | return result; |
2272 | } |
2273 | |
2274 | void Node::registerMutationObserver(MutationObserver& observer, MutationObserverOptions options, const HashSet<AtomicString>& attributeFilter) |
2275 | { |
2276 | MutationObserverRegistration* registration = nullptr; |
2277 | auto& registry = ensureRareData().ensureMutationObserverData().registry; |
2278 | |
2279 | for (auto& candidateRegistration : registry) { |
2280 | if (&candidateRegistration->observer() == &observer) { |
2281 | registration = candidateRegistration.get(); |
2282 | registration->resetObservation(options, attributeFilter); |
2283 | } |
2284 | } |
2285 | |
2286 | if (!registration) { |
2287 | registry.append(std::make_unique<MutationObserverRegistration>(observer, *this, options, attributeFilter)); |
2288 | registration = registry.last().get(); |
2289 | } |
2290 | |
2291 | document().addMutationObserverTypes(registration->mutationTypes()); |
2292 | } |
2293 | |
2294 | void Node::unregisterMutationObserver(MutationObserverRegistration& registration) |
2295 | { |
2296 | auto* registry = mutationObserverRegistry(); |
2297 | ASSERT(registry); |
2298 | if (!registry) |
2299 | return; |
2300 | |
2301 | registry->removeFirstMatching([®istration] (auto& current) { |
2302 | return current.get() == ®istration; |
2303 | }); |
2304 | } |
2305 | |
2306 | void Node::registerTransientMutationObserver(MutationObserverRegistration& registration) |
2307 | { |
2308 | ensureRareData().ensureMutationObserverData().transientRegistry.add(®istration); |
2309 | } |
2310 | |
2311 | void Node::unregisterTransientMutationObserver(MutationObserverRegistration& registration) |
2312 | { |
2313 | auto* transientRegistry = transientMutationObserverRegistry(); |
2314 | ASSERT(transientRegistry); |
2315 | if (!transientRegistry) |
2316 | return; |
2317 | |
2318 | ASSERT(transientRegistry->contains(®istration)); |
2319 | transientRegistry->remove(®istration); |
2320 | } |
2321 | |
2322 | void Node::notifyMutationObserversNodeWillDetach() |
2323 | { |
2324 | if (!document().hasMutationObservers()) |
2325 | return; |
2326 | |
2327 | for (Node* node = parentNode(); node; node = node->parentNode()) { |
2328 | if (auto* registry = node->mutationObserverRegistry()) { |
2329 | for (auto& registration : *registry) |
2330 | registration->observedSubtreeNodeWillDetach(*this); |
2331 | } |
2332 | if (auto* transientRegistry = node->transientMutationObserverRegistry()) { |
2333 | for (auto* registration : *transientRegistry) |
2334 | registration->observedSubtreeNodeWillDetach(*this); |
2335 | } |
2336 | } |
2337 | } |
2338 | |
2339 | void Node::handleLocalEvents(Event& event, EventInvokePhase phase) |
2340 | { |
2341 | if (!hasEventTargetData()) |
2342 | return; |
2343 | |
2344 | // FIXME: Should we deliver wheel events to disabled form controls or not? |
2345 | if (is<Element>(*this) && downcast<Element>(*this).isDisabledFormControl() && event.isMouseEvent() && !event.isWheelEvent()) |
2346 | return; |
2347 | |
2348 | fireEventListeners(event, phase); |
2349 | } |
2350 | |
2351 | void Node::dispatchScopedEvent(Event& event) |
2352 | { |
2353 | EventDispatcher::dispatchScopedEvent(*this, event); |
2354 | } |
2355 | |
2356 | void Node::dispatchEvent(Event& event) |
2357 | { |
2358 | EventDispatcher::dispatchEvent(*this, event); |
2359 | } |
2360 | |
2361 | void Node::dispatchSubtreeModifiedEvent() |
2362 | { |
2363 | if (isInShadowTree()) |
2364 | return; |
2365 | |
2366 | ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::InMainThread::isEventDispatchAllowedInSubtree(*this)); |
2367 | |
2368 | if (!document().hasListenerType(Document::DOMSUBTREEMODIFIED_LISTENER)) |
2369 | return; |
2370 | const AtomicString& subtreeModifiedEventName = eventNames().DOMSubtreeModifiedEvent; |
2371 | if (!parentNode() && !hasEventListeners(subtreeModifiedEventName)) |
2372 | return; |
2373 | |
2374 | dispatchScopedEvent(MutationEvent::create(subtreeModifiedEventName, Event::CanBubble::Yes)); |
2375 | } |
2376 | |
2377 | void Node::dispatchDOMActivateEvent(Event& underlyingClickEvent) |
2378 | { |
2379 | ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::InMainThread::isScriptAllowed()); |
2380 | int detail = is<UIEvent>(underlyingClickEvent) ? downcast<UIEvent>(underlyingClickEvent).detail() : 0; |
2381 | auto event = UIEvent::create(eventNames().DOMActivateEvent, Event::CanBubble::Yes, Event::IsCancelable::Yes, Event::IsComposed::Yes, document().windowProxy(), detail); |
2382 | event->setUnderlyingEvent(&underlyingClickEvent); |
2383 | dispatchScopedEvent(event); |
2384 | if (event->defaultHandled()) |
2385 | underlyingClickEvent.setDefaultHandled(); |
2386 | } |
2387 | |
2388 | bool Node::dispatchBeforeLoadEvent(const String& sourceURL) |
2389 | { |
2390 | if (!document().hasListenerType(Document::BEFORELOAD_LISTENER)) |
2391 | return true; |
2392 | |
2393 | Ref<Node> protectedThis(*this); |
2394 | auto event = BeforeLoadEvent::create(sourceURL); |
2395 | dispatchEvent(event); |
2396 | return !event->defaultPrevented(); |
2397 | } |
2398 | |
2399 | void Node::dispatchInputEvent() |
2400 | { |
2401 | dispatchScopedEvent(Event::create(eventNames().inputEvent, Event::CanBubble::Yes, Event::IsCancelable::No, Event::IsComposed::Yes)); |
2402 | } |
2403 | |
2404 | void Node::defaultEventHandler(Event& event) |
2405 | { |
2406 | if (event.target() != this) |
2407 | return; |
2408 | const AtomicString& eventType = event.type(); |
2409 | if (eventType == eventNames().keydownEvent || eventType == eventNames().keypressEvent) { |
2410 | if (is<KeyboardEvent>(event)) { |
2411 | if (Frame* frame = document().frame()) |
2412 | frame->eventHandler().defaultKeyboardEventHandler(downcast<KeyboardEvent>(event)); |
2413 | } |
2414 | } else if (eventType == eventNames().clickEvent) { |
2415 | dispatchDOMActivateEvent(event); |
2416 | #if ENABLE(CONTEXT_MENUS) |
2417 | } else if (eventType == eventNames().contextmenuEvent) { |
2418 | if (Frame* frame = document().frame()) |
2419 | if (Page* page = frame->page()) |
2420 | page->contextMenuController().handleContextMenuEvent(event); |
2421 | #endif |
2422 | } else if (eventType == eventNames().textInputEvent) { |
2423 | if (is<TextEvent>(event)) { |
2424 | if (Frame* frame = document().frame()) |
2425 | frame->eventHandler().defaultTextInputEventHandler(downcast<TextEvent>(event)); |
2426 | } |
2427 | #if ENABLE(PAN_SCROLLING) |
2428 | } else if (eventType == eventNames().mousedownEvent && is<MouseEvent>(event)) { |
2429 | if (downcast<MouseEvent>(event).button() == MiddleButton) { |
2430 | if (enclosingLinkEventParentOrSelf()) |
2431 | return; |
2432 | |
2433 | RenderObject* renderer = this->renderer(); |
2434 | while (renderer && (!is<RenderBox>(*renderer) || !downcast<RenderBox>(*renderer).canBeScrolledAndHasScrollableArea())) |
2435 | renderer = renderer->parent(); |
2436 | |
2437 | if (renderer) { |
2438 | if (Frame* frame = document().frame()) |
2439 | frame->eventHandler().startPanScrolling(downcast<RenderBox>(*renderer)); |
2440 | } |
2441 | } |
2442 | #endif |
2443 | } else if (eventNames().isWheelEventType(eventType) && is<WheelEvent>(event)) { |
2444 | // If we don't have a renderer, send the wheel event to the first node we find with a renderer. |
2445 | // This is needed for <option> and <optgroup> elements so that <select>s get a wheel scroll. |
2446 | Node* startNode = this; |
2447 | while (startNode && !startNode->renderer()) |
2448 | startNode = startNode->parentOrShadowHostNode(); |
2449 | |
2450 | if (startNode && startNode->renderer()) |
2451 | if (Frame* frame = document().frame()) |
2452 | frame->eventHandler().defaultWheelEventHandler(startNode, downcast<WheelEvent>(event)); |
2453 | #if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS_FAMILY) |
2454 | } else if (is<TouchEvent>(event) && eventNames().isTouchRelatedEventType(document(), eventType)) { |
2455 | RenderObject* renderer = this->renderer(); |
2456 | while (renderer && (!is<RenderBox>(*renderer) || !downcast<RenderBox>(*renderer).canBeScrolledAndHasScrollableArea())) |
2457 | renderer = renderer->parent(); |
2458 | |
2459 | if (renderer && renderer->node()) { |
2460 | if (Frame* frame = document().frame()) |
2461 | frame->eventHandler().defaultTouchEventHandler(*renderer->node(), downcast<TouchEvent>(event)); |
2462 | } |
2463 | #endif |
2464 | } |
2465 | } |
2466 | |
2467 | bool Node::willRespondToMouseMoveEvents() |
2468 | { |
2469 | // FIXME: Why is the iOS code path different from the non-iOS code path? |
2470 | #if !PLATFORM(IOS_FAMILY) |
2471 | if (!is<Element>(*this)) |
2472 | return false; |
2473 | if (downcast<Element>(*this).isDisabledFormControl()) |
2474 | return false; |
2475 | #endif |
2476 | return hasEventListeners(eventNames().mousemoveEvent) || hasEventListeners(eventNames().mouseoverEvent) || hasEventListeners(eventNames().mouseoutEvent); |
2477 | } |
2478 | |
2479 | bool Node::willRespondToMouseClickEvents() |
2480 | { |
2481 | // FIXME: Why is the iOS code path different from the non-iOS code path? |
2482 | #if PLATFORM(IOS_FAMILY) |
2483 | return isContentEditable() || hasEventListeners(eventNames().mouseupEvent) || hasEventListeners(eventNames().mousedownEvent) || hasEventListeners(eventNames().clickEvent); |
2484 | #else |
2485 | if (!is<Element>(*this)) |
2486 | return false; |
2487 | if (downcast<Element>(*this).isDisabledFormControl()) |
2488 | return false; |
2489 | return computeEditability(UserSelectAllIsAlwaysNonEditable, ShouldUpdateStyle::Update) != Editability::ReadOnly |
2490 | || hasEventListeners(eventNames().mouseupEvent) || hasEventListeners(eventNames().mousedownEvent) || hasEventListeners(eventNames().clickEvent) || hasEventListeners(eventNames().DOMActivateEvent); |
2491 | #endif |
2492 | } |
2493 | |
2494 | bool Node::willRespondToMouseWheelEvents() |
2495 | { |
2496 | return hasEventListeners(eventNames().mousewheelEvent); |
2497 | } |
2498 | |
2499 | // It's important not to inline removedLastRef, because we don't want to inline the code to |
2500 | // delete a Node at each deref call site. |
2501 | void Node::removedLastRef() |
2502 | { |
2503 | ASSERT(m_refCountAndParentBit == s_refCountIncrement); |
2504 | |
2505 | // An explicit check for Document here is better than a virtual function since it is |
2506 | // faster for non-Document nodes, and because the call to removedLastRef that is inlined |
2507 | // at all deref call sites is smaller if it's a non-virtual function. |
2508 | if (is<Document>(*this)) { |
2509 | downcast<Document>(*this).removedLastRef(); |
2510 | return; |
2511 | } |
2512 | |
2513 | // Now it is time to detach the SVGElement from all its properties. These properties |
2514 | // may outlive the SVGElement. The only difference after the detach is no commit will |
2515 | // be carried out unless these properties are attached to another owner. |
2516 | if (is<SVGElement>(*this)) |
2517 | downcast<SVGElement>(*this).detachAllProperties(); |
2518 | |
2519 | #ifndef NDEBUG |
2520 | m_deletionHasBegun = true; |
2521 | #endif |
2522 | delete this; |
2523 | } |
2524 | |
2525 | void Node::textRects(Vector<IntRect>& rects) const |
2526 | { |
2527 | auto range = Range::create(document()); |
2528 | range->selectNodeContents(const_cast<Node&>(*this)); |
2529 | range->absoluteTextRects(rects); |
2530 | } |
2531 | |
2532 | unsigned Node::connectedSubframeCount() const |
2533 | { |
2534 | return hasRareData() ? rareData()->connectedSubframeCount() : 0; |
2535 | } |
2536 | |
2537 | void Node::incrementConnectedSubframeCount(unsigned amount) |
2538 | { |
2539 | ASSERT(isContainerNode()); |
2540 | ensureRareData().incrementConnectedSubframeCount(amount); |
2541 | } |
2542 | |
2543 | void Node::decrementConnectedSubframeCount(unsigned amount) |
2544 | { |
2545 | rareData()->decrementConnectedSubframeCount(amount); |
2546 | } |
2547 | |
2548 | void Node::updateAncestorConnectedSubframeCountForRemoval() const |
2549 | { |
2550 | unsigned count = connectedSubframeCount(); |
2551 | |
2552 | if (!count) |
2553 | return; |
2554 | |
2555 | for (Node* node = parentOrShadowHostNode(); node; node = node->parentOrShadowHostNode()) |
2556 | node->decrementConnectedSubframeCount(count); |
2557 | } |
2558 | |
2559 | void Node::updateAncestorConnectedSubframeCountForInsertion() const |
2560 | { |
2561 | unsigned count = connectedSubframeCount(); |
2562 | |
2563 | if (!count) |
2564 | return; |
2565 | |
2566 | for (Node* node = parentOrShadowHostNode(); node; node = node->parentOrShadowHostNode()) |
2567 | node->incrementConnectedSubframeCount(count); |
2568 | } |
2569 | |
2570 | bool Node::inRenderedDocument() const |
2571 | { |
2572 | return isConnected() && document().hasLivingRenderTree(); |
2573 | } |
2574 | |
2575 | void* Node::opaqueRootSlow() const |
2576 | { |
2577 | const Node* node = this; |
2578 | for (;;) { |
2579 | const Node* nextNode = node->parentOrShadowHostNode(); |
2580 | if (!nextNode) |
2581 | break; |
2582 | node = nextNode; |
2583 | } |
2584 | return const_cast<void*>(static_cast<const void*>(node)); |
2585 | } |
2586 | |
2587 | } // namespace WebCore |
2588 | |
2589 | #if ENABLE(TREE_DEBUGGING) |
2590 | |
2591 | void showTree(const WebCore::Node* node) |
2592 | { |
2593 | if (node) |
2594 | node->showTreeForThis(); |
2595 | } |
2596 | |
2597 | void showNodePath(const WebCore::Node* node) |
2598 | { |
2599 | if (node) |
2600 | node->showNodePathForThis(); |
2601 | } |
2602 | |
2603 | #endif // ENABLE(TREE_DEBUGGING) |
2604 | |