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-2016 Apple Inc. All rights reserved.
8 * (C) 2007 Eric Seidel (eric@webkit.org)
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.
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.
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.
29 #include "AXObjectCache.h"
31 #include "AttributeChangeInvalidation.h"
32 #include "CSSParser.h"
34 #include "ChromeClient.h"
35 #include "ClassChangeInvalidation.h"
36 #include "ClientRect.h"
37 #include "ClientRectList.h"
38 #include "ComposedTreeAncestorIterator.h"
39 #include "ContainerNodeAlgorithms.h"
40 #include "CustomElementReactionQueue.h"
41 #include "CustomElementRegistry.h"
42 #include "DOMTokenList.h"
43 #include "DocumentAnimation.h"
44 #include "DocumentSharedObjectPool.h"
45 #include "ElementIterator.h"
46 #include "ElementRareData.h"
47 #include "EventDispatcher.h"
48 #include "EventHandler.h"
49 #include "EventNames.h"
50 #include "FlowThreadController.h"
51 #include "FocusController.h"
52 #include "FocusEvent.h"
53 #include "FrameSelection.h"
54 #include "FrameView.h"
55 #include "HTMLBodyElement.h"
56 #include "HTMLCanvasElement.h"
57 #include "HTMLCollection.h"
58 #include "HTMLDocument.h"
59 #include "HTMLHtmlElement.h"
60 #include "HTMLLabelElement.h"
61 #include "HTMLNameCollection.h"
62 #include "HTMLObjectElement.h"
63 #include "HTMLParserIdioms.h"
64 #include "HTMLTemplateElement.h"
65 #include "IdChangeInvalidation.h"
66 #include "IdTargetObserverRegistry.h"
67 #include "InspectorInstrumentation.h"
68 #include "JSLazyEventListener.h"
69 #include "KeyboardEvent.h"
70 #include "KeyframeEffect.h"
71 #include "MainFrame.h"
72 #include "MutationObserverInterestGroup.h"
73 #include "MutationRecord.h"
74 #include "NoEventDispatchAssertion.h"
75 #include "NodeRenderStyle.h"
76 #include "PlatformWheelEvent.h"
77 #include "PointerLockController.h"
78 #include "RenderFlowThread.h"
79 #include "RenderLayer.h"
80 #include "RenderNamedFlowFragment.h"
81 #include "RenderRegion.h"
82 #include "RenderTheme.h"
83 #include "RenderTreeUpdater.h"
84 #include "RenderView.h"
85 #include "RenderWidget.h"
86 #include "SVGDocumentExtensions.h"
87 #include "SVGElement.h"
89 #include "SVGSVGElement.h"
90 #include "ScrollLatchingState.h"
91 #include "SelectorQuery.h"
93 #include "SimulatedClick.h"
94 #include "SlotAssignment.h"
95 #include "StyleProperties.h"
96 #include "StyleResolver.h"
97 #include "StyleScope.h"
98 #include "StyleTreeResolver.h"
99 #include "TextIterator.h"
100 #include "VoidCallback.h"
101 #include "WheelEvent.h"
102 #include "XLinkNames.h"
103 #include "XMLNSNames.h"
104 #include "XMLNames.h"
105 #include "htmlediting.h"
107 #include <wtf/CurrentTime.h>
108 #include <wtf/NeverDestroyed.h>
109 #include <wtf/text/CString.h>
113 using namespace HTMLNames;
114 using namespace XMLNames;
116 static HashMap<Element*, Vector<RefPtr<Attr>>>& attrNodeListMap()
118 static NeverDestroyed<HashMap<Element*, Vector<RefPtr<Attr>>>> map;
122 static Vector<RefPtr<Attr>>* attrNodeListForElement(Element& element)
124 if (!element.hasSyntheticAttrChildNodes())
126 ASSERT(attrNodeListMap().contains(&element));
127 return &attrNodeListMap().find(&element)->value;
130 static Vector<RefPtr<Attr>>& ensureAttrNodeListForElement(Element& element)
132 if (element.hasSyntheticAttrChildNodes()) {
133 ASSERT(attrNodeListMap().contains(&element));
134 return attrNodeListMap().find(&element)->value;
136 ASSERT(!attrNodeListMap().contains(&element));
137 element.setHasSyntheticAttrChildNodes(true);
138 return attrNodeListMap().add(&element, Vector<RefPtr<Attr>>()).iterator->value;
141 static void removeAttrNodeListForElement(Element& element)
143 ASSERT(element.hasSyntheticAttrChildNodes());
144 ASSERT(attrNodeListMap().contains(&element));
145 attrNodeListMap().remove(&element);
146 element.setHasSyntheticAttrChildNodes(false);
149 static Attr* findAttrNodeInList(Vector<RefPtr<Attr>>& attrNodeList, const QualifiedName& name)
151 for (auto& node : attrNodeList) {
152 if (node->qualifiedName().matches(name))
158 static Attr* findAttrNodeInList(Vector<RefPtr<Attr>>& attrNodeList, const AtomicString& localName, bool shouldIgnoreAttributeCase)
160 const AtomicString& caseAdjustedName = shouldIgnoreAttributeCase ? localName.convertToASCIILowercase() : localName;
161 for (auto& node : attrNodeList) {
162 if (node->qualifiedName().localName() == caseAdjustedName)
168 Ref<Element> Element::create(const QualifiedName& tagName, Document& document)
170 return adoptRef(*new Element(tagName, document, CreateElement));
173 Element::Element(const QualifiedName& tagName, Document& document, ConstructionType type)
174 : ContainerNode(document, type)
182 if (document().hasLivingRenderTree()) {
183 // When the document is not destroyed, an element that was part of a named flow
184 // content nodes should have been removed from the content nodes collection
185 // and the isNamedFlowContentElement flag reset.
186 ASSERT_WITH_SECURITY_IMPLICATION(!isNamedFlowContentElement());
190 ASSERT(!beforePseudoElement());
191 ASSERT(!afterPseudoElement());
195 if (hasSyntheticAttrChildNodes())
196 detachAllAttrNodesFromElement();
198 if (hasPendingResources()) {
199 document().accessSVGExtensions().removeElementFromPendingResources(this);
200 ASSERT(!hasPendingResources());
204 inline ElementRareData* Element::elementRareData() const
206 ASSERT_WITH_SECURITY_IMPLICATION(hasRareData());
207 return static_cast<ElementRareData*>(rareData());
210 inline ElementRareData& Element::ensureElementRareData()
212 return static_cast<ElementRareData&>(ensureRareData());
215 void Element::clearTabIndexExplicitlyIfNeeded()
218 elementRareData()->clearTabIndexExplicitly();
221 void Element::setTabIndexExplicitly(int tabIndex)
223 ensureElementRareData().setTabIndexExplicitly(tabIndex);
226 bool Element::tabIndexSetExplicitly() const
228 return hasRareData() && elementRareData()->tabIndexSetExplicitly();
231 bool Element::supportsFocus() const
233 return tabIndexSetExplicitly();
236 Element* Element::focusDelegate()
241 int Element::tabIndex() const
243 return hasRareData() ? elementRareData()->tabIndex() : 0;
246 void Element::setTabIndex(int value)
248 setIntegralAttribute(tabindexAttr, value);
251 bool Element::isKeyboardFocusable(KeyboardEvent&) const
253 return isFocusable() && tabIndex() >= 0;
256 bool Element::isMouseFocusable() const
258 return isFocusable();
261 bool Element::shouldUseInputMethod()
263 return computeEditability(UserSelectAllIsAlwaysNonEditable, ShouldUpdateStyle::Update) != Editability::ReadOnly;
266 static bool isForceEvent(const PlatformMouseEvent& platformEvent)
268 return platformEvent.type() == PlatformEvent::MouseForceChanged || platformEvent.type() == PlatformEvent::MouseForceDown || platformEvent.type() == PlatformEvent::MouseForceUp;
271 bool Element::dispatchMouseEvent(const PlatformMouseEvent& platformEvent, const AtomicString& eventType, int detail, Element* relatedTarget)
273 if (isDisabledFormControl())
276 if (isForceEvent(platformEvent) && !document().hasListenerTypeForEventType(platformEvent.type()))
279 Ref<MouseEvent> mouseEvent = MouseEvent::create(eventType, document().defaultView(), platformEvent, detail, relatedTarget);
281 if (mouseEvent->type().isEmpty())
282 return true; // Shouldn't happen.
284 ASSERT(!mouseEvent->target() || mouseEvent->target() != relatedTarget);
285 bool didNotSwallowEvent = dispatchEvent(mouseEvent) && !mouseEvent->defaultHandled();
287 if (mouseEvent->type() == eventNames().clickEvent && mouseEvent->detail() == 2) {
288 // Special case: If it's a double click event, we also send the dblclick event. This is not part
289 // of the DOM specs, but is used for compatibility with the ondblclick="" attribute. This is treated
290 // as a separate event in other DOM-compliant browsers like Firefox, and so we do the same.
291 Ref<MouseEvent> doubleClickEvent = MouseEvent::create(eventNames().dblclickEvent,
292 mouseEvent->bubbles(), mouseEvent->cancelable(), mouseEvent->view(), mouseEvent->detail(),
293 mouseEvent->screenX(), mouseEvent->screenY(), mouseEvent->clientX(), mouseEvent->clientY(),
294 mouseEvent->ctrlKey(), mouseEvent->altKey(), mouseEvent->shiftKey(), mouseEvent->metaKey(),
295 mouseEvent->button(), mouseEvent->syntheticClickType(), relatedTarget);
297 if (mouseEvent->defaultHandled())
298 doubleClickEvent->setDefaultHandled();
300 dispatchEvent(doubleClickEvent);
301 if (doubleClickEvent->defaultHandled() || doubleClickEvent->defaultPrevented())
304 return didNotSwallowEvent;
308 bool Element::dispatchWheelEvent(const PlatformWheelEvent& event)
310 Ref<WheelEvent> wheelEvent = WheelEvent::create(event, document().defaultView());
312 // Events with no deltas are important because they convey platform information about scroll gestures
313 // and momentum beginning or ending. However, those events should not be sent to the DOM since some
314 // websites will break. They need to be dispatched because dispatching them will call into the default
315 // event handler, and our platform code will correctly handle the phase changes. Calling stopPropogation()
316 // will prevent the event from being sent to the DOM, but will still call the default event handler.
317 if (!event.deltaX() && !event.deltaY())
318 wheelEvent->stopPropagation();
320 return EventDispatcher::dispatchEvent(*this, wheelEvent) && !wheelEvent->defaultHandled();
323 bool Element::dispatchKeyEvent(const PlatformKeyboardEvent& platformEvent)
325 Ref<KeyboardEvent> event = KeyboardEvent::create(platformEvent, document().defaultView());
326 if (Frame* frame = document().frame()) {
327 if (frame->eventHandler().accessibilityPreventsEventPropogation(event))
328 event->stopPropagation();
330 return EventDispatcher::dispatchEvent(*this, event) && !event->defaultHandled();
333 void Element::dispatchSimulatedClick(Event* underlyingEvent, SimulatedClickMouseEventOptions eventOptions, SimulatedClickVisualOptions visualOptions)
335 simulateClick(*this, underlyingEvent, eventOptions, visualOptions, SimulatedClickSource::UserAgent);
338 Ref<Node> Element::cloneNodeInternal(Document& targetDocument, CloningOperation type)
341 case CloningOperation::OnlySelf:
342 case CloningOperation::SelfWithTemplateContent:
343 return cloneElementWithoutChildren(targetDocument);
344 case CloningOperation::Everything:
347 return cloneElementWithChildren(targetDocument);
350 Ref<Element> Element::cloneElementWithChildren(Document& targetDocument)
352 Ref<Element> clone = cloneElementWithoutChildren(targetDocument);
353 cloneChildNodes(clone);
357 Ref<Element> Element::cloneElementWithoutChildren(Document& targetDocument)
359 Ref<Element> clone = cloneElementWithoutAttributesAndChildren(targetDocument);
360 // This will catch HTML elements in the wrong namespace that are not correctly copied.
361 // This is a sanity check as HTML overloads some of the DOM methods.
362 ASSERT(isHTMLElement() == clone->isHTMLElement());
364 clone->cloneDataFromElement(*this);
368 Ref<Element> Element::cloneElementWithoutAttributesAndChildren(Document& targetDocument)
370 return targetDocument.createElement(tagQName(), false);
373 Ref<Attr> Element::detachAttribute(unsigned index)
375 ASSERT(elementData());
377 const Attribute& attribute = elementData()->attributeAt(index);
379 RefPtr<Attr> attrNode = attrIfExists(attribute.name());
381 detachAttrNodeFromElementWithValue(attrNode.get(), attribute.value());
383 attrNode = Attr::create(document(), attribute.name(), attribute.value());
385 removeAttributeInternal(index, NotInSynchronizationOfLazyAttribute);
386 return attrNode.releaseNonNull();
389 bool Element::removeAttribute(const QualifiedName& name)
394 unsigned index = elementData()->findAttributeIndexByName(name);
395 if (index == ElementData::attributeNotFound)
398 removeAttributeInternal(index, NotInSynchronizationOfLazyAttribute);
402 void Element::setBooleanAttribute(const QualifiedName& name, bool value)
405 setAttribute(name, emptyAtom);
407 removeAttribute(name);
410 NamedNodeMap& Element::attributes() const
412 ElementRareData& rareData = const_cast<Element*>(this)->ensureElementRareData();
413 if (NamedNodeMap* attributeMap = rareData.attributeMap())
414 return *attributeMap;
416 rareData.setAttributeMap(std::make_unique<NamedNodeMap>(const_cast<Element&>(*this)));
417 return *rareData.attributeMap();
420 Node::NodeType Element::nodeType() const
425 bool Element::hasAttribute(const QualifiedName& name) const
427 return hasAttributeNS(name.namespaceURI(), name.localName());
430 void Element::synchronizeAllAttributes() const
434 if (elementData()->styleAttributeIsDirty()) {
435 ASSERT(isStyledElement());
436 static_cast<const StyledElement*>(this)->synchronizeStyleAttributeInternal();
439 if (elementData()->animatedSVGAttributesAreDirty()) {
440 ASSERT(isSVGElement());
441 downcast<SVGElement>(*this).synchronizeAnimatedSVGAttribute(anyQName());
445 ALWAYS_INLINE void Element::synchronizeAttribute(const QualifiedName& name) const
449 if (UNLIKELY(name == styleAttr && elementData()->styleAttributeIsDirty())) {
450 ASSERT_WITH_SECURITY_IMPLICATION(isStyledElement());
451 static_cast<const StyledElement*>(this)->synchronizeStyleAttributeInternal();
455 if (UNLIKELY(elementData()->animatedSVGAttributesAreDirty())) {
456 ASSERT(isSVGElement());
457 downcast<SVGElement>(*this).synchronizeAnimatedSVGAttribute(name);
461 static ALWAYS_INLINE bool isStyleAttribute(const Element& element, const AtomicString& attributeLocalName)
463 if (shouldIgnoreAttributeCase(element))
464 return equalLettersIgnoringASCIICase(attributeLocalName, "style");
465 return attributeLocalName == styleAttr.localName();
468 ALWAYS_INLINE void Element::synchronizeAttribute(const AtomicString& localName) const
470 // This version of synchronizeAttribute() is streamlined for the case where you don't have a full QualifiedName,
471 // e.g when called from DOM API.
474 if (elementData()->styleAttributeIsDirty() && isStyleAttribute(*this, localName)) {
475 ASSERT_WITH_SECURITY_IMPLICATION(isStyledElement());
476 static_cast<const StyledElement*>(this)->synchronizeStyleAttributeInternal();
479 if (elementData()->animatedSVGAttributesAreDirty()) {
480 // We're not passing a namespace argument on purpose. SVGNames::*Attr are defined w/o namespaces as well.
481 ASSERT_WITH_SECURITY_IMPLICATION(isSVGElement());
482 downcast<SVGElement>(*this).synchronizeAnimatedSVGAttribute(QualifiedName(nullAtom, localName, nullAtom));
486 const AtomicString& Element::getAttribute(const QualifiedName& name) const
490 synchronizeAttribute(name);
491 if (const Attribute* attribute = findAttributeByName(name))
492 return attribute->value();
496 Vector<String> Element::getAttributeNames() const
498 Vector<String> attributesVector;
499 if (!hasAttributes())
500 return attributesVector;
502 auto attributes = attributesIterator();
503 attributesVector.reserveInitialCapacity(attributes.attributeCount());
504 for (auto& attribute : attributes)
505 attributesVector.uncheckedAppend(attribute.name().toString());
506 return attributesVector;
509 bool Element::isFocusable() const
511 if (!isConnected() || !supportsFocus())
515 // If the node is in a display:none tree it might say it needs style recalc but
516 // the whole document is actually up to date.
517 ASSERT(!needsStyleRecalc() || !document().childNeedsStyleRecalc());
519 // Elements in canvas fallback content are not rendered, but they are allowed to be
520 // focusable as long as their canvas is displayed and visible.
521 if (auto* canvas = ancestorsOfType<HTMLCanvasElement>(*this).first())
522 return canvas->renderer() && canvas->renderer()->style().visibility() == VISIBLE;
525 // FIXME: Even if we are not visible, we might have a child that is visible.
526 // Hyatt wants to fix that some day with a "has visible content" flag or the like.
527 if (!renderer() || renderer()->style().visibility() != VISIBLE)
533 bool Element::isUserActionElementInActiveChain() const
535 ASSERT(isUserActionElement());
536 return document().userActionElements().isInActiveChain(this);
539 bool Element::isUserActionElementActive() const
541 ASSERT(isUserActionElement());
542 return document().userActionElements().isActive(this);
545 bool Element::isUserActionElementFocused() const
547 ASSERT(isUserActionElement());
548 return document().userActionElements().isFocused(this);
551 bool Element::isUserActionElementHovered() const
553 ASSERT(isUserActionElement());
554 return document().userActionElements().isHovered(this);
557 void Element::setActive(bool flag, bool pause)
559 if (flag == active())
562 document().userActionElements().setActive(this, flag);
564 const RenderStyle* renderStyle = this->renderStyle();
565 bool reactsToPress = (renderStyle && renderStyle->affectedByActive()) || styleAffectedByActive();
567 invalidateStyleForSubtree();
572 if (renderer()->style().hasAppearance() && renderer()->theme().stateChanged(*renderer(), ControlStates::PressedState))
573 reactsToPress = true;
575 // The rest of this function implements a feature that only works if the
576 // platform supports immediate invalidations on the ChromeClient, so bail if
577 // that isn't supported.
578 if (!document().page()->chrome().client().supportsImmediateInvalidation())
581 if (reactsToPress && pause) {
582 // The delay here is subtle. It relies on an assumption, namely that the amount of time it takes
583 // to repaint the "down" state of the control is about the same time as it would take to repaint the
584 // "up" state. Once you assume this, you can just delay for 100ms - that time (assuming that after you
585 // leave this method, it will be about that long before the flush of the up state happens again).
586 #ifdef HAVE_FUNC_USLEEP
587 double startTime = monotonicallyIncreasingTime();
590 document().updateStyleIfNeeded();
592 // Do an immediate repaint.
594 renderer()->repaint();
596 // FIXME: Come up with a less ridiculous way of doing this.
597 #ifdef HAVE_FUNC_USLEEP
598 // Now pause for a small amount of time (1/10th of a second from before we repainted in the pressed state)
599 double remainingTime = 0.1 - (monotonicallyIncreasingTime() - startTime);
600 if (remainingTime > 0)
601 usleep(static_cast<useconds_t>(remainingTime * 1000000.0));
606 void Element::setFocus(bool flag)
608 if (flag == focused())
611 document().userActionElements().setFocused(this, flag);
612 invalidateStyleForSubtree();
614 for (Element* element = this; element; element = element->parentOrShadowHostElement())
615 element->setHasFocusWithin(flag);
618 void Element::setHovered(bool flag)
620 if (flag == hovered())
623 document().userActionElements().setHovered(this, flag);
626 // When setting hover to false, the style needs to be recalc'd even when
627 // there's no renderer (imagine setting display:none in the :hover class,
628 // if a nil renderer would prevent this element from recalculating its
629 // style, it would never go back to its normal style and remain
630 // stuck in its hovered style).
632 invalidateStyleForSubtree();
637 if (renderer()->style().affectedByHover() || childrenAffectedByHover())
638 invalidateStyleForSubtree();
640 if (renderer()->style().hasAppearance())
641 renderer()->theme().stateChanged(*renderer(), ControlStates::HoverState);
644 void Element::scrollIntoView(bool alignToTop)
646 document().updateLayoutIgnorePendingStylesheets();
652 LayoutRect absoluteBounds = renderer()->absoluteAnchorRect(&insideFixed);
653 // Align to the top / bottom and to the closest edge.
655 renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, absoluteBounds, insideFixed, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignTopAlways);
657 renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, absoluteBounds, insideFixed, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignBottomAlways);
660 void Element::scrollIntoViewIfNeeded(bool centerIfNeeded)
662 document().updateLayoutIgnorePendingStylesheets();
668 LayoutRect absoluteBounds = renderer()->absoluteAnchorRect(&insideFixed);
670 renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, absoluteBounds, insideFixed, ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded);
672 renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, absoluteBounds, insideFixed, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded);
675 void Element::scrollIntoViewIfNotVisible(bool centerIfNotVisible)
677 document().updateLayoutIgnorePendingStylesheets();
683 LayoutRect absoluteBounds = renderer()->absoluteAnchorRect(&insideFixed);
684 if (centerIfNotVisible)
685 renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, absoluteBounds, insideFixed, ScrollAlignment::alignCenterIfNotVisible, ScrollAlignment::alignCenterIfNotVisible);
687 renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, absoluteBounds, insideFixed, ScrollAlignment::alignToEdgeIfNotVisible, ScrollAlignment::alignToEdgeIfNotVisible);
690 void Element::scrollBy(const ScrollToOptions& options)
692 return scrollBy(options.left.value_or(0), options.top.value_or(0));
695 static inline double normalizeNonFiniteValue(double f)
697 return std::isfinite(f) ? f : 0;
700 void Element::scrollBy(double x, double y)
702 scrollTo(scrollLeft() + normalizeNonFiniteValue(x), scrollTop() + normalizeNonFiniteValue(y));
705 void Element::scrollTo(const ScrollToOptions& options)
707 // If the element is the root element and document is in quirks mode, terminate these steps.
708 // Note that WebKit always uses quirks mode document scrolling behavior. See Document::scrollingElement().
709 if (this == document().documentElement())
712 document().updateLayoutIgnorePendingStylesheets();
714 // If the element does not have any associated CSS layout box, the element has no associated scrolling box,
715 // or the element has no overflow, terminate these steps.
716 RenderBox* renderer = renderBox();
717 if (!renderer || !renderer->hasOverflowClip())
720 // Normalize non-finite values for left and top dictionary members of options, if present.
721 double x = options.left ? normalizeNonFiniteValue(options.left.value()) : adjustForAbsoluteZoom(renderer->scrollLeft(), *renderer);
722 double y = options.top ? normalizeNonFiniteValue(options.top.value()) : adjustForAbsoluteZoom(renderer->scrollTop(), *renderer);
724 renderer->setScrollLeft(clampToInteger(x * renderer->style().effectiveZoom()));
725 renderer->setScrollTop(clampToInteger(y * renderer->style().effectiveZoom()));
728 void Element::scrollTo(double x, double y)
733 void Element::scrollByUnits(int units, ScrollGranularity granularity)
735 document().updateLayoutIgnorePendingStylesheets();
737 auto* renderer = this->renderer();
741 if (!renderer->hasOverflowClip())
744 ScrollDirection direction = ScrollDown;
746 direction = ScrollUp;
749 Element* stopElement = this;
750 downcast<RenderBox>(*renderer).scroll(direction, granularity, units, &stopElement);
753 void Element::scrollByLines(int lines)
755 scrollByUnits(lines, ScrollByLine);
758 void Element::scrollByPages(int pages)
760 scrollByUnits(pages, ScrollByPage);
763 static double localZoomForRenderer(const RenderElement& renderer)
765 // FIXME: This does the wrong thing if two opposing zooms are in effect and canceled each
766 // other out, but the alternative is that we'd have to crawl up the whole render tree every
767 // time (or store an additional bit in the RenderStyle to indicate that a zoom was specified).
768 double zoomFactor = 1;
769 if (renderer.style().effectiveZoom() != 1) {
770 // Need to find the nearest enclosing RenderElement that set up
771 // a differing zoom, and then we divide our result by it to eliminate the zoom.
772 const RenderElement* prev = &renderer;
773 for (RenderElement* curr = prev->parent(); curr; curr = curr->parent()) {
774 if (curr->style().effectiveZoom() != prev->style().effectiveZoom()) {
775 zoomFactor = prev->style().zoom();
780 if (prev->isRenderView())
781 zoomFactor = prev->style().zoom();
786 static double adjustForLocalZoom(LayoutUnit value, const RenderElement& renderer, double& zoomFactor)
788 zoomFactor = localZoomForRenderer(renderer);
790 return value.toDouble();
791 return value.toDouble() / zoomFactor;
794 enum LegacyCSSOMElementMetricsRoundingStrategy { Round, Floor };
796 static bool subpixelMetricsEnabled(const Document& document)
798 return document.settings().subpixelCSSOMElementMetricsEnabled();
801 static double convertToNonSubpixelValueIfNeeded(double value, const Document& document, LegacyCSSOMElementMetricsRoundingStrategy roundStrategy = Round)
803 return subpixelMetricsEnabled(document) ? value : roundStrategy == Round ? round(value) : floor(value);
806 double Element::offsetLeft()
808 document().updateLayoutIgnorePendingStylesheets();
809 if (RenderBoxModelObject* renderer = renderBoxModelObject()) {
810 LayoutUnit offsetLeft = subpixelMetricsEnabled(renderer->document()) ? renderer->offsetLeft() : LayoutUnit(roundToInt(renderer->offsetLeft()));
811 double zoomFactor = 1;
812 double offsetLeftAdjustedWithZoom = adjustForLocalZoom(offsetLeft, *renderer, zoomFactor);
813 return convertToNonSubpixelValueIfNeeded(offsetLeftAdjustedWithZoom, renderer->document(), zoomFactor == 1 ? Floor : Round);
818 double Element::offsetTop()
820 document().updateLayoutIgnorePendingStylesheets();
821 if (RenderBoxModelObject* renderer = renderBoxModelObject()) {
822 LayoutUnit offsetTop = subpixelMetricsEnabled(renderer->document()) ? renderer->offsetTop() : LayoutUnit(roundToInt(renderer->offsetTop()));
823 double zoomFactor = 1;
824 double offsetTopAdjustedWithZoom = adjustForLocalZoom(offsetTop, *renderer, zoomFactor);
825 return convertToNonSubpixelValueIfNeeded(offsetTopAdjustedWithZoom, renderer->document(), zoomFactor == 1 ? Floor : Round);
830 double Element::offsetWidth()
832 document().updateLayoutIfDimensionsOutOfDate(*this, WidthDimensionsCheck);
833 if (RenderBoxModelObject* renderer = renderBoxModelObject()) {
834 LayoutUnit offsetWidth = subpixelMetricsEnabled(renderer->document()) ? renderer->offsetWidth() : LayoutUnit(roundToInt(renderer->offsetWidth()));
835 return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(offsetWidth, *renderer).toDouble(), renderer->document());
840 double Element::offsetHeight()
842 document().updateLayoutIfDimensionsOutOfDate(*this, HeightDimensionsCheck);
843 if (RenderBoxModelObject* renderer = renderBoxModelObject()) {
844 LayoutUnit offsetHeight = subpixelMetricsEnabled(renderer->document()) ? renderer->offsetHeight() : LayoutUnit(roundToInt(renderer->offsetHeight()));
845 return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(offsetHeight, *renderer).toDouble(), renderer->document());
850 Element* Element::bindingsOffsetParent()
852 Element* element = offsetParent();
853 if (!element || !element->isInShadowTree())
855 return element->containingShadowRoot()->mode() == ShadowRootMode::UserAgent ? nullptr : element;
858 Element* Element::offsetParent()
860 document().updateLayoutIgnorePendingStylesheets();
861 auto renderer = this->renderer();
864 auto offsetParent = renderer->offsetParent();
867 return offsetParent->element();
870 double Element::clientLeft()
872 document().updateLayoutIgnorePendingStylesheets();
874 if (auto* renderer = renderBox()) {
875 LayoutUnit clientLeft = subpixelMetricsEnabled(renderer->document()) ? renderer->clientLeft() : LayoutUnit(roundToInt(renderer->clientLeft()));
876 return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(clientLeft, *renderer).toDouble(), renderer->document());
881 double Element::clientTop()
883 document().updateLayoutIgnorePendingStylesheets();
885 if (auto* renderer = renderBox()) {
886 LayoutUnit clientTop = subpixelMetricsEnabled(renderer->document()) ? renderer->clientTop() : LayoutUnit(roundToInt(renderer->clientTop()));
887 return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(clientTop, *renderer).toDouble(), renderer->document());
892 double Element::clientWidth()
894 document().updateLayoutIfDimensionsOutOfDate(*this, WidthDimensionsCheck);
896 if (!document().hasLivingRenderTree())
899 RenderView& renderView = *document().renderView();
901 // When in strict mode, clientWidth for the document element should return the width of the containing frame.
902 // When in quirks mode, clientWidth for the body element should return the width of the containing frame.
903 bool inQuirksMode = document().inQuirksMode();
904 if ((!inQuirksMode && document().documentElement() == this) || (inQuirksMode && isHTMLElement() && document().bodyOrFrameset() == this))
905 return adjustForAbsoluteZoom(renderView.frameView().layoutWidth(), renderView);
907 if (RenderBox* renderer = renderBox()) {
908 LayoutUnit clientWidth = subpixelMetricsEnabled(renderer->document()) ? renderer->clientWidth() : LayoutUnit(roundToInt(renderer->clientWidth()));
909 return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(clientWidth, *renderer).toDouble(), renderer->document());
914 double Element::clientHeight()
916 document().updateLayoutIfDimensionsOutOfDate(*this, HeightDimensionsCheck);
917 if (!document().hasLivingRenderTree())
920 RenderView& renderView = *document().renderView();
922 // When in strict mode, clientHeight for the document element should return the height of the containing frame.
923 // When in quirks mode, clientHeight for the body element should return the height of the containing frame.
924 bool inQuirksMode = document().inQuirksMode();
925 if ((!inQuirksMode && document().documentElement() == this) || (inQuirksMode && isHTMLElement() && document().bodyOrFrameset() == this))
926 return adjustForAbsoluteZoom(renderView.frameView().layoutHeight(), renderView);
928 if (RenderBox* renderer = renderBox()) {
929 LayoutUnit clientHeight = subpixelMetricsEnabled(renderer->document()) ? renderer->clientHeight() : LayoutUnit(roundToInt(renderer->clientHeight()));
930 return convertToNonSubpixelValueIfNeeded(adjustLayoutUnitForAbsoluteZoom(clientHeight, *renderer).toDouble(), renderer->document());
935 int Element::scrollLeft()
937 document().updateLayoutIgnorePendingStylesheets();
939 if (auto* renderer = renderBox())
940 return adjustForAbsoluteZoom(renderer->scrollLeft(), *renderer);
944 int Element::scrollTop()
946 document().updateLayoutIgnorePendingStylesheets();
948 if (RenderBox* renderer = renderBox())
949 return adjustForAbsoluteZoom(renderer->scrollTop(), *renderer);
953 void Element::setScrollLeft(int newLeft)
955 document().updateLayoutIgnorePendingStylesheets();
957 if (auto* renderer = renderBox()) {
958 renderer->setScrollLeft(static_cast<int>(newLeft * renderer->style().effectiveZoom()));
959 if (auto* scrollableArea = renderer->layer())
960 scrollableArea->setScrolledProgrammatically(true);
964 void Element::setScrollTop(int newTop)
966 document().updateLayoutIgnorePendingStylesheets();
968 if (auto* renderer = renderBox()) {
969 renderer->setScrollTop(static_cast<int>(newTop * renderer->style().effectiveZoom()));
970 if (auto* scrollableArea = renderer->layer())
971 scrollableArea->setScrolledProgrammatically(true);
975 int Element::scrollWidth()
977 document().updateLayoutIfDimensionsOutOfDate(*this, WidthDimensionsCheck);
978 if (auto* renderer = renderBox())
979 return adjustForAbsoluteZoom(renderer->scrollWidth(), *renderer);
983 int Element::scrollHeight()
985 document().updateLayoutIfDimensionsOutOfDate(*this, HeightDimensionsCheck);
986 if (auto* renderer = renderBox())
987 return adjustForAbsoluteZoom(renderer->scrollHeight(), *renderer);
991 IntRect Element::boundsInRootViewSpace()
993 document().updateLayoutIgnorePendingStylesheets();
995 FrameView* view = document().view();
999 Vector<FloatQuad> quads;
1001 if (isSVGElement() && renderer()) {
1002 // Get the bounding rectangle from the SVG model.
1003 SVGElement& svgElement = downcast<SVGElement>(*this);
1004 FloatRect localRect;
1005 if (svgElement.getBoundingBox(localRect))
1006 quads.append(renderer()->localToAbsoluteQuad(localRect));
1008 // Get the bounding rectangle from the box model.
1009 if (renderBoxModelObject())
1010 renderBoxModelObject()->absoluteQuads(quads);
1013 if (quads.isEmpty())
1016 IntRect result = quads[0].enclosingBoundingBox();
1017 for (size_t i = 1; i < quads.size(); ++i)
1018 result.unite(quads[i].enclosingBoundingBox());
1020 result = view->contentsToRootView(result);
1024 static bool layoutOverflowRectContainsAllDescendants(const RenderBox& renderBox)
1026 if (renderBox.isRenderView())
1029 if (!renderBox.element())
1032 // If there are any position:fixed inside of us, game over.
1033 if (auto* viewPositionedObjects = renderBox.view().positionedObjects()) {
1034 for (auto* positionedBox : *viewPositionedObjects) {
1035 if (positionedBox == &renderBox)
1037 if (positionedBox->style().position() == FixedPosition && renderBox.element()->contains(positionedBox->element()))
1042 if (renderBox.canContainAbsolutelyPositionedObjects()) {
1043 // Our layout overflow will include all descendant positioned elements.
1047 // This renderer may have positioned descendants whose containing block is some ancestor.
1048 if (auto* containingBlock = renderBox.containingBlockForAbsolutePosition()) {
1049 if (auto* positionedObjects = containingBlock->positionedObjects()) {
1050 for (auto* positionedBox : *positionedObjects) {
1051 if (positionedBox == &renderBox)
1053 if (renderBox.element()->contains(positionedBox->element()))
1061 LayoutRect Element::absoluteEventBounds(bool& boundsIncludeAllDescendantElements, bool& includesFixedPositionElements)
1063 boundsIncludeAllDescendantElements = false;
1064 includesFixedPositionElements = false;
1067 return LayoutRect();
1070 if (isSVGElement()) {
1071 // Get the bounding rectangle from the SVG model.
1072 SVGElement& svgElement = downcast<SVGElement>(*this);
1073 FloatRect localRect;
1074 if (svgElement.getBoundingBox(localRect, SVGLocatable::DisallowStyleUpdate))
1075 result = LayoutRect(renderer()->localToAbsoluteQuad(localRect, UseTransforms, &includesFixedPositionElements).boundingBox());
1077 auto* renderer = this->renderer();
1078 if (is<RenderBox>(renderer)) {
1079 auto& box = downcast<RenderBox>(*renderer);
1081 bool computedBounds = false;
1083 if (RenderFlowThread* flowThread = box.flowThreadContainingBlock()) {
1084 bool wasFixed = false;
1085 Vector<FloatQuad> quads;
1086 FloatRect localRect(0, 0, box.width(), box.height());
1087 if (flowThread->absoluteQuadsForBox(quads, &wasFixed, &box, localRect.y(), localRect.maxY())) {
1088 FloatRect quadBounds = quads[0].boundingBox();
1089 for (size_t i = 1; i < quads.size(); ++i)
1090 quadBounds.unite(quads[i].boundingBox());
1092 result = LayoutRect(quadBounds);
1093 computedBounds = true;
1095 // Probably columns. Just return the bounds of the multicol block for now.
1096 // FIXME: this doesn't handle nested columns.
1097 RenderElement* multicolContainer = flowThread->parent();
1098 if (multicolContainer && is<RenderBox>(multicolContainer)) {
1099 auto overflowRect = downcast<RenderBox>(*multicolContainer).layoutOverflowRect();
1100 result = LayoutRect(multicolContainer->localToAbsoluteQuad(FloatRect(overflowRect), UseTransforms, &includesFixedPositionElements).boundingBox());
1101 computedBounds = true;
1106 if (!computedBounds) {
1107 LayoutRect overflowRect = box.layoutOverflowRect();
1108 result = LayoutRect(box.localToAbsoluteQuad(FloatRect(overflowRect), UseTransforms, &includesFixedPositionElements).boundingBox());
1109 boundsIncludeAllDescendantElements = layoutOverflowRectContainsAllDescendants(box);
1112 result = LayoutRect(renderer->absoluteBoundingBoxRect(true /* useTransforms */, &includesFixedPositionElements));
1118 LayoutRect Element::absoluteEventBoundsOfElementAndDescendants(bool& includesFixedPositionElements)
1120 bool boundsIncludeDescendants;
1121 LayoutRect result = absoluteEventBounds(boundsIncludeDescendants, includesFixedPositionElements);
1122 if (boundsIncludeDescendants)
1125 for (auto& child : childrenOfType<Element>(*this)) {
1126 bool includesFixedPosition = false;
1127 LayoutRect childBounds = child.absoluteEventBoundsOfElementAndDescendants(includesFixedPosition);
1128 includesFixedPositionElements |= includesFixedPosition;
1129 result.unite(childBounds);
1135 LayoutRect Element::absoluteEventHandlerBounds(bool& includesFixedPositionElements)
1137 // This is not web-exposed, so don't call the FOUC-inducing updateLayoutIgnorePendingStylesheets().
1138 FrameView* frameView = document().view();
1140 return LayoutRect();
1142 if (frameView->needsLayout())
1143 frameView->layout();
1145 return absoluteEventBoundsOfElementAndDescendants(includesFixedPositionElements);
1148 Ref<ClientRectList> Element::getClientRects()
1150 document().updateLayoutIgnorePendingStylesheets();
1152 RenderBoxModelObject* renderBoxModelObject = this->renderBoxModelObject();
1153 if (!renderBoxModelObject)
1154 return ClientRectList::create();
1156 // FIXME: Handle SVG elements.
1157 // FIXME: Handle table/inline-table with a caption.
1159 Vector<FloatQuad> quads;
1160 renderBoxModelObject->absoluteQuads(quads);
1161 document().adjustFloatQuadsForScrollAndAbsoluteZoomAndFrameScale(quads, renderBoxModelObject->style());
1162 return ClientRectList::create(quads);
1165 Ref<ClientRect> Element::getBoundingClientRect()
1167 document().updateLayoutIgnorePendingStylesheets();
1169 Vector<FloatQuad> quads;
1170 if (isSVGElement() && renderer() && !renderer()->isSVGRoot()) {
1171 // Get the bounding rectangle from the SVG model.
1172 SVGElement& svgElement = downcast<SVGElement>(*this);
1173 FloatRect localRect;
1174 if (svgElement.getBoundingBox(localRect))
1175 quads.append(renderer()->localToAbsoluteQuad(localRect));
1177 // Get the bounding rectangle from the box model.
1178 if (renderBoxModelObject())
1179 renderBoxModelObject()->absoluteQuads(quads);
1182 if (quads.isEmpty())
1183 return ClientRect::create();
1185 FloatRect result = quads[0].boundingBox();
1186 for (size_t i = 1; i < quads.size(); ++i)
1187 result.unite(quads[i].boundingBox());
1189 document().adjustFloatRectForScrollAndAbsoluteZoomAndFrameScale(result, renderer()->style());
1190 return ClientRect::create(result);
1193 IntRect Element::clientRect() const
1195 if (RenderObject* renderer = this->renderer())
1196 return document().view()->contentsToRootView(renderer->absoluteBoundingBoxRect());
1200 IntRect Element::screenRect() const
1202 if (RenderObject* renderer = this->renderer())
1203 return document().view()->contentsToScreen(renderer->absoluteBoundingBoxRect());
1207 const AtomicString& Element::getAttribute(const AtomicString& localName) const
1211 synchronizeAttribute(localName);
1212 if (const Attribute* attribute = elementData()->findAttributeByName(localName, shouldIgnoreAttributeCase(*this)))
1213 return attribute->value();
1217 const AtomicString& Element::getAttributeNS(const AtomicString& namespaceURI, const AtomicString& localName) const
1219 return getAttribute(QualifiedName(nullAtom, localName, namespaceURI));
1222 ExceptionOr<void> Element::setAttribute(const AtomicString& localName, const AtomicString& value)
1224 if (!Document::isValidName(localName))
1225 return Exception { INVALID_CHARACTER_ERR };
1227 synchronizeAttribute(localName);
1228 auto caseAdjustedLocalName = shouldIgnoreAttributeCase(*this) ? localName.convertToASCIILowercase() : localName;
1229 unsigned index = elementData() ? elementData()->findAttributeIndexByName(caseAdjustedLocalName, false) : ElementData::attributeNotFound;
1230 auto name = index != ElementData::attributeNotFound ? attributeAt(index).name() : QualifiedName { nullAtom, caseAdjustedLocalName, nullAtom };
1231 setAttributeInternal(index, name, value, NotInSynchronizationOfLazyAttribute);
1236 void Element::setAttribute(const QualifiedName& name, const AtomicString& value)
1238 synchronizeAttribute(name);
1239 unsigned index = elementData() ? elementData()->findAttributeIndexByName(name) : ElementData::attributeNotFound;
1240 setAttributeInternal(index, name, value, NotInSynchronizationOfLazyAttribute);
1243 void Element::setAttributeWithoutSynchronization(const QualifiedName& name, const AtomicString& value)
1245 unsigned index = elementData() ? elementData()->findAttributeIndexByName(name) : ElementData::attributeNotFound;
1246 setAttributeInternal(index, name, value, NotInSynchronizationOfLazyAttribute);
1249 void Element::setSynchronizedLazyAttribute(const QualifiedName& name, const AtomicString& value)
1251 unsigned index = elementData() ? elementData()->findAttributeIndexByName(name) : ElementData::attributeNotFound;
1252 setAttributeInternal(index, name, value, InSynchronizationOfLazyAttribute);
1255 inline void Element::setAttributeInternal(unsigned index, const QualifiedName& name, const AtomicString& newValue, SynchronizationOfLazyAttribute inSynchronizationOfLazyAttribute)
1257 if (newValue.isNull()) {
1258 if (index != ElementData::attributeNotFound)
1259 removeAttributeInternal(index, inSynchronizationOfLazyAttribute);
1263 if (index == ElementData::attributeNotFound) {
1264 addAttributeInternal(name, newValue, inSynchronizationOfLazyAttribute);
1268 if (inSynchronizationOfLazyAttribute) {
1269 ensureUniqueElementData().attributeAt(index).setValue(newValue);
1273 const Attribute& attribute = attributeAt(index);
1274 QualifiedName attributeName = attribute.name();
1275 AtomicString oldValue = attribute.value();
1277 willModifyAttribute(attributeName, oldValue, newValue);
1279 if (newValue != oldValue) {
1280 // If there is an Attr node hooked to this attribute, the Attr::setValue() call below
1281 // will write into the ElementData.
1282 // FIXME: Refactor this so it makes some sense.
1283 if (RefPtr<Attr> attrNode = attrIfExists(attributeName))
1284 attrNode->setValue(newValue);
1286 Style::AttributeChangeInvalidation styleInvalidation(*this, name, oldValue, newValue);
1287 ensureUniqueElementData().attributeAt(index).setValue(newValue);
1291 didModifyAttribute(attributeName, oldValue, newValue);
1294 static inline AtomicString makeIdForStyleResolution(const AtomicString& value, bool inQuirksMode)
1297 return value.convertToASCIILowercase();
1301 void Element::attributeChanged(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason)
1303 bool valueIsSameAsBefore = oldValue == newValue;
1305 if (!valueIsSameAsBefore) {
1306 if (name == HTMLNames::idAttr) {
1307 if (!oldValue.isEmpty())
1308 treeScope().idTargetObserverRegistry().notifyObservers(*oldValue.impl());
1309 if (!newValue.isEmpty())
1310 treeScope().idTargetObserverRegistry().notifyObservers(*newValue.impl());
1312 AtomicString oldId = elementData()->idForStyleResolution();
1313 AtomicString newId = makeIdForStyleResolution(newValue, document().inQuirksMode());
1314 if (newId != oldId) {
1315 Style::IdChangeInvalidation styleInvalidation(*this, oldId, newId);
1316 elementData()->setIdForStyleResolution(newId);
1318 } else if (name == classAttr)
1319 classAttributeChanged(newValue);
1320 else if (name == HTMLNames::nameAttr)
1321 elementData()->setHasNameAttribute(!newValue.isNull());
1322 else if (name == HTMLNames::pseudoAttr) {
1323 if (needsStyleInvalidation() && isInShadowTree())
1324 invalidateStyleForSubtree();
1326 else if (name == HTMLNames::slotAttr) {
1327 if (auto* parent = parentElement()) {
1328 if (auto* shadowRoot = parent->shadowRoot())
1329 shadowRoot->hostChildElementDidChangeSlotAttribute(*this, oldValue, newValue);
1334 parseAttribute(name, newValue);
1336 document().incDOMTreeVersion();
1338 if (UNLIKELY(isDefinedCustomElement()))
1339 CustomElementReactionQueue::enqueueAttributeChangedCallbackIfNeeded(*this, name, oldValue, newValue);
1341 if (valueIsSameAsBefore)
1344 invalidateNodeListAndCollectionCachesInAncestors(&name, this);
1346 if (AXObjectCache* cache = document().existingAXObjectCache())
1347 cache->handleAttributeChanged(name, this);
1350 template <typename CharacterType>
1351 static inline bool classStringHasClassName(const CharacterType* characters, unsigned length)
1357 if (isNotHTMLSpace(characters[i]))
1360 } while (i < length);
1365 static inline bool classStringHasClassName(const AtomicString& newClassString)
1367 unsigned length = newClassString.length();
1372 if (newClassString.is8Bit())
1373 return classStringHasClassName(newClassString.characters8(), length);
1374 return classStringHasClassName(newClassString.characters16(), length);
1377 void Element::classAttributeChanged(const AtomicString& newClassString)
1379 // Note: We'll need ElementData, but it doesn't have to be UniqueElementData.
1381 ensureUniqueElementData();
1383 bool shouldFoldCase = document().inQuirksMode();
1384 bool newStringHasClasses = classStringHasClassName(newClassString);
1386 auto oldClassNames = elementData()->classNames();
1387 auto newClassNames = newStringHasClasses ? SpaceSplitString(newClassString, shouldFoldCase) : SpaceSplitString();
1389 Style::ClassChangeInvalidation styleInvalidation(*this, oldClassNames, newClassNames);
1390 elementData()->setClassNames(newClassNames);
1393 if (hasRareData()) {
1394 if (auto* classList = elementRareData()->classList())
1395 classList->associatedAttributeValueChanged(newClassString);
1399 URL Element::absoluteLinkURL() const
1404 AtomicString linkAttribute;
1405 if (hasTagName(SVGNames::aTag))
1406 linkAttribute = getAttribute(XLinkNames::hrefAttr);
1408 linkAttribute = getAttribute(HTMLNames::hrefAttr);
1410 if (linkAttribute.isEmpty())
1413 return document().completeURL(stripLeadingAndTrailingHTMLSpaces(linkAttribute));
1416 #if ENABLE(TOUCH_EVENTS)
1417 bool Element::allowsDoubleTapGesture() const
1419 if (renderStyle() && renderStyle()->touchAction() != TouchAction::Auto)
1422 Element* parent = parentElement();
1423 return !parent || parent->allowsDoubleTapGesture();
1427 StyleResolver& Element::styleResolver()
1429 if (auto* shadowRoot = containingShadowRoot())
1430 return shadowRoot->styleScope().resolver();
1432 return document().styleScope().resolver();
1435 ElementStyle Element::resolveStyle(const RenderStyle* parentStyle)
1437 return styleResolver().styleForElement(*this, parentStyle);
1440 void Element::invalidateStyle()
1442 Node::invalidateStyle(Style::Validity::ElementInvalid);
1445 void Element::invalidateStyleAndLayerComposition()
1447 Node::invalidateStyle(Style::Validity::ElementInvalid, Style::InvalidationMode::RecompositeLayer);
1450 void Element::invalidateStyleForSubtree()
1452 Node::invalidateStyle(Style::Validity::SubtreeInvalid);
1455 void Element::invalidateStyleAndRenderersForSubtree()
1457 Node::invalidateStyle(Style::Validity::SubtreeAndRenderersInvalid);
1460 #if ENABLE(WEB_ANIMATIONS)
1461 WebAnimationVector Element::getAnimations()
1463 auto checkTarget = [this](AnimationEffect const& effect)
1465 return (static_cast<KeyframeEffect const&>(effect).target() == this);
1468 auto* document = DocumentAnimation::from(&this->document());
1470 return document->getAnimations(checkTarget);
1471 return WebAnimationVector();
1475 bool Element::hasDisplayContents() const
1477 return hasRareData() && elementRareData()->hasDisplayContents();
1480 void Element::setHasDisplayContents(bool value)
1482 if (hasDisplayContents() == value)
1484 ensureElementRareData().setHasDisplayContents(value);
1487 // Returns true is the given attribute is an event handler.
1488 // We consider an event handler any attribute that begins with "on".
1489 // It is a simple solution that has the advantage of not requiring any
1490 // code or configuration change if a new event handler is defined.
1492 static inline bool isEventHandlerAttribute(const Attribute& attribute)
1494 return attribute.name().namespaceURI().isNull() && attribute.name().localName().startsWith("on");
1497 bool Element::isJavaScriptURLAttribute(const Attribute& attribute) const
1499 return isURLAttribute(attribute) && protocolIsJavaScript(stripLeadingAndTrailingHTMLSpaces(attribute.value()));
1502 void Element::stripScriptingAttributes(Vector<Attribute>& attributeVector) const
1504 attributeVector.removeAllMatching([this](auto& attribute) -> bool {
1505 return isEventHandlerAttribute(attribute)
1506 || this->isJavaScriptURLAttribute(attribute)
1507 || this->isHTMLContentAttribute(attribute);
1511 void Element::parserSetAttributes(const Vector<Attribute>& attributeVector)
1513 ASSERT(!isConnected());
1514 ASSERT(!parentNode());
1515 ASSERT(!m_elementData);
1517 if (!attributeVector.isEmpty()) {
1518 if (document().sharedObjectPool())
1519 m_elementData = document().sharedObjectPool()->cachedShareableElementDataWithAttributes(attributeVector);
1521 m_elementData = ShareableElementData::createWithAttributes(attributeVector);
1525 parserDidSetAttributes();
1527 // Use attributeVector instead of m_elementData because attributeChanged might modify m_elementData.
1528 for (const auto& attribute : attributeVector)
1529 attributeChanged(attribute.name(), nullAtom, attribute.value(), ModifiedDirectly);
1532 void Element::parserDidSetAttributes()
1536 void Element::didMoveToNewDocument(Document& oldDocument)
1538 Node::didMoveToNewDocument(oldDocument);
1540 if (oldDocument.inQuirksMode() != document().inQuirksMode()) {
1541 // ElementData::m_classNames or ElementData::m_idForStyleResolution need to be updated with the right case.
1543 attributeChanged(idAttr, nullAtom, getIdAttribute());
1545 attributeChanged(classAttr, nullAtom, getAttribute(classAttr));
1548 if (UNLIKELY(isDefinedCustomElement()))
1549 CustomElementReactionQueue::enqueueAdoptedCallbackIfNeeded(*this, oldDocument, document());
1552 bool Element::hasAttributes() const
1554 synchronizeAllAttributes();
1555 return elementData() && elementData()->length();
1558 bool Element::hasEquivalentAttributes(const Element* other) const
1560 synchronizeAllAttributes();
1561 other->synchronizeAllAttributes();
1562 if (elementData() == other->elementData())
1565 return elementData()->isEquivalent(other->elementData());
1566 if (other->elementData())
1567 return other->elementData()->isEquivalent(elementData());
1571 String Element::nodeName() const
1573 return m_tagName.toString();
1576 String Element::nodeNamePreservingCase() const
1578 return m_tagName.toString();
1581 ExceptionOr<void> Element::setPrefix(const AtomicString& prefix)
1583 auto result = checkSetPrefix(prefix);
1584 if (result.hasException())
1585 return result.releaseException();
1587 m_tagName.setPrefix(prefix.isEmpty() ? nullAtom : prefix);
1591 const AtomicString& Element::imageSourceURL() const
1593 return attributeWithoutSynchronization(srcAttr);
1596 bool Element::rendererIsNeeded(const RenderStyle& style)
1598 return style.display() != NONE && style.display() != CONTENTS;
1601 RenderPtr<RenderElement> Element::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
1603 return RenderElement::createFor(*this, WTFMove(style));
1606 Node::InsertionNotificationRequest Element::insertedInto(ContainerNode& insertionPoint)
1608 bool wasInDocument = isConnected();
1609 // need to do superclass processing first so isConnected() is true
1610 // by the time we reach updateId
1611 ContainerNode::insertedInto(insertionPoint);
1612 ASSERT(!wasInDocument || isConnected());
1614 #if ENABLE(FULLSCREEN_API)
1615 if (containsFullScreenElement() && parentElement() && !parentElement()->containsFullScreenElement())
1616 setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true);
1619 if (parentNode() == &insertionPoint) {
1620 if (auto* shadowRoot = parentNode()->shadowRoot())
1621 shadowRoot->hostChildElementDidChange(*this);
1624 if (!insertionPoint.isInTreeScope())
1625 return InsertionDone;
1627 // This function could be called when this element's shadow root's host or its ancestor is inserted.
1628 // This element is new to the shadow tree (and its tree scope) only if the parent into which this element
1629 // or its ancestor is inserted belongs to the same tree scope as this element's.
1630 TreeScope* newScope = &insertionPoint.treeScope();
1631 bool becomeConnected = !wasInDocument && isConnected();
1632 HTMLDocument* newDocument = becomeConnected && is<HTMLDocument>(newScope->documentScope()) ? &downcast<HTMLDocument>(newScope->documentScope()) : nullptr;
1633 if (newScope != &treeScope())
1636 const AtomicString& idValue = getIdAttribute();
1637 if (!idValue.isNull()) {
1639 updateIdForTreeScope(*newScope, nullAtom, idValue);
1641 updateIdForDocument(*newDocument, nullAtom, idValue, AlwaysUpdateHTMLDocumentNamedItemMaps);
1644 const AtomicString& nameValue = getNameAttribute();
1645 if (!nameValue.isNull()) {
1647 updateNameForTreeScope(*newScope, nullAtom, nameValue);
1649 updateNameForDocument(*newDocument, nullAtom, nameValue);
1652 if (newScope && hasTagName(labelTag)) {
1653 if (newScope->shouldCacheLabelsByForAttribute())
1654 updateLabel(*newScope, nullAtom, attributeWithoutSynchronization(forAttr));
1657 if (becomeConnected) {
1658 if (UNLIKELY(isCustomElementUpgradeCandidate()))
1659 CustomElementReactionQueue::enqueueElementUpgradeIfDefined(*this);
1660 if (UNLIKELY(isDefinedCustomElement()))
1661 CustomElementReactionQueue::enqueueConnectedCallbackIfNeeded(*this);
1664 return InsertionDone;
1667 void Element::removedFrom(ContainerNode& insertionPoint)
1669 #if ENABLE(FULLSCREEN_API)
1670 if (containsFullScreenElement())
1671 setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
1673 #if ENABLE(POINTER_LOCK)
1674 if (document().page())
1675 document().page()->pointerLockController().elementRemoved(*this);
1678 setSavedLayerScrollPosition(ScrollPosition());
1680 if (insertionPoint.isInTreeScope()) {
1681 TreeScope* oldScope = &insertionPoint.treeScope();
1682 bool becomeDisconnected = isConnected();
1683 HTMLDocument* oldDocument = becomeDisconnected && is<HTMLDocument>(oldScope->documentScope()) ? &downcast<HTMLDocument>(oldScope->documentScope()) : nullptr;
1685 // ContainerNode::removeBetween always sets the removed chid's tree scope to Document's but InTreeScope flag is unset in Node::removedFrom.
1686 // So this element has been removed from the old tree scope only if InTreeScope flag is set and this element's tree scope is Document's.
1687 if (!isInTreeScope() || &treeScope() != &document())
1690 const AtomicString& idValue = getIdAttribute();
1691 if (!idValue.isNull()) {
1693 updateIdForTreeScope(*oldScope, idValue, nullAtom);
1695 updateIdForDocument(*oldDocument, idValue, nullAtom, AlwaysUpdateHTMLDocumentNamedItemMaps);
1698 const AtomicString& nameValue = getNameAttribute();
1699 if (!nameValue.isNull()) {
1701 updateNameForTreeScope(*oldScope, nameValue, nullAtom);
1703 updateNameForDocument(*oldDocument, nameValue, nullAtom);
1706 if (oldScope && hasTagName(labelTag)) {
1707 if (oldScope->shouldCacheLabelsByForAttribute())
1708 updateLabel(*oldScope, attributeWithoutSynchronization(forAttr), nullAtom);
1711 if (becomeDisconnected && UNLIKELY(isDefinedCustomElement()))
1712 CustomElementReactionQueue::enqueueDisconnectedCallbackIfNeeded(*this);
1715 if (!parentNode()) {
1716 if (auto* shadowRoot = insertionPoint.shadowRoot())
1717 shadowRoot->hostChildElementDidChange(*this);
1720 ContainerNode::removedFrom(insertionPoint);
1722 if (hasPendingResources())
1723 document().accessSVGExtensions().removeElementFromPendingResources(this);
1727 if (Frame* frame = document().frame())
1728 frame->mainFrame().removeLatchingStateForTarget(*this);
1732 void Element::unregisterNamedFlowContentElement()
1734 if (isNamedFlowContentElement() && document().renderView())
1735 document().renderView()->flowThreadController().unregisterNamedFlowContentElement(*this);
1738 ShadowRoot* Element::shadowRoot() const
1740 return hasRareData() ? elementRareData()->shadowRoot() : nullptr;
1743 void Element::addShadowRoot(Ref<ShadowRoot>&& newShadowRoot)
1745 ASSERT(!shadowRoot());
1748 RenderTreeUpdater::tearDownRenderers(*this);
1750 ShadowRoot& shadowRoot = newShadowRoot;
1751 ensureElementRareData().setShadowRoot(WTFMove(newShadowRoot));
1753 shadowRoot.setHost(this);
1754 shadowRoot.setParentTreeScope(treeScope());
1756 NodeVector postInsertionNotificationTargets;
1757 notifyChildNodeInserted(*this, shadowRoot, postInsertionNotificationTargets);
1758 for (auto& target : postInsertionNotificationTargets)
1759 target->finishedInsertingSubtree();
1761 invalidateStyleAndRenderersForSubtree();
1763 InspectorInstrumentation::didPushShadowRoot(*this, shadowRoot);
1765 if (shadowRoot.mode() == ShadowRootMode::UserAgent)
1766 didAddUserAgentShadowRoot(&shadowRoot);
1769 void Element::removeShadowRoot()
1771 RefPtr<ShadowRoot> oldRoot = shadowRoot();
1775 InspectorInstrumentation::willPopShadowRoot(*this, *oldRoot);
1776 document().removeFocusedNodeOfSubtree(*oldRoot);
1778 ASSERT(!oldRoot->renderer());
1780 elementRareData()->clearShadowRoot();
1782 oldRoot->setHost(nullptr);
1783 oldRoot->setParentTreeScope(document());
1786 static bool canAttachAuthorShadowRoot(const Element& element)
1788 static NeverDestroyed<HashSet<AtomicString>> tagNames = [] {
1789 static const HTMLQualifiedName* const tagList[] = {
1808 HashSet<AtomicString> set;
1809 for (auto& name : tagList)
1810 set.add(name->localName());
1814 if (!is<HTMLElement>(element))
1817 const auto& localName = element.localName();
1818 return tagNames.get().contains(localName) || Document::validateCustomElementName(localName) == CustomElementNameValidationStatus::Valid;
1821 ExceptionOr<ShadowRoot&> Element::attachShadow(const ShadowRootInit& init)
1823 if (!canAttachAuthorShadowRoot(*this))
1824 return Exception { NOT_SUPPORTED_ERR };
1826 return Exception { INVALID_STATE_ERR };
1827 if (init.mode == ShadowRootMode::UserAgent)
1828 return Exception { TypeError };
1829 auto shadow = ShadowRoot::create(document(), init.mode);
1830 auto& result = shadow.get();
1831 addShadowRoot(WTFMove(shadow));
1835 ShadowRoot* Element::shadowRootForBindings(JSC::ExecState& state) const
1837 auto* shadow = shadowRoot();
1840 if (shadow->mode() == ShadowRootMode::Open)
1842 if (JSC::jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject())->world().shadowRootIsAlwaysOpen())
1847 ShadowRoot* Element::userAgentShadowRoot() const
1849 ASSERT(!shadowRoot() || shadowRoot()->mode() == ShadowRootMode::UserAgent);
1850 return shadowRoot();
1853 ShadowRoot& Element::ensureUserAgentShadowRoot()
1855 if (auto* shadow = userAgentShadowRoot())
1857 auto newShadow = ShadowRoot::create(document(), ShadowRootMode::UserAgent);
1858 ShadowRoot& shadow = newShadow;
1859 addShadowRoot(WTFMove(newShadow));
1863 void Element::setIsDefinedCustomElement(JSCustomElementInterface& elementInterface)
1865 clearFlag(IsEditingTextOrUndefinedCustomElementFlag);
1866 setFlag(IsCustomElement);
1867 auto& data = ensureElementRareData();
1868 if (!data.customElementReactionQueue())
1869 data.setCustomElementReactionQueue(std::make_unique<CustomElementReactionQueue>(elementInterface));
1870 InspectorInstrumentation::didChangeCustomElementState(*this);
1873 void Element::setIsFailedCustomElement(JSCustomElementInterface&)
1875 ASSERT(isUndefinedCustomElement());
1876 ASSERT(getFlag(IsEditingTextOrUndefinedCustomElementFlag));
1877 clearFlag(IsCustomElement);
1879 if (hasRareData()) {
1880 // Clear the queue instead of deleting it since this function can be called inside CustomElementReactionQueue::invokeAll during upgrades.
1881 if (auto* queue = elementRareData()->customElementReactionQueue())
1884 InspectorInstrumentation::didChangeCustomElementState(*this);
1887 void Element::setIsCustomElementUpgradeCandidate()
1889 ASSERT(!getFlag(IsCustomElement));
1890 setFlag(IsCustomElement);
1891 setFlag(IsEditingTextOrUndefinedCustomElementFlag);
1892 InspectorInstrumentation::didChangeCustomElementState(*this);
1895 void Element::enqueueToUpgrade(JSCustomElementInterface& elementInterface)
1897 ASSERT(!isDefinedCustomElement() && !isFailedCustomElement());
1898 setFlag(IsCustomElement);
1899 setFlag(IsEditingTextOrUndefinedCustomElementFlag);
1900 InspectorInstrumentation::didChangeCustomElementState(*this);
1902 auto& data = ensureElementRareData();
1903 ASSERT(!data.customElementReactionQueue());
1905 data.setCustomElementReactionQueue(std::make_unique<CustomElementReactionQueue>(elementInterface));
1906 data.customElementReactionQueue()->enqueueElementUpgrade(*this);
1909 CustomElementReactionQueue* Element::reactionQueue() const
1911 ASSERT(isDefinedCustomElement() || isCustomElementUpgradeCandidate());
1914 return elementRareData()->customElementReactionQueue();
1917 const AtomicString& Element::shadowPseudoId() const
1922 bool Element::childTypeAllowed(NodeType type) const
1928 case PROCESSING_INSTRUCTION_NODE:
1929 case CDATA_SECTION_NODE:
1937 static void checkForEmptyStyleChange(Element& element)
1939 if (element.styleAffectedByEmpty()) {
1940 auto* style = element.renderStyle();
1941 if (!style || (!style->emptyState() || element.hasChildNodes()))
1942 element.invalidateStyleForSubtree();
1946 enum SiblingCheckType { FinishedParsingChildren, SiblingElementRemoved, Other };
1948 static void checkForSiblingStyleChanges(Element& parent, SiblingCheckType checkType, Element* elementBeforeChange, Element* elementAfterChange)
1951 checkForEmptyStyleChange(parent);
1953 if (parent.styleValidity() >= Style::Validity::SubtreeInvalid)
1956 // :first-child. In the parser callback case, we don't have to check anything, since we were right the first time.
1957 // In the DOM case, we only need to do something if |afterChange| is not 0.
1958 // |afterChange| is 0 in the parser case, so it works out that we'll skip this block.
1959 if (parent.childrenAffectedByFirstChildRules() && elementAfterChange) {
1960 // Find our new first child.
1961 Element* newFirstElement = ElementTraversal::firstChild(parent);
1962 // Find the first element node following |afterChange|
1964 // This is the insert/append case.
1965 if (newFirstElement != elementAfterChange) {
1966 auto* style = elementAfterChange->renderStyle();
1967 if (!style || style->firstChildState())
1968 elementAfterChange->invalidateStyleForSubtree();
1971 // We also have to handle node removal.
1972 if (checkType == SiblingElementRemoved && newFirstElement == elementAfterChange && newFirstElement) {
1973 auto* style = newFirstElement->renderStyle();
1974 if (!style || !style->firstChildState())
1975 newFirstElement->invalidateStyleForSubtree();
1979 // :last-child. In the parser callback case, we don't have to check anything, since we were right the first time.
1980 // In the DOM case, we only need to do something if |afterChange| is not 0.
1981 if (parent.childrenAffectedByLastChildRules() && elementBeforeChange) {
1982 // Find our new last child.
1983 Element* newLastElement = ElementTraversal::lastChild(parent);
1985 if (newLastElement != elementBeforeChange) {
1986 auto* style = elementBeforeChange->renderStyle();
1987 if (!style || style->lastChildState())
1988 elementBeforeChange->invalidateStyleForSubtree();
1991 // 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
1993 if ((checkType == SiblingElementRemoved || checkType == FinishedParsingChildren) && newLastElement == elementBeforeChange && newLastElement) {
1994 auto* style = newLastElement->renderStyle();
1995 if (!style || !style->lastChildState())
1996 newLastElement->invalidateStyleForSubtree();
2000 if (elementAfterChange) {
2001 if (elementAfterChange->styleIsAffectedByPreviousSibling())
2002 elementAfterChange->invalidateStyleForSubtree();
2003 else if (elementAfterChange->affectsNextSiblingElementStyle()) {
2004 Element* elementToInvalidate = elementAfterChange;
2006 elementToInvalidate = elementToInvalidate->nextElementSibling();
2007 } while (elementToInvalidate && !elementToInvalidate->styleIsAffectedByPreviousSibling());
2009 if (elementToInvalidate)
2010 elementToInvalidate->invalidateStyleForSubtree();
2014 // Backward positional selectors include nth-last-child, nth-last-of-type, last-of-type and only-of-type.
2015 // We have to invalidate everything following the insertion point in the forward case, and everything before the insertion point in the
2017 // |afterChange| is 0 in the parser callback case, so we won't do any work for the forward case if we don't have to.
2018 // For performance reasons we just mark the parent node as changed, since we don't want to make childrenChanged O(n^2) by crawling all our kids
2019 // here. recalcStyle will then force a walk of the children when it sees that this has happened.
2020 if (parent.childrenAffectedByBackwardPositionalRules() && elementBeforeChange)
2021 parent.invalidateStyleForSubtree();
2024 void Element::childrenChanged(const ChildChange& change)
2026 ContainerNode::childrenChanged(change);
2027 if (change.source == ChildChangeSourceParser)
2028 checkForEmptyStyleChange(*this);
2030 SiblingCheckType checkType = change.type == ElementRemoved ? SiblingElementRemoved : Other;
2031 checkForSiblingStyleChanges(*this, checkType, change.previousSiblingElement, change.nextSiblingElement);
2034 if (ShadowRoot* shadowRoot = this->shadowRoot()) {
2035 switch (change.type) {
2036 case ElementInserted:
2037 case ElementRemoved:
2038 // For elements, we notify shadowRoot in Element::insertedInto and Element::removedFrom.
2040 case AllChildrenRemoved:
2041 case AllChildrenReplaced:
2042 shadowRoot->didRemoveAllChildrenOfShadowHost();
2047 shadowRoot->didChangeDefaultSlot();
2049 case NonContentsChildInserted:
2050 case NonContentsChildRemoved:
2056 void Element::setAttributeEventListener(const AtomicString& eventType, const QualifiedName& attributeName, const AtomicString& attributeValue)
2058 setAttributeEventListener(eventType, JSLazyEventListener::create(*this, attributeName, attributeValue));
2061 void Element::setIsNamedFlowContentElement()
2063 ensureElementRareData().setIsNamedFlowContentElement(true);
2066 void Element::clearIsNamedFlowContentElement()
2068 ensureElementRareData().setIsNamedFlowContentElement(false);
2071 void Element::removeAllEventListeners()
2073 ContainerNode::removeAllEventListeners();
2074 if (ShadowRoot* shadowRoot = this->shadowRoot())
2075 shadowRoot->removeAllEventListeners();
2078 void Element::beginParsingChildren()
2080 clearIsParsingChildrenFinished();
2083 void Element::finishParsingChildren()
2085 ContainerNode::finishParsingChildren();
2086 setIsParsingChildrenFinished();
2087 checkForSiblingStyleChanges(*this, FinishedParsingChildren, ElementTraversal::lastChild(*this), nullptr);
2090 #if ENABLE(TREE_DEBUGGING)
2091 void Element::formatForDebugger(char* buffer, unsigned length) const
2093 StringBuilder result;
2096 result.append(nodeName());
2098 s = getIdAttribute();
2099 if (s.length() > 0) {
2100 if (result.length() > 0)
2101 result.appendLiteral("; ");
2102 result.appendLiteral("id=");
2106 s = getAttribute(classAttr);
2107 if (s.length() > 0) {
2108 if (result.length() > 0)
2109 result.appendLiteral("; ");
2110 result.appendLiteral("class=");
2114 strncpy(buffer, result.toString().utf8().data(), length - 1);
2118 const Vector<RefPtr<Attr>>& Element::attrNodeList()
2120 ASSERT(hasSyntheticAttrChildNodes());
2121 return *attrNodeListForElement(*this);
2124 ExceptionOr<RefPtr<Attr>> Element::setAttributeNode(Attr& attrNode)
2126 RefPtr<Attr> oldAttrNode = attrIfExists(attrNode.localName(), shouldIgnoreAttributeCase(*this));
2127 if (oldAttrNode.get() == &attrNode)
2128 return WTFMove(oldAttrNode);
2130 // INUSE_ATTRIBUTE_ERR: Raised if node is an Attr that is already an attribute of another Element object.
2131 // The DOM user must explicitly clone Attr nodes to re-use them in other elements.
2132 if (attrNode.ownerElement() && attrNode.ownerElement() != this)
2133 return Exception { INUSE_ATTRIBUTE_ERR };
2135 synchronizeAllAttributes();
2136 auto& elementData = ensureUniqueElementData();
2138 auto existingAttributeIndex = elementData.findAttributeIndexByName(attrNode.localName(), shouldIgnoreAttributeCase(*this));
2139 if (existingAttributeIndex == ElementData::attributeNotFound)
2140 setAttributeInternal(elementData.findAttributeIndexByName(attrNode.qualifiedName()), attrNode.qualifiedName(), attrNode.value(), NotInSynchronizationOfLazyAttribute);
2142 const Attribute& attribute = attributeAt(existingAttributeIndex);
2144 detachAttrNodeFromElementWithValue(oldAttrNode.get(), attribute.value());
2146 oldAttrNode = Attr::create(document(), attrNode.qualifiedName(), attribute.value());
2148 if (attribute.name().matches(attrNode.qualifiedName()))
2149 setAttributeInternal(existingAttributeIndex, attrNode.qualifiedName(), attrNode.value(), NotInSynchronizationOfLazyAttribute);
2151 removeAttributeInternal(existingAttributeIndex, NotInSynchronizationOfLazyAttribute);
2152 setAttributeInternal(ensureUniqueElementData().findAttributeIndexByName(attrNode.qualifiedName()), attrNode.qualifiedName(), attrNode.value(), NotInSynchronizationOfLazyAttribute);
2155 if (attrNode.ownerElement() != this) {
2156 attrNode.attachToElement(*this);
2157 treeScope().adoptIfNeeded(attrNode);
2158 ensureAttrNodeListForElement(*this).append(&attrNode);
2160 return WTFMove(oldAttrNode);
2163 ExceptionOr<RefPtr<Attr>> Element::setAttributeNodeNS(Attr& attrNode)
2165 RefPtr<Attr> oldAttrNode = attrIfExists(attrNode.qualifiedName());
2166 if (oldAttrNode.get() == &attrNode)
2167 return WTFMove(oldAttrNode);
2169 // INUSE_ATTRIBUTE_ERR: Raised if node is an Attr that is already an attribute of another Element object.
2170 // The DOM user must explicitly clone Attr nodes to re-use them in other elements.
2171 if (attrNode.ownerElement() && attrNode.ownerElement() != this)
2172 return Exception { INUSE_ATTRIBUTE_ERR };
2174 synchronizeAllAttributes();
2175 auto& elementData = ensureUniqueElementData();
2177 auto index = elementData.findAttributeIndexByName(attrNode.qualifiedName());
2178 if (index != ElementData::attributeNotFound) {
2180 detachAttrNodeFromElementWithValue(oldAttrNode.get(), elementData.attributeAt(index).value());
2182 oldAttrNode = Attr::create(document(), attrNode.qualifiedName(), elementData.attributeAt(index).value());
2185 setAttributeInternal(index, attrNode.qualifiedName(), attrNode.value(), NotInSynchronizationOfLazyAttribute);
2187 attrNode.attachToElement(*this);
2188 treeScope().adoptIfNeeded(attrNode);
2189 ensureAttrNodeListForElement(*this).append(&attrNode);
2191 return WTFMove(oldAttrNode);
2194 ExceptionOr<Ref<Attr>> Element::removeAttributeNode(Attr& attr)
2196 if (attr.ownerElement() != this)
2197 return Exception { NOT_FOUND_ERR };
2199 ASSERT(&document() == &attr.document());
2201 synchronizeAllAttributes();
2204 return Exception { NOT_FOUND_ERR };
2206 auto existingAttributeIndex = m_elementData->findAttributeIndexByName(attr.qualifiedName());
2207 if (existingAttributeIndex == ElementData::attributeNotFound)
2208 return Exception { NOT_FOUND_ERR };
2210 Ref<Attr> oldAttrNode { attr };
2212 detachAttrNodeFromElementWithValue(&attr, m_elementData->attributeAt(existingAttributeIndex).value());
2213 removeAttributeInternal(existingAttributeIndex, NotInSynchronizationOfLazyAttribute);
2215 return WTFMove(oldAttrNode);
2218 ExceptionOr<QualifiedName> Element::parseAttributeName(const AtomicString& namespaceURI, const AtomicString& qualifiedName)
2220 auto parseResult = Document::parseQualifiedName(namespaceURI, qualifiedName);
2221 if (parseResult.hasException())
2222 return parseResult.releaseException();
2223 QualifiedName parsedAttributeName { parseResult.releaseReturnValue() };
2224 if (!Document::hasValidNamespaceForAttributes(parsedAttributeName))
2225 return Exception { NAMESPACE_ERR };
2226 return WTFMove(parsedAttributeName);
2229 ExceptionOr<void> Element::setAttributeNS(const AtomicString& namespaceURI, const AtomicString& qualifiedName, const AtomicString& value)
2231 auto result = parseAttributeName(namespaceURI, qualifiedName);
2232 if (result.hasException())
2233 return result.releaseException();
2234 setAttribute(result.releaseReturnValue(), value);
2238 void Element::removeAttributeInternal(unsigned index, SynchronizationOfLazyAttribute inSynchronizationOfLazyAttribute)
2240 ASSERT_WITH_SECURITY_IMPLICATION(index < attributeCount());
2242 UniqueElementData& elementData = ensureUniqueElementData();
2244 QualifiedName name = elementData.attributeAt(index).name();
2245 AtomicString valueBeingRemoved = elementData.attributeAt(index).value();
2247 if (RefPtr<Attr> attrNode = attrIfExists(name))
2248 detachAttrNodeFromElementWithValue(attrNode.get(), elementData.attributeAt(index).value());
2250 if (inSynchronizationOfLazyAttribute) {
2251 elementData.removeAttribute(index);
2255 if (!valueBeingRemoved.isNull())
2256 willModifyAttribute(name, valueBeingRemoved, nullAtom);
2259 Style::AttributeChangeInvalidation styleInvalidation(*this, name, valueBeingRemoved, nullAtom);
2260 elementData.removeAttribute(index);
2263 didRemoveAttribute(name, valueBeingRemoved);
2266 void Element::addAttributeInternal(const QualifiedName& name, const AtomicString& value, SynchronizationOfLazyAttribute inSynchronizationOfLazyAttribute)
2268 if (inSynchronizationOfLazyAttribute) {
2269 ensureUniqueElementData().addAttribute(name, value);
2273 willModifyAttribute(name, nullAtom, value);
2275 Style::AttributeChangeInvalidation styleInvalidation(*this, name, nullAtom, value);
2276 ensureUniqueElementData().addAttribute(name, value);
2278 didAddAttribute(name, value);
2281 bool Element::removeAttribute(const AtomicString& name)
2286 AtomicString localName = shouldIgnoreAttributeCase(*this) ? name.convertToASCIILowercase() : name;
2287 unsigned index = elementData()->findAttributeIndexByName(localName, false);
2288 if (index == ElementData::attributeNotFound) {
2289 if (UNLIKELY(localName == styleAttr) && elementData()->styleAttributeIsDirty() && is<StyledElement>(*this))
2290 downcast<StyledElement>(*this).removeAllInlineStyleProperties();
2294 removeAttributeInternal(index, NotInSynchronizationOfLazyAttribute);
2298 bool Element::removeAttributeNS(const AtomicString& namespaceURI, const AtomicString& localName)
2300 return removeAttribute(QualifiedName(nullAtom, localName, namespaceURI));
2303 RefPtr<Attr> Element::getAttributeNode(const AtomicString& localName)
2307 synchronizeAttribute(localName);
2308 const Attribute* attribute = elementData()->findAttributeByName(localName, shouldIgnoreAttributeCase(*this));
2311 return ensureAttr(attribute->name());
2314 RefPtr<Attr> Element::getAttributeNodeNS(const AtomicString& namespaceURI, const AtomicString& localName)
2318 QualifiedName qName(nullAtom, localName, namespaceURI);
2319 synchronizeAttribute(qName);
2320 const Attribute* attribute = elementData()->findAttributeByName(qName);
2323 return ensureAttr(attribute->name());
2326 bool Element::hasAttribute(const AtomicString& localName) const
2330 synchronizeAttribute(localName);
2331 return elementData()->findAttributeByName(localName, shouldIgnoreAttributeCase(*this));
2334 bool Element::hasAttributeNS(const AtomicString& namespaceURI, const AtomicString& localName) const
2338 QualifiedName qName(nullAtom, localName, namespaceURI);
2339 synchronizeAttribute(qName);
2340 return elementData()->findAttributeByName(qName);
2343 CSSStyleDeclaration* Element::cssomStyle()
2348 void Element::focus(bool restorePreviousSelection, FocusDirection direction)
2353 if (document().focusedElement() == this) {
2354 if (document().page())
2355 document().page()->chrome().client().elementDidRefocus(*this);
2360 // If the stylesheets have already been loaded we can reliably check isFocusable.
2361 // If not, we continue and set the focused node on the focus controller below so
2362 // that it can be updated soon after attach.
2363 if (document().haveStylesheetsLoaded()) {
2364 document().updateLayoutIgnorePendingStylesheets();
2369 if (!supportsFocus())
2372 RefPtr<Node> protect;
2373 if (Page* page = document().page()) {
2374 // Focus and change event handlers can cause us to lose our last ref.
2375 // If a focus event handler changes the focus to a different node it
2376 // does not make sense to continue and update appearence.
2378 if (!page->focusController().setFocusedElement(this, *document().frame(), direction))
2382 // Setting the focused node above might have invalidated the layout due to scripts.
2383 document().updateLayoutIgnorePendingStylesheets();
2385 if (!isFocusable()) {
2386 ensureElementRareData().setNeedsFocusAppearanceUpdateSoonAfterAttach(true);
2390 cancelFocusAppearanceUpdate();
2392 SelectionRevealMode revealMode = SelectionRevealMode::Reveal;
2394 // Focusing a form element triggers animation in UIKit to scroll to the right position.
2395 // Calling updateFocusAppearance() would generate an unnecessary call to ScrollView::setScrollPosition(),
2396 // which would jump us around during this animation. See <rdar://problem/6699741>.
2397 bool isFormControl = is<HTMLFormControlElement>(*this);
2399 revealMode = SelectionRevealMode::RevealUpToMainFrame;
2402 updateFocusAppearance(restorePreviousSelection ? SelectionRestorationMode::Restore : SelectionRestorationMode::SetDefault, revealMode);
2405 void Element::updateFocusAppearanceAfterAttachIfNeeded()
2409 ElementRareData* data = elementRareData();
2410 if (!data->needsFocusAppearanceUpdateSoonAfterAttach())
2412 if (isFocusable() && document().focusedElement() == this)
2413 document().updateFocusAppearanceSoon(SelectionRestorationMode::SetDefault);
2414 data->setNeedsFocusAppearanceUpdateSoonAfterAttach(false);
2417 void Element::updateFocusAppearance(SelectionRestorationMode, SelectionRevealMode revealMode)
2419 if (isRootEditableElement()) {
2420 // Keep frame alive in this method, since setSelection() may release the last reference to |frame|.
2421 RefPtr<Frame> frame = document().frame();
2425 // When focusing an editable element in an iframe, don't reset the selection if it already contains a selection.
2426 if (this == frame->selection().selection().rootEditableElement())
2429 // FIXME: We should restore the previous selection if there is one.
2430 VisibleSelection newSelection = VisibleSelection(firstPositionInOrBeforeNode(this), DOWNSTREAM);
2432 if (frame->selection().shouldChangeSelection(newSelection)) {
2433 frame->selection().setSelection(newSelection, FrameSelection::defaultSetSelectionOptions(), Element::defaultFocusTextStateChangeIntent());
2434 frame->selection().revealSelection(revealMode);
2436 } else if (renderer() && !renderer()->isWidget()) {
2438 LayoutRect absoluteBounds = renderer()->absoluteAnchorRect(&insideFixed);
2439 renderer()->scrollRectToVisible(revealMode, absoluteBounds, insideFixed);
2443 void Element::blur()
2445 cancelFocusAppearanceUpdate();
2446 if (treeScope().focusedElementInScope() == this) {
2447 if (Frame* frame = document().frame())
2448 frame->page()->focusController().setFocusedElement(nullptr, *frame);
2450 document().setFocusedElement(nullptr);
2454 void Element::dispatchFocusInEvent(const AtomicString& eventType, RefPtr<Element>&& oldFocusedElement)
2456 ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventAllowedInMainThread());
2457 ASSERT(eventType == eventNames().focusinEvent || eventType == eventNames().DOMFocusInEvent);
2458 dispatchScopedEvent(FocusEvent::create(eventType, true, false, document().defaultView(), 0, WTFMove(oldFocusedElement)));
2461 void Element::dispatchFocusOutEvent(const AtomicString& eventType, RefPtr<Element>&& newFocusedElement)
2463 ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventAllowedInMainThread());
2464 ASSERT(eventType == eventNames().focusoutEvent || eventType == eventNames().DOMFocusOutEvent);
2465 dispatchScopedEvent(FocusEvent::create(eventType, true, false, document().defaultView(), 0, WTFMove(newFocusedElement)));
2468 void Element::dispatchFocusEvent(RefPtr<Element>&& oldFocusedElement, FocusDirection)
2470 if (document().page())
2471 document().page()->chrome().client().elementDidFocus(*this);
2473 EventDispatcher::dispatchEvent(*this, FocusEvent::create(eventNames().focusEvent, false, false, document().defaultView(), 0, WTFMove(oldFocusedElement)));
2476 void Element::dispatchBlurEvent(RefPtr<Element>&& newFocusedElement)
2478 if (document().page())
2479 document().page()->chrome().client().elementDidBlur(*this);
2481 EventDispatcher::dispatchEvent(*this, FocusEvent::create(eventNames().blurEvent, false, false, document().defaultView(), 0, WTFMove(newFocusedElement)));
2484 bool Element::dispatchMouseForceWillBegin()
2486 #if ENABLE(MOUSE_FORCE_EVENTS)
2487 if (!document().hasListenerType(Document::FORCEWILLBEGIN_LISTENER))
2490 Frame* frame = document().frame();
2494 PlatformMouseEvent platformMouseEvent { frame->eventHandler().lastKnownMousePosition(), frame->eventHandler().lastKnownMouseGlobalPosition(), NoButton, PlatformEvent::NoType, 1, false, false, false, false, WTF::currentTime(), ForceAtClick, NoTap };
2495 auto mouseForceWillBeginEvent = MouseEvent::create(eventNames().webkitmouseforcewillbeginEvent, document().defaultView(), platformMouseEvent, 0, nullptr);
2496 mouseForceWillBeginEvent->setTarget(this);
2497 dispatchEvent(mouseForceWillBeginEvent);
2499 if (mouseForceWillBeginEvent->defaultHandled() || mouseForceWillBeginEvent->defaultPrevented())
2506 ExceptionOr<void> Element::mergeWithNextTextNode(Text& node)
2508 auto* next = node.nextSibling();
2509 if (!is<Text>(next))
2511 Ref<Text> textNext { downcast<Text>(*next) };
2512 node.appendData(textNext->data());
2513 return textNext->remove();
2516 String Element::innerHTML() const
2518 return createMarkup(*this, ChildrenOnly);
2521 String Element::outerHTML() const
2523 return createMarkup(*this);
2526 ExceptionOr<void> Element::setOuterHTML(const String& html)
2528 auto* parentElement = this->parentElement();
2529 if (!is<HTMLElement>(parentElement))
2530 return Exception { NO_MODIFICATION_ALLOWED_ERR };
2532 Ref<HTMLElement> parent = downcast<HTMLElement>(*parentElement);
2533 RefPtr<Node> prev = previousSibling();
2534 RefPtr<Node> next = nextSibling();
2536 auto fragment = createFragmentForInnerOuterHTML(parent, html, AllowScriptingContent);
2537 if (fragment.hasException())
2538 return fragment.releaseException();
2540 auto replaceResult = parent->replaceChild(fragment.releaseReturnValue().get(), *this);
2541 if (replaceResult.hasException())
2542 return replaceResult.releaseException();
2544 RefPtr<Node> node = next ? next->previousSibling() : nullptr;
2545 if (is<Text>(node.get())) {
2546 auto result = mergeWithNextTextNode(downcast<Text>(*node));
2547 if (result.hasException())
2548 return result.releaseException();
2550 if (is<Text>(prev.get())) {
2551 auto result = mergeWithNextTextNode(downcast<Text>(*prev));
2552 if (result.hasException())
2553 return result.releaseException();
2559 ExceptionOr<void> Element::setInnerHTML(const String& html)
2561 auto fragment = createFragmentForInnerOuterHTML(*this, html, AllowScriptingContent);
2562 if (fragment.hasException())
2563 return fragment.releaseException();
2565 ContainerNode* container;
2566 if (!is<HTMLTemplateElement>(*this))
2569 container = &downcast<HTMLTemplateElement>(*this).content();
2571 return replaceChildrenWithFragment(*container, fragment.releaseReturnValue());
2574 String Element::innerText()
2576 // We need to update layout, since plainText uses line boxes in the render tree.
2577 document().updateLayoutIgnorePendingStylesheets();
2580 return textContent(true);
2582 return plainText(rangeOfContents(*this).ptr());
2585 String Element::outerText()
2587 // Getting outerText is the same as getting innerText, only
2588 // setting is different. You would think this should get the plain
2589 // text for the outer range, but this is wrong, <br> for instance
2590 // would return different values for inner and outer text by such
2591 // a rule, but it doesn't in WinIE, and we want to match that.
2595 String Element::title() const
2600 const AtomicString& Element::pseudo() const
2602 return attributeWithoutSynchronization(pseudoAttr);
2605 void Element::setPseudo(const AtomicString& value)
2607 setAttributeWithoutSynchronization(pseudoAttr, value);
2610 LayoutSize Element::minimumSizeForResizing() const
2612 return hasRareData() ? elementRareData()->minimumSizeForResizing() : defaultMinimumSizeForResizing();
2615 void Element::setMinimumSizeForResizing(const LayoutSize& size)
2617 if (!hasRareData() && size == defaultMinimumSizeForResizing())
2619 ensureElementRareData().setMinimumSizeForResizing(size);
2622 void Element::willBecomeFullscreenElement()
2624 for (auto& child : descendantsOfType<Element>(*this))
2625 child.ancestorWillEnterFullscreen();
2628 static PseudoElement* beforeOrAfterPseudoElement(Element& host, PseudoId pseudoElementSpecifier)
2630 switch (pseudoElementSpecifier) {
2632 return host.beforePseudoElement();
2634 return host.afterPseudoElement();
2640 const RenderStyle* Element::existingComputedStyle()
2642 if (auto* renderTreeStyle = renderStyle())
2643 return renderTreeStyle;
2646 return elementRareData()->computedStyle();
2651 const RenderStyle& Element::resolveComputedStyle()
2653 ASSERT(isConnected());
2654 ASSERT(!existingComputedStyle());
2656 Deque<Element*, 32> elementsRequiringComputedStyle({ this });
2657 const RenderStyle* computedStyle = nullptr;
2659 // Collect ancestors until we find one that has style.
2660 auto composedAncestors = composedTreeAncestors(*this);
2661 for (auto& ancestor : composedAncestors) {
2662 elementsRequiringComputedStyle.prepend(&ancestor);
2663 if (auto* existingStyle = ancestor.existingComputedStyle()) {
2664 computedStyle = existingStyle;
2669 // Resolve and cache styles starting from the most distant ancestor.
2670 for (auto* element : elementsRequiringComputedStyle) {
2671 auto style = document().styleForElementIgnoringPendingStylesheets(*element, computedStyle);
2672 computedStyle = style.get();
2673 ElementRareData& rareData = element->ensureElementRareData();
2674 rareData.setComputedStyle(WTFMove(style));
2677 return *computedStyle;
2680 const RenderStyle* Element::computedStyle(PseudoId pseudoElementSpecifier)
2685 if (PseudoElement* pseudoElement = beforeOrAfterPseudoElement(*this, pseudoElementSpecifier))
2686 return pseudoElement->computedStyle();
2688 auto* style = existingComputedStyle();
2690 style = &resolveComputedStyle();
2692 if (pseudoElementSpecifier) {
2693 if (auto* cachedPseudoStyle = style->getCachedPseudoStyle(pseudoElementSpecifier))
2694 return cachedPseudoStyle;
2700 bool Element::needsStyleInvalidation() const
2702 if (!inRenderedDocument())
2704 if (styleValidity() >= Style::Validity::SubtreeInvalid)
2706 if (document().hasPendingForcedStyleRecalc())
2712 void Element::setStyleAffectedByEmpty()
2714 ensureElementRareData().setStyleAffectedByEmpty(true);
2717 void Element::setStyleAffectedByFocusWithin()
2719 ensureElementRareData().setStyleAffectedByFocusWithin(true);
2722 void Element::setStyleAffectedByActive()
2724 ensureElementRareData().setStyleAffectedByActive(true);
2727 void Element::setChildrenAffectedByDrag()
2729 ensureElementRareData().setChildrenAffectedByDrag(true);
2732 void Element::setChildrenAffectedByBackwardPositionalRules()
2734 ensureElementRareData().setChildrenAffectedByBackwardPositionalRules(true);
2737 void Element::setChildrenAffectedByPropertyBasedBackwardPositionalRules()
2739 ensureElementRareData().setChildrenAffectedByPropertyBasedBackwardPositionalRules(true);
2742 void Element::setChildIndex(unsigned index)
2744 ElementRareData& rareData = ensureElementRareData();
2745 rareData.setChildIndex(index);
2748 bool Element::hasFlagsSetDuringStylingOfChildren() const
2750 if (childrenAffectedByHover() || childrenAffectedByFirstChildRules() || childrenAffectedByLastChildRules())
2755 return rareDataStyleAffectedByActive()
2756 || rareDataChildrenAffectedByDrag()
2757 || rareDataChildrenAffectedByBackwardPositionalRules()
2758 || rareDataChildrenAffectedByPropertyBasedBackwardPositionalRules();
2761 bool Element::rareDataStyleAffectedByEmpty() const
2763 ASSERT(hasRareData());
2764 return elementRareData()->styleAffectedByEmpty();
2767 bool Element::rareDataStyleAffectedByFocusWithin() const
2769 ASSERT(hasRareData());
2770 return elementRareData()->styleAffectedByFocusWithin();
2773 bool Element::rareDataIsNamedFlowContentElement() const
2775 ASSERT(hasRareData());
2776 return elementRareData()->isNamedFlowContentElement();
2779 bool Element::rareDataStyleAffectedByActive() const
2781 ASSERT(hasRareData());
2782 return elementRareData()->styleAffectedByActive();
2785 bool Element::rareDataChildrenAffectedByDrag() const
2787 ASSERT(hasRareData());
2788 return elementRareData()->childrenAffectedByDrag();
2791 bool Element::rareDataChildrenAffectedByBackwardPositionalRules() const
2793 ASSERT(hasRareData());
2794 return elementRareData()->childrenAffectedByBackwardPositionalRules();
2797 bool Element::rareDataChildrenAffectedByPropertyBasedBackwardPositionalRules() const
2799 ASSERT(hasRareData());
2800 return elementRareData()->childrenAffectedByPropertyBasedBackwardPositionalRules();
2803 unsigned Element::rareDataChildIndex() const
2805 ASSERT(hasRareData());
2806 return elementRareData()->childIndex();
2809 void Element::setRegionOversetState(RegionOversetState state)
2811 ensureElementRareData().setRegionOversetState(state);
2814 RegionOversetState Element::regionOversetState() const
2816 return hasRareData() ? elementRareData()->regionOversetState() : RegionUndefined;
2819 AtomicString Element::computeInheritedLanguage() const
2821 if (const ElementData* elementData = this->elementData()) {
2822 if (const Attribute* attribute = elementData->findLanguageAttribute())
2823 return attribute->value();
2826 // The language property is inherited, so we iterate over the parents to find the first language.
2827 const Node* currentNode = this;
2828 while ((currentNode = currentNode->parentNode())) {
2829 if (is<Element>(*currentNode)) {
2830 if (const ElementData* elementData = downcast<Element>(*currentNode).elementData()) {
2831 if (const Attribute* attribute = elementData->findLanguageAttribute())
2832 return attribute->value();
2834 } else if (is<Document>(*currentNode)) {
2835 // checking the MIME content-language
2836 return downcast<Document>(*currentNode).contentLanguage();
2843 Locale& Element::locale() const
2845 return document().getCachedLocale(computeInheritedLanguage());
2848 void Element::cancelFocusAppearanceUpdate()
2851 elementRareData()->setNeedsFocusAppearanceUpdateSoonAfterAttach(false);
2852 if (document().focusedElement() == this)
2853 document().cancelFocusAppearanceUpdate();
2856 void Element::normalizeAttributes()
2858 if (!hasAttributes())
2861 auto* attrNodeList = attrNodeListForElement(*this);
2865 // Copy the Attr Vector because Node::normalize() can fire synchronous JS
2866 // events (e.g. DOMSubtreeModified) and a JS listener could add / remove
2867 // attributes while we are iterating.
2868 auto copyOfAttrNodeList = *attrNodeList;
2869 for (auto& attrNode : copyOfAttrNodeList)
2870 attrNode->normalize();
2873 PseudoElement* Element::beforePseudoElement() const
2875 return hasRareData() ? elementRareData()->beforePseudoElement() : nullptr;
2878 PseudoElement* Element::afterPseudoElement() const
2880 return hasRareData() ? elementRareData()->afterPseudoElement() : nullptr;
2883 void Element::setBeforePseudoElement(Ref<PseudoElement>&& element)
2885 ensureElementRareData().setBeforePseudoElement(WTFMove(element));
2888 void Element::setAfterPseudoElement(Ref<PseudoElement>&& element)
2890 ensureElementRareData().setAfterPseudoElement(WTFMove(element));
2893 static void disconnectPseudoElement(PseudoElement* pseudoElement)
2897 if (pseudoElement->renderer())
2898 RenderTreeUpdater::tearDownRenderers(*pseudoElement);
2899 ASSERT(pseudoElement->hostElement());
2900 pseudoElement->clearHostElement();
2903 void Element::clearBeforePseudoElement()
2907 disconnectPseudoElement(elementRareData()->beforePseudoElement());
2908 elementRareData()->setBeforePseudoElement(nullptr);
2911 void Element::clearAfterPseudoElement()
2915 disconnectPseudoElement(elementRareData()->afterPseudoElement());
2916 elementRareData()->setAfterPseudoElement(nullptr);
2919 bool Element::matchesValidPseudoClass() const
2924 bool Element::matchesInvalidPseudoClass() const
2929 bool Element::matchesReadWritePseudoClass() const
2934 bool Element::matchesIndeterminatePseudoClass() const
2936 return shouldAppearIndeterminate();
2939 bool Element::matchesDefaultPseudoClass() const
2944 ExceptionOr<bool> Element::matches(const String& selector)
2946 auto query = document().selectorQueryForString(selector);
2947 if (query.hasException())
2948 return query.releaseException();
2949 return query.releaseReturnValue().matches(*this);
2952 ExceptionOr<Element*> Element::closest(const String& selector)
2954 auto query = document().selectorQueryForString(selector);
2955 if (query.hasException())
2956 return query.releaseException();
2957 return query.releaseReturnValue().closest(*this);
2960 bool Element::shouldAppearIndeterminate() const
2965 bool Element::mayCauseRepaintInsideViewport(const IntRect* visibleRect) const
2967 return renderer() && renderer()->mayCauseRepaintInsideViewport(visibleRect);
2970 DOMTokenList& Element::classList()
2972 ElementRareData& data = ensureElementRareData();
2973 if (!data.classList())
2974 data.setClassList(std::make_unique<DOMTokenList>(*this, HTMLNames::classAttr));
2975 return *data.classList();
2978 DatasetDOMStringMap& Element::dataset()
2980 ElementRareData& data = ensureElementRareData();
2981 if (!data.dataset())
2982 data.setDataset(std::make_unique<DatasetDOMStringMap>(*this));
2983 return *data.dataset();
2986 URL Element::getURLAttribute(const QualifiedName& name) const
2988 #if !ASSERT_DISABLED
2989 if (elementData()) {
2990 if (const Attribute* attribute = findAttributeByName(name))
2991 ASSERT(isURLAttribute(*attribute));
2994 return document().completeURL(stripLeadingAndTrailingHTMLSpaces(getAttribute(name)));
2997 URL Element::getNonEmptyURLAttribute(const QualifiedName& name) const
2999 #if !ASSERT_DISABLED
3000 if (elementData()) {
3001 if (const Attribute* attribute = findAttributeByName(name))
3002 ASSERT(isURLAttribute(*attribute));
3005 String value = stripLeadingAndTrailingHTMLSpaces(getAttribute(name));
3006 if (value.isEmpty())
3008 return document().completeURL(value);
3011 int Element::getIntegralAttribute(const QualifiedName& attributeName) const
3013 return parseHTMLInteger(getAttribute(attributeName)).value_or(0);
3016 void Element::setIntegralAttribute(const QualifiedName& attributeName, int value)
3018 setAttribute(attributeName, AtomicString::number(value));
3021 unsigned Element::getUnsignedIntegralAttribute(const QualifiedName& attributeName) const
3023 return parseHTMLNonNegativeInteger(getAttribute(attributeName)).value_or(0);
3026 void Element::setUnsignedIntegralAttribute(const QualifiedName& attributeName, unsigned value)
3028 setAttribute(attributeName, AtomicString::number(limitToOnlyHTMLNonNegative(value)));
3031 bool Element::childShouldCreateRenderer(const Node& child) const
3033 // Only create renderers for SVG elements whose parents are SVG elements, or for proper <svg xmlns="svgNS"> subdocuments.
3034 if (child.isSVGElement()) {
3035 ASSERT(!isSVGElement());
3036 const SVGElement& childElement = downcast<SVGElement>(child);
3037 return is<SVGSVGElement>(childElement) && childElement.isValid();
3042 #if ENABLE(FULLSCREEN_API)
3043 void Element::webkitRequestFullscreen()
3045 document().requestFullScreenForElement(this, Document::EnforceIFrameAllowFullScreenRequirement);
3048 bool Element::containsFullScreenElement() const
3050 return hasRareData() && elementRareData()->containsFullScreenElement();
3053 void Element::setContainsFullScreenElement(bool flag)
3055 ensureElementRareData().setContainsFullScreenElement(flag);
3056 invalidateStyleAndLayerComposition();
3059 static Element* parentCrossingFrameBoundaries(Element* element)
3062 return element->parentElement() ? element->parentElement() : element->document().ownerElement();
3065 void Element::setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(bool flag)
3067 Element* element = this;
3068 while ((element = parentCrossingFrameBoundaries(element)))
3069 element->setContainsFullScreenElement(flag);
3073 #if ENABLE(POINTER_LOCK)
3074 void Element::requestPointerLock()
3076 if (document().page())
3077 document().page()->pointerLockController().requestPointerLock(this);
3081 SpellcheckAttributeState Element::spellcheckAttributeState() const
3083 const AtomicString& value = attributeWithoutSynchronization(HTMLNames::spellcheckAttr);
3085 return SpellcheckAttributeDefault;
3086 if (value.isEmpty() || equalLettersIgnoringASCIICase(value, "true"))
3087 return SpellcheckAttributeTrue;
3088 if (equalLettersIgnoringASCIICase(value, "false"))
3089 return SpellcheckAttributeFalse;
3090 return SpellcheckAttributeDefault;
3093 bool Element::isSpellCheckingEnabled() const
3095 for (const Element* element = this; element; element = element->parentOrShadowHostElement()) {
3096 switch (element->spellcheckAttributeState()) {
3097 case SpellcheckAttributeTrue:
3099 case SpellcheckAttributeFalse:
3101 case SpellcheckAttributeDefault:
3109 RenderNamedFlowFragment* Element::renderNamedFlowFragment() const
3111 if (renderer() && renderer()->isRenderNamedFlowFragmentContainer())
3112 return downcast<RenderBlockFlow>(*renderer()).renderNamedFlowFragment();
3117 #if ENABLE(CSS_REGIONS)
3119 bool Element::shouldMoveToFlowThread(const RenderStyle& styleToUse) const
3121 #if ENABLE(FULLSCREEN_API)
3122 if (document().webkitIsFullScreen() && document().webkitCurrentFullScreenElement() == this)
3126 if (isInShadowTree())
3129 if (!styleToUse.hasFlowInto())
3135 const AtomicString& Element::webkitRegionOverset() const
3137 document().updateLayoutIgnorePendingStylesheets();
3139 static NeverDestroyed<AtomicString> undefinedState("undefined", AtomicString::ConstructFromLiteral);
3140 if (!renderNamedFlowFragment())
3141 return undefinedState;
3143 switch (regionOversetState()) {
3145 static NeverDestroyed<AtomicString> fitState("fit", AtomicString::ConstructFromLiteral);
3149 static NeverDestroyed<AtomicString> emptyState("empty", AtomicString::ConstructFromLiteral);
3152 case RegionOverset: {
3153 static NeverDestroyed<AtomicString> overflowState("overset", AtomicString::ConstructFromLiteral);
3154 return overflowState;
3156 case RegionUndefined:
3157 return undefinedState;
3160 ASSERT_NOT_REACHED();
3161 return undefinedState;
3164 Vector<RefPtr<Range>> Element::webkitGetRegionFlowRanges() const
3166 Vector<RefPtr<Range>> rangeObjects;
3167 document().updateLayoutIgnorePendingStylesheets();
3168 auto* renderer = this->renderer();
3169 if (renderer && renderer->isRenderNamedFlowFragmentContainer()) {
3170 auto& namedFlowFragment = *downcast<RenderBlockFlow>(*renderer).renderNamedFlowFragment();
3171 if (namedFlowFragment.isValid())
3172 namedFlowFragment.getRanges(rangeObjects);
3174 return rangeObjects;
3180 bool Element::fastAttributeLookupAllowed(const QualifiedName& name) const
3182 if (name == HTMLNames::styleAttr)
3186 return !downcast<SVGElement>(*this).isAnimatableAttribute(name);
3192 #ifdef DUMP_NODE_STATISTICS
3193 bool Element::hasNamedNodeMap() const
3195 return hasRareData() && elementRareData()->attributeMap();
3199 inline void Element::updateName(const AtomicString& oldName, const AtomicString& newName)
3201 if (!isInTreeScope())
3204 if (oldName == newName)
3207 updateNameForTreeScope(treeScope(), oldName, newName);
3211 if (!is<HTMLDocument>(document()))
3213 updateNameForDocument(downcast<HTMLDocument>(document()), oldName, newName);
3216 void Element::updateNameForTreeScope(TreeScope& scope, const AtomicString& oldName, const AtomicString& newName)
3218 ASSERT(oldName != newName);
3220 if (!oldName.isEmpty())
3221 scope.removeElementByName(*oldName.impl(), *this);
3222 if (!newName.isEmpty())
3223 scope.addElementByName(*newName.impl(), *this);
3226 void Element::updateNameForDocument(HTMLDocument& document, const AtomicString& oldName, const AtomicString& newName)
3228 ASSERT(oldName != newName);
3230 if (isInShadowTree())
3233 if (WindowNameCollection::elementMatchesIfNameAttributeMatch(*this)) {
3234 const AtomicString& id = WindowNameCollection::elementMatchesIfIdAttributeMatch(*this) ? getIdAttribute() : nullAtom;
3235 if (!oldName.isEmpty() && oldName != id)
3236 document.removeWindowNamedItem(*oldName.impl(), *this);
3237 if (!newName.isEmpty() && newName != id)
3238 document.addWindowNamedItem(*newName.impl(), *this);
3241 if (DocumentNameCollection::elementMatchesIfNameAttributeMatch(*this)) {
3242 const AtomicString& id = DocumentNameCollection::elementMatchesIfIdAttributeMatch(*this) ? getIdAttribute() : nullAtom;
3243 if (!oldName.isEmpty() && oldName != id)
3244 document.removeDocumentNamedItem(*oldName.impl(), *this);
3245 if (!newName.isEmpty() && newName != id)
3246 document.addDocumentNamedItem(*newName.impl(), *this);
3250 inline void Element::updateId(const AtomicString& oldId, const AtomicString& newId, NotifyObservers notifyObservers)
3252 if (!isInTreeScope())
3258 updateIdForTreeScope(treeScope(), oldId, newId, notifyObservers);
3262 if (!is<HTMLDocument>(document()))
3264 updateIdForDocument(downcast<HTMLDocument>(document()), oldId, newId, UpdateHTMLDocumentNamedItemMapsOnlyIfDiffersFromNameAttribute);
3267 void Element::updateIdForTreeScope(TreeScope& scope, const AtomicString& oldId, const AtomicString& newId, NotifyObservers notifyObservers)
3269 ASSERT(isInTreeScope());
3270 ASSERT(oldId != newId);
3272 if (!oldId.isEmpty())
3273 scope.removeElementById(*oldId.impl(), *this, notifyObservers == NotifyObservers::Yes);
3274 if (!newId.isEmpty())
3275 scope.addElementById(*newId.impl(), *this, notifyObservers == NotifyObservers::Yes);
3278 void Element::updateIdForDocument(HTMLDocument& document, const AtomicString& oldId, const AtomicString& newId, HTMLDocumentNamedItemMapsUpdatingCondition condition)
3280 ASSERT(isConnected());
3281 ASSERT(oldId != newId);
3283 if (isInShadowTree())
3286 if (WindowNameCollection::elementMatchesIfIdAttributeMatch(*this)) {
3287 const AtomicString& name = condition == UpdateHTMLDocumentNamedItemMapsOnlyIfDiffersFromNameAttribute && WindowNameCollection::elementMatchesIfNameAttributeMatch(*this) ? getNameAttribute() : nullAtom;
3288 if (!oldId.isEmpty() && oldId != name)
3289 document.removeWindowNamedItem(*oldId.impl(), *this);
3290 if (!newId.isEmpty() && newId != name)
3291 document.addWindowNamedItem(*newId.impl(), *this);
3294 if (DocumentNameCollection::elementMatchesIfIdAttributeMatch(*this)) {
3295 const AtomicString& name = condition == UpdateHTMLDocumentNamedItemMapsOnlyIfDiffersFromNameAttribute && DocumentNameCollection::elementMatchesIfNameAttributeMatch(*this) ? getNameAttribute() : nullAtom;
3296 if (!oldId.isEmpty() && oldId != name)
3297 document.removeDocumentNamedItem(*oldId.impl(), *this);
3298 if (!newId.isEmpty() && newId != name)
3299 document.addDocumentNamedItem(*newId.impl(), *this);
3303 void Element::updateLabel(TreeScope& scope, const AtomicString& oldForAttributeValue, const AtomicString& newForAttributeValue)
3305 ASSERT(hasTagName(labelTag));
3310 if (oldForAttributeValue == newForAttributeValue)
3313 if (!oldForAttributeValue.isEmpty())
3314 scope.removeLabel(*oldForAttributeValue.impl(), downcast<HTMLLabelElement>(*this));
3315 if (!newForAttributeValue.isEmpty())
3316 scope.addLabel(*newForAttributeValue.impl(), downcast<HTMLLabelElement>(*this));
3319 void Element::willModifyAttribute(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue)
3321 if (name == HTMLNames::idAttr)
3322 updateId(oldValue, newValue, NotifyObservers::No); // Will notify observers after the attribute is actually changed.
3323 else if (name == HTMLNames::nameAttr)
3324 updateName(oldValue, newValue);
3325 else if (name == HTMLNames::forAttr && hasTagName(labelTag)) {
3326 if (treeScope().shouldCacheLabelsByForAttribute())
3327 updateLabel(treeScope(), oldValue, newValue);
3330 if (auto recipients = MutationObserverInterestGroup::createForAttributesMutation(*this, name))
3331 recipients->enqueueMutationRecord(MutationRecord::createAttributes(*this, name, oldValue));
3333 InspectorInstrumentation::willModifyDOMAttr(document(), *this, oldValue, newValue);
3336 void Element::didAddAttribute(const QualifiedName& name, const AtomicString& value)
3338 attributeChanged(name, nullAtom, value);
3339 InspectorInstrumentation::didModifyDOMAttr(document(), *this, name.localName(), value);
3340 dispatchSubtreeModifiedEvent();
3343 void Element::didModifyAttribute(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue)
3345 attributeChanged(name, oldValue, newValue);
3346 InspectorInstrumentation::didModifyDOMAttr(document(), *this, name.localName(), newValue);
3347 // Do not dispatch a DOMSubtreeModified event here; see bug 81141.
3350 void Element::didRemoveAttribute(const QualifiedName& name, const AtomicString& oldValue)
3352 attributeChanged(name, oldValue, nullAtom);
3353 InspectorInstrumentation::didRemoveDOMAttr(document(), *this, name.localName());
3354 dispatchSubtreeModifiedEvent();
3357 IntPoint Element::savedLayerScrollPosition() const
3359 return hasRareData() ? elementRareData()->savedLayerScrollPosition() : IntPoint();
3362 void Element::setSavedLayerScrollPosition(const IntPoint& position)
3364 if (position.isZero() && !hasRareData())
3366 ensureElementRareData().setSavedLayerScrollPosition(position);
3369 RefPtr<Attr> Element::attrIfExists(const AtomicString& localName, bool shouldIgnoreAttributeCase)
3371 if (auto* attrNodeList = attrNodeListForElement(*this))
3372 return findAttrNodeInList(*attrNodeList, localName, shouldIgnoreAttributeCase);
3376 RefPtr<Attr> Element::attrIfExists(const QualifiedName& name)
3378 if (auto* attrNodeList = attrNodeListForElement(*this))
3379 return findAttrNodeInList(*attrNodeList, name);
3383 Ref<Attr> Element::ensureAttr(const QualifiedName& name)
3385 auto& attrNodeList = ensureAttrNodeListForElement(*this);
3386 RefPtr<Attr> attrNode = findAttrNodeInList(attrNodeList, name);
3388 attrNode = Attr::create(*this, name);
3389 treeScope().adoptIfNeeded(*attrNode);
3390 attrNodeList.append(attrNode);
3392 return attrNode.releaseNonNull();
3395 void Element::detachAttrNodeFromElementWithValue(Attr* attrNode, const AtomicString& value)
3397 ASSERT(hasSyntheticAttrChildNodes());
3398 attrNode->detachFromElementWithValue(value);
3400 auto& attrNodeList = *attrNodeListForElement(*this);
3401 bool found = attrNodeList.removeFirstMatching([attrNode](auto& attribute) {
3402 return attribute->qualifiedName() == attrNode->qualifiedName();
3404 ASSERT_UNUSED(found, found);
3405 if (attrNodeList.isEmpty())
3406 removeAttrNodeListForElement(*this);
3409 void Element::detachAllAttrNodesFromElement()
3411 auto* attrNodeList = attrNodeListForElement(*this);
3412 ASSERT(attrNodeList);
3414 for (const Attribute& attribute : attributesIterator()) {
3415 if (RefPtr<Attr> attrNode = findAttrNodeInList(*attrNodeList, attribute.name()))
3416 attrNode->detachFromElementWithValue(attribute.value());
3419 removeAttrNodeListForElement(*this);
3422 void Element::resetComputedStyle()
3424 if (!hasRareData() || !elementRareData()->computedStyle())
3427 auto reset = [](Element& element) {
3428 if (!element.hasRareData() || !element.elementRareData()->computedStyle())
3430 if (element.hasCustomStyleResolveCallbacks())
3431 element.willResetComputedStyle();
3432 element.elementRareData()->resetComputedStyle();
3435 for (auto& child : descendantsOfType<Element>(*this))
3439 void Element::clearStyleDerivedDataBeforeDetachingRenderer()
3441 unregisterNamedFlowContentElement();
3442 cancelFocusAppearanceUpdate();
3443 clearBeforePseudoElement();
3444 clearAfterPseudoElement();
3447 ElementRareData* data = elementRareData();
3448 data->resetComputedStyle();
3449 data->resetDynamicRestyleObservations();
3452 void Element::clearHoverAndActiveStatusBeforeDetachingRenderer()
3454 if (!isUserActionElement())
3457 document().hoveredElementDidDetach(this);
3458 if (inActiveChain())
3459 document().elementInActiveChainDidDetach(this);
3460 document().userActionElements().didDetach(this);
3463 void Element::willRecalcStyle(Style::Change)
3465 ASSERT(hasCustomStyleResolveCallbacks());
3468 void Element::didRecalcStyle(Style::Change)
3470 ASSERT(hasCustomStyleResolveCallbacks());
3473 void Element::willResetComputedStyle()
3475 ASSERT(hasCustomStyleResolveCallbacks());
3478 void Element::willAttachRenderers()
3480 ASSERT(hasCustomStyleResolveCallbacks());
3483 void Element::didAttachRenderers()
3485 ASSERT(hasCustomStyleResolveCallbacks());
3488 void Element::willDetachRenderers()
3490 ASSERT(hasCustomStyleResolveCallbacks());
3493 void Element::didDetachRenderers()
3495 ASSERT(hasCustomStyleResolveCallbacks());
3498 std::optional<ElementStyle> Element::resolveCustomStyle(const RenderStyle&, const RenderStyle*)
3500 ASSERT(hasCustomStyleResolveCallbacks());
3501 return std::nullopt;
3504 void Element::cloneAttributesFromElement(const Element& other)
3506 if (hasSyntheticAttrChildNodes())
3507 detachAllAttrNodesFromElement();
3509 other.synchronizeAllAttributes();
3510 if (!other.m_elementData) {
3511 m_elementData = nullptr;
3515 // 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.
3516 // Fortunately, those named item maps are only updated when this element is in the document, which should never be the case.
3517 ASSERT(!isConnected());
3519 const AtomicString& oldID = getIdAttribute();
3520 const AtomicString& newID = other.getIdAttribute();
3522 if (!oldID.isNull() || !newID.isNull())
3523 updateId(oldID, newID, NotifyObservers::No); // Will notify observers after the attribute is actually changed.
3525 const AtomicString& oldName = getNameAttribute();
3526 const AtomicString& newName = other.getNameAttribute();
3528 if (!oldName.isNull() || !newName.isNull())
3529 updateName(oldName, newName);
3531 // If 'other' has a mutable ElementData, convert it to an immutable one so we can share it between both elements.
3532 // We can only do this if there is no CSSOM wrapper for other's inline style, and there are no presentation attributes.
3533 if (is<UniqueElementData>(*other.m_elementData)
3534 && !other.m_elementData->presentationAttributeStyle()
3535 && (!other.m_elementData->inlineStyle() || !other.m_elementData->inlineStyle()->hasCSSOMWrapper()))
3536 const_cast<Element&>(other).m_elementData = downcast<UniqueElementData>(*other.m_elementData).makeShareableCopy();
3538 if (!other.m_elementData->isUnique())
3539 m_elementData = other.m_elementData;
3541 m_elementData = other.m_elementData->makeUniqueCopy();
3543 for (const Attribute& attribute : attributesIterator())
3544 attributeChanged(attribute.name(), nullAtom, attribute.value(), ModifiedByCloning);
3547 void Element::cloneDataFromElement(const Element& other)
3549 cloneAttributesFromElement(other);
3550 copyNonAttributePropertiesFromElement(other);
3553 void Element::createUniqueElementData()
3556 m_elementData = UniqueElementData::create();
3558 m_elementData = downcast<ShareableElementData>(*m_elementData).makeUniqueCopy();
3561 bool Element::hasPendingResources() const
3563 return hasRareData() && elementRareData()->hasPendingResources();
3566 void Element::setHasPendingResources()
3568 ensureElementRareData().setHasPendingResources(true);
3571 void Element::clearHasPendingResources()
3573 ensureElementRareData().setHasPendingResources(false);
3576 bool Element::canContainRangeEndPoint() const
3578 return !equalLettersIgnoringASCIICase(attributeWithoutSynchronization(roleAttr), "img");
3581 String Element::completeURLsInAttributeValue(const URL& base, const Attribute& attribute) const
3583 return URL(base, attribute.value()).string();
3586 ExceptionOr<Node*> Element::insertAdjacent(const String& where, Ref<Node>&& newChild)
3588 // In Internet Explorer if the element has no parent and where is "beforeBegin" or "afterEnd",
3589 // a document fragment is created and the elements appended in the correct order. This document
3590 // fragment isn't returned anywhere.
3592 // This is impossible for us to implement as the DOM tree does not allow for such structures,
3593 // Opera also appears to disallow such usage.
3595 if (equalLettersIgnoringASCIICase(where, "beforebegin")) {
3596 auto* parent = this->parentNode();
3599 auto result = parent->insertBefore(newChild, this);
3600 if (result.hasException())
3601 return result.releaseException();
3602 return newChild.ptr();
3605 if (equalLettersIgnoringASCIICase(where, "afterbegin")) {
3606 auto result = insertBefore(newChild, firstChild());
3607 if (result.hasException())
3608 return result.releaseException();
3609 return newChild.ptr();
3612 if (equalLettersIgnoringASCIICase(where, "beforeend")) {
3613 auto result = appendChild(newChild);
3614 if (result.hasException())
3615 return result.releaseException();
3616 return newChild.ptr();
3619 if (equalLettersIgnoringASCIICase(where, "afterend")) {
3620 auto* parent = this->parentNode();
3623 auto result = parent->insertBefore(newChild, nextSibling());
3624 if (result.hasException())
3625 return result.releaseException();
3626 return newChild.ptr();
3629 return Exception { SYNTAX_ERR };
3632 ExceptionOr<Element*> Element::insertAdjacentElement(const String& where, Element& newChild)
3634 auto result = insertAdjacent(where, newChild);
3635 if (result.hasException())
3636 return result.releaseException();
3637 return downcast<Element>(result.releaseReturnValue());
3640 // Step 1 of https://w3c.github.io/DOM-Parsing/#dom-element-insertadjacenthtml.
3641 static ExceptionOr<ContainerNode&> contextNodeForInsertion(const String& where, Element& element)
3643 if (equalLettersIgnoringASCIICase(where, "beforebegin") || equalLettersIgnoringASCIICase(where, "afterend")) {
3644 auto* parent = element.parentNode();
3645 if (!parent || is<Document>(*parent))
3646 return Exception { NO_MODIFICATION_ALLOWED_ERR };
3649 if (equalLettersIgnoringASCIICase(where, "afterbegin") || equalLettersIgnoringASCIICase(where, "beforeend"))
3651 return Exception { SYNTAX_ERR };
3654 // Step 2 of https://w3c.github.io/DOM-Parsing/#dom-element-insertadjacenthtml.
3655 static ExceptionOr<Ref<Element>> contextElementForInsertion(const String& where, Element& element)
3657 auto contextNodeResult = contextNodeForInsertion(where, element);
3658 if (contextNodeResult.hasException())
3659 return contextNodeResult.releaseException();
3660 auto& contextNode = contextNodeResult.releaseReturnValue();
3661 if (!is<Element>(contextNode) || (contextNode.document().isHTMLDocument() && is<HTMLHtmlElement>(contextNode)))
3662 return Ref<Element> { HTMLBodyElement::create(contextNode.document()) };
3663 return Ref<Element> { downcast<Element>(contextNode) };
3666 // https://w3c.github.io/DOM-Parsing/#dom-element-insertadjacenthtml
3667 ExceptionOr<void> Element::insertAdjacentHTML(const String& where, const String& markup)
3670 auto contextElement = contextElementForInsertion(where, *this);
3671 if (contextElement.hasException())
3672 return contextElement.releaseException();
3674 auto fragment = createFragmentForInnerOuterHTML(contextElement.releaseReturnValue(), markup, AllowScriptingContent);
3675 if (fragment.hasException())
3676 return fragment.releaseException();
3678 auto result = insertAdjacent(where, fragment.releaseReturnValue());
3679 if (result.hasException())
3680 return result.releaseException();
3684 ExceptionOr<void> Element::insertAdjacentText(const String& where, const String& text)