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