1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Peter Kelly (pmk@post.com)
5 * (C) 2001 Dirk Mueller (mueller@kde.org)
6 * (C) 2007 David Smith (catfish.man@gmail.com)
7 * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
8 * (C) 2007 Eric Seidel (eric@webkit.org)
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25
26#include "config.h"
27#include "Element.h"
28
29#include "AXObjectCache.h"
30#include "Attr.h"
31#include "AttributeChangeInvalidation.h"
32#include "CSSAnimationController.h"
33#include "CSSParser.h"
34#include "Chrome.h"
35#include "ChromeClient.h"
36#include "ClassChangeInvalidation.h"
37#include "ComposedTreeAncestorIterator.h"
38#include "ContainerNodeAlgorithms.h"
39#include "CustomElementReactionQueue.h"
40#include "CustomElementRegistry.h"
41#include "DOMRect.h"
42#include "DOMRectList.h"
43#include "DOMTokenList.h"
44#include "DOMWindow.h"
45#include "DocumentSharedObjectPool.h"
46#include "DocumentTimeline.h"
47#include "Editing.h"
48#include "ElementIterator.h"
49#include "ElementRareData.h"
50#include "EventDispatcher.h"
51#include "EventHandler.h"
52#include "EventNames.h"
53#include "FocusController.h"
54#include "FocusEvent.h"
55#include "Frame.h"
56#include "FrameSelection.h"
57#include "FrameView.h"
58#include "FullscreenManager.h"
59#include "HTMLBodyElement.h"
60#include "HTMLCanvasElement.h"
61#include "HTMLCollection.h"
62#include "HTMLDocument.h"
63#include "HTMLHtmlElement.h"
64#include "HTMLLabelElement.h"
65#include "HTMLNameCollection.h"
66#include "HTMLObjectElement.h"
67#include "HTMLOptGroupElement.h"
68#include "HTMLOptionElement.h"
69#include "HTMLParserIdioms.h"
70#include "HTMLSelectElement.h"
71#include "HTMLTemplateElement.h"
72#include "IdChangeInvalidation.h"
73#include "IdTargetObserverRegistry.h"
74#include "InspectorInstrumentation.h"
75#include "JSLazyEventListener.h"
76#include "KeyboardEvent.h"
77#include "KeyframeEffect.h"
78#include "MutationObserverInterestGroup.h"
79#include "MutationRecord.h"
80#include "NodeRenderStyle.h"
81#include "PlatformWheelEvent.h"
82#include "PointerCaptureController.h"
83#include "PointerEvent.h"
84#include "PointerLockController.h"
85#include "RenderFragmentContainer.h"
86#include "RenderLayer.h"
87#include "RenderLayerBacking.h"
88#include "RenderLayerCompositor.h"
89#include "RenderListBox.h"
90#include "RenderTheme.h"
91#include "RenderTreeUpdater.h"
92#include "RenderView.h"
93#include "RenderWidget.h"
94#include "RuntimeEnabledFeatures.h"
95#include "SVGDocumentExtensions.h"
96#include "SVGElement.h"
97#include "SVGNames.h"
98#include "SVGSVGElement.h"
99#include "ScriptDisallowedScope.h"
100#include "ScrollIntoViewOptions.h"
101#include "ScrollLatchingState.h"
102#include "SelectorQuery.h"
103#include "Settings.h"
104#include "SimulatedClick.h"
105#include "SlotAssignment.h"
106#include "StyleProperties.h"
107#include "StyleResolver.h"
108#include "StyleScope.h"
109#include "StyleTreeResolver.h"
110#include "TextIterator.h"
111#include "TouchAction.h"
112#include "VoidCallback.h"
113#include "WebAnimation.h"
114#include "WheelEvent.h"
115#include "XLinkNames.h"
116#include "XMLNSNames.h"
117#include "XMLNames.h"
118#include "markup.h"
119#include <wtf/IsoMallocInlines.h>
120#include <wtf/NeverDestroyed.h>
121#include <wtf/text/CString.h>
122
123namespace WebCore {
124
125WTF_MAKE_ISO_ALLOCATED_IMPL(Element);
126
127using namespace HTMLNames;
128using namespace XMLNames;
129
130static HashMap<Element*, Vector<RefPtr<Attr>>>& attrNodeListMap()
131{
132 static NeverDestroyed<HashMap<Element*, Vector<RefPtr<Attr>>>> map;
133 return map;
134}
135
136static Vector<RefPtr<Attr>>* attrNodeListForElement(Element& element)
137{
138 if (!element.hasSyntheticAttrChildNodes())
139 return nullptr;
140 ASSERT(attrNodeListMap().contains(&element));
141 return &attrNodeListMap().find(&element)->value;
142}
143
144static Vector<RefPtr<Attr>>& ensureAttrNodeListForElement(Element& element)
145{
146 if (element.hasSyntheticAttrChildNodes()) {
147 ASSERT(attrNodeListMap().contains(&element));
148 return attrNodeListMap().find(&element)->value;
149 }
150 ASSERT(!attrNodeListMap().contains(&element));
151 element.setHasSyntheticAttrChildNodes(true);
152 return attrNodeListMap().add(&element, Vector<RefPtr<Attr>>()).iterator->value;
153}
154
155static void removeAttrNodeListForElement(Element& element)
156{
157 ASSERT(element.hasSyntheticAttrChildNodes());
158 ASSERT(attrNodeListMap().contains(&element));
159 attrNodeListMap().remove(&element);
160 element.setHasSyntheticAttrChildNodes(false);
161}
162
163static Attr* findAttrNodeInList(Vector<RefPtr<Attr>>& attrNodeList, const QualifiedName& name)
164{
165 for (auto& node : attrNodeList) {
166 if (node->qualifiedName().matches(name))
167 return node.get();
168 }
169 return nullptr;
170}
171
172static Attr* findAttrNodeInList(Vector<RefPtr<Attr>>& attrNodeList, const AtomString& localName, bool shouldIgnoreAttributeCase)
173{
174 const AtomString& caseAdjustedName = shouldIgnoreAttributeCase ? localName.convertToASCIILowercase() : localName;
175 for (auto& node : attrNodeList) {
176 if (node->qualifiedName().localName() == caseAdjustedName)
177 return node.get();
178 }
179 return nullptr;
180}
181
182Ref<Element> Element::create(const QualifiedName& tagName, Document& document)
183{
184 return adoptRef(*new Element(tagName, document, CreateElement));
185}
186
187Element::Element(const QualifiedName& tagName, Document& document, ConstructionType type)
188 : ContainerNode(document, type)
189 , m_tagName(tagName)
190{
191}
192
193Element::~Element()
194{
195 ASSERT(!beforePseudoElement());
196 ASSERT(!afterPseudoElement());
197
198#if ENABLE(INTERSECTION_OBSERVER)
199 disconnectFromIntersectionObservers();
200#endif
201
202#if ENABLE(RESIZE_OBSERVER)
203 disconnectFromResizeObservers();
204#endif
205
206 removeShadowRoot();
207
208 if (hasSyntheticAttrChildNodes())
209 detachAllAttrNodesFromElement();
210
211#if ENABLE(CSS_TYPED_OM)
212 if (hasRareData()) {
213 if (auto* map = elementRareData()->attributeStyleMap())
214 map->clearElement();
215 }
216#endif
217
218 if (hasPendingResources()) {
219 document().accessSVGExtensions().removeElementFromPendingResources(*this);
220 ASSERT(!hasPendingResources());
221 }
222}
223
224inline ElementRareData* Element::elementRareData() const
225{
226 ASSERT_WITH_SECURITY_IMPLICATION(hasRareData());
227 return static_cast<ElementRareData*>(rareData());
228}
229
230inline ElementRareData& Element::ensureElementRareData()
231{
232 return static_cast<ElementRareData&>(ensureRareData());
233}
234
235void Element::clearTabIndexExplicitlyIfNeeded()
236{
237 if (hasRareData())
238 elementRareData()->clearTabIndexExplicitly();
239}
240
241void Element::setTabIndexExplicitly(int tabIndex)
242{
243 ensureElementRareData().setTabIndexExplicitly(tabIndex);
244}
245
246bool Element::tabIndexSetExplicitly() const
247{
248 return hasRareData() && elementRareData()->tabIndexSetExplicitly();
249}
250
251bool Element::supportsFocus() const
252{
253 return tabIndexSetExplicitly();
254}
255
256RefPtr<Element> Element::focusDelegate()
257{
258 return this;
259}
260
261int Element::tabIndex() const
262{
263 return hasRareData() ? elementRareData()->tabIndex() : 0;
264}
265
266void Element::setTabIndex(int value)
267{
268 setIntegralAttribute(tabindexAttr, value);
269}
270
271bool Element::isKeyboardFocusable(KeyboardEvent*) const
272{
273 return isFocusable() && tabIndex() >= 0;
274}
275
276bool Element::isMouseFocusable() const
277{
278 return isFocusable();
279}
280
281bool Element::shouldUseInputMethod()
282{
283 return computeEditability(UserSelectAllIsAlwaysNonEditable, ShouldUpdateStyle::Update) != Editability::ReadOnly;
284}
285
286static bool isForceEvent(const PlatformMouseEvent& platformEvent)
287{
288 return platformEvent.type() == PlatformEvent::MouseForceChanged || platformEvent.type() == PlatformEvent::MouseForceDown || platformEvent.type() == PlatformEvent::MouseForceUp;
289}
290
291#if ENABLE(POINTER_EVENTS) && !ENABLE(TOUCH_EVENTS)
292static bool isCompatibilityMouseEvent(const MouseEvent& mouseEvent)
293{
294 // https://www.w3.org/TR/pointerevents/#compatibility-mapping-with-mouse-events
295 const auto& type = mouseEvent.type();
296 return type != eventNames().clickEvent && type != eventNames().mouseoverEvent && type != eventNames().mouseoutEvent && type != eventNames().mouseenterEvent && type != eventNames().mouseleaveEvent;
297}
298#endif
299
300bool Element::dispatchMouseEvent(const PlatformMouseEvent& platformEvent, const AtomString& eventType, int detail, Element* relatedTarget)
301{
302 if (isDisabledFormControl())
303 return false;
304
305 if (isForceEvent(platformEvent) && !document().hasListenerTypeForEventType(platformEvent.type()))
306 return false;
307
308 Ref<MouseEvent> mouseEvent = MouseEvent::create(eventType, document().windowProxy(), platformEvent, detail, relatedTarget);
309
310 if (mouseEvent->type().isEmpty())
311 return true; // Shouldn't happen.
312
313 bool didNotSwallowEvent = true;
314
315#if ENABLE(POINTER_EVENTS)
316 if (RuntimeEnabledFeatures::sharedFeatures().pointerEventsEnabled()) {
317 if (auto* page = document().page()) {
318 auto& pointerCaptureController = page->pointerCaptureController();
319#if ENABLE(TOUCH_EVENTS)
320 if (mouseEvent->type() != eventNames().clickEvent && pointerCaptureController.preventsCompatibilityMouseEventsForIdentifier(platformEvent.pointerId()))
321 return false;
322#else
323 if (auto pointerEvent = pointerCaptureController.pointerEventForMouseEvent(mouseEvent)) {
324 pointerCaptureController.dispatchEvent(*pointerEvent, this);
325 if (isCompatibilityMouseEvent(mouseEvent) && pointerCaptureController.preventsCompatibilityMouseEventsForIdentifier(pointerEvent->pointerId()))
326 return false;
327 if (pointerEvent->defaultPrevented() || pointerEvent->defaultHandled()) {
328 didNotSwallowEvent = false;
329 if (pointerEvent->type() == eventNames().pointerdownEvent)
330 return false;
331 }
332 }
333#endif
334 }
335 }
336#endif
337
338 ASSERT(!mouseEvent->target() || mouseEvent->target() != relatedTarget);
339 dispatchEvent(mouseEvent);
340 if (mouseEvent->defaultPrevented() || mouseEvent->defaultHandled())
341 didNotSwallowEvent = false;
342
343 if (mouseEvent->type() == eventNames().clickEvent && mouseEvent->detail() == 2) {
344 // Special case: If it's a double click event, we also send the dblclick event. This is not part
345 // of the DOM specs, but is used for compatibility with the ondblclick="" attribute. This is treated
346 // as a separate event in other DOM-compliant browsers like Firefox, and so we do the same.
347 // FIXME: Is it okay that mouseEvent may have been mutated by scripts via initMouseEvent in dispatchEvent above?
348 Ref<MouseEvent> doubleClickEvent = MouseEvent::create(eventNames().dblclickEvent,
349 mouseEvent->bubbles() ? Event::CanBubble::Yes : Event::CanBubble::No,
350 mouseEvent->cancelable() ? Event::IsCancelable::Yes : Event::IsCancelable::No,
351 Event::IsComposed::Yes,
352 mouseEvent->view(), mouseEvent->detail(),
353 mouseEvent->screenX(), mouseEvent->screenY(), mouseEvent->clientX(), mouseEvent->clientY(),
354 mouseEvent->modifierKeys(), mouseEvent->button(), mouseEvent->buttons(), mouseEvent->syntheticClickType(), relatedTarget);
355
356 if (mouseEvent->defaultHandled())
357 doubleClickEvent->setDefaultHandled();
358
359 dispatchEvent(doubleClickEvent);
360 if (doubleClickEvent->defaultHandled() || doubleClickEvent->defaultPrevented())
361 return false;
362 }
363 return didNotSwallowEvent;
364}
365
366bool Element::dispatchWheelEvent(const PlatformWheelEvent& platformEvent)
367{
368 auto event = WheelEvent::create(platformEvent, document().windowProxy());
369
370 // Events with no deltas are important because they convey platform information about scroll gestures
371 // and momentum beginning or ending. However, those events should not be sent to the DOM since some
372 // websites will break. They need to be dispatched because dispatching them will call into the default
373 // event handler, and our platform code will correctly handle the phase changes. Calling stopPropogation()
374 // will prevent the event from being sent to the DOM, but will still call the default event handler.
375 // FIXME: Move this logic into WheelEvent::create.
376 if (!platformEvent.deltaX() && !platformEvent.deltaY())
377 event->stopPropagation();
378
379 dispatchEvent(event);
380 return !event->defaultPrevented() && !event->defaultHandled();
381}
382
383bool Element::dispatchKeyEvent(const PlatformKeyboardEvent& platformEvent)
384{
385 auto event = KeyboardEvent::create(platformEvent, document().windowProxy());
386
387 if (Frame* frame = document().frame()) {
388 if (frame->eventHandler().accessibilityPreventsEventPropagation(event))
389 event->stopPropagation();
390 }
391
392 dispatchEvent(event);
393 return !event->defaultPrevented() && !event->defaultHandled();
394}
395
396void Element::dispatchSimulatedClick(Event* underlyingEvent, SimulatedClickMouseEventOptions eventOptions, SimulatedClickVisualOptions visualOptions)
397{
398 simulateClick(*this, underlyingEvent, eventOptions, visualOptions, SimulatedClickSource::UserAgent);
399}
400
401Ref<Node> Element::cloneNodeInternal(Document& targetDocument, CloningOperation type)
402{
403 switch (type) {
404 case CloningOperation::OnlySelf:
405 case CloningOperation::SelfWithTemplateContent:
406 return cloneElementWithoutChildren(targetDocument);
407 case CloningOperation::Everything:
408 break;
409 }
410 return cloneElementWithChildren(targetDocument);
411}
412
413Ref<Element> Element::cloneElementWithChildren(Document& targetDocument)
414{
415 Ref<Element> clone = cloneElementWithoutChildren(targetDocument);
416 cloneChildNodes(clone);
417 return clone;
418}
419
420Ref<Element> Element::cloneElementWithoutChildren(Document& targetDocument)
421{
422 Ref<Element> clone = cloneElementWithoutAttributesAndChildren(targetDocument);
423
424 // This will catch HTML elements in the wrong namespace that are not correctly copied.
425 // This is a sanity check as HTML overloads some of the DOM methods.
426 ASSERT(isHTMLElement() == clone->isHTMLElement());
427
428 clone->cloneDataFromElement(*this);
429 return clone;
430}
431
432Ref<Element> Element::cloneElementWithoutAttributesAndChildren(Document& targetDocument)
433{
434 return targetDocument.createElement(tagQName(), false);
435}
436
437Ref<Attr> Element::detachAttribute(unsigned index)
438{
439 ASSERT(elementData());
440
441 const Attribute& attribute = elementData()->attributeAt(index);
442
443 RefPtr<Attr> attrNode = attrIfExists(attribute.name());
444 if (attrNode)
445 detachAttrNodeFromElementWithValue(attrNode.get(), attribute.value());
446 else
447 attrNode = Attr::create(document(), attribute.name(), attribute.value());
448
449 removeAttributeInternal(index, NotInSynchronizationOfLazyAttribute);
450 return attrNode.releaseNonNull();
451}
452
453bool Element::removeAttribute(const QualifiedName& name)
454{
455 if (!elementData())
456 return false;
457
458 unsigned index = elementData()->findAttributeIndexByName(name);
459 if (index == ElementData::attributeNotFound)
460 return false;
461
462 removeAttributeInternal(index, NotInSynchronizationOfLazyAttribute);
463 return true;
464}
465
466void Element::setBooleanAttribute(const QualifiedName& name, bool value)
467{
468 if (value)
469 setAttribute(name, emptyAtom());
470 else
471 removeAttribute(name);
472}
473
474NamedNodeMap& Element::attributes() const
475{
476 ElementRareData& rareData = const_cast<Element*>(this)->ensureElementRareData();
477 if (NamedNodeMap* attributeMap = rareData.attributeMap())
478 return *attributeMap;
479
480 rareData.setAttributeMap(std::make_unique<NamedNodeMap>(const_cast<Element&>(*this)));
481 return *rareData.attributeMap();
482}
483
484Node::NodeType Element::nodeType() const
485{
486 return ELEMENT_NODE;
487}
488
489bool Element::hasAttribute(const QualifiedName& name) const
490{
491 return hasAttributeNS(name.namespaceURI(), name.localName());
492}
493
494void Element::synchronizeAllAttributes() const
495{
496 if (!elementData())
497 return;
498 if (elementData()->styleAttributeIsDirty()) {
499 ASSERT(isStyledElement());
500 static_cast<const StyledElement*>(this)->synchronizeStyleAttributeInternal();
501 }
502
503 if (isSVGElement())
504 downcast<SVGElement>(const_cast<Element&>(*this)).synchronizeAllAttributes();
505}
506
507ALWAYS_INLINE void Element::synchronizeAttribute(const QualifiedName& name) const
508{
509 if (!elementData())
510 return;
511 if (UNLIKELY(name == styleAttr && elementData()->styleAttributeIsDirty())) {
512 ASSERT_WITH_SECURITY_IMPLICATION(isStyledElement());
513 static_cast<const StyledElement*>(this)->synchronizeStyleAttributeInternal();
514 return;
515 }
516
517 if (isSVGElement())
518 downcast<SVGElement>(const_cast<Element&>(*this)).synchronizeAttribute(name);
519}
520
521static ALWAYS_INLINE bool isStyleAttribute(const Element& element, const AtomString& attributeLocalName)
522{
523 if (shouldIgnoreAttributeCase(element))
524 return equalLettersIgnoringASCIICase(attributeLocalName, "style");
525 return attributeLocalName == styleAttr->localName();
526}
527
528ALWAYS_INLINE void Element::synchronizeAttribute(const AtomString& localName) const
529{
530 // This version of synchronizeAttribute() is streamlined for the case where you don't have a full QualifiedName,
531 // e.g when called from DOM API.
532 if (!elementData())
533 return;
534 if (elementData()->styleAttributeIsDirty() && isStyleAttribute(*this, localName)) {
535 ASSERT_WITH_SECURITY_IMPLICATION(isStyledElement());
536 static_cast<const StyledElement*>(this)->synchronizeStyleAttributeInternal();
537 return;
538 }
539
540 if (isSVGElement())
541 downcast<SVGElement>(const_cast<Element&>(*this)).synchronizeAttribute(QualifiedName(nullAtom(), localName, nullAtom()));
542}
543
544const AtomString& Element::getAttribute(const QualifiedName& name) const
545{
546 if (!elementData())
547 return nullAtom();
548 synchronizeAttribute(name);
549 if (const Attribute* attribute = findAttributeByName(name))
550 return attribute->value();
551 return nullAtom();
552}
553
554Vector<String> Element::getAttributeNames() const
555{
556 Vector<String> attributesVector;
557 if (!hasAttributes())
558 return attributesVector;
559
560 auto attributes = attributesIterator();
561 attributesVector.reserveInitialCapacity(attributes.attributeCount());
562 for (auto& attribute : attributes)
563 attributesVector.uncheckedAppend(attribute.name().toString());
564 return attributesVector;
565}
566
567bool Element::isFocusable() const
568{
569 if (!isConnected() || !supportsFocus())
570 return false;
571
572 if (!renderer()) {
573 // If the node is in a display:none tree it might say it needs style recalc but
574 // the whole document is actually up to date.
575 // FIXME: We should be able to assert !needsStyleRecalc() || !document().childNeedsStyleRecalc()
576 // but it hits too frequently on websites like Gmail and Microsoft Exchange.
577
578 // Elements in canvas fallback content are not rendered, but they are allowed to be
579 // focusable as long as their canvas is displayed and visible.
580 if (auto* canvas = ancestorsOfType<HTMLCanvasElement>(*this).first())
581 return canvas->renderer() && canvas->renderer()->style().visibility() == Visibility::Visible;
582 }
583
584 // FIXME: Even if we are not visible, we might have a child that is visible.
585 // Hyatt wants to fix that some day with a "has visible content" flag or the like.
586 if (!renderer() || renderer()->style().visibility() != Visibility::Visible)
587 return false;
588
589 return true;
590}
591
592bool Element::isUserActionElementInActiveChain() const
593{
594 ASSERT(isUserActionElement());
595 return document().userActionElements().isInActiveChain(*this);
596}
597
598bool Element::isUserActionElementActive() const
599{
600 ASSERT(isUserActionElement());
601 return document().userActionElements().isActive(*this);
602}
603
604bool Element::isUserActionElementFocused() const
605{
606 ASSERT(isUserActionElement());
607 return document().userActionElements().isFocused(*this);
608}
609
610bool Element::isUserActionElementHovered() const
611{
612 ASSERT(isUserActionElement());
613 return document().userActionElements().isHovered(*this);
614}
615
616void Element::setActive(bool flag, bool pause)
617{
618 if (flag == active())
619 return;
620
621 document().userActionElements().setActive(*this, flag);
622
623 auto* renderStyle = renderOrDisplayContentsStyle();
624 bool reactsToPress = (renderStyle && renderStyle->affectedByActive()) || styleAffectedByActive();
625 if (reactsToPress)
626 invalidateStyleForSubtree();
627
628 if (!renderer())
629 return;
630
631 if (renderer()->style().hasAppearance() && renderer()->theme().stateChanged(*renderer(), ControlStates::PressedState))
632 reactsToPress = true;
633
634 // The rest of this function implements a feature that only works if the
635 // platform supports immediate invalidations on the ChromeClient, so bail if
636 // that isn't supported.
637 if (!document().page()->chrome().client().supportsImmediateInvalidation())
638 return;
639
640 if (reactsToPress && pause) {
641 // The delay here is subtle. It relies on an assumption, namely that the amount of time it takes
642 // to repaint the "down" state of the control is about the same time as it would take to repaint the
643 // "up" state. Once you assume this, you can just delay for 100ms - that time (assuming that after you
644 // leave this method, it will be about that long before the flush of the up state happens again).
645#ifdef HAVE_FUNC_USLEEP
646 MonotonicTime startTime = MonotonicTime::now();
647#endif
648
649 document().updateStyleIfNeeded();
650
651 // Do an immediate repaint.
652 if (renderer())
653 renderer()->repaint();
654
655 // FIXME: Come up with a less ridiculous way of doing this.
656#ifdef HAVE_FUNC_USLEEP
657 // Now pause for a small amount of time (1/10th of a second from before we repainted in the pressed state)
658 Seconds remainingTime = 100_ms - (MonotonicTime::now() - startTime);
659 if (remainingTime > 0_s)
660 usleep(static_cast<useconds_t>(remainingTime.microseconds()));
661#endif
662 }
663}
664
665void Element::setFocus(bool flag)
666{
667 if (flag == focused())
668 return;
669
670 document().userActionElements().setFocused(*this, flag);
671 invalidateStyleForSubtree();
672
673 for (Element* element = this; element; element = element->parentElementInComposedTree())
674 element->setHasFocusWithin(flag);
675}
676
677void Element::setHovered(bool flag)
678{
679 if (flag == hovered())
680 return;
681
682 document().userActionElements().setHovered(*this, flag);
683
684 auto* style = renderOrDisplayContentsStyle();
685 if (style && (style->affectedByHover() || childrenAffectedByHover()))
686 invalidateStyleForSubtree();
687
688 if (!renderer()) {
689 // When setting hover to false, the style needs to be recalc'd even when
690 // there's no renderer (imagine setting display:none in the :hover class,
691 // if a nil renderer would prevent this element from recalculating its
692 // style, it would never go back to its normal style and remain
693 // stuck in its hovered style).
694 if (!flag && !style)
695 invalidateStyleForSubtree();
696
697 return;
698 }
699
700 if (style->hasAppearance())
701 renderer()->theme().stateChanged(*renderer(), ControlStates::HoverState);
702}
703
704// FIXME(webkit.org/b/161611): Take into account orientation/direction.
705inline ScrollAlignment toScrollAlignment(Optional<ScrollLogicalPosition> position, bool isVertical)
706{
707 switch (position.valueOr(isVertical ? ScrollLogicalPosition::Start : ScrollLogicalPosition::Nearest)) {
708 case ScrollLogicalPosition::Start:
709 return isVertical ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignLeftAlways;
710 case ScrollLogicalPosition::Center:
711 return ScrollAlignment::alignCenterAlways;
712 case ScrollLogicalPosition::End:
713 return isVertical ? ScrollAlignment::alignBottomAlways : ScrollAlignment::alignRightAlways;
714 case ScrollLogicalPosition::Nearest:
715 return ScrollAlignment::alignToEdgeIfNeeded;
716 default:
717 ASSERT_NOT_REACHED();
718 return ScrollAlignment::alignToEdgeIfNeeded;
719 }
720}
721
722void Element::scrollIntoView(Optional<Variant<bool, ScrollIntoViewOptions>>&& arg)
723{
724 document().updateLayoutIgnorePendingStylesheets();
725
726 if (!renderer())
727 return;
728
729 bool insideFixed;
730 LayoutRect absoluteBounds = renderer()->absoluteAnchorRect(&insideFixed);
731
732 // FIXME(webkit.org/b/188043): Support ScrollBehavior.
733 ScrollIntoViewOptions options;
734 if (arg) {
735 auto value = arg.value();
736 if (WTF::holds_alternative<ScrollIntoViewOptions>(value))
737 options = WTF::get<ScrollIntoViewOptions>(value);
738 else if (!WTF::get<bool>(value))
739 options.blockPosition = ScrollLogicalPosition::End;
740 }
741
742 ScrollAlignment alignX = toScrollAlignment(options.inlinePosition, false);
743 ScrollAlignment alignY = toScrollAlignment(options.blockPosition, true);
744 renderer()->scrollRectToVisible(absoluteBounds, insideFixed, { SelectionRevealMode::Reveal, alignX, alignY, ShouldAllowCrossOriginScrolling::No });
745}
746
747void Element::scrollIntoView(bool alignToTop)
748{
749 document().updateLayoutIgnorePendingStylesheets();
750
751 if (!renderer())
752 return;
753
754 bool insideFixed;
755 LayoutRect absoluteBounds = renderer()->absoluteAnchorRect(&insideFixed);
756 // Align to the top / bottom and to the closest edge.
757 if (alignToTop)
758 renderer()->scrollRectToVisible(absoluteBounds, insideFixed, { SelectionRevealMode::Reveal, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignTopAlways, ShouldAllowCrossOriginScrolling::No });
759 else
760 renderer()->scrollRectToVisible(absoluteBounds, insideFixed, { SelectionRevealMode::Reveal, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignBottomAlways, ShouldAllowCrossOriginScrolling::No });
761}
762
763void Element::scrollIntoViewIfNeeded(bool centerIfNeeded)
764{
765 document().updateLayoutIgnorePendingStylesheets();
766
767 if (!renderer())
768 return;
769
770 bool insideFixed;
771 LayoutRect absoluteBounds = renderer()->absoluteAnchorRect(&insideFixed);
772 if (centerIfNeeded)
773 renderer()->scrollRectToVisible(absoluteBounds, insideFixed, { SelectionRevealMode::Reveal, ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded, ShouldAllowCrossOriginScrolling::No });
774 else
775 renderer()->scrollRectToVisible(absoluteBounds, insideFixed, { SelectionRevealMode::Reveal, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded, ShouldAllowCrossOriginScrolling::No });
776}
777
778void Element::scrollIntoViewIfNotVisible(bool centerIfNotVisible)
779{
780 document().updateLayoutIgnorePendingStylesheets();
781
782 if (!renderer())
783 return;
784
785 bool insideFixed;
786 LayoutRect absoluteBounds = renderer()->absoluteAnchorRect(&insideFixed);
787 if (centerIfNotVisible)
788 renderer()->scrollRectToVisible(absoluteBounds, insideFixed, { SelectionRevealMode::Reveal, ScrollAlignment::alignCenterIfNotVisible, ScrollAlignment::alignCenterIfNotVisible, ShouldAllowCrossOriginScrolling::No });
789 else
790 renderer()->scrollRectToVisible(absoluteBounds, insideFixed, { SelectionRevealMode::Reveal, ScrollAlignment::alignToEdgeIfNotVisible, ScrollAlignment::alignToEdgeIfNotVisible, ShouldAllowCrossOriginScrolling::No });
791}
792
793void Element::scrollBy(const ScrollToOptions& options)
794{
795 ScrollToOptions scrollToOptions = normalizeNonFiniteCoordinatesOrFallBackTo(options, 0, 0);
796 scrollToOptions.left.value() += scrollLeft();
797 scrollToOptions.top.value() += scrollTop();
798 scrollTo(scrollToOptions);
799}
800
801void Element::scrollBy(double x, double y)
802{
803 scrollBy({ x, y });
804}
805
806void Element::scrollTo(const ScrollToOptions& options, ScrollClamping clamping)
807{
808 if (!document().settings().CSSOMViewScrollingAPIEnabled()) {
809 // If the element is the root element and document is in quirks mode, terminate these steps.
810 // Note that WebKit always uses quirks mode document scrolling behavior. See Document::scrollingElement().
811 if (this == document().documentElement())
812 return;
813 }
814
815 document().updateLayoutIgnorePendingStylesheets();
816
817 if (document().scrollingElement() == this) {
818 // If the element is the scrolling element and is not potentially scrollable,
819 // invoke scroll() on window with options as the only argument, and terminate these steps.
820 // FIXME: Scrolling an independently scrollable body is broken: webkit.org/b/161612.
821 auto window = makeRefPtr(document().domWindow());
822 if (!window)
823 return;
824
825 window->scrollTo(options);
826 return;
827 }
828
829 // If the element does not have any associated CSS layout box, the element has no associated scrolling box,
830 // or the element has no overflow, terminate these steps.
831 RenderBox* renderer = renderBox();
832 if (!renderer || !renderer->hasOverflowClip())
833 return;
834
835 ScrollToOptions scrollToOptions = normalizeNonFiniteCoordinatesOrFallBackTo(options,
836 adjustForAbsoluteZoom(renderer->scrollLeft(), *renderer),
837 adjustForAbsoluteZoom(renderer->scrollTop(), *renderer)
838 );
839 renderer->setScrollLeft(clampToInteger(scrollToOptions.left.value() * renderer->style().effectiveZoom()), ScrollType::Programmatic, clamping);
840 renderer->setScrollTop(clampToInteger(scrollToOptions.top.value() * renderer->style().effectiveZoom()), ScrollType::Programmatic, clamping);
841}
842
843void Element::scrollTo(double x, double y)
844{
845 scrollTo({ x, y });
846}
847
848void Element::scrollByUnits(int units, ScrollGranularity granularity)
849{
850 document().updateLayoutIgnorePendingStylesheets();
851
852 auto* renderer = this->renderer();
853 if (!renderer)
854 return;
855
856 if (!renderer->hasOverflowClip())
857 return;
858
859 ScrollDirection direction = ScrollDown;
860 if (units < 0) {
861 direction = ScrollUp;
862 units = -units;
863 }
864 Element* stopElement = this;
865 downcast<RenderBox>(*renderer).scroll(direction, granularity, units, &stopElement);
866}
867
868void Element::scrollByLines(int lines)
869{
870 scrollByUnits(lines, ScrollByLine);
871}
872
873void Element::scrollByPages(int pages)
874{
875 scrollByUnits(pages, ScrollByPage);
876}
877
878static double localZoomForRenderer(const RenderElement& renderer)
879{
880 // FIXME: This does the wrong thing if two opposing zooms are in effect and canceled each
881 // other out, but the alternative is that we'd have to crawl up the whole render tree every
882 // time (or store an additional bit in the RenderStyle to indicate that a zoom was specified).
883 double zoomFactor = 1;
884 if (renderer.style().effectiveZoom() != 1) {
885 // Need to find the nearest enclosing RenderElement that set up
886 // a differing zoom, and then we divide our result by it to eliminate the zoom.
887 const RenderElement* prev = &renderer;
888 for (RenderElement* curr = prev->parent(); curr; curr = curr->parent()) {
889 if (curr->style().effectiveZoom() != prev->style().effectiveZoom()) {
890 zoomFactor = prev->style().zoom();
891 break;
892 }
893 prev = curr;
894 }
895 if (prev->isRenderView())
896 zoomFactor = prev->style().zoom();
897 }
898 return zoomFactor;
899}
900
901static double adjustForLocalZoom(LayoutUnit value, const RenderElement& renderer, double& zoomFactor)
902{
903 zoomFactor = localZoomForRenderer(renderer);
904 if (zoomFactor == 1)
905 return value.toDouble();
906 return value.toDouble() / zoomFactor;
907}
908
909static int adjustContentsScrollPositionOrSizeForZoom(int value, const Frame& frame)
910{
911 double zoomFactor = frame.pageZoomFactor() * frame.frameScaleFactor();
912 if (zoomFactor == 1)
913 return value;
914 // FIXME (webkit.org/b/189397): Why can't we just ceil/floor?
915 // Needed because of truncation (rather than rounding) when scaling up.
916 if (zoomFactor > 1)
917 value++;
918 return static_cast<int>(value / zoomFactor);
919}
920
921enum LegacyCSSOMElementMetricsRoundingStrategy { Round, Floor };
922
923static bool subpixelMetricsEnabled(const Document& document)
924{
925 return document.settings().subpixelCSSOMElementMetricsEnabled();
926}
927
928static double convertToNonSubpixelValueIfNeeded(double value, const Document& document, LegacyCSSOMElementMetricsRoundingStrategy roundStrategy = Round)
929{
930 return subpixelMetricsEnabled(document) ? value : roundStrategy == Round ? round(value) : floor(value);
931}
932
933static double adjustOffsetForZoomAndSubpixelLayout(RenderBoxModelObject* renderer, const LayoutUnit& offset)
934{
935 LayoutUnit offsetLeft = subpixelMetricsEnabled(renderer->document()) ? offset : LayoutUnit(roundToInt(offset));
936 double zoomFactor = 1;
937 double offsetLeftAdjustedWithZoom = adjustForLocalZoom(offsetLeft, *renderer, zoomFactor);
938 return convertToNonSubpixelValueIfNeeded(offsetLeftAdjustedWithZoom, renderer->document(), zoomFactor == 1 ? Floor : Round);
939}
940
941static HashSet<TreeScope*> collectAncestorTreeScopeAsHashSet(Node& node)
942{
943 HashSet<TreeScope*> ancestors;
944 for (auto* currentScope = &node.treeScope(); currentScope; currentScope = currentScope->parentTreeScope())
945 ancestors.add(currentScope);
946 return ancestors;
947}
948
949double Element::offsetLeftForBindings()
950{
951 auto offset = offsetLeft();
952
953 auto parent = makeRefPtr(offsetParent());
954 if (!parent || !parent->isInShadowTree())
955 return offset;
956
957 ASSERT(&parent->document() == &document());
958 if (&parent->treeScope() == &treeScope())
959 return offset;
960
961 auto ancestorTreeScopes = collectAncestorTreeScopeAsHashSet(*this);
962 while (parent && !ancestorTreeScopes.contains(&parent->treeScope())) {
963 offset += parent->offsetLeft();
964 parent = parent->offsetParent();
965 }
966
967 return offset;
968}
969
970double Element::offsetLeft()
971{
972 document().updateLayoutIgnorePendingStylesheets();
973 if (RenderBoxModelObject* renderer = renderBoxModelObject())
974 return adjustOffsetForZoomAndSubpixelLayout(renderer, renderer->offsetLeft());
975 return 0;
976}
977
978double Element::offsetTopForBindings()
979{
980 auto offset = offsetTop();
981
982 auto parent = makeRefPtr(offsetParent());
983 if (!parent || !parent->isInShadowTree())
984 return offset;
985
986 ASSERT(&parent->document() == &document());
987 if (&parent->treeScope() == &treeScope())
988 return offset;
989
990 auto ancestorTreeScopes = collectAncestorTreeScopeAsHashSet(*this);
991 while (parent && !ancestorTreeScopes.contains(&parent->treeScope())) {
992 offset += parent->offsetTop();
993 parent = parent->offsetParent();
994 }
995
996 return offset;
997}
998
999double Element::offsetTop()
1000{
1001 document().updateLayoutIgnorePendingStylesheets();
1002 if (RenderBoxModelObject* renderer = renderBoxModelObject())
1003 return adjustOffsetForZoomAndSubpixelLayout(renderer, renderer->offsetTop());
1004 return 0;
1005}
1006
1007double Element::offsetWidth()
1008{
1009 document().updateLayoutIfDimensionsOutOfDate(*this, WidthDimensionsCheck);
1010 if (RenderBoxModelObject* renderer = renderBoxModelObject()) {
1011 LayoutUnit offsetWidth = subpixelMetricsEnabled(renderer->document()) ? renderer->offsetWidth() : LayoutUnit(roundToInt(renderer->offsetWidth()));
1012 return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(offsetWidth, *renderer).toDouble(), renderer->document());
1013 }
1014 return 0;
1015}
1016
1017double Element::offsetHeight()
1018{
1019 document().updateLayoutIfDimensionsOutOfDate(*this, HeightDimensionsCheck);
1020 if (RenderBoxModelObject* renderer = renderBoxModelObject()) {
1021 LayoutUnit offsetHeight = subpixelMetricsEnabled(renderer->document()) ? renderer->offsetHeight() : LayoutUnit(roundToInt(renderer->offsetHeight()));
1022 return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(offsetHeight, *renderer).toDouble(), renderer->document());
1023 }
1024 return 0;
1025}
1026
1027Element* Element::offsetParentForBindings()
1028{
1029 Element* element = offsetParent();
1030 if (!element || !element->isInShadowTree())
1031 return element;
1032 while (element && !isDescendantOrShadowDescendantOf(&element->rootNode()))
1033 element = element->offsetParent();
1034 return element;
1035}
1036
1037Element* Element::offsetParent()
1038{
1039 document().updateLayoutIgnorePendingStylesheets();
1040 auto renderer = this->renderer();
1041 if (!renderer)
1042 return nullptr;
1043 auto offsetParent = renderer->offsetParent();
1044 if (!offsetParent)
1045 return nullptr;
1046 return offsetParent->element();
1047}
1048
1049double Element::clientLeft()
1050{
1051 document().updateLayoutIgnorePendingStylesheets();
1052
1053 if (auto* renderer = renderBox()) {
1054 LayoutUnit clientLeft = subpixelMetricsEnabled(renderer->document()) ? renderer->clientLeft() : LayoutUnit(roundToInt(renderer->clientLeft()));
1055 return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(clientLeft, *renderer).toDouble(), renderer->document());
1056 }
1057 return 0;
1058}
1059
1060double Element::clientTop()
1061{
1062 document().updateLayoutIgnorePendingStylesheets();
1063
1064 if (auto* renderer = renderBox()) {
1065 LayoutUnit clientTop = subpixelMetricsEnabled(renderer->document()) ? renderer->clientTop() : LayoutUnit(roundToInt(renderer->clientTop()));
1066 return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(clientTop, *renderer).toDouble(), renderer->document());
1067 }
1068 return 0;
1069}
1070
1071double Element::clientWidth()
1072{
1073 document().updateLayoutIfDimensionsOutOfDate(*this, WidthDimensionsCheck);
1074
1075 if (!document().hasLivingRenderTree())
1076 return 0;
1077
1078 RenderView& renderView = *document().renderView();
1079
1080 // When in strict mode, clientWidth for the document element should return the width of the containing frame.
1081 // When in quirks mode, clientWidth for the body element should return the width of the containing frame.
1082 bool inQuirksMode = document().inQuirksMode();
1083 if ((!inQuirksMode && document().documentElement() == this) || (inQuirksMode && isHTMLElement() && document().bodyOrFrameset() == this))
1084 return adjustForAbsoluteZoom(renderView.frameView().layoutWidth(), renderView);
1085
1086 if (RenderBox* renderer = renderBox()) {
1087 LayoutUnit clientWidth = subpixelMetricsEnabled(renderer->document()) ? renderer->clientWidth() : LayoutUnit(roundToInt(renderer->clientWidth()));
1088 return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(clientWidth, *renderer).toDouble(), renderer->document());
1089 }
1090 return 0;
1091}
1092
1093double Element::clientHeight()
1094{
1095 document().updateLayoutIfDimensionsOutOfDate(*this, HeightDimensionsCheck);
1096 if (!document().hasLivingRenderTree())
1097 return 0;
1098
1099 RenderView& renderView = *document().renderView();
1100
1101 // When in strict mode, clientHeight for the document element should return the height of the containing frame.
1102 // When in quirks mode, clientHeight for the body element should return the height of the containing frame.
1103 bool inQuirksMode = document().inQuirksMode();
1104 if ((!inQuirksMode && document().documentElement() == this) || (inQuirksMode && isHTMLElement() && document().bodyOrFrameset() == this))
1105 return adjustForAbsoluteZoom(renderView.frameView().layoutHeight(), renderView);
1106
1107 if (RenderBox* renderer = renderBox()) {
1108 LayoutUnit clientHeight = subpixelMetricsEnabled(renderer->document()) ? renderer->clientHeight() : LayoutUnit(roundToInt(renderer->clientHeight()));
1109 return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(clientHeight, *renderer).toDouble(), renderer->document());
1110 }
1111 return 0;
1112}
1113
1114ALWAYS_INLINE Frame* Element::documentFrameWithNonNullView() const
1115{
1116 auto* frame = document().frame();
1117 return frame && frame->view() ? frame : nullptr;
1118}
1119
1120int Element::scrollLeft()
1121{
1122 document().updateLayoutIgnorePendingStylesheets();
1123
1124 if (document().scrollingElement() == this) {
1125 if (auto* frame = documentFrameWithNonNullView())
1126 return adjustContentsScrollPositionOrSizeForZoom(frame->view()->contentsScrollPosition().x(), *frame);
1127 return 0;
1128 }
1129
1130 if (auto* renderer = renderBox())
1131 return adjustForAbsoluteZoom(renderer->scrollLeft(), *renderer);
1132 return 0;
1133}
1134
1135int Element::scrollTop()
1136{
1137 document().updateLayoutIgnorePendingStylesheets();
1138
1139 if (document().scrollingElement() == this) {
1140 if (auto* frame = documentFrameWithNonNullView())
1141 return adjustContentsScrollPositionOrSizeForZoom(frame->view()->contentsScrollPosition().y(), *frame);
1142 return 0;
1143 }
1144
1145 if (RenderBox* renderer = renderBox())
1146 return adjustForAbsoluteZoom(renderer->scrollTop(), *renderer);
1147 return 0;
1148}
1149
1150void Element::setScrollLeft(int newLeft)
1151{
1152 document().updateLayoutIgnorePendingStylesheets();
1153
1154 if (document().scrollingElement() == this) {
1155 if (auto* frame = documentFrameWithNonNullView())
1156 frame->view()->setScrollPosition(IntPoint(static_cast<int>(newLeft * frame->pageZoomFactor() * frame->frameScaleFactor()), frame->view()->scrollY()));
1157 return;
1158 }
1159
1160 if (auto* renderer = renderBox()) {
1161 renderer->setScrollLeft(static_cast<int>(newLeft * renderer->style().effectiveZoom()), ScrollType::Programmatic);
1162 if (auto* scrollableArea = renderer->layer())
1163 scrollableArea->setScrollShouldClearLatchedState(true);
1164 }
1165}
1166
1167void Element::setScrollTop(int newTop)
1168{
1169 document().updateLayoutIgnorePendingStylesheets();
1170
1171 if (document().scrollingElement() == this) {
1172 if (auto* frame = documentFrameWithNonNullView())
1173 frame->view()->setScrollPosition(IntPoint(frame->view()->scrollX(), static_cast<int>(newTop * frame->pageZoomFactor() * frame->frameScaleFactor())));
1174 return;
1175 }
1176
1177 if (auto* renderer = renderBox()) {
1178 renderer->setScrollTop(static_cast<int>(newTop * renderer->style().effectiveZoom()), ScrollType::Programmatic);
1179 if (auto* scrollableArea = renderer->layer())
1180 scrollableArea->setScrollShouldClearLatchedState(true);
1181 }
1182}
1183
1184int Element::scrollWidth()
1185{
1186 document().updateLayoutIfDimensionsOutOfDate(*this, WidthDimensionsCheck);
1187
1188 if (document().scrollingElement() == this) {
1189 // FIXME (webkit.org/b/182289): updateLayoutIfDimensionsOutOfDate seems to ignore zoom level change.
1190 document().updateLayoutIgnorePendingStylesheets();
1191 if (auto* frame = documentFrameWithNonNullView())
1192 return adjustContentsScrollPositionOrSizeForZoom(frame->view()->contentsWidth(), *frame);
1193 return 0;
1194 }
1195
1196 if (auto* renderer = renderBox())
1197 return adjustForAbsoluteZoom(renderer->scrollWidth(), *renderer);
1198 return 0;
1199}
1200
1201int Element::scrollHeight()
1202{
1203 document().updateLayoutIfDimensionsOutOfDate(*this, HeightDimensionsCheck);
1204
1205 if (document().scrollingElement() == this) {
1206 // FIXME (webkit.org/b/182289): updateLayoutIfDimensionsOutOfDate seems to ignore zoom level change.
1207 document().updateLayoutIgnorePendingStylesheets();
1208 if (auto* frame = documentFrameWithNonNullView())
1209 return adjustContentsScrollPositionOrSizeForZoom(frame->view()->contentsHeight(), *frame);
1210 return 0;
1211 }
1212
1213 if (auto* renderer = renderBox())
1214 return adjustForAbsoluteZoom(renderer->scrollHeight(), *renderer);
1215 return 0;
1216}
1217
1218IntRect Element::boundsInRootViewSpace()
1219{
1220 document().updateLayoutIgnorePendingStylesheets();
1221
1222 FrameView* view = document().view();
1223 if (!view)
1224 return IntRect();
1225
1226 Vector<FloatQuad> quads;
1227
1228 if (isSVGElement() && renderer()) {
1229 // Get the bounding rectangle from the SVG model.
1230 SVGElement& svgElement = downcast<SVGElement>(*this);
1231 FloatRect localRect;
1232 if (svgElement.getBoundingBox(localRect))
1233 quads.append(renderer()->localToAbsoluteQuad(localRect));
1234 } else {
1235 // Get the bounding rectangle from the box model.
1236 if (renderBoxModelObject())
1237 renderBoxModelObject()->absoluteQuads(quads);
1238 }
1239
1240 if (quads.isEmpty())
1241 return IntRect();
1242
1243 IntRect result = quads[0].enclosingBoundingBox();
1244 for (size_t i = 1; i < quads.size(); ++i)
1245 result.unite(quads[i].enclosingBoundingBox());
1246
1247 result = view->contentsToRootView(result);
1248 return result;
1249}
1250
1251static bool layoutOverflowRectContainsAllDescendants(const RenderBox& renderBox)
1252{
1253 if (renderBox.isRenderView())
1254 return true;
1255
1256 if (!renderBox.element())
1257 return false;
1258
1259 // If there are any position:fixed inside of us, game over.
1260 if (auto* viewPositionedObjects = renderBox.view().positionedObjects()) {
1261 for (auto* positionedBox : *viewPositionedObjects) {
1262 if (positionedBox == &renderBox)
1263 continue;
1264 if (positionedBox->isFixedPositioned() && renderBox.element()->contains(positionedBox->element()))
1265 return false;
1266 }
1267 }
1268
1269 if (renderBox.canContainAbsolutelyPositionedObjects()) {
1270 // Our layout overflow will include all descendant positioned elements.
1271 return true;
1272 }
1273
1274 // This renderer may have positioned descendants whose containing block is some ancestor.
1275 if (auto* containingBlock = renderBox.containingBlockForAbsolutePosition()) {
1276 if (auto* positionedObjects = containingBlock->positionedObjects()) {
1277 for (auto* positionedBox : *positionedObjects) {
1278 if (positionedBox == &renderBox)
1279 continue;
1280 if (renderBox.element()->contains(positionedBox->element()))
1281 return false;
1282 }
1283 }
1284 }
1285 return false;
1286}
1287
1288LayoutRect Element::absoluteEventBounds(bool& boundsIncludeAllDescendantElements, bool& includesFixedPositionElements)
1289{
1290 boundsIncludeAllDescendantElements = false;
1291 includesFixedPositionElements = false;
1292
1293 if (!renderer())
1294 return LayoutRect();
1295
1296 LayoutRect result;
1297 if (isSVGElement()) {
1298 // Get the bounding rectangle from the SVG model.
1299 SVGElement& svgElement = downcast<SVGElement>(*this);
1300 FloatRect localRect;
1301 if (svgElement.getBoundingBox(localRect, SVGLocatable::DisallowStyleUpdate))
1302 result = LayoutRect(renderer()->localToAbsoluteQuad(localRect, UseTransforms, &includesFixedPositionElements).boundingBox());
1303 } else {
1304 auto* renderer = this->renderer();
1305 if (is<RenderBox>(renderer)) {
1306 auto& box = downcast<RenderBox>(*renderer);
1307
1308 bool computedBounds = false;
1309
1310 if (RenderFragmentedFlow* fragmentedFlow = box.enclosingFragmentedFlow()) {
1311 bool wasFixed = false;
1312 Vector<FloatQuad> quads;
1313 FloatRect localRect(0, 0, box.width(), box.height());
1314 if (fragmentedFlow->absoluteQuadsForBox(quads, &wasFixed, &box, localRect.y(), localRect.maxY())) {
1315 FloatRect quadBounds = quads[0].boundingBox();
1316 for (size_t i = 1; i < quads.size(); ++i)
1317 quadBounds.unite(quads[i].boundingBox());
1318
1319 result = LayoutRect(quadBounds);
1320 computedBounds = true;
1321 } else {
1322 // Probably columns. Just return the bounds of the multicol block for now.
1323 // FIXME: this doesn't handle nested columns.
1324 RenderElement* multicolContainer = fragmentedFlow->parent();
1325 if (multicolContainer && is<RenderBox>(multicolContainer)) {
1326 auto overflowRect = downcast<RenderBox>(*multicolContainer).layoutOverflowRect();
1327 result = LayoutRect(multicolContainer->localToAbsoluteQuad(FloatRect(overflowRect), UseTransforms, &includesFixedPositionElements).boundingBox());
1328 computedBounds = true;
1329 }
1330 }
1331 }
1332
1333 if (!computedBounds) {
1334 LayoutRect overflowRect = box.layoutOverflowRect();
1335 result = LayoutRect(box.localToAbsoluteQuad(FloatRect(overflowRect), UseTransforms, &includesFixedPositionElements).boundingBox());
1336 boundsIncludeAllDescendantElements = layoutOverflowRectContainsAllDescendants(box);
1337 }
1338 } else
1339 result = LayoutRect(renderer->absoluteBoundingBoxRect(true /* useTransforms */, &includesFixedPositionElements));
1340 }
1341
1342 return result;
1343}
1344
1345LayoutRect Element::absoluteEventBoundsOfElementAndDescendants(bool& includesFixedPositionElements)
1346{
1347 bool boundsIncludeDescendants;
1348 LayoutRect result = absoluteEventBounds(boundsIncludeDescendants, includesFixedPositionElements);
1349 if (boundsIncludeDescendants)
1350 return result;
1351
1352 for (auto& child : childrenOfType<Element>(*this)) {
1353 bool includesFixedPosition = false;
1354 LayoutRect childBounds = child.absoluteEventBoundsOfElementAndDescendants(includesFixedPosition);
1355 includesFixedPositionElements |= includesFixedPosition;
1356 result.unite(childBounds);
1357 }
1358
1359 return result;
1360}
1361
1362LayoutRect Element::absoluteEventHandlerBounds(bool& includesFixedPositionElements)
1363{
1364 // This is not web-exposed, so don't call the FOUC-inducing updateLayoutIgnorePendingStylesheets().
1365 FrameView* frameView = document().view();
1366 if (!frameView)
1367 return LayoutRect();
1368
1369 return absoluteEventBoundsOfElementAndDescendants(includesFixedPositionElements);
1370}
1371
1372static Optional<std::pair<RenderObject*, LayoutRect>> listBoxElementBoundingBox(Element& element)
1373{
1374 HTMLSelectElement* selectElement;
1375 bool isGroup;
1376 if (is<HTMLOptionElement>(element)) {
1377 selectElement = downcast<HTMLOptionElement>(element).ownerSelectElement();
1378 isGroup = false;
1379 } else if (is<HTMLOptGroupElement>(element)) {
1380 selectElement = downcast<HTMLOptGroupElement>(element).ownerSelectElement();
1381 isGroup = true;
1382 } else
1383 return WTF::nullopt;
1384
1385 if (!selectElement || !selectElement->renderer() || !is<RenderListBox>(selectElement->renderer()))
1386 return WTF::nullopt;
1387
1388 auto& renderer = downcast<RenderListBox>(*selectElement->renderer());
1389 Optional<LayoutRect> boundingBox;
1390 int optionIndex = 0;
1391 for (auto* item : selectElement->listItems()) {
1392 if (item == &element) {
1393 LayoutPoint additionOffset;
1394 boundingBox = renderer.itemBoundingBoxRect(additionOffset, optionIndex);
1395 if (!isGroup)
1396 break;
1397 } else if (isGroup && boundingBox) {
1398 if (item->parentNode() != &element)
1399 break;
1400 LayoutPoint additionOffset;
1401 boundingBox->setHeight(boundingBox->height() + renderer.itemBoundingBoxRect(additionOffset, optionIndex).height());
1402 }
1403 ++optionIndex;
1404 }
1405
1406 if (!boundingBox)
1407 return WTF::nullopt;
1408
1409 return std::pair<RenderObject*, LayoutRect> { &renderer, boundingBox.value() };
1410}
1411
1412Ref<DOMRectList> Element::getClientRects()
1413{
1414 document().updateLayoutIgnorePendingStylesheets();
1415
1416 RenderObject* renderer = this->renderer();
1417 Vector<FloatQuad> quads;
1418
1419 if (auto pair = listBoxElementBoundingBox(*this)) {
1420 renderer = pair.value().first;
1421 quads.append(renderer->localToAbsoluteQuad(FloatQuad { pair.value().second }));
1422 } else if (auto* renderBoxModelObject = this->renderBoxModelObject())
1423 renderBoxModelObject->absoluteQuads(quads);
1424
1425 // FIXME: Handle SVG elements.
1426 // FIXME: Handle table/inline-table with a caption.
1427
1428 if (quads.isEmpty())
1429 return DOMRectList::create();
1430
1431 document().convertAbsoluteToClientQuads(quads, renderer->style());
1432 return DOMRectList::create(quads);
1433}
1434
1435Optional<std::pair<RenderObject*, FloatRect>> Element::boundingAbsoluteRectWithoutLayout()
1436{
1437 RenderObject* renderer = this->renderer();
1438 Vector<FloatQuad> quads;
1439 if (isSVGElement() && renderer && !renderer->isSVGRoot()) {
1440 // Get the bounding rectangle from the SVG model.
1441 SVGElement& svgElement = downcast<SVGElement>(*this);
1442 FloatRect localRect;
1443 if (svgElement.getBoundingBox(localRect))
1444 quads.append(renderer->localToAbsoluteQuad(localRect));
1445 } else if (auto pair = listBoxElementBoundingBox(*this)) {
1446 renderer = pair.value().first;
1447 quads.append(renderer->localToAbsoluteQuad(FloatQuad { pair.value().second }));
1448 } else if (auto* renderBoxModelObject = this->renderBoxModelObject())
1449 renderBoxModelObject->absoluteQuads(quads);
1450
1451 if (quads.isEmpty())
1452 return WTF::nullopt;
1453
1454 FloatRect result = quads[0].boundingBox();
1455 for (size_t i = 1; i < quads.size(); ++i)
1456 result.unite(quads[i].boundingBox());
1457
1458 return std::make_pair(renderer, result);
1459}
1460
1461FloatRect Element::boundingClientRect()
1462{
1463 document().updateLayoutIgnorePendingStylesheets();
1464 auto pair = boundingAbsoluteRectWithoutLayout();
1465 if (!pair)
1466 return { };
1467 RenderObject* renderer = pair->first;
1468 FloatRect result = pair->second;
1469 document().convertAbsoluteToClientRect(result, renderer->style());
1470 return result;
1471}
1472
1473Ref<DOMRect> Element::getBoundingClientRect()
1474{
1475 return DOMRect::create(boundingClientRect());
1476}
1477
1478// Note that this is not web-exposed, and does not use the same coordinate system as getBoundingClientRect() and friends.
1479IntRect Element::clientRect() const
1480{
1481 if (RenderObject* renderer = this->renderer())
1482 return document().view()->contentsToRootView(renderer->absoluteBoundingBoxRect());
1483 return IntRect();
1484}
1485
1486IntRect Element::screenRect() const
1487{
1488 if (RenderObject* renderer = this->renderer())
1489 return document().view()->contentsToScreen(renderer->absoluteBoundingBoxRect());
1490 return IntRect();
1491}
1492
1493const AtomString& Element::getAttribute(const AtomString& qualifiedName) const
1494{
1495 if (!elementData())
1496 return nullAtom();
1497 synchronizeAttribute(qualifiedName);
1498 if (const Attribute* attribute = elementData()->findAttributeByName(qualifiedName, shouldIgnoreAttributeCase(*this)))
1499 return attribute->value();
1500 return nullAtom();
1501}
1502
1503const AtomString& Element::getAttributeNS(const AtomString& namespaceURI, const AtomString& localName) const
1504{
1505 return getAttribute(QualifiedName(nullAtom(), localName, namespaceURI));
1506}
1507
1508// https://dom.spec.whatwg.org/#dom-element-toggleattribute
1509ExceptionOr<bool> Element::toggleAttribute(const AtomString& qualifiedName, Optional<bool> force)
1510{
1511 if (!Document::isValidName(qualifiedName))
1512 return Exception { InvalidCharacterError };
1513
1514 synchronizeAttribute(qualifiedName);
1515
1516 auto caseAdjustedQualifiedName = shouldIgnoreAttributeCase(*this) ? qualifiedName.convertToASCIILowercase() : qualifiedName;
1517 unsigned index = elementData() ? elementData()->findAttributeIndexByName(caseAdjustedQualifiedName, false) : ElementData::attributeNotFound;
1518 if (index == ElementData::attributeNotFound) {
1519 if (!force || *force) {
1520 setAttributeInternal(index, QualifiedName { nullAtom(), caseAdjustedQualifiedName, nullAtom() }, emptyString(), NotInSynchronizationOfLazyAttribute);
1521 return true;
1522 }
1523 return false;
1524 }
1525
1526 if (!force || !*force) {
1527 removeAttributeInternal(index, NotInSynchronizationOfLazyAttribute);
1528 return false;
1529 }
1530 return true;
1531}
1532
1533ExceptionOr<void> Element::setAttribute(const AtomString& qualifiedName, const AtomString& value)
1534{
1535 if (!Document::isValidName(qualifiedName))
1536 return Exception { InvalidCharacterError };
1537
1538 synchronizeAttribute(qualifiedName);
1539 auto caseAdjustedQualifiedName = shouldIgnoreAttributeCase(*this) ? qualifiedName.convertToASCIILowercase() : qualifiedName;
1540 unsigned index = elementData() ? elementData()->findAttributeIndexByName(caseAdjustedQualifiedName, false) : ElementData::attributeNotFound;
1541 auto name = index != ElementData::attributeNotFound ? attributeAt(index).name() : QualifiedName { nullAtom(), caseAdjustedQualifiedName, nullAtom() };
1542 setAttributeInternal(index, name, value, NotInSynchronizationOfLazyAttribute);
1543
1544 return { };
1545}
1546
1547void Element::setAttribute(const QualifiedName& name, const AtomString& value)
1548{
1549 synchronizeAttribute(name);
1550 unsigned index = elementData() ? elementData()->findAttributeIndexByName(name) : ElementData::attributeNotFound;
1551 setAttributeInternal(index, name, value, NotInSynchronizationOfLazyAttribute);
1552}
1553
1554void Element::setAttributeWithoutSynchronization(const QualifiedName& name, const AtomString& value)
1555{
1556 unsigned index = elementData() ? elementData()->findAttributeIndexByName(name) : ElementData::attributeNotFound;
1557 setAttributeInternal(index, name, value, NotInSynchronizationOfLazyAttribute);
1558}
1559
1560void Element::setSynchronizedLazyAttribute(const QualifiedName& name, const AtomString& value)
1561{
1562 unsigned index = elementData() ? elementData()->findAttributeIndexByName(name) : ElementData::attributeNotFound;
1563 setAttributeInternal(index, name, value, InSynchronizationOfLazyAttribute);
1564}
1565
1566inline void Element::setAttributeInternal(unsigned index, const QualifiedName& name, const AtomString& newValue, SynchronizationOfLazyAttribute inSynchronizationOfLazyAttribute)
1567{
1568 if (newValue.isNull()) {
1569 if (index != ElementData::attributeNotFound)
1570 removeAttributeInternal(index, inSynchronizationOfLazyAttribute);
1571 return;
1572 }
1573
1574 if (index == ElementData::attributeNotFound) {
1575 addAttributeInternal(name, newValue, inSynchronizationOfLazyAttribute);
1576 return;
1577 }
1578
1579 if (inSynchronizationOfLazyAttribute) {
1580 ensureUniqueElementData().attributeAt(index).setValue(newValue);
1581 return;
1582 }
1583
1584 const Attribute& attribute = attributeAt(index);
1585 QualifiedName attributeName = attribute.name();
1586 AtomString oldValue = attribute.value();
1587
1588 willModifyAttribute(attributeName, oldValue, newValue);
1589
1590 if (newValue != oldValue) {
1591 Style::AttributeChangeInvalidation styleInvalidation(*this, name, oldValue, newValue);
1592 ensureUniqueElementData().attributeAt(index).setValue(newValue);
1593 }
1594
1595 didModifyAttribute(attributeName, oldValue, newValue);
1596}
1597
1598static inline AtomString makeIdForStyleResolution(const AtomString& value, bool inQuirksMode)
1599{
1600 if (inQuirksMode)
1601 return value.convertToASCIILowercase();
1602 return value;
1603}
1604
1605void Element::attributeChanged(const QualifiedName& name, const AtomString& oldValue, const AtomString& newValue, AttributeModificationReason)
1606{
1607 bool valueIsSameAsBefore = oldValue == newValue;
1608
1609 if (!valueIsSameAsBefore) {
1610 if (name == HTMLNames::accesskeyAttr)
1611 document().invalidateAccessKeyCache();
1612 else if (name == HTMLNames::classAttr)
1613 classAttributeChanged(newValue);
1614 else if (name == HTMLNames::idAttr) {
1615 AtomString oldId = elementData()->idForStyleResolution();
1616 AtomString newId = makeIdForStyleResolution(newValue, document().inQuirksMode());
1617 if (newId != oldId) {
1618 Style::IdChangeInvalidation styleInvalidation(*this, oldId, newId);
1619 elementData()->setIdForStyleResolution(newId);
1620 }
1621
1622 if (!oldValue.isEmpty())
1623 treeScope().idTargetObserverRegistry().notifyObservers(*oldValue.impl());
1624 if (!newValue.isEmpty())
1625 treeScope().idTargetObserverRegistry().notifyObservers(*newValue.impl());
1626 } else if (name == HTMLNames::nameAttr)
1627 elementData()->setHasNameAttribute(!newValue.isNull());
1628 else if (name == HTMLNames::pseudoAttr) {
1629 if (needsStyleInvalidation() && isInShadowTree())
1630 invalidateStyleForSubtree();
1631 } else if (name == HTMLNames::slotAttr) {
1632 if (auto* parent = parentElement()) {
1633 if (auto* shadowRoot = parent->shadowRoot())
1634 shadowRoot->hostChildElementDidChangeSlotAttribute(*this, oldValue, newValue);
1635 }
1636 }
1637 }
1638
1639 parseAttribute(name, newValue);
1640
1641 document().incDOMTreeVersion();
1642
1643 if (UNLIKELY(isDefinedCustomElement()))
1644 CustomElementReactionQueue::enqueueAttributeChangedCallbackIfNeeded(*this, name, oldValue, newValue);
1645
1646 if (valueIsSameAsBefore)
1647 return;
1648
1649 invalidateNodeListAndCollectionCachesInAncestorsForAttribute(name);
1650
1651 if (AXObjectCache* cache = document().existingAXObjectCache())
1652 cache->deferAttributeChangeIfNeeded(name, this);
1653}
1654
1655template <typename CharacterType>
1656static inline bool classStringHasClassName(const CharacterType* characters, unsigned length)
1657{
1658 ASSERT(length > 0);
1659
1660 unsigned i = 0;
1661 do {
1662 if (isNotHTMLSpace(characters[i]))
1663 break;
1664 ++i;
1665 } while (i < length);
1666
1667 return i < length;
1668}
1669
1670static inline bool classStringHasClassName(const AtomString& newClassString)
1671{
1672 unsigned length = newClassString.length();
1673
1674 if (!length)
1675 return false;
1676
1677 if (newClassString.is8Bit())
1678 return classStringHasClassName(newClassString.characters8(), length);
1679 return classStringHasClassName(newClassString.characters16(), length);
1680}
1681
1682void Element::classAttributeChanged(const AtomString& newClassString)
1683{
1684 // Note: We'll need ElementData, but it doesn't have to be UniqueElementData.
1685 if (!elementData())
1686 ensureUniqueElementData();
1687
1688 bool shouldFoldCase = document().inQuirksMode();
1689 bool newStringHasClasses = classStringHasClassName(newClassString);
1690
1691 auto oldClassNames = elementData()->classNames();
1692 auto newClassNames = newStringHasClasses ? SpaceSplitString(newClassString, shouldFoldCase) : SpaceSplitString();
1693 {
1694 Style::ClassChangeInvalidation styleInvalidation(*this, oldClassNames, newClassNames);
1695 elementData()->setClassNames(newClassNames);
1696 }
1697
1698 if (hasRareData()) {
1699 if (auto* classList = elementRareData()->classList())
1700 classList->associatedAttributeValueChanged(newClassString);
1701 }
1702}
1703
1704URL Element::absoluteLinkURL() const
1705{
1706 if (!isLink())
1707 return URL();
1708
1709 AtomString linkAttribute;
1710 if (hasTagName(SVGNames::aTag))
1711 linkAttribute = getAttribute(SVGNames::hrefAttr, XLinkNames::hrefAttr);
1712 else
1713 linkAttribute = getAttribute(HTMLNames::hrefAttr);
1714
1715 if (linkAttribute.isEmpty())
1716 return URL();
1717
1718 return document().completeURL(stripLeadingAndTrailingHTMLSpaces(linkAttribute));
1719}
1720
1721#if ENABLE(TOUCH_EVENTS)
1722bool Element::allowsDoubleTapGesture() const
1723{
1724#if ENABLE(POINTER_EVENTS)
1725 if (renderStyle() && renderStyle()->touchActions() != TouchAction::Auto)
1726 return false;
1727#endif
1728
1729 Element* parent = parentElement();
1730 return !parent || parent->allowsDoubleTapGesture();
1731}
1732#endif
1733
1734StyleResolver& Element::styleResolver()
1735{
1736 if (auto* shadowRoot = containingShadowRoot())
1737 return shadowRoot->styleScope().resolver();
1738
1739 return document().styleScope().resolver();
1740}
1741
1742ElementStyle Element::resolveStyle(const RenderStyle* parentStyle)
1743{
1744 return styleResolver().styleForElement(*this, parentStyle);
1745}
1746
1747static void invalidateForSiblingCombinators(Element* sibling)
1748{
1749 for (; sibling; sibling = sibling->nextElementSibling()) {
1750 if (sibling->styleIsAffectedByPreviousSibling())
1751 sibling->invalidateStyleInternal();
1752 if (sibling->descendantsAffectedByPreviousSibling()) {
1753 for (auto* siblingChild = sibling->firstElementChild(); siblingChild; siblingChild = siblingChild->nextElementSibling())
1754 siblingChild->invalidateStyleForSubtreeInternal();
1755 }
1756 if (!sibling->affectsNextSiblingElementStyle())
1757 return;
1758 }
1759}
1760
1761static void invalidateSiblingsIfNeeded(Element& element)
1762{
1763 if (!element.affectsNextSiblingElementStyle())
1764 return;
1765 auto* parent = element.parentElement();
1766 if (parent && parent->styleValidity() >= Style::Validity::SubtreeInvalid)
1767 return;
1768
1769 invalidateForSiblingCombinators(element.nextElementSibling());
1770}
1771
1772void Element::invalidateStyle()
1773{
1774 Node::invalidateStyle(Style::Validity::ElementInvalid);
1775 invalidateSiblingsIfNeeded(*this);
1776}
1777
1778void Element::invalidateStyleAndLayerComposition()
1779{
1780 Node::invalidateStyle(Style::Validity::ElementInvalid, Style::InvalidationMode::RecompositeLayer);
1781 invalidateSiblingsIfNeeded(*this);
1782}
1783
1784void Element::invalidateStyleForSubtree()
1785{
1786 Node::invalidateStyle(Style::Validity::SubtreeInvalid);
1787 invalidateSiblingsIfNeeded(*this);
1788}
1789
1790void Element::invalidateStyleAndRenderersForSubtree()
1791{
1792 Node::invalidateStyle(Style::Validity::SubtreeAndRenderersInvalid);
1793 invalidateSiblingsIfNeeded(*this);
1794}
1795
1796void Element::invalidateStyleInternal()
1797{
1798 Node::invalidateStyle(Style::Validity::ElementInvalid);
1799}
1800
1801void Element::invalidateStyleForSubtreeInternal()
1802{
1803 Node::invalidateStyle(Style::Validity::SubtreeInvalid);
1804}
1805
1806bool Element::hasDisplayContents() const
1807{
1808 if (!hasRareData())
1809 return false;
1810
1811 const RenderStyle* style = elementRareData()->computedStyle();
1812 return style && style->display() == DisplayType::Contents;
1813}
1814
1815void Element::storeDisplayContentsStyle(std::unique_ptr<RenderStyle> style)
1816{
1817 ASSERT(style && style->display() == DisplayType::Contents);
1818 ASSERT(!renderer() || isPseudoElement());
1819 ensureElementRareData().setComputedStyle(WTFMove(style));
1820}
1821
1822// Returns true is the given attribute is an event handler.
1823// We consider an event handler any attribute that begins with "on".
1824// It is a simple solution that has the advantage of not requiring any
1825// code or configuration change if a new event handler is defined.
1826
1827bool Element::isEventHandlerAttribute(const Attribute& attribute) const
1828{
1829 return attribute.name().namespaceURI().isNull() && attribute.name().localName().startsWith("on");
1830}
1831
1832bool Element::isJavaScriptURLAttribute(const Attribute& attribute) const
1833{
1834 return isURLAttribute(attribute) && WTF::protocolIsJavaScript(stripLeadingAndTrailingHTMLSpaces(attribute.value()));
1835}
1836
1837void Element::stripScriptingAttributes(Vector<Attribute>& attributeVector) const
1838{
1839 attributeVector.removeAllMatching([this](auto& attribute) -> bool {
1840 return this->isEventHandlerAttribute(attribute)
1841 || this->isJavaScriptURLAttribute(attribute)
1842 || this->isHTMLContentAttribute(attribute);
1843 });
1844}
1845
1846void Element::parserSetAttributes(const Vector<Attribute>& attributeVector)
1847{
1848 ASSERT(!isConnected());
1849 ASSERT(!parentNode());
1850 ASSERT(!m_elementData);
1851
1852 if (!attributeVector.isEmpty()) {
1853 if (document().sharedObjectPool())
1854 m_elementData = document().sharedObjectPool()->cachedShareableElementDataWithAttributes(attributeVector);
1855 else
1856 m_elementData = ShareableElementData::createWithAttributes(attributeVector);
1857
1858 }
1859
1860 parserDidSetAttributes();
1861
1862 // Use attributeVector instead of m_elementData because attributeChanged might modify m_elementData.
1863 for (const auto& attribute : attributeVector)
1864 attributeChanged(attribute.name(), nullAtom(), attribute.value(), ModifiedDirectly);
1865}
1866
1867void Element::parserDidSetAttributes()
1868{
1869}
1870
1871void Element::didMoveToNewDocument(Document& oldDocument, Document& newDocument)
1872{
1873 ASSERT_WITH_SECURITY_IMPLICATION(&document() == &newDocument);
1874
1875 if (oldDocument.inQuirksMode() != document().inQuirksMode()) {
1876 // ElementData::m_classNames or ElementData::m_idForStyleResolution need to be updated with the right case.
1877 if (hasID())
1878 attributeChanged(idAttr, nullAtom(), getIdAttribute());
1879 if (hasClass())
1880 attributeChanged(classAttr, nullAtom(), getAttribute(classAttr));
1881 }
1882
1883 if (UNLIKELY(isDefinedCustomElement()))
1884 CustomElementReactionQueue::enqueueAdoptedCallbackIfNeeded(*this, oldDocument, newDocument);
1885
1886#if ENABLE(INTERSECTION_OBSERVER)
1887 if (auto* observerData = intersectionObserverData()) {
1888 for (const auto& observer : observerData->observers) {
1889 if (observer->hasObservationTargets()) {
1890 oldDocument.removeIntersectionObserver(*observer);
1891 newDocument.addIntersectionObserver(*observer);
1892 }
1893 }
1894 }
1895#endif
1896}
1897
1898bool Element::hasAttributes() const
1899{
1900 synchronizeAllAttributes();
1901 return elementData() && elementData()->length();
1902}
1903
1904bool Element::hasEquivalentAttributes(const Element& other) const
1905{
1906 synchronizeAllAttributes();
1907 other.synchronizeAllAttributes();
1908 if (elementData() == other.elementData())
1909 return true;
1910 if (elementData())
1911 return elementData()->isEquivalent(other.elementData());
1912 if (other.elementData())
1913 return other.elementData()->isEquivalent(elementData());
1914 return true;
1915}
1916
1917String Element::nodeName() const
1918{
1919 return m_tagName.toString();
1920}
1921
1922String Element::nodeNamePreservingCase() const
1923{
1924 return m_tagName.toString();
1925}
1926
1927ExceptionOr<void> Element::setPrefix(const AtomString& prefix)
1928{
1929 auto result = checkSetPrefix(prefix);
1930 if (result.hasException())
1931 return result.releaseException();
1932
1933 m_tagName.setPrefix(prefix.isEmpty() ? nullAtom() : prefix);
1934 return { };
1935}
1936
1937const AtomString& Element::imageSourceURL() const
1938{
1939 return attributeWithoutSynchronization(srcAttr);
1940}
1941
1942bool Element::rendererIsNeeded(const RenderStyle& style)
1943{
1944 return style.display() != DisplayType::None && style.display() != DisplayType::Contents;
1945}
1946
1947RenderPtr<RenderElement> Element::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
1948{
1949 return RenderElement::createFor(*this, WTFMove(style));
1950}
1951
1952Node::InsertedIntoAncestorResult Element::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
1953{
1954 ContainerNode::insertedIntoAncestor(insertionType, parentOfInsertedTree);
1955
1956#if ENABLE(FULLSCREEN_API)
1957 if (containsFullScreenElement() && parentElement() && !parentElement()->containsFullScreenElement())
1958 setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true);
1959#endif
1960
1961 if (parentNode() == &parentOfInsertedTree) {
1962 if (auto* shadowRoot = parentNode()->shadowRoot())
1963 shadowRoot->hostChildElementDidChange(*this);
1964 }
1965
1966 if (!parentOfInsertedTree.isInTreeScope())
1967 return InsertedIntoAncestorResult::Done;
1968
1969 bool becomeConnected = insertionType.connectedToDocument;
1970 TreeScope* newScope = &parentOfInsertedTree.treeScope();
1971 HTMLDocument* newDocument = becomeConnected && is<HTMLDocument>(newScope->documentScope()) ? &downcast<HTMLDocument>(newScope->documentScope()) : nullptr;
1972 if (!insertionType.treeScopeChanged)
1973 newScope = nullptr;
1974
1975 const AtomString& idValue = getIdAttribute();
1976 if (!idValue.isNull()) {
1977 if (newScope)
1978 updateIdForTreeScope(*newScope, nullAtom(), idValue);
1979 if (newDocument)
1980 updateIdForDocument(*newDocument, nullAtom(), idValue, AlwaysUpdateHTMLDocumentNamedItemMaps);
1981 }
1982
1983 const AtomString& nameValue = getNameAttribute();
1984 if (!nameValue.isNull()) {
1985 if (newScope)
1986 updateNameForTreeScope(*newScope, nullAtom(), nameValue);
1987 if (newDocument)
1988 updateNameForDocument(*newDocument, nullAtom(), nameValue);
1989 }
1990
1991 if (newScope && hasTagName(labelTag)) {
1992 if (newScope->shouldCacheLabelsByForAttribute())
1993 updateLabel(*newScope, nullAtom(), attributeWithoutSynchronization(forAttr));
1994 }
1995
1996 if (becomeConnected) {
1997 if (UNLIKELY(isCustomElementUpgradeCandidate())) {
1998 ASSERT(isConnected());
1999 CustomElementReactionQueue::enqueueElementUpgradeIfDefined(*this);
2000 }
2001 if (UNLIKELY(isDefinedCustomElement()))
2002 CustomElementReactionQueue::enqueueConnectedCallbackIfNeeded(*this);
2003 }
2004
2005 if (UNLIKELY(hasTagName(articleTag) && newDocument))
2006 newDocument->registerArticleElement(*this);
2007
2008 return InsertedIntoAncestorResult::Done;
2009}
2010
2011void Element::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
2012{
2013#if ENABLE(FULLSCREEN_API)
2014 if (containsFullScreenElement())
2015 setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
2016#endif
2017#if ENABLE(POINTER_LOCK)
2018 if (document().page())
2019 document().page()->pointerLockController().elementRemoved(*this);
2020#endif
2021#if ENABLE(POINTER_EVENTS)
2022 if (document().page() && RuntimeEnabledFeatures::sharedFeatures().pointerEventsEnabled())
2023 document().page()->pointerCaptureController().elementWasRemoved(*this);
2024#endif
2025
2026 setSavedLayerScrollPosition(ScrollPosition());
2027
2028 if (oldParentOfRemovedTree.isInTreeScope()) {
2029 TreeScope* oldScope = &oldParentOfRemovedTree.treeScope();
2030 Document* oldDocument = removalType.disconnectedFromDocument ? &oldScope->documentScope() : nullptr;
2031 HTMLDocument* oldHTMLDocument = oldDocument && is<HTMLDocument>(*oldDocument) ? &downcast<HTMLDocument>(*oldDocument) : nullptr;
2032 if (!removalType.treeScopeChanged)
2033 oldScope = nullptr;
2034
2035 const AtomString& idValue = getIdAttribute();
2036 if (!idValue.isNull()) {
2037 if (oldScope)
2038 updateIdForTreeScope(*oldScope, idValue, nullAtom());
2039 if (oldHTMLDocument)
2040 updateIdForDocument(*oldHTMLDocument, idValue, nullAtom(), AlwaysUpdateHTMLDocumentNamedItemMaps);
2041 }
2042
2043 const AtomString& nameValue = getNameAttribute();
2044 if (!nameValue.isNull()) {
2045 if (oldScope)
2046 updateNameForTreeScope(*oldScope, nameValue, nullAtom());
2047 if (oldHTMLDocument)
2048 updateNameForDocument(*oldHTMLDocument, nameValue, nullAtom());
2049 }
2050
2051 if (oldScope && hasTagName(labelTag)) {
2052 if (oldScope->shouldCacheLabelsByForAttribute())
2053 updateLabel(*oldScope, attributeWithoutSynchronization(forAttr), nullAtom());
2054 }
2055
2056 if (oldDocument) {
2057 if (oldDocument->cssTarget() == this)
2058 oldDocument->setCSSTarget(nullptr);
2059 if (UNLIKELY(hasTagName(articleTag)))
2060 oldDocument->unregisterArticleElement(*this);
2061 }
2062
2063 if (removalType.disconnectedFromDocument && UNLIKELY(isDefinedCustomElement()))
2064 CustomElementReactionQueue::enqueueDisconnectedCallbackIfNeeded(*this);
2065 }
2066
2067 if (!parentNode()) {
2068 if (auto* shadowRoot = oldParentOfRemovedTree.shadowRoot())
2069 shadowRoot->hostChildElementDidChange(*this);
2070 }
2071
2072 clearBeforePseudoElement();
2073 clearAfterPseudoElement();
2074
2075 ContainerNode::removedFromAncestor(removalType, oldParentOfRemovedTree);
2076
2077 if (hasPendingResources())
2078 document().accessSVGExtensions().removeElementFromPendingResources(*this);
2079
2080 RefPtr<Frame> frame = document().frame();
2081 if (auto* timeline = document().existingTimeline())
2082 timeline->elementWasRemoved(*this);
2083 if (frame)
2084 frame->animation().cancelAnimations(*this);
2085
2086#if PLATFORM(MAC)
2087 if (frame && frame->page())
2088 frame->page()->removeLatchingStateForTarget(*this);
2089#endif
2090
2091 if (hasRareData() && elementRareData()->hasElementIdentifier()) {
2092 document().identifiedElementWasRemovedFromDocument(*this);
2093 elementRareData()->setHasElementIdentifier(false);
2094 }
2095}
2096
2097ShadowRoot* Element::shadowRoot() const
2098{
2099 return hasRareData() ? elementRareData()->shadowRoot() : nullptr;
2100}
2101
2102void Element::addShadowRoot(Ref<ShadowRoot>&& newShadowRoot)
2103{
2104 ASSERT(!newShadowRoot->hasChildNodes());
2105 ASSERT(!shadowRoot());
2106
2107 ShadowRoot& shadowRoot = newShadowRoot;
2108 {
2109 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
2110 if (renderer())
2111 RenderTreeUpdater::tearDownRenderers(*this);
2112
2113 ensureElementRareData().setShadowRoot(WTFMove(newShadowRoot));
2114
2115 shadowRoot.setHost(this);
2116 shadowRoot.setParentTreeScope(treeScope());
2117
2118#if !ASSERT_DISABLED
2119 ASSERT(notifyChildNodeInserted(*this, shadowRoot).isEmpty());
2120#else
2121 notifyChildNodeInserted(*this, shadowRoot);
2122#endif
2123
2124 invalidateStyleAndRenderersForSubtree();
2125 }
2126
2127 if (shadowRoot.mode() == ShadowRootMode::UserAgent)
2128 didAddUserAgentShadowRoot(shadowRoot);
2129
2130 InspectorInstrumentation::didPushShadowRoot(*this, shadowRoot);
2131}
2132
2133void Element::removeShadowRoot()
2134{
2135 RefPtr<ShadowRoot> oldRoot = shadowRoot();
2136 if (!oldRoot)
2137 return;
2138
2139 InspectorInstrumentation::willPopShadowRoot(*this, *oldRoot);
2140 document().adjustFocusedNodeOnNodeRemoval(*oldRoot);
2141
2142 ASSERT(!oldRoot->renderer());
2143
2144 elementRareData()->clearShadowRoot();
2145
2146 oldRoot->setHost(nullptr);
2147 oldRoot->setParentTreeScope(document());
2148}
2149
2150static bool canAttachAuthorShadowRoot(const Element& element)
2151{
2152 static NeverDestroyed<HashSet<AtomString>> tagNames = [] {
2153 static const HTMLQualifiedName* const tagList[] = {
2154 &articleTag.get(),
2155 &asideTag.get(),
2156 &blockquoteTag.get(),
2157 &bodyTag.get(),
2158 &divTag.get(),
2159 &footerTag.get(),
2160 &h1Tag.get(),
2161 &h2Tag.get(),
2162 &h3Tag.get(),
2163 &h4Tag.get(),
2164 &h5Tag.get(),
2165 &h6Tag.get(),
2166 &headerTag.get(),
2167 &navTag.get(),
2168 &pTag.get(),
2169 &sectionTag.get(),
2170 &spanTag.get()
2171 };
2172 HashSet<AtomString> set;
2173 for (auto& name : tagList)
2174 set.add(name->localName());
2175 return set;
2176 }();
2177
2178 if (!is<HTMLElement>(element))
2179 return false;
2180
2181 const auto& localName = element.localName();
2182 return tagNames.get().contains(localName) || Document::validateCustomElementName(localName) == CustomElementNameValidationStatus::Valid;
2183}
2184
2185ExceptionOr<ShadowRoot&> Element::attachShadow(const ShadowRootInit& init)
2186{
2187 if (!canAttachAuthorShadowRoot(*this))
2188 return Exception { NotSupportedError };
2189 if (shadowRoot())
2190 return Exception { InvalidStateError };
2191 if (init.mode == ShadowRootMode::UserAgent)
2192 return Exception { TypeError };
2193 auto shadow = ShadowRoot::create(document(), init.mode);
2194 auto& result = shadow.get();
2195 addShadowRoot(WTFMove(shadow));
2196 return result;
2197}
2198
2199ShadowRoot* Element::shadowRootForBindings(JSC::ExecState& state) const
2200{
2201 auto* shadow = shadowRoot();
2202 if (!shadow)
2203 return nullptr;
2204 if (shadow->mode() == ShadowRootMode::Open)
2205 return shadow;
2206 if (JSC::jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject())->world().shadowRootIsAlwaysOpen())
2207 return shadow;
2208 return nullptr;
2209}
2210
2211RefPtr<ShadowRoot> Element::userAgentShadowRoot() const
2212{
2213 ASSERT(!shadowRoot() || shadowRoot()->mode() == ShadowRootMode::UserAgent);
2214 return shadowRoot();
2215}
2216
2217ShadowRoot& Element::ensureUserAgentShadowRoot()
2218{
2219 if (auto shadow = userAgentShadowRoot())
2220 return *shadow;
2221 auto newShadow = ShadowRoot::create(document(), ShadowRootMode::UserAgent);
2222 ShadowRoot& shadow = newShadow;
2223 addShadowRoot(WTFMove(newShadow));
2224 return shadow;
2225}
2226
2227void Element::setIsDefinedCustomElement(JSCustomElementInterface& elementInterface)
2228{
2229 clearFlag(IsEditingTextOrUndefinedCustomElementFlag);
2230 setFlag(IsCustomElement);
2231 auto& data = ensureElementRareData();
2232 if (!data.customElementReactionQueue())
2233 data.setCustomElementReactionQueue(std::make_unique<CustomElementReactionQueue>(elementInterface));
2234 invalidateStyleForSubtree();
2235 InspectorInstrumentation::didChangeCustomElementState(*this);
2236}
2237
2238void Element::setIsFailedCustomElement(JSCustomElementInterface&)
2239{
2240 ASSERT(isUndefinedCustomElement());
2241 ASSERT(getFlag(IsEditingTextOrUndefinedCustomElementFlag));
2242 clearFlag(IsCustomElement);
2243
2244 if (hasRareData()) {
2245 // Clear the queue instead of deleting it since this function can be called inside CustomElementReactionQueue::invokeAll during upgrades.
2246 if (auto* queue = elementRareData()->customElementReactionQueue())
2247 queue->clear();
2248 }
2249 InspectorInstrumentation::didChangeCustomElementState(*this);
2250}
2251
2252void Element::setIsCustomElementUpgradeCandidate()
2253{
2254 ASSERT(!getFlag(IsCustomElement));
2255 setFlag(IsCustomElement);
2256 setFlag(IsEditingTextOrUndefinedCustomElementFlag);
2257 InspectorInstrumentation::didChangeCustomElementState(*this);
2258}
2259
2260void Element::enqueueToUpgrade(JSCustomElementInterface& elementInterface)
2261{
2262 ASSERT(!isDefinedCustomElement() && !isFailedCustomElement());
2263 setFlag(IsCustomElement);
2264 setFlag(IsEditingTextOrUndefinedCustomElementFlag);
2265 InspectorInstrumentation::didChangeCustomElementState(*this);
2266
2267 auto& data = ensureElementRareData();
2268 bool alreadyScheduledToUpgrade = data.customElementReactionQueue();
2269 if (!alreadyScheduledToUpgrade)
2270 data.setCustomElementReactionQueue(std::make_unique<CustomElementReactionQueue>(elementInterface));
2271 data.customElementReactionQueue()->enqueueElementUpgrade(*this, alreadyScheduledToUpgrade);
2272}
2273
2274CustomElementReactionQueue* Element::reactionQueue() const
2275{
2276 ASSERT(isDefinedCustomElement() || isCustomElementUpgradeCandidate());
2277 if (!hasRareData())
2278 return nullptr;
2279 return elementRareData()->customElementReactionQueue();
2280}
2281
2282const AtomString& Element::shadowPseudoId() const
2283{
2284 return pseudo();
2285}
2286
2287bool Element::childTypeAllowed(NodeType type) const
2288{
2289 switch (type) {
2290 case ELEMENT_NODE:
2291 case TEXT_NODE:
2292 case COMMENT_NODE:
2293 case PROCESSING_INSTRUCTION_NODE:
2294 case CDATA_SECTION_NODE:
2295 return true;
2296 default:
2297 break;
2298 }
2299 return false;
2300}
2301
2302static void checkForEmptyStyleChange(Element& element)
2303{
2304 if (element.styleAffectedByEmpty()) {
2305 auto* style = element.renderStyle();
2306 if (!style || (!style->emptyState() || element.hasChildNodes()))
2307 element.invalidateStyleForSubtree();
2308 }
2309}
2310
2311
2312static void invalidateForForwardPositionalRules(Element& parent, Element* elementAfterChange)
2313{
2314 bool childrenAffected = parent.childrenAffectedByForwardPositionalRules();
2315 bool descendantsAffected = parent.descendantsAffectedByForwardPositionalRules();
2316
2317 if (!childrenAffected && !descendantsAffected)
2318 return;
2319
2320 for (auto* sibling = elementAfterChange; sibling; sibling = sibling->nextElementSibling()) {
2321 if (childrenAffected)
2322 sibling->invalidateStyleInternal();
2323 if (descendantsAffected) {
2324 for (auto* siblingChild = sibling->firstElementChild(); siblingChild; siblingChild = siblingChild->nextElementSibling())
2325 siblingChild->invalidateStyleForSubtreeInternal();
2326 }
2327 }
2328}
2329
2330static void invalidateForBackwardPositionalRules(Element& parent, Element* elementBeforeChange)
2331{
2332 bool childrenAffected = parent.childrenAffectedByBackwardPositionalRules();
2333 bool descendantsAffected = parent.descendantsAffectedByBackwardPositionalRules();
2334
2335 if (!childrenAffected && !descendantsAffected)
2336 return;
2337
2338 for (auto* sibling = elementBeforeChange; sibling; sibling = sibling->previousElementSibling()) {
2339 if (childrenAffected)
2340 sibling->invalidateStyleInternal();
2341 if (descendantsAffected) {
2342 for (auto* siblingChild = sibling->firstElementChild(); siblingChild; siblingChild = siblingChild->nextElementSibling())
2343 siblingChild->invalidateStyleForSubtreeInternal();
2344 }
2345 }
2346}
2347
2348enum SiblingCheckType { FinishedParsingChildren, SiblingElementRemoved, Other };
2349
2350static void checkForSiblingStyleChanges(Element& parent, SiblingCheckType checkType, Element* elementBeforeChange, Element* elementAfterChange)
2351{
2352 // :empty selector.
2353 checkForEmptyStyleChange(parent);
2354
2355 if (parent.styleValidity() >= Style::Validity::SubtreeInvalid)
2356 return;
2357
2358 // :first-child. In the parser callback case, we don't have to check anything, since we were right the first time.
2359 // In the DOM case, we only need to do something if |afterChange| is not 0.
2360 // |afterChange| is 0 in the parser case, so it works out that we'll skip this block.
2361 if (parent.childrenAffectedByFirstChildRules() && elementAfterChange) {
2362 // Find our new first child.
2363 RefPtr<Element> newFirstElement = ElementTraversal::firstChild(parent);
2364 // Find the first element node following |afterChange|
2365
2366 // This is the insert/append case.
2367 if (newFirstElement != elementAfterChange) {
2368 auto* style = elementAfterChange->renderStyle();
2369 if (!style || style->firstChildState())
2370 elementAfterChange->invalidateStyleForSubtreeInternal();
2371 }
2372
2373 // We also have to handle node removal.
2374 if (checkType == SiblingElementRemoved && newFirstElement == elementAfterChange && newFirstElement) {
2375 auto* style = newFirstElement->renderStyle();
2376 if (!style || !style->firstChildState())
2377 newFirstElement->invalidateStyleForSubtreeInternal();
2378 }
2379 }
2380
2381 // :last-child. In the parser callback case, we don't have to check anything, since we were right the first time.
2382 // In the DOM case, we only need to do something if |afterChange| is not 0.
2383 if (parent.childrenAffectedByLastChildRules() && elementBeforeChange) {
2384 // Find our new last child.
2385 RefPtr<Element> newLastElement = ElementTraversal::lastChild(parent);
2386
2387 if (newLastElement != elementBeforeChange) {
2388 auto* style = elementBeforeChange->renderStyle();
2389 if (!style || style->lastChildState())
2390 elementBeforeChange->invalidateStyleForSubtreeInternal();
2391 }
2392
2393 // We also have to handle node removal. The parser callback case is similar to node removal as well in that we need to change the last child
2394 // to match now.
2395 if ((checkType == SiblingElementRemoved || checkType == FinishedParsingChildren) && newLastElement == elementBeforeChange && newLastElement) {
2396 auto* style = newLastElement->renderStyle();
2397 if (!style || !style->lastChildState())
2398 newLastElement->invalidateStyleForSubtreeInternal();
2399 }
2400 }
2401
2402 invalidateForSiblingCombinators(elementAfterChange);
2403
2404 invalidateForForwardPositionalRules(parent, elementAfterChange);
2405 invalidateForBackwardPositionalRules(parent, elementBeforeChange);
2406}
2407
2408void Element::childrenChanged(const ChildChange& change)
2409{
2410 ContainerNode::childrenChanged(change);
2411 if (change.source == ChildChangeSource::Parser)
2412 checkForEmptyStyleChange(*this);
2413 else {
2414 SiblingCheckType checkType = change.type == ElementRemoved ? SiblingElementRemoved : Other;
2415 checkForSiblingStyleChanges(*this, checkType, change.previousSiblingElement, change.nextSiblingElement);
2416 }
2417
2418 if (ShadowRoot* shadowRoot = this->shadowRoot()) {
2419 switch (change.type) {
2420 case ElementInserted:
2421 case ElementRemoved:
2422 // For elements, we notify shadowRoot in Element::insertedIntoAncestor and Element::removedFromAncestor.
2423 break;
2424 case AllChildrenRemoved:
2425 case AllChildrenReplaced:
2426 shadowRoot->didRemoveAllChildrenOfShadowHost();
2427 break;
2428 case TextInserted:
2429 case TextRemoved:
2430 case TextChanged:
2431 shadowRoot->didChangeDefaultSlot();
2432 break;
2433 case NonContentsChildInserted:
2434 case NonContentsChildRemoved:
2435 break;
2436 }
2437 }
2438}
2439
2440void Element::setAttributeEventListener(const AtomString& eventType, const QualifiedName& attributeName, const AtomString& attributeValue)
2441{
2442 setAttributeEventListener(eventType, JSLazyEventListener::create(*this, attributeName, attributeValue), mainThreadNormalWorld());
2443}
2444
2445void Element::removeAllEventListeners()
2446{
2447 ContainerNode::removeAllEventListeners();
2448 if (ShadowRoot* shadowRoot = this->shadowRoot())
2449 shadowRoot->removeAllEventListeners();
2450}
2451
2452void Element::beginParsingChildren()
2453{
2454 clearIsParsingChildrenFinished();
2455}
2456
2457void Element::finishParsingChildren()
2458{
2459 ContainerNode::finishParsingChildren();
2460 setIsParsingChildrenFinished();
2461 checkForSiblingStyleChanges(*this, FinishedParsingChildren, ElementTraversal::lastChild(*this), nullptr);
2462}
2463
2464#if ENABLE(TREE_DEBUGGING)
2465void Element::formatForDebugger(char* buffer, unsigned length) const
2466{
2467 StringBuilder result;
2468 String s;
2469
2470 result.append(nodeName());
2471
2472 s = getIdAttribute();
2473 if (s.length() > 0) {
2474 if (result.length() > 0)
2475 result.appendLiteral("; ");
2476 result.appendLiteral("id=");
2477 result.append(s);
2478 }
2479
2480 s = getAttribute(classAttr);
2481 if (s.length() > 0) {
2482 if (result.length() > 0)
2483 result.appendLiteral("; ");
2484 result.appendLiteral("class=");
2485 result.append(s);
2486 }
2487
2488 strncpy(buffer, result.toString().utf8().data(), length - 1);
2489}
2490#endif
2491
2492const Vector<RefPtr<Attr>>& Element::attrNodeList()
2493{
2494 ASSERT(hasSyntheticAttrChildNodes());
2495 return *attrNodeListForElement(*this);
2496}
2497
2498void Element::attachAttributeNodeIfNeeded(Attr& attrNode)
2499{
2500 ASSERT(!attrNode.ownerElement() || attrNode.ownerElement() == this);
2501 if (attrNode.ownerElement() == this)
2502 return;
2503
2504 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
2505
2506 attrNode.attachToElement(*this);
2507 ensureAttrNodeListForElement(*this).append(&attrNode);
2508}
2509
2510ExceptionOr<RefPtr<Attr>> Element::setAttributeNode(Attr& attrNode)
2511{
2512 RefPtr<Attr> oldAttrNode = attrIfExists(attrNode.localName(), shouldIgnoreAttributeCase(*this));
2513 if (oldAttrNode.get() == &attrNode)
2514 return oldAttrNode;
2515
2516 // InUseAttributeError: Raised if node is an Attr that is already an attribute of another Element object.
2517 // The DOM user must explicitly clone Attr nodes to re-use them in other elements.
2518 if (attrNode.ownerElement() && attrNode.ownerElement() != this)
2519 return Exception { InUseAttributeError };
2520
2521 {
2522 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
2523 synchronizeAllAttributes();
2524 }
2525
2526 auto& elementData = ensureUniqueElementData();
2527
2528 auto existingAttributeIndex = elementData.findAttributeIndexByName(attrNode.localName(), shouldIgnoreAttributeCase(*this));
2529
2530 // Attr::value() will return its 'm_standaloneValue' member any time its Element is set to nullptr. We need to cache this value
2531 // before making changes to attrNode's Element connections.
2532 auto attrNodeValue = attrNode.value();
2533
2534 if (existingAttributeIndex == ElementData::attributeNotFound) {
2535 attachAttributeNodeIfNeeded(attrNode);
2536 setAttributeInternal(elementData.findAttributeIndexByName(attrNode.qualifiedName()), attrNode.qualifiedName(), attrNodeValue, NotInSynchronizationOfLazyAttribute);
2537 } else {
2538 const Attribute& attribute = attributeAt(existingAttributeIndex);
2539 if (oldAttrNode)
2540 detachAttrNodeFromElementWithValue(oldAttrNode.get(), attribute.value());
2541 else
2542 oldAttrNode = Attr::create(document(), attrNode.qualifiedName(), attribute.value());
2543
2544 attachAttributeNodeIfNeeded(attrNode);
2545
2546 if (attribute.name().matches(attrNode.qualifiedName()))
2547 setAttributeInternal(existingAttributeIndex, attrNode.qualifiedName(), attrNodeValue, NotInSynchronizationOfLazyAttribute);
2548 else {
2549 removeAttributeInternal(existingAttributeIndex, NotInSynchronizationOfLazyAttribute);
2550 setAttributeInternal(ensureUniqueElementData().findAttributeIndexByName(attrNode.qualifiedName()), attrNode.qualifiedName(), attrNodeValue, NotInSynchronizationOfLazyAttribute);
2551 }
2552 }
2553
2554 return oldAttrNode;
2555}
2556
2557ExceptionOr<RefPtr<Attr>> Element::setAttributeNodeNS(Attr& attrNode)
2558{
2559 RefPtr<Attr> oldAttrNode = attrIfExists(attrNode.qualifiedName());
2560 if (oldAttrNode.get() == &attrNode)
2561 return oldAttrNode;
2562
2563 // InUseAttributeError: Raised if node is an Attr that is already an attribute of another Element object.
2564 // The DOM user must explicitly clone Attr nodes to re-use them in other elements.
2565 if (attrNode.ownerElement() && attrNode.ownerElement() != this)
2566 return Exception { InUseAttributeError };
2567
2568 // Attr::value() will return its 'm_standaloneValue' member any time its Element is set to nullptr. We need to cache this value
2569 // before making changes to attrNode's Element connections.
2570 auto attrNodeValue = attrNode.value();
2571 unsigned index = 0;
2572 {
2573 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
2574 synchronizeAllAttributes();
2575 auto& elementData = ensureUniqueElementData();
2576
2577 index = elementData.findAttributeIndexByName(attrNode.qualifiedName());
2578
2579 if (index != ElementData::attributeNotFound) {
2580 if (oldAttrNode)
2581 detachAttrNodeFromElementWithValue(oldAttrNode.get(), elementData.attributeAt(index).value());
2582 else
2583 oldAttrNode = Attr::create(document(), attrNode.qualifiedName(), elementData.attributeAt(index).value());
2584 }
2585 }
2586
2587 attachAttributeNodeIfNeeded(attrNode);
2588 setAttributeInternal(index, attrNode.qualifiedName(), attrNodeValue, NotInSynchronizationOfLazyAttribute);
2589
2590 return oldAttrNode;
2591}
2592
2593ExceptionOr<Ref<Attr>> Element::removeAttributeNode(Attr& attr)
2594{
2595 if (attr.ownerElement() != this)
2596 return Exception { NotFoundError };
2597
2598 ASSERT(&document() == &attr.document());
2599
2600 synchronizeAllAttributes();
2601
2602 if (!m_elementData)
2603 return Exception { NotFoundError };
2604
2605 auto existingAttributeIndex = m_elementData->findAttributeIndexByName(attr.qualifiedName());
2606 if (existingAttributeIndex == ElementData::attributeNotFound)
2607 return Exception { NotFoundError };
2608
2609 Ref<Attr> oldAttrNode { attr };
2610
2611 detachAttrNodeFromElementWithValue(&attr, m_elementData->attributeAt(existingAttributeIndex).value());
2612 removeAttributeInternal(existingAttributeIndex, NotInSynchronizationOfLazyAttribute);
2613
2614 return oldAttrNode;
2615}
2616
2617ExceptionOr<QualifiedName> Element::parseAttributeName(const AtomString& namespaceURI, const AtomString& qualifiedName)
2618{
2619 auto parseResult = Document::parseQualifiedName(namespaceURI, qualifiedName);
2620 if (parseResult.hasException())
2621 return parseResult.releaseException();
2622 QualifiedName parsedAttributeName { parseResult.releaseReturnValue() };
2623 if (!Document::hasValidNamespaceForAttributes(parsedAttributeName))
2624 return Exception { NamespaceError };
2625 return parsedAttributeName;
2626}
2627
2628ExceptionOr<void> Element::setAttributeNS(const AtomString& namespaceURI, const AtomString& qualifiedName, const AtomString& value)
2629{
2630 auto result = parseAttributeName(namespaceURI, qualifiedName);
2631 if (result.hasException())
2632 return result.releaseException();
2633 setAttribute(result.releaseReturnValue(), value);
2634 return { };
2635}
2636
2637void Element::removeAttributeInternal(unsigned index, SynchronizationOfLazyAttribute inSynchronizationOfLazyAttribute)
2638{
2639 ASSERT_WITH_SECURITY_IMPLICATION(index < attributeCount());
2640
2641 UniqueElementData& elementData = ensureUniqueElementData();
2642
2643 QualifiedName name = elementData.attributeAt(index).name();
2644 AtomString valueBeingRemoved = elementData.attributeAt(index).value();
2645
2646 if (RefPtr<Attr> attrNode = attrIfExists(name))
2647 detachAttrNodeFromElementWithValue(attrNode.get(), elementData.attributeAt(index).value());
2648
2649 if (inSynchronizationOfLazyAttribute) {
2650 elementData.removeAttribute(index);
2651 return;
2652 }
2653
2654 ASSERT(!valueBeingRemoved.isNull());
2655 willModifyAttribute(name, valueBeingRemoved, nullAtom());
2656 {
2657 Style::AttributeChangeInvalidation styleInvalidation(*this, name, valueBeingRemoved, nullAtom());
2658 elementData.removeAttribute(index);
2659 }
2660
2661 didRemoveAttribute(name, valueBeingRemoved);
2662}
2663
2664void Element::addAttributeInternal(const QualifiedName& name, const AtomString& value, SynchronizationOfLazyAttribute inSynchronizationOfLazyAttribute)
2665{
2666 if (inSynchronizationOfLazyAttribute) {
2667 ensureUniqueElementData().addAttribute(name, value);
2668 return;
2669 }
2670
2671 willModifyAttribute(name, nullAtom(), value);
2672 {
2673 Style::AttributeChangeInvalidation styleInvalidation(*this, name, nullAtom(), value);
2674 ensureUniqueElementData().addAttribute(name, value);
2675 }
2676 didAddAttribute(name, value);
2677}
2678
2679bool Element::removeAttribute(const AtomString& qualifiedName)
2680{
2681 if (!elementData())
2682 return false;
2683
2684 AtomString caseAdjustedQualifiedName = shouldIgnoreAttributeCase(*this) ? qualifiedName.convertToASCIILowercase() : qualifiedName;
2685 unsigned index = elementData()->findAttributeIndexByName(caseAdjustedQualifiedName, false);
2686 if (index == ElementData::attributeNotFound) {
2687 if (UNLIKELY(caseAdjustedQualifiedName == styleAttr) && elementData()->styleAttributeIsDirty() && is<StyledElement>(*this))
2688 downcast<StyledElement>(*this).removeAllInlineStyleProperties();
2689 return false;
2690 }
2691
2692 removeAttributeInternal(index, NotInSynchronizationOfLazyAttribute);
2693 return true;
2694}
2695
2696bool Element::removeAttributeNS(const AtomString& namespaceURI, const AtomString& localName)
2697{
2698 return removeAttribute(QualifiedName(nullAtom(), localName, namespaceURI));
2699}
2700
2701RefPtr<Attr> Element::getAttributeNode(const AtomString& qualifiedName)
2702{
2703 if (!elementData())
2704 return nullptr;
2705 synchronizeAttribute(qualifiedName);
2706 const Attribute* attribute = elementData()->findAttributeByName(qualifiedName, shouldIgnoreAttributeCase(*this));
2707 if (!attribute)
2708 return nullptr;
2709 return ensureAttr(attribute->name());
2710}
2711
2712RefPtr<Attr> Element::getAttributeNodeNS(const AtomString& namespaceURI, const AtomString& localName)
2713{
2714 if (!elementData())
2715 return 0;
2716 QualifiedName qName(nullAtom(), localName, namespaceURI);
2717 synchronizeAttribute(qName);
2718 const Attribute* attribute = elementData()->findAttributeByName(qName);
2719 if (!attribute)
2720 return 0;
2721 return ensureAttr(attribute->name());
2722}
2723
2724bool Element::hasAttribute(const AtomString& qualifiedName) const
2725{
2726 if (!elementData())
2727 return false;
2728 synchronizeAttribute(qualifiedName);
2729 return elementData()->findAttributeByName(qualifiedName, shouldIgnoreAttributeCase(*this));
2730}
2731
2732bool Element::hasAttributeNS(const AtomString& namespaceURI, const AtomString& localName) const
2733{
2734 if (!elementData())
2735 return false;
2736 QualifiedName qName(nullAtom(), localName, namespaceURI);
2737 synchronizeAttribute(qName);
2738 return elementData()->findAttributeByName(qName);
2739}
2740
2741void Element::focus(bool restorePreviousSelection, FocusDirection direction)
2742{
2743 if (!isConnected())
2744 return;
2745
2746 if (document().focusedElement() == this) {
2747 if (document().page())
2748 document().page()->chrome().client().elementDidRefocus(*this);
2749
2750 return;
2751 }
2752
2753 // If the stylesheets have already been loaded we can reliably check isFocusable.
2754 // If not, we continue and set the focused node on the focus controller below so
2755 // that it can be updated soon after attach.
2756 if (document().haveStylesheetsLoaded()) {
2757 document().updateStyleIfNeeded();
2758 if (!isFocusable())
2759 return;
2760 }
2761
2762 if (!supportsFocus())
2763 return;
2764
2765 RefPtr<Node> protect;
2766 if (Page* page = document().page()) {
2767 // Focus and change event handlers can cause us to lose our last ref.
2768 // If a focus event handler changes the focus to a different node it
2769 // does not make sense to continue and update appearence.
2770 protect = this;
2771 if (!page->focusController().setFocusedElement(this, *document().frame(), direction))
2772 return;
2773 }
2774
2775 SelectionRevealMode revealMode = SelectionRevealMode::Reveal;
2776#if PLATFORM(IOS_FAMILY)
2777 // Focusing a form element triggers animation in UIKit to scroll to the right position.
2778 // Calling updateFocusAppearance() would generate an unnecessary call to ScrollView::setScrollPosition(),
2779 // which would jump us around during this animation. See <rdar://problem/6699741>.
2780 bool isFormControl = is<HTMLFormControlElement>(*this);
2781 if (isFormControl)
2782 revealMode = SelectionRevealMode::RevealUpToMainFrame;
2783#endif
2784
2785 auto target = focusAppearanceUpdateTarget();
2786 if (!target)
2787 return;
2788
2789 target->updateFocusAppearance(restorePreviousSelection ? SelectionRestorationMode::Restore : SelectionRestorationMode::SetDefault, revealMode);
2790}
2791
2792RefPtr<Element> Element::focusAppearanceUpdateTarget()
2793{
2794 return this;
2795}
2796
2797void Element::updateFocusAppearance(SelectionRestorationMode, SelectionRevealMode revealMode)
2798{
2799 if (isRootEditableElement()) {
2800 // Keep frame alive in this method, since setSelection() may release the last reference to |frame|.
2801 RefPtr<Frame> frame = document().frame();
2802 if (!frame)
2803 return;
2804
2805 // When focusing an editable element in an iframe, don't reset the selection if it already contains a selection.
2806 if (this == frame->selection().selection().rootEditableElement())
2807 return;
2808
2809 // FIXME: We should restore the previous selection if there is one.
2810 VisibleSelection newSelection = VisibleSelection(firstPositionInOrBeforeNode(this), DOWNSTREAM);
2811
2812 if (frame->selection().shouldChangeSelection(newSelection)) {
2813 frame->selection().setSelection(newSelection, FrameSelection::defaultSetSelectionOptions(), Element::defaultFocusTextStateChangeIntent());
2814 frame->selection().revealSelection(revealMode);
2815 return;
2816 }
2817 }
2818
2819 if (RefPtr<FrameView> view = document().view())
2820 view->scheduleScrollToFocusedElement(revealMode);
2821}
2822
2823void Element::blur()
2824{
2825 if (treeScope().focusedElementInScope() == this) {
2826 if (Frame* frame = document().frame())
2827 frame->page()->focusController().setFocusedElement(nullptr, *frame);
2828 else
2829 document().setFocusedElement(nullptr);
2830 }
2831}
2832
2833void Element::dispatchFocusInEvent(const AtomString& eventType, RefPtr<Element>&& oldFocusedElement)
2834{
2835 ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::InMainThread::isScriptAllowed());
2836 ASSERT(eventType == eventNames().focusinEvent || eventType == eventNames().DOMFocusInEvent);
2837 dispatchScopedEvent(FocusEvent::create(eventType, Event::CanBubble::Yes, Event::IsCancelable::No, document().windowProxy(), 0, WTFMove(oldFocusedElement)));
2838}
2839
2840void Element::dispatchFocusOutEvent(const AtomString& eventType, RefPtr<Element>&& newFocusedElement)
2841{
2842 ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::InMainThread::isScriptAllowed());
2843 ASSERT(eventType == eventNames().focusoutEvent || eventType == eventNames().DOMFocusOutEvent);
2844 dispatchScopedEvent(FocusEvent::create(eventType, Event::CanBubble::Yes, Event::IsCancelable::No, document().windowProxy(), 0, WTFMove(newFocusedElement)));
2845}
2846
2847void Element::dispatchFocusEvent(RefPtr<Element>&& oldFocusedElement, FocusDirection)
2848{
2849 if (auto* page = document().page())
2850 page->chrome().client().elementDidFocus(*this);
2851 dispatchEvent(FocusEvent::create(eventNames().focusEvent, Event::CanBubble::No, Event::IsCancelable::No, document().windowProxy(), 0, WTFMove(oldFocusedElement)));
2852}
2853
2854void Element::dispatchBlurEvent(RefPtr<Element>&& newFocusedElement)
2855{
2856 if (auto* page = document().page())
2857 page->chrome().client().elementDidBlur(*this);
2858 dispatchEvent(FocusEvent::create(eventNames().blurEvent, Event::CanBubble::No, Event::IsCancelable::No, document().windowProxy(), 0, WTFMove(newFocusedElement)));
2859}
2860
2861void Element::dispatchWebKitImageReadyEventForTesting()
2862{
2863 if (document().settings().webkitImageReadyEventEnabled())
2864 dispatchEvent(Event::create("webkitImageFrameReady", Event::CanBubble::Yes, Event::IsCancelable::Yes));
2865}
2866
2867bool Element::dispatchMouseForceWillBegin()
2868{
2869#if ENABLE(MOUSE_FORCE_EVENTS)
2870 if (!document().hasListenerType(Document::FORCEWILLBEGIN_LISTENER))
2871 return false;
2872
2873 Frame* frame = document().frame();
2874 if (!frame)
2875 return false;
2876
2877 PlatformMouseEvent platformMouseEvent { frame->eventHandler().lastKnownMousePosition(), frame->eventHandler().lastKnownMouseGlobalPosition(), NoButton, PlatformEvent::NoType, 1, false, false, false, false, WallTime::now(), ForceAtClick, NoTap };
2878 auto mouseForceWillBeginEvent = MouseEvent::create(eventNames().webkitmouseforcewillbeginEvent, document().windowProxy(), platformMouseEvent, 0, nullptr);
2879 mouseForceWillBeginEvent->setTarget(this);
2880 dispatchEvent(mouseForceWillBeginEvent);
2881
2882 if (mouseForceWillBeginEvent->defaultHandled() || mouseForceWillBeginEvent->defaultPrevented())
2883 return true;
2884#endif
2885
2886 return false;
2887}
2888
2889ExceptionOr<void> Element::mergeWithNextTextNode(Text& node)
2890{
2891 auto* next = node.nextSibling();
2892 if (!is<Text>(next))
2893 return { };
2894 Ref<Text> textNext { downcast<Text>(*next) };
2895 node.appendData(textNext->data());
2896 return textNext->remove();
2897}
2898
2899String Element::innerHTML() const
2900{
2901 return serializeFragment(*this, SerializedNodes::SubtreesOfChildren);
2902}
2903
2904String Element::outerHTML() const
2905{
2906 return serializeFragment(*this, SerializedNodes::SubtreeIncludingNode);
2907}
2908
2909ExceptionOr<void> Element::setOuterHTML(const String& html)
2910{
2911 auto* parentElement = this->parentElement();
2912 if (!is<HTMLElement>(parentElement))
2913 return Exception { NoModificationAllowedError };
2914
2915 Ref<HTMLElement> parent = downcast<HTMLElement>(*parentElement);
2916 RefPtr<Node> prev = previousSibling();
2917 RefPtr<Node> next = nextSibling();
2918
2919 auto fragment = createFragmentForInnerOuterHTML(parent, html, AllowScriptingContent);
2920 if (fragment.hasException())
2921 return fragment.releaseException();
2922
2923 auto replaceResult = parent->replaceChild(fragment.releaseReturnValue().get(), *this);
2924 if (replaceResult.hasException())
2925 return replaceResult.releaseException();
2926
2927 RefPtr<Node> node = next ? next->previousSibling() : nullptr;
2928 if (is<Text>(node)) {
2929 auto result = mergeWithNextTextNode(downcast<Text>(*node));
2930 if (result.hasException())
2931 return result.releaseException();
2932 }
2933 if (is<Text>(prev)) {
2934 auto result = mergeWithNextTextNode(downcast<Text>(*prev));
2935 if (result.hasException())
2936 return result.releaseException();
2937 }
2938 return { };
2939}
2940
2941
2942ExceptionOr<void> Element::setInnerHTML(const String& html)
2943{
2944 auto fragment = createFragmentForInnerOuterHTML(*this, html, AllowScriptingContent);
2945 if (fragment.hasException())
2946 return fragment.releaseException();
2947
2948 ContainerNode* container;
2949 if (!is<HTMLTemplateElement>(*this))
2950 container = this;
2951 else
2952 container = &downcast<HTMLTemplateElement>(*this).content();
2953
2954 return replaceChildrenWithFragment(*container, fragment.releaseReturnValue());
2955}
2956
2957String Element::innerText()
2958{
2959 // We need to update layout, since plainText uses line boxes in the render tree.
2960 document().updateLayoutIgnorePendingStylesheets();
2961
2962 if (!renderer())
2963 return textContent(true);
2964
2965 return plainText(rangeOfContents(*this).ptr());
2966}
2967
2968String Element::outerText()
2969{
2970 // Getting outerText is the same as getting innerText, only
2971 // setting is different. You would think this should get the plain
2972 // text for the outer range, but this is wrong, <br> for instance
2973 // would return different values for inner and outer text by such
2974 // a rule, but it doesn't in WinIE, and we want to match that.
2975 return innerText();
2976}
2977
2978String Element::title() const
2979{
2980 return String();
2981}
2982
2983const AtomString& Element::pseudo() const
2984{
2985 return attributeWithoutSynchronization(pseudoAttr);
2986}
2987
2988void Element::setPseudo(const AtomString& value)
2989{
2990 setAttributeWithoutSynchronization(pseudoAttr, value);
2991}
2992
2993LayoutSize Element::minimumSizeForResizing() const
2994{
2995 return hasRareData() ? elementRareData()->minimumSizeForResizing() : defaultMinimumSizeForResizing();
2996}
2997
2998void Element::setMinimumSizeForResizing(const LayoutSize& size)
2999{
3000 if (!hasRareData() && size == defaultMinimumSizeForResizing())
3001 return;
3002 ensureElementRareData().setMinimumSizeForResizing(size);
3003}
3004
3005void Element::willBecomeFullscreenElement()
3006{
3007 for (auto& child : descendantsOfType<Element>(*this))
3008 child.ancestorWillEnterFullscreen();
3009}
3010
3011static PseudoElement* beforeOrAfterPseudoElement(Element& host, PseudoId pseudoElementSpecifier)
3012{
3013 switch (pseudoElementSpecifier) {
3014 case PseudoId::Before:
3015 return host.beforePseudoElement();
3016 case PseudoId::After:
3017 return host.afterPseudoElement();
3018 default:
3019 return nullptr;
3020 }
3021}
3022
3023const RenderStyle* Element::existingComputedStyle() const
3024{
3025 if (hasRareData()) {
3026 if (auto* style = elementRareData()->computedStyle())
3027 return style;
3028 }
3029
3030 return renderStyle();
3031}
3032
3033const RenderStyle* Element::renderOrDisplayContentsStyle() const
3034{
3035 if (auto* style = renderStyle())
3036 return style;
3037
3038 if (!hasRareData())
3039 return nullptr;
3040 auto* style = elementRareData()->computedStyle();
3041 if (style && style->display() == DisplayType::Contents)
3042 return style;
3043
3044 return nullptr;
3045}
3046
3047const RenderStyle& Element::resolveComputedStyle()
3048{
3049 ASSERT(isConnected());
3050 ASSERT(!existingComputedStyle());
3051
3052 Deque<RefPtr<Element>, 32> elementsRequiringComputedStyle({ this });
3053 const RenderStyle* computedStyle = nullptr;
3054
3055 // Collect ancestors until we find one that has style.
3056 auto composedAncestors = composedTreeAncestors(*this);
3057 for (auto& ancestor : composedAncestors) {
3058 if (auto* existingStyle = ancestor.existingComputedStyle()) {
3059 computedStyle = existingStyle;
3060 break;
3061 }
3062 elementsRequiringComputedStyle.prepend(&ancestor);
3063 }
3064
3065 // Resolve and cache styles starting from the most distant ancestor.
3066 for (auto& element : elementsRequiringComputedStyle) {
3067 auto style = document().styleForElementIgnoringPendingStylesheets(*element, computedStyle);
3068 computedStyle = style.get();
3069 ElementRareData& rareData = element->ensureElementRareData();
3070 rareData.setComputedStyle(WTFMove(style));
3071 }
3072
3073 return *computedStyle;
3074}
3075
3076const RenderStyle& Element::resolvePseudoElementStyle(PseudoId pseudoElementSpecifier)
3077{
3078 ASSERT(!isPseudoElement());
3079
3080 auto* parentStyle = existingComputedStyle();
3081 ASSERT(parentStyle);
3082 ASSERT(!parentStyle->getCachedPseudoStyle(pseudoElementSpecifier));
3083
3084 auto style = document().styleForElementIgnoringPendingStylesheets(*this, parentStyle, pseudoElementSpecifier);
3085 if (!style) {
3086 style = RenderStyle::createPtr();
3087 style->inheritFrom(*parentStyle);
3088 style->setStyleType(pseudoElementSpecifier);
3089 }
3090
3091 auto* computedStyle = style.get();
3092 const_cast<RenderStyle*>(parentStyle)->addCachedPseudoStyle(WTFMove(style));
3093 return *computedStyle;
3094}
3095
3096const RenderStyle* Element::computedStyle(PseudoId pseudoElementSpecifier)
3097{
3098 if (!isConnected())
3099 return nullptr;
3100
3101 if (PseudoElement* pseudoElement = beforeOrAfterPseudoElement(*this, pseudoElementSpecifier))
3102 return pseudoElement->computedStyle();
3103
3104 auto* style = existingComputedStyle();
3105 if (!style)
3106 style = &resolveComputedStyle();
3107
3108 if (pseudoElementSpecifier != PseudoId::None) {
3109 if (auto* cachedPseudoStyle = style->getCachedPseudoStyle(pseudoElementSpecifier))
3110 return cachedPseudoStyle;
3111 return &resolvePseudoElementStyle(pseudoElementSpecifier);
3112 }
3113
3114 return style;
3115}
3116
3117bool Element::needsStyleInvalidation() const
3118{
3119 if (!inRenderedDocument())
3120 return false;
3121 if (styleValidity() >= Style::Validity::SubtreeInvalid)
3122 return false;
3123 if (document().hasPendingFullStyleRebuild())
3124 return false;
3125
3126 return true;
3127}
3128
3129void Element::setStyleAffectedByEmpty()
3130{
3131 ensureElementRareData().setStyleAffectedByEmpty(true);
3132}
3133
3134void Element::setStyleAffectedByFocusWithin()
3135{
3136 ensureElementRareData().setStyleAffectedByFocusWithin(true);
3137}
3138
3139void Element::setStyleAffectedByActive()
3140{
3141 ensureElementRareData().setStyleAffectedByActive(true);
3142}
3143
3144void Element::setChildrenAffectedByDrag()
3145{
3146 ensureElementRareData().setChildrenAffectedByDrag(true);
3147}
3148
3149void Element::setChildrenAffectedByForwardPositionalRules()
3150{
3151 ensureElementRareData().setChildrenAffectedByForwardPositionalRules(true);
3152}
3153
3154void Element::setDescendantsAffectedByForwardPositionalRules()
3155{
3156 ensureElementRareData().setDescendantsAffectedByForwardPositionalRules(true);
3157}
3158
3159void Element::setChildrenAffectedByBackwardPositionalRules()
3160{
3161 ensureElementRareData().setChildrenAffectedByBackwardPositionalRules(true);
3162}
3163
3164void Element::setDescendantsAffectedByBackwardPositionalRules()
3165{
3166 ensureElementRareData().setDescendantsAffectedByBackwardPositionalRules(true);
3167}
3168
3169void Element::setChildrenAffectedByPropertyBasedBackwardPositionalRules()
3170{
3171 ensureElementRareData().setChildrenAffectedByPropertyBasedBackwardPositionalRules(true);
3172}
3173
3174void Element::setChildIndex(unsigned index)
3175{
3176 ElementRareData& rareData = ensureElementRareData();
3177 rareData.setChildIndex(index);
3178}
3179
3180bool Element::hasFlagsSetDuringStylingOfChildren() const
3181{
3182 if (childrenAffectedByHover() || childrenAffectedByFirstChildRules() || childrenAffectedByLastChildRules())
3183 return true;
3184
3185 if (!hasRareData())
3186 return false;
3187 return rareDataStyleAffectedByActive()
3188 || rareDataChildrenAffectedByDrag()
3189 || rareDataChildrenAffectedByForwardPositionalRules()
3190 || rareDataDescendantsAffectedByForwardPositionalRules()
3191 || rareDataChildrenAffectedByBackwardPositionalRules()
3192 || rareDataDescendantsAffectedByBackwardPositionalRules()
3193 || rareDataChildrenAffectedByPropertyBasedBackwardPositionalRules();
3194}
3195
3196bool Element::rareDataStyleAffectedByEmpty() const
3197{
3198 ASSERT(hasRareData());
3199 return elementRareData()->styleAffectedByEmpty();
3200}
3201
3202bool Element::rareDataStyleAffectedByFocusWithin() const
3203{
3204 ASSERT(hasRareData());
3205 return elementRareData()->styleAffectedByFocusWithin();
3206}
3207
3208bool Element::rareDataStyleAffectedByActive() const
3209{
3210 ASSERT(hasRareData());
3211 return elementRareData()->styleAffectedByActive();
3212}
3213
3214bool Element::rareDataChildrenAffectedByDrag() const
3215{
3216 ASSERT(hasRareData());
3217 return elementRareData()->childrenAffectedByDrag();
3218}
3219
3220bool Element::rareDataChildrenAffectedByForwardPositionalRules() const
3221{
3222 ASSERT(hasRareData());
3223 return elementRareData()->childrenAffectedByForwardPositionalRules();
3224}
3225
3226bool Element::rareDataDescendantsAffectedByForwardPositionalRules() const
3227{
3228 ASSERT(hasRareData());
3229 return elementRareData()->descendantsAffectedByForwardPositionalRules();
3230}
3231
3232bool Element::rareDataChildrenAffectedByBackwardPositionalRules() const
3233{
3234 ASSERT(hasRareData());
3235 return elementRareData()->childrenAffectedByBackwardPositionalRules();
3236}
3237
3238bool Element::rareDataDescendantsAffectedByBackwardPositionalRules() const
3239{
3240 ASSERT(hasRareData());
3241 return elementRareData()->descendantsAffectedByBackwardPositionalRules();
3242}
3243
3244bool Element::rareDataChildrenAffectedByPropertyBasedBackwardPositionalRules() const
3245{
3246 ASSERT(hasRareData());
3247 return elementRareData()->childrenAffectedByPropertyBasedBackwardPositionalRules();
3248}
3249
3250unsigned Element::rareDataChildIndex() const
3251{
3252 ASSERT(hasRareData());
3253 return elementRareData()->childIndex();
3254}
3255
3256AtomString Element::computeInheritedLanguage() const
3257{
3258 if (const ElementData* elementData = this->elementData()) {
3259 if (const Attribute* attribute = elementData->findLanguageAttribute())
3260 return attribute->value();
3261 }
3262
3263 // The language property is inherited, so we iterate over the parents to find the first language.
3264 const Node* currentNode = this;
3265 while ((currentNode = currentNode->parentNode())) {
3266 if (is<Element>(*currentNode)) {
3267 if (const ElementData* elementData = downcast<Element>(*currentNode).elementData()) {
3268 if (const Attribute* attribute = elementData->findLanguageAttribute())
3269 return attribute->value();
3270 }
3271 } else if (is<Document>(*currentNode)) {
3272 // checking the MIME content-language
3273 return downcast<Document>(*currentNode).contentLanguage();
3274 }
3275 }
3276
3277 return nullAtom();
3278}
3279
3280Locale& Element::locale() const
3281{
3282 return document().getCachedLocale(computeInheritedLanguage());
3283}
3284
3285void Element::normalizeAttributes()
3286{
3287 if (!hasAttributes())
3288 return;
3289
3290 auto* attrNodeList = attrNodeListForElement(*this);
3291 if (!attrNodeList)
3292 return;
3293
3294 // Copy the Attr Vector because Node::normalize() can fire synchronous JS
3295 // events (e.g. DOMSubtreeModified) and a JS listener could add / remove
3296 // attributes while we are iterating.
3297 auto copyOfAttrNodeList = *attrNodeList;
3298 for (auto& attrNode : copyOfAttrNodeList)
3299 attrNode->normalize();
3300}
3301
3302PseudoElement* Element::beforePseudoElement() const
3303{
3304 return hasRareData() ? elementRareData()->beforePseudoElement() : nullptr;
3305}
3306
3307PseudoElement* Element::afterPseudoElement() const
3308{
3309 return hasRareData() ? elementRareData()->afterPseudoElement() : nullptr;
3310}
3311
3312void Element::setBeforePseudoElement(Ref<PseudoElement>&& element)
3313{
3314 ensureElementRareData().setBeforePseudoElement(WTFMove(element));
3315}
3316
3317void Element::setAfterPseudoElement(Ref<PseudoElement>&& element)
3318{
3319 ensureElementRareData().setAfterPseudoElement(WTFMove(element));
3320}
3321
3322static void disconnectPseudoElement(PseudoElement* pseudoElement)
3323{
3324 if (!pseudoElement)
3325 return;
3326 ASSERT(!pseudoElement->renderer());
3327 ASSERT(pseudoElement->hostElement());
3328 pseudoElement->clearHostElement();
3329}
3330
3331void Element::clearBeforePseudoElement()
3332{
3333 if (!hasRareData())
3334 return;
3335 disconnectPseudoElement(elementRareData()->beforePseudoElement());
3336 elementRareData()->setBeforePseudoElement(nullptr);
3337}
3338
3339void Element::clearAfterPseudoElement()
3340{
3341 if (!hasRareData())
3342 return;
3343 disconnectPseudoElement(elementRareData()->afterPseudoElement());
3344 elementRareData()->setAfterPseudoElement(nullptr);
3345}
3346
3347bool Element::matchesValidPseudoClass() const
3348{
3349 return false;
3350}
3351
3352bool Element::matchesInvalidPseudoClass() const
3353{
3354 return false;
3355}
3356
3357bool Element::matchesReadWritePseudoClass() const
3358{
3359 return false;
3360}
3361
3362bool Element::matchesIndeterminatePseudoClass() const
3363{
3364 return shouldAppearIndeterminate();
3365}
3366
3367bool Element::matchesDefaultPseudoClass() const
3368{
3369 return false;
3370}
3371
3372ExceptionOr<bool> Element::matches(const String& selector)
3373{
3374 auto query = document().selectorQueryForString(selector);
3375 if (query.hasException())
3376 return query.releaseException();
3377 return query.releaseReturnValue().matches(*this);
3378}
3379
3380ExceptionOr<Element*> Element::closest(const String& selector)
3381{
3382 auto query = document().selectorQueryForString(selector);
3383 if (query.hasException())
3384 return query.releaseException();
3385 return query.releaseReturnValue().closest(*this);
3386}
3387
3388bool Element::shouldAppearIndeterminate() const
3389{
3390 return false;
3391}
3392
3393bool Element::mayCauseRepaintInsideViewport(const IntRect* visibleRect) const
3394{
3395 return renderer() && renderer()->mayCauseRepaintInsideViewport(visibleRect);
3396}
3397
3398DOMTokenList& Element::classList()
3399{
3400 ElementRareData& data = ensureElementRareData();
3401 if (!data.classList())
3402 data.setClassList(std::make_unique<DOMTokenList>(*this, HTMLNames::classAttr));
3403 return *data.classList();
3404}
3405
3406DatasetDOMStringMap& Element::dataset()
3407{
3408 ElementRareData& data = ensureElementRareData();
3409 if (!data.dataset())
3410 data.setDataset(std::make_unique<DatasetDOMStringMap>(*this));
3411 return *data.dataset();
3412}
3413
3414URL Element::getURLAttribute(const QualifiedName& name) const
3415{
3416#if !ASSERT_DISABLED
3417 if (elementData()) {
3418 if (const Attribute* attribute = findAttributeByName(name))
3419 ASSERT(isURLAttribute(*attribute));
3420 }
3421#endif
3422 return document().completeURL(stripLeadingAndTrailingHTMLSpaces(getAttribute(name)));
3423}
3424
3425URL Element::getNonEmptyURLAttribute(const QualifiedName& name) const
3426{
3427#if !ASSERT_DISABLED
3428 if (elementData()) {
3429 if (const Attribute* attribute = findAttributeByName(name))
3430 ASSERT(isURLAttribute(*attribute));
3431 }
3432#endif
3433 String value = stripLeadingAndTrailingHTMLSpaces(getAttribute(name));
3434 if (value.isEmpty())
3435 return URL();
3436 return document().completeURL(value);
3437}
3438
3439int Element::getIntegralAttribute(const QualifiedName& attributeName) const
3440{
3441 return parseHTMLInteger(getAttribute(attributeName)).value_or(0);
3442}
3443
3444void Element::setIntegralAttribute(const QualifiedName& attributeName, int value)
3445{
3446 setAttribute(attributeName, AtomString::number(value));
3447}
3448
3449unsigned Element::getUnsignedIntegralAttribute(const QualifiedName& attributeName) const
3450{
3451 return parseHTMLNonNegativeInteger(getAttribute(attributeName)).value_or(0);
3452}
3453
3454void Element::setUnsignedIntegralAttribute(const QualifiedName& attributeName, unsigned value)
3455{
3456 setAttribute(attributeName, AtomString::number(limitToOnlyHTMLNonNegative(value)));
3457}
3458
3459bool Element::childShouldCreateRenderer(const Node& child) const
3460{
3461 // Only create renderers for SVG elements whose parents are SVG elements, or for proper <svg xmlns="svgNS"> subdocuments.
3462 if (child.isSVGElement()) {
3463 ASSERT(!isSVGElement());
3464 const SVGElement& childElement = downcast<SVGElement>(child);
3465 return is<SVGSVGElement>(childElement) && childElement.isValid();
3466 }
3467 return true;
3468}
3469
3470#if ENABLE(FULLSCREEN_API)
3471static Element* parentCrossingFrameBoundaries(const Element* element)
3472{
3473 ASSERT(element);
3474 if (auto* parent = element->parentElementInComposedTree())
3475 return parent;
3476 return element->document().ownerElement();
3477}
3478
3479void Element::webkitRequestFullscreen()
3480{
3481 document().fullscreenManager().requestFullscreenForElement(this, FullscreenManager::EnforceIFrameAllowFullscreenRequirement);
3482}
3483
3484bool Element::containsFullScreenElement() const
3485{
3486 return hasRareData() && elementRareData()->containsFullScreenElement();
3487}
3488
3489void Element::setContainsFullScreenElement(bool flag)
3490{
3491 ensureElementRareData().setContainsFullScreenElement(flag);
3492 invalidateStyleAndLayerComposition();
3493}
3494
3495void Element::setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(bool flag)
3496{
3497 Element* element = this;
3498 while ((element = parentCrossingFrameBoundaries(element)))
3499 element->setContainsFullScreenElement(flag);
3500}
3501#endif
3502
3503#if ENABLE(POINTER_EVENTS)
3504ExceptionOr<void> Element::setPointerCapture(int32_t pointerId)
3505{
3506 if (document().page())
3507 return document().page()->pointerCaptureController().setPointerCapture(this, pointerId);
3508 return { };
3509}
3510
3511ExceptionOr<void> Element::releasePointerCapture(int32_t pointerId)
3512{
3513 if (document().page())
3514 return document().page()->pointerCaptureController().releasePointerCapture(this, pointerId);
3515 return { };
3516}
3517
3518bool Element::hasPointerCapture(int32_t pointerId)
3519{
3520 if (document().page())
3521 return document().page()->pointerCaptureController().hasPointerCapture(this, pointerId);
3522 return false;
3523}
3524#endif
3525
3526#if ENABLE(POINTER_LOCK)
3527void Element::requestPointerLock()
3528{
3529 if (document().page())
3530 document().page()->pointerLockController().requestPointerLock(this);
3531}
3532#endif
3533
3534#if ENABLE(INTERSECTION_OBSERVER)
3535void Element::disconnectFromIntersectionObservers()
3536{
3537 auto* observerData = intersectionObserverData();
3538 if (!observerData)
3539 return;
3540
3541 for (const auto& registration : observerData->registrations)
3542 registration.observer->targetDestroyed(*this);
3543 observerData->registrations.clear();
3544
3545 for (const auto& observer : observerData->observers)
3546 observer->rootDestroyed();
3547 observerData->observers.clear();
3548}
3549
3550IntersectionObserverData& Element::ensureIntersectionObserverData()
3551{
3552 auto& rareData = ensureElementRareData();
3553 if (!rareData.intersectionObserverData())
3554 rareData.setIntersectionObserverData(std::make_unique<IntersectionObserverData>());
3555 return *rareData.intersectionObserverData();
3556}
3557
3558IntersectionObserverData* Element::intersectionObserverData()
3559{
3560 return hasRareData() ? elementRareData()->intersectionObserverData() : nullptr;
3561}
3562#endif
3563
3564#if ENABLE(RESIZE_OBSERVER)
3565void Element::disconnectFromResizeObservers()
3566{
3567 auto* observerData = resizeObserverData();
3568 if (!observerData)
3569 return;
3570
3571 for (const auto& observer : observerData->observers)
3572 observer->targetDestroyed(*this);
3573 observerData->observers.clear();
3574}
3575
3576ResizeObserverData& Element::ensureResizeObserverData()
3577{
3578 auto& rareData = ensureElementRareData();
3579 if (!rareData.resizeObserverData())
3580 rareData.setResizeObserverData(std::make_unique<ResizeObserverData>());
3581 return *rareData.resizeObserverData();
3582}
3583
3584ResizeObserverData* Element::resizeObserverData()
3585{
3586 return hasRareData() ? elementRareData()->resizeObserverData() : nullptr;
3587}
3588#endif
3589
3590SpellcheckAttributeState Element::spellcheckAttributeState() const
3591{
3592 const AtomString& value = attributeWithoutSynchronization(HTMLNames::spellcheckAttr);
3593 if (value.isNull())
3594 return SpellcheckAttributeDefault;
3595 if (value.isEmpty() || equalLettersIgnoringASCIICase(value, "true"))
3596 return SpellcheckAttributeTrue;
3597 if (equalLettersIgnoringASCIICase(value, "false"))
3598 return SpellcheckAttributeFalse;
3599 return SpellcheckAttributeDefault;
3600}
3601
3602bool Element::isSpellCheckingEnabled() const
3603{
3604 for (const Element* element = this; element; element = element->parentOrShadowHostElement()) {
3605 switch (element->spellcheckAttributeState()) {
3606 case SpellcheckAttributeTrue:
3607 return true;
3608 case SpellcheckAttributeFalse:
3609 return false;
3610 case SpellcheckAttributeDefault:
3611 break;
3612 }
3613 }
3614
3615 return true;
3616}
3617
3618#ifndef NDEBUG
3619bool Element::fastAttributeLookupAllowed(const QualifiedName& name) const
3620{
3621 if (name == HTMLNames::styleAttr)
3622 return false;
3623
3624 if (isSVGElement())
3625 return !downcast<SVGElement>(*this).isAnimatedPropertyAttribute(name);
3626
3627 return true;
3628}
3629#endif
3630
3631#if DUMP_NODE_STATISTICS
3632bool Element::hasNamedNodeMap() const
3633{
3634 return hasRareData() && elementRareData()->attributeMap();
3635}
3636#endif
3637
3638inline void Element::updateName(const AtomString& oldName, const AtomString& newName)
3639{
3640 if (!isInTreeScope())
3641 return;
3642
3643 if (oldName == newName)
3644 return;
3645
3646 updateNameForTreeScope(treeScope(), oldName, newName);
3647
3648 if (!isConnected())
3649 return;
3650 if (!is<HTMLDocument>(document()))
3651 return;
3652 updateNameForDocument(downcast<HTMLDocument>(document()), oldName, newName);
3653}
3654
3655void Element::updateNameForTreeScope(TreeScope& scope, const AtomString& oldName, const AtomString& newName)
3656{
3657 ASSERT(oldName != newName);
3658
3659 if (!oldName.isEmpty())
3660 scope.removeElementByName(*oldName.impl(), *this);
3661 if (!newName.isEmpty())
3662 scope.addElementByName(*newName.impl(), *this);
3663}
3664
3665void Element::updateNameForDocument(HTMLDocument& document, const AtomString& oldName, const AtomString& newName)
3666{
3667 ASSERT(oldName != newName);
3668
3669 if (isInShadowTree())
3670 return;
3671
3672 if (WindowNameCollection::elementMatchesIfNameAttributeMatch(*this)) {
3673 const AtomString& id = WindowNameCollection::elementMatchesIfIdAttributeMatch(*this) ? getIdAttribute() : nullAtom();
3674 if (!oldName.isEmpty() && oldName != id)
3675 document.removeWindowNamedItem(*oldName.impl(), *this);
3676 if (!newName.isEmpty() && newName != id)
3677 document.addWindowNamedItem(*newName.impl(), *this);
3678 }
3679
3680 if (DocumentNameCollection::elementMatchesIfNameAttributeMatch(*this)) {
3681 const AtomString& id = DocumentNameCollection::elementMatchesIfIdAttributeMatch(*this) ? getIdAttribute() : nullAtom();
3682 if (!oldName.isEmpty() && oldName != id)
3683 document.removeDocumentNamedItem(*oldName.impl(), *this);
3684 if (!newName.isEmpty() && newName != id)
3685 document.addDocumentNamedItem(*newName.impl(), *this);
3686 }
3687}
3688
3689inline void Element::updateId(const AtomString& oldId, const AtomString& newId, NotifyObservers notifyObservers)
3690{
3691 if (!isInTreeScope())
3692 return;
3693
3694 if (oldId == newId)
3695 return;
3696
3697 updateIdForTreeScope(treeScope(), oldId, newId, notifyObservers);
3698
3699 if (!isConnected())
3700 return;
3701 if (!is<HTMLDocument>(document()))
3702 return;
3703 updateIdForDocument(downcast<HTMLDocument>(document()), oldId, newId, UpdateHTMLDocumentNamedItemMapsOnlyIfDiffersFromNameAttribute);
3704}
3705
3706void Element::updateIdForTreeScope(TreeScope& scope, const AtomString& oldId, const AtomString& newId, NotifyObservers notifyObservers)
3707{
3708 ASSERT(isInTreeScope());
3709 ASSERT(oldId != newId);
3710
3711 if (!oldId.isEmpty())
3712 scope.removeElementById(*oldId.impl(), *this, notifyObservers == NotifyObservers::Yes);
3713 if (!newId.isEmpty())
3714 scope.addElementById(*newId.impl(), *this, notifyObservers == NotifyObservers::Yes);
3715}
3716
3717void Element::updateIdForDocument(HTMLDocument& document, const AtomString& oldId, const AtomString& newId, HTMLDocumentNamedItemMapsUpdatingCondition condition)
3718{
3719 ASSERT(isConnected());
3720 ASSERT(oldId != newId);
3721
3722 if (isInShadowTree())
3723 return;
3724
3725 if (WindowNameCollection::elementMatchesIfIdAttributeMatch(*this)) {
3726 const AtomString& name = condition == UpdateHTMLDocumentNamedItemMapsOnlyIfDiffersFromNameAttribute && WindowNameCollection::elementMatchesIfNameAttributeMatch(*this) ? getNameAttribute() : nullAtom();
3727 if (!oldId.isEmpty() && oldId != name)
3728 document.removeWindowNamedItem(*oldId.impl(), *this);
3729 if (!newId.isEmpty() && newId != name)
3730 document.addWindowNamedItem(*newId.impl(), *this);
3731 }
3732
3733 if (DocumentNameCollection::elementMatchesIfIdAttributeMatch(*this)) {
3734 const AtomString& name = condition == UpdateHTMLDocumentNamedItemMapsOnlyIfDiffersFromNameAttribute && DocumentNameCollection::elementMatchesIfNameAttributeMatch(*this) ? getNameAttribute() : nullAtom();
3735 if (!oldId.isEmpty() && oldId != name)
3736 document.removeDocumentNamedItem(*oldId.impl(), *this);
3737 if (!newId.isEmpty() && newId != name)
3738 document.addDocumentNamedItem(*newId.impl(), *this);
3739 }
3740}
3741
3742void Element::updateLabel(TreeScope& scope, const AtomString& oldForAttributeValue, const AtomString& newForAttributeValue)
3743{
3744 ASSERT(hasTagName(labelTag));
3745
3746 if (!isConnected())
3747 return;
3748
3749 if (oldForAttributeValue == newForAttributeValue)
3750 return;
3751
3752 if (!oldForAttributeValue.isEmpty())
3753 scope.removeLabel(*oldForAttributeValue.impl(), downcast<HTMLLabelElement>(*this));
3754 if (!newForAttributeValue.isEmpty())
3755 scope.addLabel(*newForAttributeValue.impl(), downcast<HTMLLabelElement>(*this));
3756}
3757
3758void Element::willModifyAttribute(const QualifiedName& name, const AtomString& oldValue, const AtomString& newValue)
3759{
3760 if (name == HTMLNames::idAttr)
3761 updateId(oldValue, newValue, NotifyObservers::No); // Will notify observers after the attribute is actually changed.
3762 else if (name == HTMLNames::nameAttr)
3763 updateName(oldValue, newValue);
3764 else if (name == HTMLNames::forAttr && hasTagName(labelTag)) {
3765 if (treeScope().shouldCacheLabelsByForAttribute())
3766 updateLabel(treeScope(), oldValue, newValue);
3767 }
3768
3769 if (auto recipients = MutationObserverInterestGroup::createForAttributesMutation(*this, name))
3770 recipients->enqueueMutationRecord(MutationRecord::createAttributes(*this, name, oldValue));
3771
3772 InspectorInstrumentation::willModifyDOMAttr(document(), *this, oldValue, newValue);
3773}
3774
3775void Element::didAddAttribute(const QualifiedName& name, const AtomString& value)
3776{
3777 attributeChanged(name, nullAtom(), value);
3778 InspectorInstrumentation::didModifyDOMAttr(document(), *this, name.localName(), value);
3779 dispatchSubtreeModifiedEvent();
3780}
3781
3782void Element::didModifyAttribute(const QualifiedName& name, const AtomString& oldValue, const AtomString& newValue)
3783{
3784 attributeChanged(name, oldValue, newValue);
3785 InspectorInstrumentation::didModifyDOMAttr(document(), *this, name.localName(), newValue);
3786 // Do not dispatch a DOMSubtreeModified event here; see bug 81141.
3787}
3788
3789void Element::didRemoveAttribute(const QualifiedName& name, const AtomString& oldValue)
3790{
3791 attributeChanged(name, oldValue, nullAtom());
3792 InspectorInstrumentation::didRemoveDOMAttr(document(), *this, name.localName());
3793 dispatchSubtreeModifiedEvent();
3794}
3795
3796IntPoint Element::savedLayerScrollPosition() const
3797{
3798 return hasRareData() ? elementRareData()->savedLayerScrollPosition() : IntPoint();
3799}
3800
3801void Element::setSavedLayerScrollPosition(const IntPoint& position)
3802{
3803 if (position.isZero() && !hasRareData())
3804 return;
3805 ensureElementRareData().setSavedLayerScrollPosition(position);
3806}
3807
3808RefPtr<Attr> Element::attrIfExists(const AtomString& localName, bool shouldIgnoreAttributeCase)
3809{
3810 if (auto* attrNodeList = attrNodeListForElement(*this))
3811 return findAttrNodeInList(*attrNodeList, localName, shouldIgnoreAttributeCase);
3812 return nullptr;
3813}
3814
3815RefPtr<Attr> Element::attrIfExists(const QualifiedName& name)
3816{
3817 if (auto* attrNodeList = attrNodeListForElement(*this))
3818 return findAttrNodeInList(*attrNodeList, name);
3819 return nullptr;
3820}
3821
3822Ref<Attr> Element::ensureAttr(const QualifiedName& name)
3823{
3824 auto& attrNodeList = ensureAttrNodeListForElement(*this);
3825 RefPtr<Attr> attrNode = findAttrNodeInList(attrNodeList, name);
3826 if (!attrNode) {
3827 attrNode = Attr::create(*this, name);
3828 attrNode->setTreeScopeRecursively(treeScope());
3829 attrNodeList.append(attrNode);
3830 }
3831 return attrNode.releaseNonNull();
3832}
3833
3834void Element::detachAttrNodeFromElementWithValue(Attr* attrNode, const AtomString& value)
3835{
3836 ASSERT(hasSyntheticAttrChildNodes());
3837 attrNode->detachFromElementWithValue(value);
3838
3839 auto& attrNodeList = *attrNodeListForElement(*this);
3840 bool found = attrNodeList.removeFirstMatching([attrNode](auto& attribute) {
3841 return attribute->qualifiedName() == attrNode->qualifiedName();
3842 });
3843 ASSERT_UNUSED(found, found);
3844 if (attrNodeList.isEmpty())
3845 removeAttrNodeListForElement(*this);
3846}
3847
3848void Element::detachAllAttrNodesFromElement()
3849{
3850 auto* attrNodeList = attrNodeListForElement(*this);
3851 ASSERT(attrNodeList);
3852
3853 for (const Attribute& attribute : attributesIterator()) {
3854 if (RefPtr<Attr> attrNode = findAttrNodeInList(*attrNodeList, attribute.name()))
3855 attrNode->detachFromElementWithValue(attribute.value());
3856 }
3857
3858 removeAttrNodeListForElement(*this);
3859}
3860
3861void Element::resetComputedStyle()
3862{
3863 if (!hasRareData() || !elementRareData()->computedStyle())
3864 return;
3865
3866 auto reset = [](Element& element) {
3867 if (!element.hasRareData() || !element.elementRareData()->computedStyle())
3868 return;
3869 if (element.hasCustomStyleResolveCallbacks())
3870 element.willResetComputedStyle();
3871 element.elementRareData()->resetComputedStyle();
3872 };
3873 reset(*this);
3874 for (auto& child : descendantsOfType<Element>(*this))
3875 reset(child);
3876}
3877
3878void Element::resetStyleRelations()
3879{
3880 if (!hasRareData())
3881 return;
3882 elementRareData()->resetStyleRelations();
3883}
3884
3885void Element::clearHoverAndActiveStatusBeforeDetachingRenderer()
3886{
3887 if (!isUserActionElement())
3888 return;
3889 if (hovered())
3890 document().hoveredElementDidDetach(*this);
3891 if (isInActiveChain())
3892 document().elementInActiveChainDidDetach(*this);
3893 document().userActionElements().clearActiveAndHovered(*this);
3894}
3895
3896void Element::willRecalcStyle(Style::Change)
3897{
3898 ASSERT(hasCustomStyleResolveCallbacks());
3899}
3900
3901void Element::didRecalcStyle(Style::Change)
3902{
3903 ASSERT(hasCustomStyleResolveCallbacks());
3904}
3905
3906void Element::willResetComputedStyle()
3907{
3908 ASSERT(hasCustomStyleResolveCallbacks());
3909}
3910
3911void Element::willAttachRenderers()
3912{
3913 ASSERT(hasCustomStyleResolveCallbacks());
3914}
3915
3916void Element::didAttachRenderers()
3917{
3918 ASSERT(hasCustomStyleResolveCallbacks());
3919}
3920
3921void Element::willDetachRenderers()
3922{
3923 ASSERT(hasCustomStyleResolveCallbacks());
3924}
3925
3926void Element::didDetachRenderers()
3927{
3928 ASSERT(hasCustomStyleResolveCallbacks());
3929}
3930
3931Optional<ElementStyle> Element::resolveCustomStyle(const RenderStyle&, const RenderStyle*)
3932{
3933 ASSERT(hasCustomStyleResolveCallbacks());
3934 return WTF::nullopt;
3935}
3936
3937void Element::cloneAttributesFromElement(const Element& other)
3938{
3939 if (hasSyntheticAttrChildNodes())
3940 detachAllAttrNodesFromElement();
3941
3942 other.synchronizeAllAttributes();
3943 if (!other.m_elementData) {
3944 m_elementData = nullptr;
3945 return;
3946 }
3947
3948 // We can't update window and document's named item maps since the presence of image and object elements depend on other attributes and children.
3949 // Fortunately, those named item maps are only updated when this element is in the document, which should never be the case.
3950 ASSERT(!isConnected());
3951
3952 const AtomString& oldID = getIdAttribute();
3953 const AtomString& newID = other.getIdAttribute();
3954
3955 if (!oldID.isNull() || !newID.isNull())
3956 updateId(oldID, newID, NotifyObservers::No); // Will notify observers after the attribute is actually changed.
3957
3958 const AtomString& oldName = getNameAttribute();
3959 const AtomString& newName = other.getNameAttribute();
3960
3961 if (!oldName.isNull() || !newName.isNull())
3962 updateName(oldName, newName);
3963
3964 // If 'other' has a mutable ElementData, convert it to an immutable one so we can share it between both elements.
3965 // We can only do this if there is no CSSOM wrapper for other's inline style, and there are no presentation attributes.
3966 if (is<UniqueElementData>(*other.m_elementData)
3967 && !other.m_elementData->presentationAttributeStyle()
3968 && (!other.m_elementData->inlineStyle() || !other.m_elementData->inlineStyle()->hasCSSOMWrapper()))
3969 const_cast<Element&>(other).m_elementData = downcast<UniqueElementData>(*other.m_elementData).makeShareableCopy();
3970
3971 if (!other.m_elementData->isUnique())
3972 m_elementData = other.m_elementData;
3973 else
3974 m_elementData = other.m_elementData->makeUniqueCopy();
3975
3976 for (const Attribute& attribute : attributesIterator())
3977 attributeChanged(attribute.name(), nullAtom(), attribute.value(), ModifiedByCloning);
3978}
3979
3980void Element::cloneDataFromElement(const Element& other)
3981{
3982 cloneAttributesFromElement(other);
3983 copyNonAttributePropertiesFromElement(other);
3984}
3985
3986void Element::createUniqueElementData()
3987{
3988 if (!m_elementData)
3989 m_elementData = UniqueElementData::create();
3990 else
3991 m_elementData = downcast<ShareableElementData>(*m_elementData).makeUniqueCopy();
3992}
3993
3994bool Element::hasPendingResources() const
3995{
3996 return hasRareData() && elementRareData()->hasPendingResources();
3997}
3998
3999void Element::setHasPendingResources()
4000{
4001 ensureElementRareData().setHasPendingResources(true);
4002}
4003
4004void Element::clearHasPendingResources()
4005{
4006 if (!hasRareData())
4007 return;
4008 elementRareData()->setHasPendingResources(false);
4009}
4010
4011bool Element::hasCSSAnimation() const
4012{
4013 return hasRareData() && elementRareData()->hasCSSAnimation();
4014}
4015
4016void Element::setHasCSSAnimation()
4017{
4018 ensureElementRareData().setHasCSSAnimation(true);
4019}
4020
4021void Element::clearHasCSSAnimation()
4022{
4023 if (!hasRareData())
4024 return;
4025 elementRareData()->setHasCSSAnimation(false);
4026}
4027
4028bool Element::canContainRangeEndPoint() const
4029{
4030 return !equalLettersIgnoringASCIICase(attributeWithoutSynchronization(roleAttr), "img");
4031}
4032
4033String Element::completeURLsInAttributeValue(const URL& base, const Attribute& attribute) const
4034{
4035 return URL(base, attribute.value()).string();
4036}
4037
4038ExceptionOr<Node*> Element::insertAdjacent(const String& where, Ref<Node>&& newChild)
4039{
4040 // In Internet Explorer if the element has no parent and where is "beforeBegin" or "afterEnd",
4041 // a document fragment is created and the elements appended in the correct order. This document
4042 // fragment isn't returned anywhere.
4043 //
4044 // This is impossible for us to implement as the DOM tree does not allow for such structures,
4045 // Opera also appears to disallow such usage.
4046
4047 if (equalLettersIgnoringASCIICase(where, "beforebegin")) {
4048 auto* parent = this->parentNode();
4049 if (!parent)
4050 return nullptr;
4051 auto result = parent->insertBefore(newChild, this);
4052 if (result.hasException())
4053 return result.releaseException();
4054 return newChild.ptr();
4055 }
4056
4057 if (equalLettersIgnoringASCIICase(where, "afterbegin")) {
4058 auto result = insertBefore(newChild, firstChild());
4059 if (result.hasException())
4060 return result.releaseException();
4061 return newChild.ptr();
4062 }
4063
4064 if (equalLettersIgnoringASCIICase(where, "beforeend")) {
4065 auto result = appendChild(newChild);
4066 if (result.hasException())
4067 return result.releaseException();
4068 return newChild.ptr();
4069 }
4070
4071 if (equalLettersIgnoringASCIICase(where, "afterend")) {
4072 auto* parent = this->parentNode();
4073 if (!parent)
4074 return nullptr;
4075 auto result = parent->insertBefore(newChild, nextSibling());
4076 if (result.hasException())
4077 return result.releaseException();
4078 return newChild.ptr();
4079 }
4080
4081 return Exception { SyntaxError };
4082}
4083
4084ExceptionOr<Element*> Element::insertAdjacentElement(const String& where, Element& newChild)
4085{
4086 auto result = insertAdjacent(where, newChild);
4087 if (result.hasException())
4088 return result.releaseException();
4089 return downcast<Element>(result.releaseReturnValue());
4090}
4091
4092// Step 1 of https://w3c.github.io/DOM-Parsing/#dom-element-insertadjacenthtml.
4093static ExceptionOr<ContainerNode&> contextNodeForInsertion(const String& where, Element& element)
4094{
4095 if (equalLettersIgnoringASCIICase(where, "beforebegin") || equalLettersIgnoringASCIICase(where, "afterend")) {
4096 auto* parent = element.parentNode();
4097 if (!parent || is<Document>(*parent))
4098 return Exception { NoModificationAllowedError };
4099 return *parent;
4100 }
4101 if (equalLettersIgnoringASCIICase(where, "afterbegin") || equalLettersIgnoringASCIICase(where, "beforeend"))
4102 return element;
4103 return Exception { SyntaxError };
4104}
4105
4106// Step 2 of https://w3c.github.io/DOM-Parsing/#dom-element-insertadjacenthtml.
4107static ExceptionOr<Ref<Element>> contextElementForInsertion(const String& where, Element& element)
4108{
4109 auto contextNodeResult = contextNodeForInsertion(where, element);
4110 if (contextNodeResult.hasException())
4111 return contextNodeResult.releaseException();
4112 auto& contextNode = contextNodeResult.releaseReturnValue();
4113 if (!is<Element>(contextNode) || (contextNode.document().isHTMLDocument() && is<HTMLHtmlElement>(contextNode)))
4114 return Ref<Element> { HTMLBodyElement::create(contextNode.document()) };
4115 return Ref<Element> { downcast<Element>(contextNode) };
4116}
4117
4118// https://w3c.github.io/DOM-Parsing/#dom-element-insertadjacenthtml
4119ExceptionOr<void> Element::insertAdjacentHTML(const String& where, const String& markup, NodeVector* addedNodes)
4120{
4121 // Steps 1 and 2.
4122 auto contextElement = contextElementForInsertion(where, *this);
4123 if (contextElement.hasException())
4124 return contextElement.releaseException();
4125 // Step 3.
4126 auto fragment = createFragmentForInnerOuterHTML(contextElement.releaseReturnValue(), markup, AllowScriptingContent);
4127 if (fragment.hasException())
4128 return fragment.releaseException();
4129
4130 if (UNLIKELY(addedNodes)) {
4131 // Must be called before insertAdjacent, as otherwise the children of fragment will be moved
4132 // to their new parent and will be harder to keep track of.
4133 *addedNodes = collectChildNodes(fragment.returnValue());
4134 }
4135
4136 // Step 4.
4137 auto result = insertAdjacent(where, fragment.releaseReturnValue());
4138 if (result.hasException())
4139 return result.releaseException();
4140 return { };
4141}
4142
4143ExceptionOr<void> Element::insertAdjacentHTML(const String& where, const String& markup)
4144{
4145 return insertAdjacentHTML(where, markup, nullptr);
4146}
4147
4148ExceptionOr<void> Element::insertAdjacentText(const String& where, const String& text)
4149{
4150 auto result = insertAdjacent(where, document().createTextNode(text));
4151 if (result.hasException())
4152 return result.releaseException();
4153 return { };
4154}
4155
4156Element* Element::findAnchorElementForLink(String& outAnchorName)
4157{
4158 if (!isLink())
4159 return nullptr;
4160
4161 const AtomString& href = attributeWithoutSynchronization(HTMLNames::hrefAttr);
4162 if (href.isNull())
4163 return nullptr;
4164
4165 Document& document = this->document();
4166 URL url = document.completeURL(href);
4167 if (!url.isValid())
4168 return nullptr;
4169
4170 if (url.hasFragmentIdentifier() && equalIgnoringFragmentIdentifier(url, document.baseURL())) {
4171 outAnchorName = url.fragmentIdentifier();
4172 return document.findAnchor(outAnchorName);
4173 }
4174
4175 return nullptr;
4176}
4177
4178ExceptionOr<Ref<WebAnimation>> Element::animate(JSC::ExecState& state, JSC::Strong<JSC::JSObject>&& keyframes, Optional<Variant<double, KeyframeAnimationOptions>>&& options)
4179{
4180 String id = "";
4181 Optional<Variant<double, KeyframeEffectOptions>> keyframeEffectOptions;
4182 if (options) {
4183 auto optionsValue = options.value();
4184 Variant<double, KeyframeEffectOptions> keyframeEffectOptionsVariant;
4185 if (WTF::holds_alternative<double>(optionsValue))
4186 keyframeEffectOptionsVariant = WTF::get<double>(optionsValue);
4187 else {
4188 auto keyframeEffectOptions = WTF::get<KeyframeAnimationOptions>(optionsValue);
4189 id = keyframeEffectOptions.id;
4190 keyframeEffectOptionsVariant = WTFMove(keyframeEffectOptions);
4191 }
4192 keyframeEffectOptions = keyframeEffectOptionsVariant;
4193 }
4194
4195 auto keyframeEffectResult = KeyframeEffect::create(state, this, WTFMove(keyframes), WTFMove(keyframeEffectOptions));
4196 if (keyframeEffectResult.hasException())
4197 return keyframeEffectResult.releaseException();
4198
4199 auto animation = WebAnimation::create(document(), &keyframeEffectResult.returnValue().get());
4200 animation->setId(id);
4201
4202 auto animationPlayResult = animation->play();
4203 if (animationPlayResult.hasException())
4204 return animationPlayResult.releaseException();
4205
4206 return animation;
4207}
4208
4209Vector<RefPtr<WebAnimation>> Element::getAnimations()
4210{
4211 // FIXME: Filter and order the list as specified (webkit.org/b/179535).
4212
4213 // For the list of animations to be current, we need to account for any pending CSS changes,
4214 // such as updates to CSS Animations and CSS Transitions.
4215 // FIXME: We might be able to use ComputedStyleExtractor which is more optimized.
4216 document().updateStyleIfNeeded();
4217
4218 Vector<RefPtr<WebAnimation>> animations;
4219 if (auto timeline = document().existingTimeline()) {
4220 for (auto& animation : timeline->animationsForElement(*this, AnimationTimeline::Ordering::Sorted)) {
4221 if (animation->isRelevant())
4222 animations.append(animation);
4223 }
4224 }
4225 return animations;
4226}
4227
4228ElementIdentifier Element::createElementIdentifier()
4229{
4230 auto& rareData = ensureElementRareData();
4231 ASSERT(!rareData.hasElementIdentifier());
4232
4233 rareData.setHasElementIdentifier(true);
4234 return ElementIdentifier::generate();
4235}
4236
4237#if ENABLE(CSS_TYPED_OM)
4238StylePropertyMap* Element::attributeStyleMap()
4239{
4240 if (!hasRareData())
4241 return nullptr;
4242 return elementRareData()->attributeStyleMap();
4243}
4244
4245void Element::setAttributeStyleMap(Ref<StylePropertyMap>&& map)
4246{
4247 ensureElementRareData().setAttributeStyleMap(WTFMove(map));
4248}
4249#endif
4250
4251#if ENABLE(POINTER_EVENTS)
4252OptionSet<TouchAction> Element::computedTouchActions() const
4253{
4254 if (auto* style = renderOrDisplayContentsStyle())
4255 return style->effectiveTouchActions();
4256
4257 return TouchAction::Auto;
4258}
4259
4260#if ENABLE(OVERFLOW_SCROLLING_TOUCH)
4261ScrollingNodeID Element::nearestScrollingNodeIDUsingTouchOverflowScrolling() const
4262{
4263 if (!renderer())
4264 return 0;
4265
4266 // We are not interested in the root, so check that we also have a valid parent.
4267 for (auto* layer = renderer()->enclosingLayer(); layer && layer->parent(); layer = layer->parent()) {
4268 if (layer->isComposited()) {
4269 if (auto scrollingNodeID = layer->backing()->scrollingNodeIDForRole(ScrollCoordinationRole::Scrolling))
4270 return scrollingNodeID;
4271 }
4272 }
4273
4274 return 0;
4275}
4276#endif
4277#endif
4278
4279} // namespace WebCore
4280