Element.cpp (205365B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* 8 * Base class for all element classes; this provides an implementation 9 * of DOM Core's Element, implements nsIContent, provides 10 * utility methods for subclasses, and so forth. 11 */ 12 13 #include "mozilla/dom/Element.h" 14 15 #include <inttypes.h> 16 17 #include <cstddef> 18 #include <utility> 19 20 #include "DOMMatrix.h" 21 #include "ExpandedPrincipal.h" 22 #include "PresShellInlines.h" 23 #include "jsapi.h" 24 #include "mozAutoDocUpdate.h" 25 #include "mozilla/AnimationComparator.h" 26 #include "mozilla/AnimationTarget.h" 27 #include "mozilla/AsyncEventDispatcher.h" 28 #include "mozilla/CORSMode.h" 29 #include "mozilla/Components.h" 30 #include "mozilla/ComputedStyle.h" 31 #include "mozilla/ContentEvents.h" 32 #include "mozilla/DebugOnly.h" 33 #include "mozilla/DeclarationBlock.h" 34 #include "mozilla/EditorBase.h" 35 #include "mozilla/EffectCompositor.h" 36 #include "mozilla/EffectSet.h" 37 #include "mozilla/ElementAnimationData.h" 38 #include "mozilla/ErrorResult.h" 39 #include "mozilla/EventDispatcher.h" 40 #include "mozilla/EventListenerManager.h" 41 #include "mozilla/EventStateManager.h" 42 #include "mozilla/FloatingPoint.h" 43 #include "mozilla/FullscreenChange.h" 44 #include "mozilla/HTMLEditor.h" 45 #include "mozilla/Likely.h" 46 #include "mozilla/LinkedList.h" 47 #include "mozilla/LookAndFeel.h" 48 #include "mozilla/MappedDeclarationsBuilder.h" 49 #include "mozilla/Maybe.h" 50 #include "mozilla/MouseEvents.h" 51 #include "mozilla/PointerLockManager.h" 52 #include "mozilla/PresShell.h" 53 #include "mozilla/PresShellForwards.h" 54 #include "mozilla/RefPtr.h" 55 #include "mozilla/ReflowOutput.h" 56 #include "mozilla/RelativeTo.h" 57 #include "mozilla/ScrollContainerFrame.h" 58 #include "mozilla/ScrollTypes.h" 59 #include "mozilla/ServoStyleConsts.h" 60 #include "mozilla/ServoStyleConstsInlines.h" 61 #include "mozilla/SizeOfState.h" 62 #include "mozilla/SourceLocation.h" 63 #include "mozilla/StaticAnalysisFunctions.h" 64 #include "mozilla/StaticPrefs_dom.h" 65 #include "mozilla/StaticPrefs_full_screen_api.h" 66 #include "mozilla/StaticString.h" 67 #include "mozilla/TextControlElement.h" 68 #include "mozilla/TextEditor.h" 69 #include "mozilla/TextEvents.h" 70 #include "mozilla/Try.h" 71 #include "mozilla/dom/AnimatableBinding.h" 72 #include "mozilla/dom/Animation.h" 73 #include "mozilla/dom/Attr.h" 74 #include "mozilla/dom/BindContext.h" 75 #include "mozilla/dom/BindingDeclarations.h" 76 #include "mozilla/dom/CSPViolationData.h" 77 #include "mozilla/dom/ChildIterator.h" 78 #include "mozilla/dom/CloseWatcher.h" 79 #include "mozilla/dom/CustomElementRegistry.h" 80 #include "mozilla/dom/DOMRect.h" 81 #include "mozilla/dom/DirectionalityUtils.h" 82 #include "mozilla/dom/Document.h" 83 #include "mozilla/dom/DocumentFragment.h" 84 #include "mozilla/dom/DocumentInlines.h" 85 #include "mozilla/dom/DocumentTimeline.h" 86 #include "mozilla/dom/ElementBinding.h" 87 #include "mozilla/dom/ElementInlines.h" 88 #include "mozilla/dom/Flex.h" 89 #include "mozilla/dom/FragmentOrElement.h" 90 #include "mozilla/dom/FromParser.h" 91 #include "mozilla/dom/Grid.h" 92 #include "mozilla/dom/HTMLDivElement.h" 93 #include "mozilla/dom/HTMLElement.h" 94 #include "mozilla/dom/HTMLParagraphElement.h" 95 #include "mozilla/dom/HTMLPreElement.h" 96 #include "mozilla/dom/HTMLSpanElement.h" 97 #include "mozilla/dom/HTMLTableCellElement.h" 98 #include "mozilla/dom/HTMLTemplateElement.h" 99 #include "mozilla/dom/KeyframeAnimationOptionsBinding.h" 100 #include "mozilla/dom/KeyframeEffect.h" 101 #include "mozilla/dom/MouseEvent.h" 102 #include "mozilla/dom/MouseEventBinding.h" 103 #include "mozilla/dom/MutationObservers.h" 104 #include "mozilla/dom/NodeInfo.h" 105 #include "mozilla/dom/PointerEventHandler.h" 106 #include "mozilla/dom/PolicyContainer.h" 107 #include "mozilla/dom/Promise.h" 108 #include "mozilla/dom/SVGElement.h" 109 #include "mozilla/dom/Sanitizer.h" 110 #include "mozilla/dom/ScriptLoader.h" 111 #include "mozilla/dom/ShadowRoot.h" 112 #include "mozilla/dom/StylePropertyMapReadOnly.h" 113 #include "mozilla/dom/Text.h" 114 #include "mozilla/dom/TreeIterator.h" 115 #include "mozilla/dom/TrustedHTML.h" 116 #include "mozilla/dom/TrustedTypeUtils.h" 117 #include "mozilla/dom/TrustedTypesConstants.h" 118 #include "mozilla/dom/UnbindContext.h" 119 #include "mozilla/dom/ViewTransition.h" 120 #include "mozilla/dom/WindowBinding.h" 121 #include "mozilla/dom/XULCommandEvent.h" 122 #include "mozilla/dom/nsCSPContext.h" 123 #include "mozilla/dom/nsCSPUtils.h" 124 #include "mozilla/gfx/BasePoint.h" 125 #include "mozilla/gfx/BaseRect.h" 126 #include "mozilla/gfx/BaseSize.h" 127 #include "mozilla/gfx/Matrix.h" 128 #include "mozilla/widget/Screen.h" 129 #include "nsAtom.h" 130 #include "nsAttrName.h" 131 #include "nsAttrValueInlines.h" 132 #include "nsAttrValueOrString.h" 133 #include "nsBaseHashtable.h" 134 #include "nsBlockFrame.h" 135 #include "nsCOMPtr.h" 136 #include "nsCSSPseudoElements.h" 137 #include "nsCompatibility.h" 138 #include "nsComputedDOMStyle.h" 139 #include "nsContainerFrame.h" 140 #include "nsContentList.h" 141 #include "nsContentListDeclarations.h" 142 #include "nsContentUtils.h" 143 #include "nsCoord.h" 144 #include "nsDOMAttributeMap.h" 145 #include "nsDOMCSSAttrDeclaration.h" 146 #include "nsDOMMutationObserver.h" 147 #include "nsDOMString.h" 148 #include "nsDOMStringMap.h" 149 #include "nsDOMTokenList.h" 150 #include "nsDocShell.h" 151 #include "nsError.h" 152 #include "nsFlexContainerFrame.h" 153 #include "nsFocusManager.h" 154 #include "nsFrameState.h" 155 #include "nsGenericHTMLElement.h" 156 #include "nsGkAtoms.h" 157 #include "nsGridContainerFrame.h" 158 #include "nsIAutoCompletePopup.h" 159 #include "nsIBrowser.h" 160 #include "nsIContentInlines.h" 161 #include "nsIContentSecurityPolicy.h" 162 #include "nsIDOMXULButtonElement.h" 163 #include "nsIDOMXULContainerElement.h" 164 #include "nsIDOMXULControlElement.h" 165 #include "nsIDOMXULMenuListElement.h" 166 #include "nsIDOMXULMultSelectCntrlEl.h" 167 #include "nsIDOMXULRadioGroupElement.h" 168 #include "nsIDOMXULRelatedElement.h" 169 #include "nsIDOMXULSelectCntrlEl.h" 170 #include "nsIDOMXULSelectCntrlItemEl.h" 171 #include "nsIDocShell.h" 172 #include "nsIFocusManager.h" 173 #include "nsIFrame.h" 174 #include "nsIFrameInlines.h" 175 #include "nsIGlobalObject.h" 176 #include "nsIIOService.h" 177 #include "nsIInterfaceRequestor.h" 178 #include "nsIMemoryReporter.h" 179 #include "nsIMutationObserver.h" 180 #include "nsIPrincipal.h" 181 #include "nsIScriptError.h" 182 #include "nsISpeculativeConnect.h" 183 #include "nsISupports.h" 184 #include "nsISupportsUtils.h" 185 #include "nsIURI.h" 186 #include "nsLayoutUtils.h" 187 #include "nsLineBox.h" 188 #include "nsLiteralString.h" 189 #include "nsNameSpaceManager.h" 190 #include "nsNodeInfoManager.h" 191 #include "nsPIDOMWindow.h" 192 #include "nsPoint.h" 193 #include "nsPresContext.h" 194 #include "nsQueryFrame.h" 195 #include "nsRefPtrHashtable.h" 196 #include "nsSize.h" 197 #include "nsString.h" 198 #include "nsStyleConsts.h" 199 #include "nsStyleStruct.h" 200 #include "nsStyledElement.h" 201 #include "nsTArray.h" 202 #include "nsTextNode.h" 203 #include "nsThreadUtils.h" 204 #include "nsWindowSizes.h" 205 #include "nsXULElement.h" 206 207 #ifdef DEBUG 208 # include "nsRange.h" 209 #endif 210 211 #ifdef ACCESSIBILITY 212 # include "nsAccessibilityService.h" 213 #endif 214 215 using mozilla::gfx::Matrix4x4; 216 217 namespace mozilla::dom { 218 219 // Verify sizes of nodes. We use a template rather than a direct static 220 // assert so that the error message actually displays the sizes. 221 // On 32 bit systems the actual allocated size varies a bit between 222 // OSes/compilers. 223 // 224 // We need different numbers on certain build types to deal with the owning 225 // thread pointer that comes with the non-threadsafe refcount on 226 // nsIContent. 227 #ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED 228 # define EXTRA_DOM_NODE_BYTES 8 229 #else 230 # define EXTRA_DOM_NODE_BYTES 0 231 #endif 232 233 #define ASSERT_NODE_SIZE(type, opt_size_64, opt_size_32) \ 234 template <int a, int sizeOn64, int sizeOn32> \ 235 struct Check##type##Size { \ 236 static_assert((sizeof(void*) == 8 && a == sizeOn64) || \ 237 (sizeof(void*) == 4 && a <= sizeOn32), \ 238 "DOM size changed"); \ 239 }; \ 240 Check##type##Size<sizeof(type), opt_size_64 + EXTRA_DOM_NODE_BYTES, \ 241 opt_size_32 + EXTRA_DOM_NODE_BYTES> \ 242 g##type##CES; 243 244 // Note that mozjemalloc uses a 16 byte quantum, so 64, 80 and 128 are 245 // bucket sizes. 246 ASSERT_NODE_SIZE(Element, 128, 80); 247 ASSERT_NODE_SIZE(HTMLDivElement, 128, 80); 248 ASSERT_NODE_SIZE(HTMLElement, 128, 80); 249 ASSERT_NODE_SIZE(HTMLParagraphElement, 128, 80); 250 ASSERT_NODE_SIZE(HTMLPreElement, 128, 80); 251 ASSERT_NODE_SIZE(HTMLSpanElement, 128, 80); 252 ASSERT_NODE_SIZE(HTMLTableCellElement, 128, 80); 253 ASSERT_NODE_SIZE(Text, 120, 80); 254 255 #undef ASSERT_NODE_SIZE 256 #undef EXTRA_DOM_NODE_BYTES 257 258 } // namespace mozilla::dom 259 260 nsAtom* nsIContent::DoGetID() const { 261 MOZ_ASSERT(HasID(), "Unexpected call"); 262 MOZ_ASSERT(IsElement(), "Only elements can have IDs"); 263 264 return AsElement()->GetParsedAttr(nsGkAtoms::id)->GetAtomValue(); 265 } 266 267 nsIFrame* nsIContent::GetPrimaryFrame(mozilla::FlushType aType) { 268 Document* doc = GetComposedDoc(); 269 if (!doc) { 270 return nullptr; 271 } 272 273 // Cause a flush, so we get up-to-date frame information. 274 if (aType != mozilla::FlushType::None) { 275 doc->FlushPendingNotifications(aType); 276 } 277 278 auto* frame = GetPrimaryFrame(); 279 if (!frame) { 280 return nullptr; 281 } 282 283 if (aType == mozilla::FlushType::Layout) { 284 frame->PresShell()->EnsureReflowIfFrameHasHiddenContent(frame); 285 frame = GetPrimaryFrame(); 286 } 287 288 return frame; 289 } 290 291 bool nsIContent::IsSelectable() const { 292 if (!IsInComposedDoc() || 293 // Generated content is not selectable. 294 IsGeneratedContentContainerForBefore() || 295 IsGeneratedContentContainerForAfter() || 296 // Fully invisible nodes like `Comment` should not be selectable. 297 (!IsElement() && !IsText() && !IsShadowRoot())) { 298 return false; 299 } 300 // If this is editable, this should be selectable even if `user-select` is set 301 // to `none`. 302 if (IsEditable()) { 303 return true; 304 } 305 // ...and same if this is a text control. 306 if (const auto* const textControlElement = 307 mozilla::TextControlElement::FromNode(this)) { 308 if (textControlElement->IsSingleLineTextControlOrTextArea()) { 309 return true; 310 } 311 } 312 // Otherwise, check `user-select` style with the layout if there is. 313 for (const nsIContent* content = this; content; 314 content = content->GetFlattenedTreeParent()) { 315 // First, ask the primary frame. 316 if (nsIFrame* const frame = content->GetPrimaryFrame()) { 317 // FYI: This does the same checks which were done before this loop so that 318 // return true for editable content or text control. 319 return frame->IsSelectable(); 320 } 321 if (!content->IsElement()) { 322 // Okay, we're a `Text` or `ShadowRoot` in a `display:none` element. Let's 323 // check the frame or style of the ancestors in the flattened tree. 324 continue; 325 } 326 // Okay, we're an element whose `display` is `contents` or `none` or which 327 // is in a `display:none` ancestors, we should check whether this element is 328 // directly specified the `user-select` style and if it's not `auto`, 329 // consider whether this is selectable not unselectable. 330 const RefPtr<const mozilla::ComputedStyle> elementStyle = 331 nsComputedDOMStyle::GetComputedStyleNoFlush(content->AsElement()); 332 if (elementStyle && 333 elementStyle->UserSelect() != mozilla::StyleUserSelect::Auto) { 334 return elementStyle->UserSelect() != mozilla::StyleUserSelect::None; 335 } 336 // Finally, if `user-select:auto`, let's check the parent. 337 } 338 return false; 339 } 340 341 namespace mozilla::dom { 342 343 const DOMTokenListSupportedToken Element::sSupportedBlockingValues[] = { 344 "render", nullptr}; 345 346 nsDOMAttributeMap* Element::Attributes() { 347 nsDOMSlots* slots = DOMSlots(); 348 if (!slots->mAttributeMap) { 349 slots->mAttributeMap = new nsDOMAttributeMap(this); 350 } 351 352 return slots->mAttributeMap; 353 } 354 355 void Element::SetPointerCapture(int32_t aPointerId, ErrorResult& aError) { 356 const PointerInfo* pointerInfo = 357 PointerEventHandler::GetPointerInfo(aPointerId); 358 if (!pointerInfo) { 359 aError.ThrowNotFoundError("Invalid pointer id"); 360 return; 361 } 362 if (!IsInComposedDoc()) { 363 aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 364 return; 365 } 366 if (OwnerDoc()->GetPointerLockElement()) { 367 // Throw an exception 'InvalidStateError' while the page has a locked 368 // element. 369 aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 370 return; 371 } 372 // XXX If pointerInfo->mIsSynthesizedForTests does not match the last 373 // WidgetPointerEvent's mFlags.mIsSynthesizedForTests, should we treat it 374 // as unknown pointerId? 375 if (!pointerInfo->mIsActive || pointerInfo->mActiveDocument != OwnerDoc()) { 376 return; 377 } 378 PointerEventHandler::RequestPointerCaptureById(aPointerId, this); 379 } 380 381 void Element::ReleasePointerCapture(int32_t aPointerId, ErrorResult& aError) { 382 if (!PointerEventHandler::GetPointerInfo(aPointerId)) { 383 aError.ThrowNotFoundError("Invalid pointer id"); 384 return; 385 } 386 if (HasPointerCapture(aPointerId)) { 387 PointerEventHandler::ReleasePointerCaptureById(aPointerId); 388 } 389 } 390 391 bool Element::HasPointerCapture(long aPointerId) { 392 PointerCaptureInfo* pointerCaptureInfo = 393 PointerEventHandler::GetPointerCaptureInfo(aPointerId); 394 if (pointerCaptureInfo && pointerCaptureInfo->mPendingElement == this) { 395 return true; 396 } 397 return false; 398 } 399 400 const nsAttrValue* Element::GetSVGAnimatedClass() const { 401 MOZ_ASSERT(MayHaveClass() && IsSVGElement(), "Unexpected call"); 402 return static_cast<const SVGElement*>(this)->GetAnimatedClassName(); 403 } 404 405 NS_IMETHODIMP 406 Element::QueryInterface(REFNSIID aIID, void** aInstancePtr) { 407 if (aIID.Equals(NS_GET_IID(Element))) { 408 NS_ADDREF_THIS(); 409 *aInstancePtr = this; 410 return NS_OK; 411 } 412 413 NS_ASSERTION(aInstancePtr, "QueryInterface requires a non-NULL destination!"); 414 nsresult rv = FragmentOrElement::QueryInterface(aIID, aInstancePtr); 415 if (NS_SUCCEEDED(rv)) { 416 return NS_OK; 417 } 418 419 return NS_NOINTERFACE; 420 } 421 422 void Element::NotifyStateChange(ElementState aStates) { 423 MOZ_ASSERT(!aStates.IsEmpty()); 424 if (Document* doc = GetComposedDoc()) { 425 nsAutoScriptBlocker scriptBlocker; 426 doc->ElementStateChanged(this, aStates); 427 } 428 } 429 430 } // namespace mozilla::dom 431 432 void nsIContent::UpdateEditableState(bool aNotify) { 433 if (IsInNativeAnonymousSubtree()) { 434 // Don't propagate the editable flag into native anonymous subtrees. 435 if (IsRootOfNativeAnonymousSubtree()) { 436 return; 437 } 438 439 // We allow setting the flag on NAC (explicitly, see 440 // nsTextControlFrame::CreateAnonymousContent for example), but not 441 // unsetting it. 442 // 443 // Otherwise, just the act of binding the NAC subtree into our non-anonymous 444 // parent would clear the flag, which is not good. As we shouldn't move NAC 445 // around, this is fine. 446 if (HasFlag(NODE_IS_EDITABLE)) { 447 return; 448 } 449 } 450 451 nsIContent* parent = GetParent(); 452 SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE)); 453 } 454 455 namespace mozilla::dom { 456 457 void Element::UpdateEditableState(bool aNotify) { 458 nsIContent::UpdateEditableState(aNotify); 459 UpdateReadOnlyState(aNotify); 460 } 461 462 bool Element::IsReadOnlyInternal() const { return !IsEditable(); } 463 464 void Element::UpdateReadOnlyState(bool aNotify) { 465 auto oldState = State(); 466 if (IsReadOnlyInternal()) { 467 RemoveStatesSilently(ElementState::READWRITE); 468 AddStatesSilently(ElementState::READONLY); 469 } else { 470 RemoveStatesSilently(ElementState::READONLY); 471 AddStatesSilently(ElementState::READWRITE); 472 } 473 if (!aNotify) { 474 return; 475 } 476 const auto newState = State(); 477 if (newState != oldState) { 478 NotifyStateChange(newState ^ oldState); 479 } 480 } 481 482 Maybe<int32_t> Element::GetTabIndexAttrValue() { 483 const nsAttrValue* attrVal = GetParsedAttr(nsGkAtoms::tabindex); 484 if (attrVal && attrVal->Type() == nsAttrValue::eInteger) { 485 return Some(attrVal->GetIntegerValue()); 486 } 487 488 return Nothing(); 489 } 490 491 int32_t Element::TabIndex() { 492 Maybe<int32_t> attrVal = GetTabIndexAttrValue(); 493 if (attrVal.isSome()) { 494 return attrVal.value(); 495 } 496 497 return TabIndexDefault(); 498 } 499 500 void Element::Focus(const FocusOptions& aOptions, CallerType aCallerType, 501 ErrorResult& aError) { 502 const RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager(); 503 if (MOZ_UNLIKELY(!fm)) { 504 return; 505 } 506 const OwningNonNull<Element> kungFuDeathGrip(*this); 507 // Also other browsers seem to have the hack to not re-focus (and flush) when 508 // the element is already focused. 509 // Until https://github.com/whatwg/html/issues/4512 is clarified, we'll 510 // maintain interoperatibility by not re-focusing, independent of aOptions. 511 // I.e., `focus({ preventScroll: true})` followed by `focus( { preventScroll: 512 // false })` won't re-focus. 513 if (fm->CanSkipFocus(this)) { 514 fm->NotifyOfReFocus(kungFuDeathGrip); 515 fm->NeedsFlushBeforeEventHandling(this); 516 return; 517 } 518 uint32_t fmFlags = nsFocusManager::ProgrammaticFocusFlags(aOptions); 519 if (aCallerType == CallerType::NonSystem) { 520 fmFlags |= nsIFocusManager::FLAG_NONSYSTEMCALLER; 521 } 522 aError = fm->SetFocus(kungFuDeathGrip, fmFlags); 523 } 524 525 void Element::SetTabIndex(int32_t aTabIndex, mozilla::ErrorResult& aError) { 526 nsAutoString value; 527 value.AppendInt(aTabIndex); 528 529 SetAttr(nsGkAtoms::tabindex, value, aError); 530 } 531 532 void Element::SetShadowRoot(ShadowRoot* aShadowRoot) { 533 nsExtendedDOMSlots* slots = ExtendedDOMSlots(); 534 MOZ_ASSERT(!aShadowRoot || !slots->mShadowRoot, 535 "We shouldn't clear the shadow root without unbind first"); 536 slots->mShadowRoot = aShadowRoot; 537 } 538 539 void Element::SetLastRememberedBSize(float aBSize) { 540 ExtendedDOMSlots()->mLastRememberedBSize = Some(aBSize); 541 } 542 543 void Element::SetLastRememberedISize(float aISize) { 544 ExtendedDOMSlots()->mLastRememberedISize = Some(aISize); 545 } 546 547 void Element::RemoveLastRememberedBSize() { 548 if (nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) { 549 slots->mLastRememberedBSize.reset(); 550 } 551 } 552 553 void Element::RemoveLastRememberedISize() { 554 if (nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) { 555 slots->mLastRememberedISize.reset(); 556 } 557 } 558 559 void Element::Blur(mozilla::ErrorResult& aError) { 560 if (!ShouldBlur(this)) { 561 return; 562 } 563 564 Document* doc = GetComposedDoc(); 565 if (!doc) { 566 return; 567 } 568 569 if (nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow()) { 570 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) { 571 aError = fm->ClearFocus(win); 572 } 573 } 574 } 575 576 ElementState Element::StyleStateFromLocks() const { 577 StyleStateLocks locksAndValues = LockedStyleStates(); 578 ElementState locks = locksAndValues.mLocks; 579 ElementState values = locksAndValues.mValues; 580 ElementState state = (mState & ~locks) | (locks & values); 581 582 if (state.HasState(ElementState::VISITED)) { 583 return state & ~ElementState::UNVISITED; 584 } 585 if (state.HasState(ElementState::UNVISITED)) { 586 return state & ~ElementState::VISITED; 587 } 588 589 return state; 590 } 591 592 Element::StyleStateLocks Element::LockedStyleStates() const { 593 StyleStateLocks* locks = 594 static_cast<StyleStateLocks*>(GetProperty(nsGkAtoms::lockedStyleStates)); 595 if (locks) { 596 return *locks; 597 } 598 return StyleStateLocks(); 599 } 600 601 void Element::NotifyStyleStateChange(ElementState aStates) { 602 if (RefPtr<Document> doc = GetComposedDoc()) { 603 if (RefPtr<PresShell> presShell = doc->GetPresShell()) { 604 nsAutoScriptBlocker scriptBlocker; 605 presShell->ElementStateChanged(doc, this, aStates); 606 } 607 } 608 } 609 610 void Element::LockStyleStates(ElementState aStates, bool aEnabled) { 611 StyleStateLocks* locks = new StyleStateLocks(LockedStyleStates()); 612 613 locks->mLocks |= aStates; 614 if (aEnabled) { 615 locks->mValues |= aStates; 616 } else { 617 locks->mValues &= ~aStates; 618 } 619 620 if (aStates.HasState(ElementState::VISITED)) { 621 locks->mLocks &= ~ElementState::UNVISITED; 622 } 623 if (aStates.HasState(ElementState::UNVISITED)) { 624 locks->mLocks &= ~ElementState::VISITED; 625 } 626 627 SetProperty(nsGkAtoms::lockedStyleStates, locks, 628 nsINode::DeleteProperty<StyleStateLocks>); 629 SetHasLockedStyleStates(); 630 631 NotifyStyleStateChange(aStates); 632 } 633 634 void Element::UnlockStyleStates(ElementState aStates) { 635 StyleStateLocks* locks = new StyleStateLocks(LockedStyleStates()); 636 637 locks->mLocks &= ~aStates; 638 639 if (locks->mLocks.IsEmpty()) { 640 RemoveProperty(nsGkAtoms::lockedStyleStates); 641 ClearHasLockedStyleStates(); 642 delete locks; 643 } else { 644 SetProperty(nsGkAtoms::lockedStyleStates, locks, 645 nsINode::DeleteProperty<StyleStateLocks>); 646 } 647 648 NotifyStyleStateChange(aStates); 649 } 650 651 void Element::ClearStyleStateLocks() { 652 StyleStateLocks locks = LockedStyleStates(); 653 654 RemoveProperty(nsGkAtoms::lockedStyleStates); 655 ClearHasLockedStyleStates(); 656 657 NotifyStyleStateChange(locks.mLocks); 658 } 659 660 /* virtual */ 661 nsINode* Element::GetScopeChainParent() const { return OwnerDoc(); } 662 663 JSObject* Element::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { 664 return Element_Binding::Wrap(aCx, this, aGivenProto); 665 } 666 667 nsDOMTokenList* Element::ClassList() { 668 nsDOMSlots* slots = DOMSlots(); 669 if (!slots->mClassList) { 670 slots->mClassList = new nsDOMTokenList(this, nsGkAtoms::_class); 671 } 672 return slots->mClassList; 673 } 674 675 nsDOMTokenList* Element::Part() { 676 nsExtendedDOMSlots* slots = ExtendedDOMSlots(); 677 if (!slots->mPart) { 678 slots->mPart = new nsDOMTokenList(this, nsGkAtoms::part); 679 } 680 return slots->mPart; 681 } 682 683 void Element::RecompileScriptEventListeners() { 684 for (uint32_t i = 0, count = mAttrs.AttrCount(); i < count; ++i) { 685 BorrowedAttrInfo attrInfo = mAttrs.AttrInfoAt(i); 686 687 // Eventlistenener-attributes are always in the null namespace 688 if (!attrInfo.mName->IsAtom()) { 689 continue; 690 } 691 692 nsAtom* attr = attrInfo.mName->Atom(); 693 if (!IsEventAttributeName(attr)) { 694 continue; 695 } 696 697 nsAutoString value; 698 attrInfo.mValue->ToString(value); 699 SetEventHandler(GetEventNameForAttr(attr), value, true); 700 } 701 } 702 703 void Element::GetAttributeNames(nsTArray<nsString>& aResult) { 704 uint32_t count = mAttrs.AttrCount(); 705 for (uint32_t i = 0; i < count; ++i) { 706 const nsAttrName* name = mAttrs.AttrNameAt(i); 707 name->GetQualifiedName(*aResult.AppendElement()); 708 } 709 } 710 711 already_AddRefed<nsIHTMLCollection> Element::GetElementsByTagName( 712 const nsAString& aLocalName) { 713 return NS_GetContentList(this, kNameSpaceID_Unknown, aLocalName); 714 } 715 716 ScrollContainerFrame* Element::GetScrollContainerFrame(nsIFrame** aFrame, 717 FlushType aFlushType) { 718 nsIFrame* frame = GetPrimaryFrame(aFlushType); 719 if (aFrame) { 720 *aFrame = frame; 721 } 722 if (frame) { 723 if (frame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) { 724 // It's unclear what to return for SVG frames, so just return null. 725 return nullptr; 726 } 727 728 if (ScrollContainerFrame* scrollContainerFrame = 729 frame->GetScrollTargetFrame()) { 730 MOZ_ASSERT(!OwnerDoc()->IsScrollingElement(this), 731 "How can we have a scroll container frame if we're the " 732 "scrollingElement for our document?"); 733 return scrollContainerFrame; 734 } 735 } 736 737 Document* doc = OwnerDoc(); 738 // Note: This IsScrollingElement() call can flush frames, if we're the body of 739 // a quirks mode document. 740 const bool isScrollingElement = doc->IsScrollingElement(this); 741 if (isScrollingElement) { 742 // Our scroll info should map to the root scroll container frame if there is 743 // one. 744 if (PresShell* presShell = doc->GetPresShell()) { 745 if (ScrollContainerFrame* rootScrollContainerFrame = 746 presShell->GetRootScrollContainerFrame()) { 747 if (aFrame) { 748 *aFrame = rootScrollContainerFrame; 749 } 750 return rootScrollContainerFrame; 751 } 752 } 753 } 754 if (aFrame) { 755 // Re-get *aFrame if the caller asked for it, because that frame flush can 756 // kill it. 757 *aFrame = GetPrimaryFrame(FlushType::None); 758 } 759 return nullptr; 760 } 761 762 bool Element::CheckVisibility(const CheckVisibilityOptions& aOptions) { 763 nsIFrame* f = 764 GetPrimaryFrame(aOptions.mFlush ? FlushType::Frames : FlushType::None); 765 if (!f) { 766 // 1. If this does not have an associated box, return false. 767 return false; 768 } 769 770 EnumSet includeContentVisibility = { 771 nsIFrame::IncludeContentVisibility::Hidden}; 772 if (aOptions.mContentVisibilityAuto) { 773 includeContentVisibility += nsIFrame::IncludeContentVisibility::Auto; 774 } 775 // Steps 2 and 5 776 if (f->IsHiddenByContentVisibilityOnAnyAncestor(includeContentVisibility)) { 777 // 2. If a shadow-including ancestor of this has content-visibility: hidden, 778 // return false. 779 // 5. If a shadow-including ancestor of this skips its content due to 780 // has content-visibility: auto, return false. 781 return false; 782 } 783 784 if ((aOptions.mOpacityProperty || aOptions.mCheckOpacity) && 785 f->Style()->IsInOpacityZeroSubtree()) { 786 // 3. If the checkOpacity dictionary member of options is true, and this, or 787 // a shadow-including ancestor of this, has a computed opacity value of 0, 788 // return false. 789 return false; 790 } 791 792 if ((aOptions.mVisibilityProperty || aOptions.mCheckVisibilityCSS) && 793 !f->StyleVisibility()->IsVisible()) { 794 // 4. If the checkVisibilityCSS dictionary member of options is true, and 795 // this is invisible, return false. 796 return false; 797 } 798 799 // 6. Return true 800 return true; 801 } 802 803 void Element::ScrollIntoView(const BooleanOrScrollIntoViewOptions& aObject) { 804 if (aObject.IsScrollIntoViewOptions()) { 805 return ScrollIntoView(aObject.GetAsScrollIntoViewOptions()); 806 } 807 808 MOZ_DIAGNOSTIC_ASSERT(aObject.IsBoolean()); 809 810 ScrollIntoViewOptions options; 811 if (aObject.GetAsBoolean()) { 812 options.mBlock = ScrollLogicalPosition::Start; 813 options.mInline = ScrollLogicalPosition::Nearest; 814 } else { 815 options.mBlock = ScrollLogicalPosition::End; 816 options.mInline = ScrollLogicalPosition::Nearest; 817 } 818 return ScrollIntoView(options); 819 } 820 821 void Element::ScrollIntoView(const ScrollIntoViewOptions& aOptions) { 822 Document* document = GetComposedDoc(); 823 if (!document) { 824 return; 825 } 826 827 // Get the presentation shell 828 RefPtr<PresShell> presShell = document->GetPresShell(); 829 if (!presShell) { 830 return; 831 } 832 833 const auto ToWhereToScroll = 834 [](ScrollLogicalPosition aPosition) -> WhereToScroll { 835 switch (aPosition) { 836 case ScrollLogicalPosition::Start: 837 return WhereToScroll::Start; 838 case ScrollLogicalPosition::Center: 839 return WhereToScroll::Center; 840 case ScrollLogicalPosition::End: 841 return WhereToScroll::End; 842 case ScrollLogicalPosition::Nearest: 843 break; 844 } 845 return WhereToScroll::Nearest; 846 }; 847 848 const auto block = ToWhereToScroll(aOptions.mBlock); 849 const auto inline_ = ToWhereToScroll(aOptions.mInline); 850 851 ScrollFlags scrollFlags = ScrollFlags::ScrollOverflowHidden | 852 ScrollFlags::TriggeredByScript | 853 ScrollFlags::AxesAreLogical; 854 if (aOptions.mBehavior == ScrollBehavior::Smooth) { 855 scrollFlags |= ScrollFlags::ScrollSmooth; 856 } else if (aOptions.mBehavior == ScrollBehavior::Auto) { 857 scrollFlags |= ScrollFlags::ScrollSmoothAuto; 858 } 859 860 presShell->ScrollContentIntoView( 861 this, ScrollAxis(block, WhenToScroll::Always), 862 ScrollAxis(inline_, WhenToScroll::Always), scrollFlags); 863 } 864 865 void Element::ScrollTo(double aXScroll, double aYScroll) { 866 ScrollToOptions options; 867 options.mLeft.Construct(aXScroll); 868 options.mTop.Construct(aYScroll); 869 ScrollTo(options); 870 } 871 872 void Element::ScrollTo(const ScrollToOptions& aOptions) { 873 // When the scroll top is 0, we don't need to flush layout to scroll to that 874 // point; we know 0 is always in range. At least we think so... But we do 875 // need to flush frames so we ensure we find the right scrollable frame if 876 // there is one. If it's nonzero, we need to flush layout because we need to 877 // figure out what our real scrollTopMax is. 878 // 879 // If we have a left value, we can't assume things based on it's value, 880 // depending on our direction and layout 0 may or may not be in our scroll 881 // range. So we need to flush layout no matter what then. 882 const bool needsLayoutFlush = 883 aOptions.mLeft.WasPassed() || 884 (aOptions.mTop.WasPassed() && aOptions.mTop.Value() != 0.0); 885 886 nsIFrame* frame; 887 ScrollContainerFrame* sf = GetScrollContainerFrame( 888 &frame, needsLayoutFlush ? FlushType::Layout : FlushType::Frames); 889 if (!sf) { 890 return; 891 } 892 893 CSSPoint scrollPos = sf->GetScrollPositionCSSPixels(); 894 if (aOptions.mLeft.WasPassed()) { 895 scrollPos.x = ToZeroIfNonfinite( 896 frame->Style()->EffectiveZoom().Zoom(aOptions.mLeft.Value())); 897 } 898 if (aOptions.mTop.WasPassed()) { 899 scrollPos.y = ToZeroIfNonfinite( 900 frame->Style()->EffectiveZoom().Zoom(aOptions.mTop.Value())); 901 } 902 ScrollMode scrollMode = sf->ScrollModeForScrollBehavior(aOptions.mBehavior); 903 sf->ScrollToCSSPixels(scrollPos, scrollMode); 904 } 905 906 void Element::ScrollBy(double aXScrollDif, double aYScrollDif) { 907 ScrollToOptions options; 908 options.mLeft.Construct(aXScrollDif); 909 options.mTop.Construct(aYScrollDif); 910 ScrollBy(options); 911 } 912 913 void Element::ScrollBy(const ScrollToOptions& aOptions) { 914 nsIFrame* frame; 915 ScrollContainerFrame* sf = GetScrollContainerFrame(&frame); 916 if (!sf) { 917 return; 918 } 919 920 CSSPoint scrollDelta; 921 if (aOptions.mLeft.WasPassed()) { 922 scrollDelta.x = ToZeroIfNonfinite( 923 frame->Style()->EffectiveZoom().Zoom(aOptions.mLeft.Value())); 924 } 925 926 if (aOptions.mTop.WasPassed()) { 927 scrollDelta.y = ToZeroIfNonfinite( 928 frame->Style()->EffectiveZoom().Zoom(aOptions.mTop.Value())); 929 } 930 931 auto scrollMode = sf->ScrollModeForScrollBehavior(aOptions.mBehavior); 932 sf->ScrollByCSSPixels(scrollDelta, scrollMode); 933 } 934 935 double Element::ScrollTop() { 936 return CSSPixel::FromAppUnits(GetScrollOrigin().y); 937 } 938 939 void Element::SetScrollTop(double aScrollTop) { 940 ScrollToOptions options; 941 options.mTop.Construct(aScrollTop); 942 ScrollTo(options); 943 } 944 945 double Element::ScrollLeft() { 946 return CSSPixel::FromAppUnits(GetScrollOrigin().x); 947 } 948 949 void Element::SetScrollLeft(double aScrollLeft) { 950 ScrollToOptions options; 951 options.mLeft.Construct(aScrollLeft); 952 ScrollTo(options); 953 } 954 955 void Element::MozScrollSnap() { 956 if (ScrollContainerFrame* sf = 957 GetScrollContainerFrame(nullptr, FlushType::None)) { 958 sf->ScrollSnap(); 959 } 960 } 961 962 nsRect Element::GetScrollRange() { 963 nsIFrame* frame; 964 ScrollContainerFrame* sf = GetScrollContainerFrame(&frame); 965 if (!sf) { 966 return nsRect(); 967 } 968 return frame->Style()->EffectiveZoom().Unzoom(sf->GetScrollRange()); 969 } 970 971 double Element::ScrollTopMin() { 972 return CSSPixel::FromAppUnits(GetScrollRange().Y()); 973 } 974 975 double Element::ScrollTopMax() { 976 return CSSPixel::FromAppUnits(GetScrollRange().YMost()); 977 } 978 979 double Element::ScrollLeftMin() { 980 return CSSPixel::FromAppUnits(GetScrollRange().X()); 981 } 982 983 double Element::ScrollLeftMax() { 984 return CSSPixel::FromAppUnits(GetScrollRange().XMost()); 985 } 986 987 static nsSize GetScrollRectSizeForOverflowVisibleFrame(nsIFrame* aFrame) { 988 if (!aFrame || aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) { 989 return nsSize(); 990 } 991 992 // This matches WebKit and Blink, which in turn (apparently, according to 993 // their source) matched old IE. 994 const nsRect paddingRect = aFrame->GetPaddingRectRelativeToSelf(); 995 const nsRect overflowRect = [&] { 996 OverflowAreas overflowAreas(paddingRect, paddingRect); 997 // Add the scrollable overflow areas of children (if any) to the 998 // paddingRect, as if aFrame was a scrolled frame. It's important to start 999 // with the paddingRect, otherwise if there are no children the overflow 1000 // rect will be 0,0,0,0 which will force the point 0,0 to be included in the 1001 // final rect. 1002 aFrame->UnionChildOverflow(overflowAreas, /* aAsIfScrolled = */ true); 1003 // Make sure that an empty padding-rect's edges are included, by adding 1004 // the padding-rect in again with UnionEdges. 1005 return overflowAreas.ScrollableOverflow().UnionEdges(paddingRect); 1006 }(); 1007 1008 auto directions = 1009 ScrollContainerFrame::ComputePerAxisScrollDirections(aFrame); 1010 const nscoord height = directions.mToBottom 1011 ? overflowRect.YMost() - paddingRect.Y() 1012 : paddingRect.YMost() - overflowRect.Y(); 1013 const nscoord width = directions.mToRight 1014 ? overflowRect.XMost() - paddingRect.X() 1015 : paddingRect.XMost() - overflowRect.X(); 1016 return nsSize(width, height); 1017 } 1018 1019 nsSize Element::GetScrollSize() { 1020 nsIFrame* frame; 1021 nsSize size; 1022 if (ScrollContainerFrame* sf = GetScrollContainerFrame(&frame)) { 1023 size = sf->GetScrollRange().Size() + sf->GetScrollPortRect().Size(); 1024 } else { 1025 size = GetScrollRectSizeForOverflowVisibleFrame(frame); 1026 } 1027 if (!frame) { 1028 return size; 1029 } 1030 return frame->Style()->EffectiveZoom().Unzoom(size); 1031 } 1032 1033 nsPoint Element::GetScrollOrigin() { 1034 nsIFrame* frame; 1035 ScrollContainerFrame* sf = GetScrollContainerFrame(&frame); 1036 if (!sf) { 1037 return nsPoint(); 1038 } 1039 return frame->Style()->EffectiveZoom().Unzoom(sf->GetScrollPosition()); 1040 } 1041 1042 int32_t Element::ScrollHeight() { 1043 return nsPresContext::AppUnitsToIntCSSPixels(GetScrollSize().height); 1044 } 1045 1046 int32_t Element::ScrollWidth() { 1047 return nsPresContext::AppUnitsToIntCSSPixels(GetScrollSize().width); 1048 } 1049 1050 nsRect Element::GetClientAreaRect() { 1051 Document* doc = OwnerDoc(); 1052 nsPresContext* presContext = doc->GetPresContext(); 1053 1054 // We can avoid a layout flush if this is the scrolling element of the 1055 // document, we have overlay scrollbars, and we aren't embedded in another 1056 // document 1057 if (presContext && presContext->UseOverlayScrollbars() && 1058 !doc->StyleOrLayoutObservablyDependsOnParentDocumentLayout() && 1059 doc->IsScrollingElement(this)) { 1060 if (RefPtr ps = doc->GetPresShell()) { 1061 return nsRect(nsPoint(), ps->MaybePendingLayoutViewportSize()); 1062 } 1063 } 1064 1065 nsIFrame* frame; 1066 if (ScrollContainerFrame* sf = GetScrollContainerFrame(&frame)) { 1067 nsRect scrollPort = sf->GetScrollPortRect(); 1068 1069 if (!sf->IsRootScrollFrameOfDocument()) { 1070 MOZ_ASSERT(frame); 1071 // We want the offset to be relative to `frame`, not `sf`... Except for 1072 // the root scroll frame, which is an ancestor of frame rather than a 1073 // descendant and thus this wouldn't particularly make sense. 1074 if (frame != sf) { 1075 scrollPort.MoveBy(sf->GetOffsetTo(frame)); 1076 } 1077 } 1078 1079 // The scroll port value might be expanded to the minimum scale size, we 1080 // should limit the size to the ICB in such cases. 1081 scrollPort.SizeTo(sf->GetLayoutSize()); 1082 return frame->Style()->EffectiveZoom().Unzoom(scrollPort); 1083 } 1084 1085 if (frame && 1086 // The display check is OK even though we're not looking at the style 1087 // frame, because the style frame only differs from "frame" for tables, 1088 // and table wrappers have the same display as the table itself. 1089 (!frame->StyleDisplay()->IsInlineFlow() || frame->IsReplaced())) { 1090 // Special case code to make client area work even when there isn't 1091 // a scroll view, see bug 180552, bug 227567. 1092 return frame->Style()->EffectiveZoom().Unzoom( 1093 frame->GetPaddingRect() - frame->GetPositionIgnoringScrolling()); 1094 } 1095 1096 // SVG nodes reach here and just return 0 1097 return nsRect(); 1098 } 1099 1100 int32_t Element::ScreenX() { 1101 nsIFrame* frame = GetPrimaryFrame(FlushType::Layout); 1102 return frame ? frame->GetScreenRect().x : 0; 1103 } 1104 1105 int32_t Element::ScreenY() { 1106 nsIFrame* frame = GetPrimaryFrame(FlushType::Layout); 1107 return frame ? frame->GetScreenRect().y : 0; 1108 } 1109 1110 already_AddRefed<nsIScreen> Element::GetScreen() { 1111 // Flush layout to guarantee that frames are created if needed, and preserve 1112 // behavior. 1113 (void)GetPrimaryFrame(FlushType::Frames); 1114 if (nsIWidget* widget = nsContentUtils::WidgetForContent(this)) { 1115 return widget->GetWidgetScreen(); 1116 } 1117 return nullptr; 1118 } 1119 1120 double Element::CurrentCSSZoom() { 1121 nsIFrame* f = GetPrimaryFrame(FlushType::Frames); 1122 if (!f) { 1123 return 1.0; 1124 } 1125 return f->Style()->EffectiveZoom().ToFloat(); 1126 } 1127 1128 already_AddRefed<DOMRect> Element::GetBoundingClientRect() { 1129 RefPtr<DOMRect> rect = new DOMRect(ToSupports(OwnerDoc())); 1130 1131 nsIFrame* frame = GetPrimaryFrame(FlushType::Layout); 1132 if (!frame) { 1133 // display:none, perhaps? Return the empty rect 1134 return rect.forget(); 1135 } 1136 1137 rect->SetLayoutRect(frame->GetBoundingClientRect()); 1138 return rect.forget(); 1139 } 1140 1141 already_AddRefed<DOMRectList> Element::GetClientRects() { 1142 RefPtr<DOMRectList> rectList = new DOMRectList(this); 1143 1144 nsIFrame* frame = GetPrimaryFrame(FlushType::Layout); 1145 if (!frame) { 1146 // display:none, perhaps? Return an empty list 1147 return rectList.forget(); 1148 } 1149 1150 nsLayoutUtils::RectListBuilder builder(rectList); 1151 nsLayoutUtils::GetAllInFlowRects( 1152 frame, nsLayoutUtils::GetContainingBlockForClientRect(frame), &builder, 1153 nsLayoutUtils::GetAllInFlowRectsFlag::AccountForTransforms); 1154 return rectList.forget(); 1155 } 1156 1157 const DOMTokenListSupportedToken Element::sAnchorAndFormRelValues[] = { 1158 "noreferrer", "noopener", "opener", nullptr}; 1159 1160 // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#lazy-loading-attribute 1161 static constexpr nsAttrValue::EnumTableEntry kLoadingTable[] = { 1162 {"eager", Element::Loading::Eager}, 1163 {"lazy", Element::Loading::Lazy}, 1164 }; 1165 1166 void Element::GetLoading(nsAString& aValue) const { 1167 GetEnumAttr(nsGkAtoms::loading, kLoadingTable[0].tag, aValue); 1168 } 1169 1170 bool Element::ParseLoadingAttribute(const nsAString& aValue, 1171 nsAttrValue& aResult) { 1172 return aResult.ParseEnumValue(aValue, kLoadingTable, 1173 /* aCaseSensitive = */ false, 1174 &kLoadingTable[0]); 1175 } 1176 1177 Element::Loading Element::LoadingState() const { 1178 const nsAttrValue* val = mAttrs.GetAttr(nsGkAtoms::loading); 1179 if (!val) { 1180 return Loading::Eager; 1181 } 1182 return static_cast<Loading>(val->GetEnumValue()); 1183 } 1184 1185 namespace { 1186 // <https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fetch-priority-attributes>. 1187 static constexpr nsAttrValue::EnumTableEntry kFetchPriorityEnumTable[] = { 1188 {kFetchPriorityAttributeValueHigh, FetchPriority::High}, 1189 {kFetchPriorityAttributeValueLow, FetchPriority::Low}, 1190 {kFetchPriorityAttributeValueAuto, FetchPriority::Auto}}; 1191 1192 // <https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fetch-priority-attributes>. 1193 static constexpr const nsAttrValue::EnumTableEntry* 1194 kFetchPriorityEnumTableInvalidValueDefault = &kFetchPriorityEnumTable[2]; 1195 } // namespace 1196 1197 void Element::ParseFetchPriority(const nsAString& aValue, 1198 nsAttrValue& aResult) { 1199 aResult.ParseEnumValue(aValue, kFetchPriorityEnumTable, 1200 false /* aCaseSensitive */, 1201 kFetchPriorityEnumTableInvalidValueDefault); 1202 } 1203 1204 FetchPriority Element::GetFetchPriority() const { 1205 const nsAttrValue* fetchpriorityAttribute = 1206 GetParsedAttr(nsGkAtoms::fetchpriority); 1207 if (fetchpriorityAttribute) { 1208 MOZ_ASSERT(fetchpriorityAttribute->Type() == nsAttrValue::eEnum); 1209 return FetchPriority(fetchpriorityAttribute->GetEnumValue()); 1210 } 1211 1212 return FetchPriority::Auto; 1213 } 1214 1215 //---------------------------------------------------------------------- 1216 1217 void Element::AddToIdTable(nsAtom* aId) { 1218 NS_ASSERTION(HasID(), "Node doesn't have an ID?"); 1219 if (IsInShadowTree()) { 1220 ShadowRoot* containingShadow = GetContainingShadow(); 1221 containingShadow->AddToIdTable(this, aId); 1222 } else { 1223 Document* doc = GetUncomposedDoc(); 1224 if (doc && !IsInNativeAnonymousSubtree()) { 1225 doc->AddToIdTable(this, aId); 1226 } 1227 } 1228 } 1229 1230 void Element::RemoveFromIdTable() { 1231 if (!HasID()) { 1232 return; 1233 } 1234 1235 nsAtom* id = DoGetID(); 1236 if (IsInShadowTree()) { 1237 ShadowRoot* containingShadow = GetContainingShadow(); 1238 // Check for containingShadow because it may have 1239 // been deleted during unlinking. 1240 if (containingShadow) { 1241 containingShadow->RemoveFromIdTable(this, id); 1242 } 1243 } else { 1244 Document* doc = GetUncomposedDoc(); 1245 if (doc && !IsInNativeAnonymousSubtree()) { 1246 doc->RemoveFromIdTable(this, id); 1247 } 1248 } 1249 } 1250 1251 void Element::SetSlot(const nsAString& aName, ErrorResult& aError) { 1252 aError = SetAttr(kNameSpaceID_None, nsGkAtoms::slot, aName, true); 1253 } 1254 1255 void Element::GetSlot(nsAString& aName) { GetAttr(nsGkAtoms::slot, aName); } 1256 1257 // https://dom.spec.whatwg.org/#dom-element-shadowroot 1258 ShadowRoot* Element::GetShadowRootByMode() const { 1259 /** 1260 * 1. Let shadow be context object's shadow root. 1261 * 2. If shadow is null or its mode is "closed", then return null. 1262 */ 1263 ShadowRoot* shadowRoot = GetShadowRoot(); 1264 if (!shadowRoot || shadowRoot->IsClosed()) { 1265 return nullptr; 1266 } 1267 1268 /** 1269 * 3. Return shadow. 1270 */ 1271 return shadowRoot; 1272 } 1273 1274 bool Element::CanAttachShadowDOM() const { 1275 /** 1276 * If context object's namespace is not the HTML namespace, 1277 * return false. 1278 * 1279 * Deviate from the spec here to allow shadow dom attachement to 1280 * XUL elements. 1281 */ 1282 if (!IsHTMLElement() && 1283 !(IsXULElement() && 1284 nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal()))) { 1285 return false; 1286 } 1287 1288 /** 1289 * If context object's local name is not 1290 * a valid custom element name, "article", "aside", "blockquote", 1291 * "body", "div", "footer", "h1", "h2", "h3", "h4", "h5", "h6", 1292 * "header", "main" "nav", "p", "section", "search", or "span", 1293 * return false. 1294 */ 1295 nsAtom* nameAtom = NodeInfo()->NameAtom(); 1296 uint32_t namespaceID = NodeInfo()->NamespaceID(); 1297 if (!(nsContentUtils::IsCustomElementName(nameAtom, namespaceID) || 1298 nameAtom == nsGkAtoms::article || nameAtom == nsGkAtoms::aside || 1299 nameAtom == nsGkAtoms::blockquote || nameAtom == nsGkAtoms::body || 1300 nameAtom == nsGkAtoms::div || nameAtom == nsGkAtoms::footer || 1301 nameAtom == nsGkAtoms::h1 || nameAtom == nsGkAtoms::h2 || 1302 nameAtom == nsGkAtoms::h3 || nameAtom == nsGkAtoms::h4 || 1303 nameAtom == nsGkAtoms::h5 || nameAtom == nsGkAtoms::h6 || 1304 nameAtom == nsGkAtoms::header || nameAtom == nsGkAtoms::main || 1305 nameAtom == nsGkAtoms::nav || nameAtom == nsGkAtoms::p || 1306 nameAtom == nsGkAtoms::section || nameAtom == nsGkAtoms::search || 1307 nameAtom == nsGkAtoms::span)) { 1308 return false; 1309 } 1310 1311 /** 1312 * 3. If context object’s local name is a valid custom element name, or 1313 * context object’s is value is not null, then: 1314 * If definition is not null and definition’s disable shadow is true, then 1315 * return false. 1316 */ 1317 // It will always have CustomElementData when the element is a valid custom 1318 // element or has is value. 1319 if (CustomElementData* ceData = GetCustomElementData()) { 1320 CustomElementDefinition* definition = ceData->GetCustomElementDefinition(); 1321 // If the definition is null, the element possible hasn't yet upgraded. 1322 // Fallback to use LookupCustomElementDefinition to find its definition. 1323 if (!definition) { 1324 definition = nsContentUtils::LookupCustomElementDefinition( 1325 NodeInfo()->GetDocument(), nameAtom, namespaceID, 1326 ceData->GetCustomElementType()); 1327 } 1328 1329 if (definition && definition->mDisableShadow) { 1330 return false; 1331 } 1332 } 1333 1334 return true; 1335 } 1336 1337 // https://dom.spec.whatwg.org/#dom-element-attachshadow 1338 already_AddRefed<ShadowRoot> Element::AttachShadow(const ShadowRootInit& aInit, 1339 ErrorResult& aError) { 1340 /** 1341 * Step 1, 2, and 3. 1342 */ 1343 if (!CanAttachShadowDOM()) { 1344 aError.ThrowNotSupportedError("Unable to attach ShadowDOM"); 1345 return nullptr; 1346 } 1347 1348 /** 1349 * 4. If element is a shadow host, then: 1350 */ 1351 if (RefPtr<ShadowRoot> root = GetShadowRoot()) { 1352 /** 1353 * 1. Let currentShadowRoot be element’s shadow root. 1354 * 1355 * 2. If any of the following are true: 1356 * currentShadowRoot’s declarative is false; or 1357 * currentShadowRoot’s mode is not mode, 1358 * then throw a "NotSupportedError" DOMException. 1359 */ 1360 if (!root->IsDeclarative() || root->Mode() != aInit.mMode) { 1361 aError.ThrowNotSupportedError( 1362 "Unable to re-attach to existing ShadowDOM"); 1363 return nullptr; 1364 } 1365 /** 1366 * 3. Otherwise: 1367 * 1. Remove all of currentShadowRoot’s children, in tree order. 1368 * 2. Set currentShadowRoot’s declarative to false. 1369 * 3. Return. 1370 */ 1371 root->ReplaceChildren(nullptr, aError); 1372 root->SetIsDeclarative(ShadowRootDeclarative::No); 1373 return root.forget(); 1374 } 1375 1376 if (StaticPrefs::dom_webcomponents_shadowdom_report_usage()) { 1377 OwnerDoc()->ReportShadowDOMUsage(); 1378 } 1379 1380 const nsString& referenceTarget = aInit.mReferenceTarget.WasPassed() 1381 ? aInit.mReferenceTarget.Value() 1382 : VoidString(); 1383 return AttachShadowWithoutNameChecks( 1384 aInit.mMode, DelegatesFocus(aInit.mDelegatesFocus), aInit.mSlotAssignment, 1385 ShadowRootClonable(aInit.mClonable), 1386 ShadowRootSerializable(aInit.mSerializable), referenceTarget); 1387 } 1388 1389 already_AddRefed<ShadowRoot> Element::AttachShadowWithoutNameChecks( 1390 ShadowRootMode aMode, DelegatesFocus aDelegatesFocus, 1391 SlotAssignmentMode aSlotAssignment, ShadowRootClonable aClonable, 1392 ShadowRootSerializable aSerializable, const nsAString& aReferenceTarget) { 1393 nsAutoScriptBlocker scriptBlocker; 1394 1395 auto* nim = mNodeInfo->NodeInfoManager(); 1396 RefPtr<mozilla::dom::NodeInfo> nodeInfo = 1397 nim->GetNodeInfo(nsGkAtoms::documentFragmentNodeName, nullptr, 1398 kNameSpaceID_None, DOCUMENT_FRAGMENT_NODE); 1399 1400 // If there are no children, the flat tree is not changing due to the presence 1401 // of the shadow root, so we don't need to invalidate style / layout. 1402 // 1403 // This is a minor optimization, but also works around nasty stuff like 1404 // bug 1397876. 1405 if (Document* doc = GetComposedDoc()) { 1406 if (PresShell* presShell = doc->GetPresShell()) { 1407 presShell->ShadowRootWillBeAttached(*this); 1408 } 1409 } 1410 1411 /** 1412 * 5. Let shadow be a new shadow root whose node document is 1413 * context object's node document, host is context object, 1414 * and mode is init's mode. 1415 */ 1416 RefPtr<ShadowRoot> shadowRoot = new (nim) 1417 ShadowRoot(this, aMode, aDelegatesFocus, aSlotAssignment, aClonable, 1418 aSerializable, ShadowRootDeclarative::No, nodeInfo.forget()); 1419 shadowRoot->SetReferenceTarget(aReferenceTarget); 1420 1421 if (NodeOrAncestorHasDirAuto()) { 1422 shadowRoot->SetAncestorHasDirAuto(); 1423 } 1424 1425 /** 1426 * 7. If this’s custom element state is "precustomized" or "custom", then set 1427 * shadow’s available to element internals to true. 1428 */ 1429 CustomElementData* ceData = GetCustomElementData(); 1430 if (ceData && (ceData->mState == CustomElementData::State::ePrecustomized || 1431 ceData->mState == CustomElementData::State::eCustom)) { 1432 shadowRoot->SetAvailableToElementInternals(); 1433 } 1434 1435 /** 1436 * 9. Set context object's shadow root to shadow. 1437 */ 1438 SetShadowRoot(shadowRoot); 1439 1440 // Dispatch a "shadowrootattached" event for devtools if needed. 1441 if (MOZ_UNLIKELY( 1442 nim->GetDocument()->DevToolsAnonymousAndShadowEventsEnabled())) { 1443 AsyncEventDispatcher* dispatcher = new AsyncEventDispatcher( 1444 this, u"shadowrootattached"_ns, CanBubble::eYes, 1445 ChromeOnlyDispatch::eYes, Composed::eYes); 1446 dispatcher->PostDOMEvent(); 1447 } 1448 1449 const LinkedList<AbstractRange>* ranges = 1450 GetExistingClosestCommonInclusiveAncestorRanges(); 1451 if (ranges) { 1452 for (const AbstractRange* range : *ranges) { 1453 if (range->MayCrossShadowBoundary()) { 1454 MOZ_ASSERT(range->IsDynamicRange()); 1455 CrossShadowBoundaryRange* crossBoundaryRange = 1456 range->AsDynamicRange()->GetCrossShadowBoundaryRange(); 1457 MOZ_ASSERT(crossBoundaryRange); 1458 // We may have previously selected this node before it 1459 // becomes a shadow host, so we need to reset the values 1460 // in RangeBoundaries to accommodate the change. 1461 crossBoundaryRange->NotifyNodeBecomesShadowHost(this); 1462 } 1463 } 1464 } 1465 /** 1466 * 10. Return shadow. 1467 */ 1468 return shadowRoot.forget(); 1469 } 1470 1471 void Element::AttachAndSetUAShadowRoot(NotifyUAWidgetSetup aNotify, 1472 DelegatesFocus aDelegatesFocus) { 1473 MOZ_DIAGNOSTIC_ASSERT(!CanAttachShadowDOM(), 1474 "Cannot be used to attach UI shadow DOM"); 1475 if (OwnerDoc()->IsStaticDocument()) { 1476 return; 1477 } 1478 1479 if (!GetShadowRoot()) { 1480 RefPtr<ShadowRoot> shadowRoot = 1481 AttachShadowWithoutNameChecks(ShadowRootMode::Closed, aDelegatesFocus); 1482 shadowRoot->SetIsUAWidget(); 1483 } 1484 1485 MOZ_ASSERT(GetShadowRoot()->IsUAWidget()); 1486 if (aNotify == NotifyUAWidgetSetup::Yes) { 1487 NotifyUAWidgetSetupOrChange(); 1488 } 1489 } 1490 1491 void Element::NotifyUAWidgetSetupOrChange() { 1492 MOZ_ASSERT(IsInComposedDoc()); 1493 Document* doc = OwnerDoc(); 1494 if (doc->IsStaticDocument()) { 1495 return; 1496 } 1497 1498 // Schedule a runnable, ensure the event dispatches before 1499 // returning to content script. 1500 // This event cause UA Widget to construct or cause onchange callback 1501 // of existing UA Widget to run; dispatching this event twice should not cause 1502 // UA Widget to re-init. 1503 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction( 1504 "Element::NotifyUAWidgetSetupOrChange::UAWidgetSetupOrChange", 1505 [self = RefPtr<Element>(this), doc = RefPtr<Document>(doc)]() { 1506 nsContentUtils::DispatchChromeEvent(doc, self, 1507 u"UAWidgetSetupOrChange"_ns, 1508 CanBubble::eYes, Cancelable::eNo); 1509 })); 1510 } 1511 1512 void Element::NotifyUAWidgetTeardown(UnattachShadowRoot aUnattachShadowRoot) { 1513 MOZ_ASSERT(IsInComposedDoc()); 1514 if (!GetShadowRoot()) { 1515 return; 1516 } 1517 MOZ_ASSERT(GetShadowRoot()->IsUAWidget()); 1518 if (aUnattachShadowRoot == UnattachShadowRoot::Yes) { 1519 UnattachShadow(); 1520 } 1521 1522 Document* doc = OwnerDoc(); 1523 if (doc->IsStaticDocument()) { 1524 return; 1525 } 1526 1527 // The runnable will dispatch an event to tear down UA Widget. 1528 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction( 1529 "Element::NotifyUAWidgetTeardownAndUnattachShadow::UAWidgetTeardown", 1530 [self = RefPtr<Element>(this), doc = RefPtr<Document>(doc)]() { 1531 // Bail out if the element is being collected by CC 1532 bool hasHadScriptObject = true; 1533 nsIScriptGlobalObject* scriptObject = 1534 doc->GetScriptHandlingObject(hasHadScriptObject); 1535 if (!scriptObject && hasHadScriptObject) { 1536 return; 1537 } 1538 1539 (void)nsContentUtils::DispatchChromeEvent( 1540 doc, self, u"UAWidgetTeardown"_ns, CanBubble::eYes, 1541 Cancelable::eNo); 1542 })); 1543 } 1544 1545 void Element::UnattachShadow() { 1546 RefPtr<ShadowRoot> shadowRoot = GetShadowRoot(); 1547 if (!shadowRoot) { 1548 return; 1549 } 1550 1551 nsAutoScriptBlocker scriptBlocker; 1552 1553 if (RefPtr<Document> doc = GetComposedDoc()) { 1554 if (PresShell* presShell = doc->GetPresShell()) { 1555 presShell->DestroyFramesForAndRestyle(this); 1556 #ifdef ACCESSIBILITY 1557 // We need to notify the accessibility service here explicitly because, 1558 // even though we're going to reconstruct the _host_, the shadow root and 1559 // its children are never really going to come back. We could plumb that 1560 // further down to DestroyFramesForAndRestyle and add a new flag to 1561 // nsCSSFrameConstructor::ContentRemoved or such, but this seems simpler 1562 // instead. 1563 if (nsAccessibilityService* accService = GetAccService()) { 1564 accService->ContentRemoved(presShell, shadowRoot); 1565 } 1566 #endif 1567 } 1568 // ContentRemoved doesn't really run script in the cases we care about (it 1569 // can only call ClearFocus when removing iframes and so on...) 1570 [&]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { 1571 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) { 1572 fm->ContentRemoved(doc, shadowRoot, {}); 1573 } 1574 }(); 1575 } 1576 MOZ_ASSERT(!GetPrimaryFrame()); 1577 1578 shadowRoot->Unattach(); 1579 SetShadowRoot(nullptr); 1580 } 1581 1582 void Element::GetAttribute(const nsAString& aName, DOMString& aReturn) { 1583 const nsAttrValue* val = mAttrs.GetAttr( 1584 aName, 1585 IsHTMLElement() && IsInHTMLDocument() ? eIgnoreCase : eCaseMatters); 1586 if (val) { 1587 val->ToString(aReturn); 1588 } else { 1589 aReturn.SetNull(); 1590 } 1591 } 1592 1593 bool Element::ToggleAttribute(const nsAString& aName, 1594 const Optional<bool>& aForce, 1595 nsIPrincipal* aTriggeringPrincipal, 1596 ErrorResult& aError) { 1597 aError = nsContentUtils::CheckQName(aName, false); 1598 if (aError.Failed()) { 1599 return false; 1600 } 1601 1602 nsAutoString nameToUse; 1603 const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse); 1604 if (!name) { 1605 if (aForce.WasPassed() && !aForce.Value()) { 1606 return false; 1607 } 1608 RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(nameToUse); 1609 if (!nameAtom) { 1610 aError.Throw(NS_ERROR_OUT_OF_MEMORY); 1611 return false; 1612 } 1613 aError = SetAttr(kNameSpaceID_None, nameAtom, u""_ns, aTriggeringPrincipal, 1614 true); 1615 return true; 1616 } 1617 if (aForce.WasPassed() && aForce.Value()) { 1618 return true; 1619 } 1620 // Hold a strong reference here so that the atom or nodeinfo doesn't go 1621 // away during UnsetAttr. If it did UnsetAttr would be left with a 1622 // dangling pointer as argument without knowing it. 1623 nsAttrName tmp(*name); 1624 1625 aError = UnsetAttr(name->NamespaceID(), name->LocalName(), true); 1626 return false; 1627 } 1628 1629 void Element::SetAttribute(const nsAString& aName, const nsAString& aValue, 1630 nsIPrincipal* aTriggeringPrincipal, 1631 ErrorResult& aError) { 1632 aError = nsContentUtils::CheckQName(aName, false); 1633 if (aError.Failed()) { 1634 return; 1635 } 1636 1637 nsAutoString nameToUse; 1638 const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse); 1639 if (!name) { 1640 RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(nameToUse); 1641 if (!nameAtom) { 1642 aError.Throw(NS_ERROR_OUT_OF_MEMORY); 1643 return; 1644 } 1645 aError = SetAttr(kNameSpaceID_None, nameAtom, aValue, aTriggeringPrincipal, 1646 true); 1647 return; 1648 } 1649 1650 aError = SetAttr(name->NamespaceID(), name->LocalName(), name->GetPrefix(), 1651 aValue, aTriggeringPrincipal, true); 1652 } 1653 1654 void Element::RemoveAttribute(const nsAString& aName, ErrorResult& aError) { 1655 const nsAttrName* name = InternalGetAttrNameFromQName(aName); 1656 1657 if (!name) { 1658 // If there is no canonical nsAttrName for this attribute name, then the 1659 // attribute does not exist and we can't get its namespace ID and 1660 // local name below, so we return early. 1661 return; 1662 } 1663 1664 // Hold a strong reference here so that the atom or nodeinfo doesn't go 1665 // away during UnsetAttr. If it did UnsetAttr would be left with a 1666 // dangling pointer as argument without knowing it. 1667 nsAttrName tmp(*name); 1668 1669 aError = UnsetAttr(name->NamespaceID(), name->LocalName(), true); 1670 } 1671 1672 Attr* Element::GetAttributeNode(const nsAString& aName) { 1673 return Attributes()->GetNamedItem(aName); 1674 } 1675 1676 already_AddRefed<Attr> Element::SetAttributeNode( 1677 Attr& aNewAttr, nsIPrincipal* aSubjectPrincipal, ErrorResult& aError) { 1678 RefPtr<nsDOMAttributeMap> attrMap = Attributes(); 1679 return attrMap->SetNamedItemNS(aNewAttr, aSubjectPrincipal, aError); 1680 } 1681 1682 already_AddRefed<Attr> Element::RemoveAttributeNode(Attr& aAttribute, 1683 ErrorResult& aError) { 1684 Element* elem = aAttribute.GetElement(); 1685 if (elem != this) { 1686 aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR); 1687 return nullptr; 1688 } 1689 1690 nsAutoString nameSpaceURI; 1691 aAttribute.NodeInfo()->GetNamespaceURI(nameSpaceURI); 1692 return Attributes()->RemoveNamedItemNS( 1693 nameSpaceURI, aAttribute.NodeInfo()->LocalName(), aError); 1694 } 1695 1696 void Element::GetAttributeNS(const nsAString& aNamespaceURI, 1697 const nsAString& aLocalName, nsAString& aReturn) { 1698 int32_t nsid = nsNameSpaceManager::GetInstance()->GetNameSpaceID( 1699 aNamespaceURI, nsContentUtils::IsChromeDoc(OwnerDoc())); 1700 1701 if (nsid == kNameSpaceID_Unknown) { 1702 // Unknown namespace means no attribute. 1703 SetDOMStringToNull(aReturn); 1704 return; 1705 } 1706 1707 RefPtr<nsAtom> name = NS_AtomizeMainThread(aLocalName); 1708 bool hasAttr = GetAttr(nsid, name, aReturn); 1709 if (!hasAttr) { 1710 SetDOMStringToNull(aReturn); 1711 } 1712 } 1713 1714 void Element::SetAttributeNS(const nsAString& aNamespaceURI, 1715 const nsAString& aQualifiedName, 1716 const nsAString& aValue, 1717 nsIPrincipal* aTriggeringPrincipal, 1718 ErrorResult& aError) { 1719 RefPtr<mozilla::dom::NodeInfo> ni; 1720 aError = nsContentUtils::GetNodeInfoFromQName( 1721 aNamespaceURI, aQualifiedName, mNodeInfo->NodeInfoManager(), 1722 ATTRIBUTE_NODE, getter_AddRefs(ni)); 1723 if (aError.Failed()) { 1724 return; 1725 } 1726 1727 aError = SetAttr(ni->NamespaceID(), ni->NameAtom(), ni->GetPrefixAtom(), 1728 aValue, aTriggeringPrincipal, true); 1729 } 1730 1731 already_AddRefed<nsIPrincipal> Element::CreateDevtoolsPrincipal() { 1732 // Return an ExpandedPrincipal that subsumes this Element's Principal, 1733 // and expands this Element's CSP to allow the actions that devtools 1734 // needs to perform. 1735 AutoTArray<nsCOMPtr<nsIPrincipal>, 1> allowList = {NodePrincipal()}; 1736 RefPtr<ExpandedPrincipal> dtPrincipal = ExpandedPrincipal::Create( 1737 allowList, NodePrincipal()->OriginAttributesRef()); 1738 1739 if (nsIPolicyContainer* policyContainer = GetPolicyContainer()) { 1740 if (nsIContentSecurityPolicy* csp = 1741 PolicyContainer::Cast(policyContainer)->GetCSP()) { 1742 RefPtr<nsCSPContext> dtCsp = new nsCSPContext(); 1743 dtCsp->InitFromOther(static_cast<nsCSPContext*>(csp)); 1744 dtCsp->SetSkipAllowInlineStyleCheck(true); 1745 1746 dtPrincipal->SetCsp(dtCsp); 1747 } 1748 } 1749 1750 return dtPrincipal.forget(); 1751 } 1752 1753 void Element::SetAttribute( 1754 const nsAString& aName, 1755 const TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString& aValue, 1756 nsIPrincipal* aTriggeringPrincipal, ErrorResult& aError) { 1757 aError = nsContentUtils::CheckQName(aName, false); 1758 if (aError.Failed()) { 1759 return; 1760 } 1761 1762 nsAutoString nameToUse; 1763 const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse); 1764 if (!name) { 1765 RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(nameToUse); 1766 Maybe<nsAutoString> compliantStringHolder; 1767 const nsAString* compliantString = 1768 TrustedTypeUtils::GetTrustedTypesCompliantAttributeValue( 1769 *this, nameAtom, kNameSpaceID_None, aValue, aTriggeringPrincipal, 1770 compliantStringHolder, aError); 1771 if (aError.Failed()) { 1772 return; 1773 } 1774 aError = SetAttr(kNameSpaceID_None, nameAtom, *compliantString, 1775 aTriggeringPrincipal, true); 1776 return; 1777 } 1778 1779 Maybe<nsAutoString> compliantStringHolder; 1780 RefPtr<nsAtom> attributeName = name->LocalName(); 1781 nsMutationGuard guard; 1782 const nsAString* compliantString = 1783 TrustedTypeUtils::GetTrustedTypesCompliantAttributeValue( 1784 *this, attributeName, name->NamespaceID(), aValue, 1785 aTriggeringPrincipal, compliantStringHolder, aError); 1786 if (aError.Failed()) { 1787 return; 1788 } 1789 if (!guard.Mutated(0)) { 1790 aError = SetAttr(name->NamespaceID(), name->LocalName(), name->GetPrefix(), 1791 *compliantString, aTriggeringPrincipal, true); 1792 return; 1793 } 1794 1795 // GetTrustedTypesCompliantAttributeValue may have modified mAttrs and made 1796 // the result of InternalGetAttrNameFromQName above invalid. It may now return 1797 // a different value, perhaps a nullptr. To be safe, just call the version of 1798 // Element::SetAttribute accepting a string value. 1799 SetAttribute(aName, *compliantString, aTriggeringPrincipal, aError); 1800 } 1801 1802 void Element::SetAttributeNS( 1803 const nsAString& aNamespaceURI, const nsAString& aQualifiedName, 1804 const TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString& aValue, 1805 nsIPrincipal* aTriggeringPrincipal, ErrorResult& aError) { 1806 RefPtr<mozilla::dom::NodeInfo> ni; 1807 aError = nsContentUtils::GetNodeInfoFromQName( 1808 aNamespaceURI, aQualifiedName, mNodeInfo->NodeInfoManager(), 1809 ATTRIBUTE_NODE, getter_AddRefs(ni)); 1810 if (aError.Failed()) { 1811 return; 1812 } 1813 1814 Maybe<nsAutoString> compliantStringHolder; 1815 RefPtr<nsAtom> attributeName = ni->NameAtom(); 1816 const nsAString* compliantString = 1817 TrustedTypeUtils::GetTrustedTypesCompliantAttributeValue( 1818 *this, attributeName, ni->NamespaceID(), aValue, aTriggeringPrincipal, 1819 compliantStringHolder, aError); 1820 if (aError.Failed()) { 1821 return; 1822 } 1823 aError = SetAttr(ni->NamespaceID(), ni->NameAtom(), ni->GetPrefixAtom(), 1824 *compliantString, aTriggeringPrincipal, true); 1825 } 1826 1827 void Element::SetAttributeDevtools(const nsAString& aName, 1828 const nsAString& aValue, 1829 ErrorResult& aError) { 1830 // Run this through SetAttribute with a devtools-ready principal. 1831 RefPtr<nsIPrincipal> dtPrincipal = CreateDevtoolsPrincipal(); 1832 SetAttribute(aName, aValue, dtPrincipal, aError); 1833 } 1834 1835 void Element::SetAttributeDevtoolsNS(const nsAString& aNamespaceURI, 1836 const nsAString& aLocalName, 1837 const nsAString& aValue, 1838 ErrorResult& aError) { 1839 // Run this through SetAttributeNS with a devtools-ready principal. 1840 RefPtr<nsIPrincipal> dtPrincipal = CreateDevtoolsPrincipal(); 1841 SetAttributeNS(aNamespaceURI, aLocalName, aValue, dtPrincipal, aError); 1842 } 1843 1844 void Element::RemoveAttributeNS(const nsAString& aNamespaceURI, 1845 const nsAString& aLocalName, 1846 ErrorResult& aError) { 1847 RefPtr<nsAtom> name = NS_AtomizeMainThread(aLocalName); 1848 int32_t nsid = nsNameSpaceManager::GetInstance()->GetNameSpaceID( 1849 aNamespaceURI, nsContentUtils::IsChromeDoc(OwnerDoc())); 1850 1851 if (nsid == kNameSpaceID_Unknown) { 1852 // If the namespace ID is unknown, it means there can't possibly be an 1853 // existing attribute. We would need a known namespace ID to pass into 1854 // UnsetAttr, so we return early if we don't have one. 1855 return; 1856 } 1857 1858 aError = UnsetAttr(nsid, name, true); 1859 } 1860 1861 Attr* Element::GetAttributeNodeNS(const nsAString& aNamespaceURI, 1862 const nsAString& aLocalName) { 1863 return GetAttributeNodeNSInternal(aNamespaceURI, aLocalName); 1864 } 1865 1866 Attr* Element::GetAttributeNodeNSInternal(const nsAString& aNamespaceURI, 1867 const nsAString& aLocalName) { 1868 return Attributes()->GetNamedItemNS(aNamespaceURI, aLocalName); 1869 } 1870 1871 already_AddRefed<Attr> Element::SetAttributeNodeNS( 1872 Attr& aNewAttr, nsIPrincipal* aSubjectPrincipal, ErrorResult& aError) { 1873 RefPtr<nsDOMAttributeMap> attrMap = Attributes(); 1874 return attrMap->SetNamedItemNS(aNewAttr, aSubjectPrincipal, aError); 1875 } 1876 1877 already_AddRefed<nsIHTMLCollection> Element::GetElementsByTagNameNS( 1878 const nsAString& aNamespaceURI, const nsAString& aLocalName, 1879 ErrorResult& aError) { 1880 int32_t nameSpaceId = kNameSpaceID_Wildcard; 1881 1882 if (!aNamespaceURI.EqualsLiteral("*")) { 1883 aError = nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespaceURI, 1884 nameSpaceId); 1885 if (aError.Failed()) { 1886 return nullptr; 1887 } 1888 } 1889 1890 NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!"); 1891 1892 return NS_GetContentList(this, nameSpaceId, aLocalName); 1893 } 1894 1895 bool Element::HasAttributeNS(const nsAString& aNamespaceURI, 1896 const nsAString& aLocalName) const { 1897 int32_t nsid = nsNameSpaceManager::GetInstance()->GetNameSpaceID( 1898 aNamespaceURI, nsContentUtils::IsChromeDoc(OwnerDoc())); 1899 1900 if (nsid == kNameSpaceID_Unknown) { 1901 // Unknown namespace means no attr... 1902 return false; 1903 } 1904 1905 RefPtr<nsAtom> name = NS_AtomizeMainThread(aLocalName); 1906 return HasAttr(nsid, name); 1907 } 1908 1909 already_AddRefed<nsIHTMLCollection> Element::GetElementsByClassName( 1910 const nsAString& aClassNames) { 1911 return nsContentUtils::GetElementsByClassName(this, aClassNames); 1912 } 1913 1914 bool Element::HasSharedRoot(const Element* aElement) const { 1915 nsINode* root = SubtreeRoot(); 1916 nsINode* attrSubtreeRoot = aElement->SubtreeRoot(); 1917 do { 1918 if (root == attrSubtreeRoot) { 1919 return true; 1920 } 1921 auto* shadow = ShadowRoot::FromNode(root); 1922 if (!shadow || !shadow->GetHost()) { 1923 break; 1924 } 1925 root = shadow->GetHost()->SubtreeRoot(); 1926 } while (true); 1927 return false; 1928 } 1929 1930 Element* Element::GetElementByIdInDocOrSubtree(nsAtom* aID) const { 1931 if (auto* docOrShadowRoot = GetContainingDocumentOrShadowRoot()) { 1932 return docOrShadowRoot->GetElementById(aID); 1933 } 1934 1935 return nsContentUtils::MatchElementId(SubtreeRoot()->AsContent(), aID); 1936 } 1937 1938 Element* Element::GetAttrAssociatedElement(nsAtom* aAttr) const { 1939 if (const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) { 1940 nsWeakPtr weakAttrEl = slots->mExplicitlySetAttrElementMap.Get(aAttr); 1941 if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakAttrEl)) { 1942 // If reflectedTarget's explicitly set attr-element |attrEl| is 1943 // a descendant of any of element's shadow-including ancestors, then 1944 // return |atrEl|. 1945 if (HasSharedRoot(attrEl)) { 1946 return attrEl; 1947 } 1948 return nullptr; 1949 } 1950 } 1951 1952 const nsAttrValue* value = GetParsedAttr(aAttr); 1953 if (!value) { 1954 return nullptr; 1955 } 1956 1957 MOZ_ASSERT(value->Type() == nsAttrValue::eAtom, 1958 "Attribute used for attr associated element must be parsed"); 1959 1960 return GetElementByIdInDocOrSubtree(value->GetAtomValue()); 1961 } 1962 1963 void Element::GetAttrAssociatedElements( 1964 nsAtom* aAttr, bool* aUseCachedValue, 1965 Nullable<nsTArray<RefPtr<Element>>>& aElements) { 1966 MOZ_ASSERT(aElements.IsNull()); 1967 1968 auto& [explicitlySetAttrElements, cachedAttrElements] = 1969 ExtendedDOMSlots()->mAttrElementsMap.LookupOrInsert(aAttr); 1970 1971 // https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#attr-associated-elements 1972 auto getAttrAssociatedElements = 1973 [&, &explicitlySetAttrElements = 1974 explicitlySetAttrElements]() -> Maybe<nsTArray<RefPtr<Element>>> { 1975 nsTArray<RefPtr<Element>> elements; 1976 1977 if (explicitlySetAttrElements) { 1978 // 3. If reflectedTarget's explicitly set attr-elements is not null 1979 for (const nsWeakPtr& weakEl : *explicitlySetAttrElements) { 1980 // For each attrElement in reflectedTarget's explicitly set 1981 // attr-elements: 1982 if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakEl)) { 1983 // If attrElement is not a descendant of any of element's 1984 // shadow-including ancestors, then continue. 1985 if (!HasSharedRoot(attrEl)) { 1986 continue; 1987 } 1988 // Append attrElement to elements. 1989 elements.AppendElement(attrEl); 1990 } 1991 } 1992 } else { 1993 // 4. Otherwise 1994 // 1. Let contentAttributeValue be the result of running 1995 // reflectedTarget's get the content attribute. 1996 const nsAttrValue* value = GetParsedAttr(aAttr); 1997 // 2. If contentAttributeValue is null, then return null. 1998 if (!value) { 1999 return Nothing(); 2000 } 2001 2002 // 3. Let tokens be contentAttributeValue, split on ASCII whitespace. 2003 MOZ_ASSERT(value->Type() == nsAttrValue::eAtomArray || 2004 value->Type() == nsAttrValue::eAtom, 2005 "Attribute used for attr associated elements must be parsed"); 2006 for (uint32_t i = 0; i < value->GetAtomCount(); i++) { 2007 // For each id of tokens: 2008 if (auto* candidate = GetElementByIdInDocOrSubtree( 2009 value->AtomAt(static_cast<int32_t>(i)))) { 2010 // Append candidate to elements. 2011 elements.AppendElement(candidate); 2012 } 2013 } 2014 } 2015 2016 return Some(std::move(elements)); 2017 }; 2018 2019 // getter steps: 2020 // 1. Let elements be the result of running this's get the attr-associated 2021 // elements. 2022 auto elements = getAttrAssociatedElements(); 2023 2024 if (elements && elements == cachedAttrElements) { 2025 // 2. If the contents of elements is equal to the contents of this's cached 2026 // attr-associated elements, then return this's cached attr-associated 2027 // elements object. 2028 MOZ_ASSERT(!*aUseCachedValue); 2029 *aUseCachedValue = true; 2030 return; 2031 } 2032 2033 // 3. Let elementsAsFrozenArray be elements, converted to a FrozenArray<T>?. 2034 // (the binding code takes aElements and returns it as a FrozenArray) 2035 // 5. Set this's cached attr-associated elements object to 2036 // elementsAsFrozenArray. 2037 // (the binding code stores the attr-associated elements object in a slot) 2038 // 6. Return elementsAsFrozenArray. 2039 if (elements) { 2040 aElements.SetValue(elements->Clone()); 2041 } 2042 2043 // 4. Set this's cached attr-associated elements to elements. 2044 cachedAttrElements = std::move(elements); 2045 } 2046 2047 void Element::ClearExplicitlySetAttrElement(nsAtom* aAttr) { 2048 if (auto* slots = GetExistingExtendedDOMSlots()) { 2049 slots->mExplicitlySetAttrElementMap.Remove(aAttr); 2050 } 2051 } 2052 2053 void Element::ClearExplicitlySetAttrElements(nsAtom* aAttr) { 2054 if (auto* slots = GetExistingExtendedDOMSlots()) { 2055 slots->mAttrElementsMap.Remove(aAttr); 2056 } 2057 } 2058 2059 void Element::ExplicitlySetAttrElement(nsAtom* aAttr, Element* aElement) { 2060 #ifdef ACCESSIBILITY 2061 nsAccessibilityService* accService = GetAccService(); 2062 #endif 2063 // Accessibility requires that no other attribute changes occur between 2064 // AttrElementWillChange and AttrElementChanged. Scripts could cause 2065 // this, so don't let them run here. We do this even if accessibility isn't 2066 // running so that the JS behavior is consistent regardless of accessibility. 2067 // Otherwise, JS might be able to use this difference to determine whether 2068 // accessibility is running, which would be a privacy concern. 2069 nsAutoScriptBlocker scriptBlocker; 2070 if (aElement) { 2071 #ifdef ACCESSIBILITY 2072 if (accService) { 2073 accService->NotifyAttrElementWillChange(this, aAttr); 2074 } 2075 #endif 2076 SetAttr(aAttr, EmptyString(), IgnoreErrors()); 2077 nsExtendedDOMSlots* slots = ExtendedDOMSlots(); 2078 slots->mExplicitlySetAttrElementMap.InsertOrUpdate( 2079 aAttr, do_GetWeakReference(aElement)); 2080 #ifdef ACCESSIBILITY 2081 if (accService) { 2082 accService->NotifyAttrElementChanged(this, aAttr); 2083 } 2084 #endif 2085 return; 2086 } 2087 2088 #ifdef ACCESSIBILITY 2089 if (accService) { 2090 accService->NotifyAttrElementWillChange(this, aAttr); 2091 } 2092 #endif 2093 ClearExplicitlySetAttrElement(aAttr); 2094 UnsetAttr(aAttr, IgnoreErrors()); 2095 #ifdef ACCESSIBILITY 2096 if (accService) { 2097 accService->NotifyAttrElementChanged(this, aAttr); 2098 } 2099 #endif 2100 } 2101 2102 void Element::ExplicitlySetAttrElements( 2103 nsAtom* aAttr, 2104 const Nullable<Sequence<OwningNonNull<Element>>>& aElements) { 2105 #ifdef ACCESSIBILITY 2106 nsAccessibilityService* accService = GetAccService(); 2107 #endif 2108 // Accessibility requires that no other attribute changes occur between 2109 // AttrElementWillChange and AttrElementChanged. Scripts could cause 2110 // this, so don't let them run here. We do this even if accessibility isn't 2111 // running so that the JS behavior is consistent regardless of accessibility. 2112 // Otherwise, JS might be able to use this difference to determine whether 2113 // accessibility is running, which would be a privacy concern. 2114 nsAutoScriptBlocker scriptBlocker; 2115 2116 #ifdef ACCESSIBILITY 2117 if (accService) { 2118 accService->NotifyAttrElementWillChange(this, aAttr); 2119 } 2120 #endif 2121 2122 if (aElements.IsNull()) { 2123 ClearExplicitlySetAttrElements(aAttr); 2124 UnsetAttr(aAttr, IgnoreErrors()); 2125 } else { 2126 SetAttr(aAttr, EmptyString(), IgnoreErrors()); 2127 auto& entry = ExtendedDOMSlots()->mAttrElementsMap.LookupOrInsert(aAttr); 2128 entry.first.emplace(nsTArray<nsWeakPtr>()); 2129 for (Element* el : aElements.Value()) { 2130 entry.first->AppendElement(do_GetWeakReference(el)); 2131 } 2132 } 2133 2134 #ifdef ACCESSIBILITY 2135 if (accService) { 2136 accService->NotifyAttrElementChanged(this, aAttr); 2137 } 2138 #endif 2139 } 2140 2141 Element* Element::GetExplicitlySetAttrElement(nsAtom* aAttr) const { 2142 if (const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) { 2143 nsWeakPtr weakAttrEl = slots->mExplicitlySetAttrElementMap.Get(aAttr); 2144 if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakAttrEl)) { 2145 return attrEl; 2146 } 2147 } 2148 return nullptr; 2149 } 2150 2151 void Element::GetExplicitlySetAttrElements( 2152 nsAtom* aAttr, nsTArray<Element*>& aElements) const { 2153 if (const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) { 2154 if (auto attrElementsMaybeEntry = slots->mAttrElementsMap.Lookup(aAttr)) { 2155 auto& [attrElements, cachedAttrElements] = attrElementsMaybeEntry.Data(); 2156 if (attrElements) { 2157 for (const nsWeakPtr& weakEl : *attrElements) { 2158 if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakEl)) { 2159 aElements.AppendElement(attrEl); 2160 } 2161 } 2162 } 2163 } 2164 } 2165 } 2166 2167 void Element::GetElementsWithGrid(nsTArray<RefPtr<Element>>& aElements) { 2168 dom::TreeIterator<dom::StyleChildrenIterator> iter(*this); 2169 while (nsIContent* cur = iter.GetCurrent()) { 2170 if (cur->IsElement()) { 2171 Element* elem = cur->AsElement(); 2172 if (elem->GetPrimaryFrame()) { 2173 // See if this has a GridContainerFrame. Use the same method that 2174 // nsGridContainerFrame uses, which deals with some edge cases. 2175 if (nsGridContainerFrame::GetGridContainerFrame( 2176 elem->GetPrimaryFrame())) { 2177 aElements.AppendElement(elem); 2178 } 2179 } 2180 2181 // Only allow the traversal to go through the children if the element 2182 // does have a display. 2183 if (elem->HasServoData()) { 2184 iter.GetNext(); 2185 continue; 2186 } 2187 } 2188 2189 // Either this isn't an element, or it has `display: none`. 2190 // Continue with the traversal but ignore all the children. 2191 iter.GetNextSkippingChildren(); 2192 } 2193 } 2194 2195 bool Element::HasVisibleScrollbars() { 2196 ScrollContainerFrame* scrollFrame = GetScrollContainerFrame(); 2197 return scrollFrame && !scrollFrame->GetScrollbarVisibility().isEmpty(); 2198 } 2199 2200 // Hash function for bloom filter (k=2) 2201 // Returns 64-bit value with bit 0 set to 1 and 2 bits set in available range. 2202 static uint64_t HashForBloomFilter(const nsAtom* aAtom) { 2203 if (!aAtom) { 2204 return 1ULL; // Just the tag bit 2205 } 2206 // On 32-bit platforms, we have 31 bits for bloom + 1 tag bit 2207 // On 64-bit platforms, we have 63 bits for bloom + 1 tag bit 2208 constexpr int kAttrBloomBits = sizeof(uintptr_t) == 4 ? 31 : 63; 2209 2210 uint32_t hash = aAtom->hash(); 2211 uint64_t filter = 1ULL; 2212 // Set 2 bits in the available range (bits 1-31 on 32-bit, 1-63 on 64-bit) 2213 uint32_t bit1 = hash % kAttrBloomBits; 2214 uint32_t bit2 = (hash >> 6) % kAttrBloomBits; 2215 filter |= 1ULL << (1 + bit1); 2216 filter |= 1ULL << (1 + bit2); 2217 return filter; 2218 } 2219 2220 // Propagates this element's bloom filter up the tree by OR-ing it with 2221 // all ancestor element bloom filters, stopping early if no new bits are added. 2222 void Element::PropagateBloomFilterToParents() { 2223 Element* toUpdate = this; 2224 Element* parent = GetParentElement(); 2225 2226 while (parent) { 2227 uint64_t childBloom = toUpdate->mAttrs.GetSubtreeBloomFilter(); 2228 uint64_t parentBloom = parent->mAttrs.GetSubtreeBloomFilter(); 2229 2230 // Check if parent already contains all child bits 2231 if ((parentBloom & childBloom) == childBloom) { 2232 break; 2233 } 2234 parent->mAttrs.SetSubtreeBloomFilter(parentBloom | childBloom); 2235 toUpdate = parent; 2236 parent = toUpdate->GetParentElement(); 2237 } 2238 } 2239 2240 // Hashes all class names in a class attribute value for the bloom filter. 2241 // Handles both single class (eAtom) and multiple classes (eAtomArray). 2242 static uint64_t HashClassesForBloom(const nsAttrValue* aValue) { 2243 uint64_t filter = 1ULL; // Start with tag bit 2244 if (!aValue) { 2245 return filter; 2246 } 2247 2248 if (aValue->Type() == nsAttrValue::eAtomArray) { 2249 const mozilla::AttrAtomArray* array = aValue->GetAtomArrayValue(); 2250 if (array) { 2251 for (const RefPtr<nsAtom>& className : array->mArray) { 2252 filter |= HashForBloomFilter(className); 2253 } 2254 } 2255 } else if (aValue->Type() == nsAttrValue::eAtom) { 2256 filter |= HashForBloomFilter(aValue->GetAtomValue()); 2257 } 2258 #ifdef DEBUG 2259 else { 2260 // Assert that only empty strings make it here. 2261 nsAutoString value; 2262 aValue->ToString(value); 2263 bool isOnlyWhitespace = true; 2264 for (uint32_t i = 0; i < value.Length(); i++) { 2265 if (!nsContentUtils::IsHTMLWhitespace(value[i])) { 2266 isOnlyWhitespace = false; 2267 break; 2268 } 2269 } 2270 MOZ_ASSERT(isOnlyWhitespace, "Expecting only empty strings here."); 2271 } 2272 #endif 2273 2274 return filter; 2275 } 2276 2277 #ifdef DEBUG 2278 // Asserts that the bloom filter contains all expected bits from 2279 // current attributes, classes, and descendant bloom filters. 2280 void Element::VerifySubtreeBloomFilter() const { 2281 uint64_t expectedBloom = 1ULL; 2282 2283 // Hash all attribute names in kNameSpaceID_None namespace 2284 uint32_t attrCount = GetAttrCount(); 2285 for (uint32_t i = 0; i < attrCount; i++) { 2286 const nsAttrName* attrName = GetAttrNameAt(i); 2287 MOZ_ASSERT(attrName, "Attribute name should not be null"); 2288 if (attrName->NamespaceEquals(kNameSpaceID_None)) { 2289 nsAtom* localName = attrName->LocalName(); 2290 expectedBloom |= HashForBloomFilter(localName); 2291 2292 if (!localName->IsAsciiLowercase()) { 2293 Document* doc = OwnerDoc(); 2294 if (!IsHTMLElement() && doc->IsHTMLDocument()) { 2295 RefPtr<nsAtom> lowercaseAttr(localName); 2296 ToLowerCaseASCII(lowercaseAttr); 2297 expectedBloom |= HashForBloomFilter(lowercaseAttr); 2298 } 2299 } 2300 } 2301 } 2302 2303 // Hash class names 2304 expectedBloom |= HashClassesForBloom(GetClasses()); 2305 2306 // Include children's bloom filters 2307 for (Element* child = GetFirstElementChild(); child; 2308 child = child->GetNextElementSibling()) { 2309 expectedBloom |= child->mAttrs.GetSubtreeBloomFilter(); 2310 } 2311 2312 uint64_t actualBloom = mAttrs.GetSubtreeBloomFilter(); 2313 // Bloom filters are append-only: bits can be set but never cleared. 2314 // So actualBloom may contain extra bits from removed attributes. 2315 // We only check that all expected bits are present. 2316 MOZ_ASSERT((actualBloom & expectedBloom) == expectedBloom, 2317 "Bloom filter missing required bits"); 2318 } 2319 #endif 2320 2321 void Element::UpdateSubtreeBloomFilterForClass(const nsAttrValue* aClassValue) { 2322 if (!aClassValue) { 2323 return; 2324 } 2325 mAttrs.UpdateSubtreeBloomFilter(HashClassesForBloom(aClassValue)); 2326 } 2327 2328 void Element::UpdateSubtreeBloomFilterForAttribute(nsAtom* aAttribute) { 2329 MOZ_ASSERT(aAttribute, "Attribute should not be null"); 2330 mAttrs.UpdateSubtreeBloomFilter(HashForBloomFilter(aAttribute)); 2331 2332 // For non-HTML elements, also add the lowercase hash. 2333 // This ensures querySelector can find these attributes with case-insensitive 2334 // matching in HTML documents, even if the element is moved to an HTML 2335 // document after attributes are set. 2336 if (!aAttribute->IsAsciiLowercase() && !IsHTMLElement()) { 2337 RefPtr<nsAtom> lowercaseAttr(aAttribute); 2338 ToLowerCaseASCII(lowercaseAttr); 2339 mAttrs.UpdateSubtreeBloomFilter(HashForBloomFilter(lowercaseAttr)); 2340 } 2341 } 2342 2343 nsresult Element::BindToTree(BindContext& aContext, nsINode& aParent) { 2344 MOZ_ASSERT(aParent.IsContent() || aParent.IsDocument(), 2345 "Must have content or document parent!"); 2346 MOZ_ASSERT(aParent.OwnerDoc() == OwnerDoc(), 2347 "Must have the same owner document"); 2348 MOZ_ASSERT(OwnerDoc() == &aContext.OwnerDoc(), "These should match too"); 2349 MOZ_ASSERT(!IsInUncomposedDoc(), "Already have a document. Unbind first!"); 2350 MOZ_ASSERT(!IsInComposedDoc(), "Already have a document. Unbind first!"); 2351 // Note that as we recurse into the kids, they'll have a non-null parent. So 2352 // only assert if our parent is _changing_ while we have a parent. 2353 MOZ_ASSERT(!GetParentNode() || &aParent == GetParentNode(), 2354 "Already have a parent. Unbind first!"); 2355 2356 const bool hadParent = !!GetParentNode(); 2357 2358 if (aParent.IsInNativeAnonymousSubtree()) { 2359 SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE); 2360 } 2361 if (IsRootOfNativeAnonymousSubtree()) { 2362 aParent.SetMayHaveAnonymousChildren(); 2363 } else if (aParent.HasFlag(NODE_HAS_BEEN_IN_UA_WIDGET)) { 2364 SetFlags(NODE_HAS_BEEN_IN_UA_WIDGET); 2365 } 2366 if (aParent.HasFlag(ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR)) { 2367 SetFlags(ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR); 2368 } 2369 aParent.SetFlags(NODE_MAY_HAVE_ELEMENT_CHILDREN); 2370 2371 // Now set the parent. 2372 mParent = &aParent; 2373 if (!hadParent && aParent.IsContent()) { 2374 SetParentIsContent(true); 2375 NS_ADDREF(mParent); 2376 } 2377 MOZ_ASSERT(!!GetParent() == aParent.IsContent()); 2378 2379 MOZ_ASSERT_IF(!aContext.IsMove(), 2380 !HasAnyOfFlags(Element::kAllServoDescendantBits)); 2381 2382 // Finally, set the document 2383 if (aParent.IsInUncomposedDoc() || aParent.IsInShadowTree()) { 2384 // We no longer need to track the subtree pointer (and in fact we'll assert 2385 // if we do this any later). 2386 ClearSubtreeRootPointer(); 2387 SetIsConnected(aParent.IsInComposedDoc()); 2388 2389 if (aParent.IsInUncomposedDoc()) { 2390 SetIsInDocument(); 2391 } else { 2392 SetFlags(NODE_IS_IN_SHADOW_TREE); 2393 MOZ_ASSERT(aParent.IsContent() && 2394 aParent.AsContent()->GetContainingShadow()); 2395 ExtendedDOMSlots()->mContainingShadow = 2396 aParent.AsContent()->GetContainingShadow(); 2397 } 2398 // Clear the lazy frame construction bits. 2399 UnsetFlags(NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES); 2400 } else { 2401 // If we're not in the doc and not in a shadow tree, 2402 // update our subtree pointer. 2403 SetSubtreeRootPointer(aParent.SubtreeRoot()); 2404 } 2405 2406 if (IsInComposedDoc()) { 2407 if (IsPendingMappedAttributeEvaluation()) { 2408 aContext.OwnerDoc().ScheduleForPresAttrEvaluation(this); 2409 } 2410 // Connected callback must be enqueued whenever a custom element becomes 2411 // connected. 2412 if (CustomElementData* data = GetCustomElementData()) { 2413 if (data->mState == CustomElementData::State::eCustom) { 2414 nsContentUtils::EnqueueLifecycleCallback( 2415 aContext.IsMove() ? ElementCallbackType::eConnectedMove 2416 : ElementCallbackType::eConnected, 2417 this, {}); 2418 } else { 2419 // Step 7.7.2.2 https://dom.spec.whatwg.org/#concept-node-insert 2420 nsContentUtils::TryToUpgradeElement(this); 2421 } 2422 } 2423 } 2424 2425 // This has to be here, rather than in nsGenericHTMLElement::BindToTree, 2426 // because it has to happen after updating the parent pointer, but before 2427 // recursively binding the kids. 2428 SetDirOnBind(this, nsIContent::FromNode(aParent)); 2429 2430 UpdateEditableState(false); 2431 2432 // Call BindToTree on shadow root children. 2433 nsresult rv; 2434 if (ShadowRoot* shadowRoot = GetShadowRoot()) { 2435 rv = shadowRoot->Bind(); 2436 NS_ENSURE_SUCCESS(rv, rv); 2437 } 2438 2439 // Now recurse into our kids. Ensure this happens after binding the shadow 2440 // root so that directionality of slots is updated. 2441 { 2442 for (nsIContent* child = GetFirstChild(); child; 2443 child = child->GetNextSibling()) { 2444 rv = child->BindToTree(aContext, *this); 2445 NS_ENSURE_SUCCESS(rv, rv); 2446 } 2447 } 2448 2449 MutationObservers::NotifyParentChainChanged(this); 2450 2451 // Ensure we only run this once, in the case we move the ShadowRoot around. 2452 if (aContext.SubtreeRootChanges()) { 2453 if (HasPartAttribute()) { 2454 if (ShadowRoot* shadow = GetContainingShadow()) { 2455 shadow->PartAdded(*this); 2456 } 2457 } 2458 if (HasID()) { 2459 AddToIdTable(DoGetID()); 2460 } 2461 HandleShadowDOMRelatedInsertionSteps(hadParent); 2462 } 2463 2464 if (MayHaveStyle()) { 2465 // If MayHaveStyle() is true, we must be an nsStyledElement. 2466 static_cast<nsStyledElement*>(this)->ReparseStyleAttribute( 2467 /* aForceInDataDoc = */ false); 2468 } 2469 2470 // XXXbz script execution during binding can trigger some of these 2471 // postcondition asserts.... But we do want that, since things will 2472 // generally be quite broken when that happens. 2473 MOZ_ASSERT(OwnerDoc() == aParent.OwnerDoc(), "Bound to wrong document"); 2474 MOZ_ASSERT(IsInComposedDoc() == aContext.InComposedDoc()); 2475 MOZ_ASSERT(IsInUncomposedDoc() == aContext.InUncomposedDoc()); 2476 MOZ_ASSERT(&aParent == GetParentNode(), "Bound to wrong parent node"); 2477 MOZ_ASSERT(aParent.IsInUncomposedDoc() == IsInUncomposedDoc()); 2478 MOZ_ASSERT(aParent.IsInComposedDoc() == IsInComposedDoc()); 2479 MOZ_ASSERT(aParent.IsInShadowTree() == IsInShadowTree()); 2480 MOZ_ASSERT(aParent.SubtreeRoot() == SubtreeRoot()); 2481 2482 #ifdef DEBUG 2483 VerifySubtreeBloomFilter(); 2484 #endif 2485 2486 // When binding to tree, propagate this element's bloom to parents. 2487 PropagateBloomFilterToParents(); 2488 return NS_OK; 2489 } 2490 2491 static bool WillDetachFromShadowOnUnbind(const Element& aElement, 2492 bool aNullParent) { 2493 // If our parent still is in a shadow tree by now, and we're not removing 2494 // ourselves from it, then we're still going to be in a shadow tree after 2495 // this. 2496 return aElement.IsInShadowTree() && 2497 (aNullParent || !aElement.GetParent()->IsInShadowTree()); 2498 } 2499 2500 void Element::UnbindFromTree(UnbindContext& aContext) { 2501 const bool nullParent = aContext.IsUnbindRoot(this); 2502 2503 HandleShadowDOMRelatedRemovalSteps(nullParent); 2504 2505 if (HasFlag(ELEMENT_IN_CONTENT_IDENTIFIER_FOR_LCP)) { 2506 OwnerDoc()->ContentIdentifiersForLCP().Remove(this); 2507 UnsetFlags(ELEMENT_IN_CONTENT_IDENTIFIER_FOR_LCP); 2508 } 2509 2510 if (HasFlag(ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR) && 2511 !IsHTMLElement(nsGkAtoms::datalist)) { 2512 if (nullParent) { 2513 UnsetFlags(ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR); 2514 } else { 2515 nsIContent* parent = GetParent(); 2516 MOZ_ASSERT(parent); 2517 if (!parent->HasFlag(ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR)) { 2518 UnsetFlags(ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR); 2519 } 2520 } 2521 } 2522 2523 const bool detachingFromShadow = 2524 WillDetachFromShadowOnUnbind(*this, nullParent); 2525 // Make sure to only remove from the ID table if our subtree root is actually 2526 // changing. 2527 if (IsInUncomposedDoc() || detachingFromShadow) { 2528 RemoveFromIdTable(); 2529 } 2530 2531 if (detachingFromShadow && HasPartAttribute()) { 2532 if (ShadowRoot* shadow = GetContainingShadow()) { 2533 shadow->PartRemoved(*this); 2534 } 2535 } 2536 2537 // Make sure to unbind this node before doing the kids 2538 Document* document = GetComposedDoc(); 2539 2540 if (HasPointerLock()) { 2541 PointerLockManager::Unlock("Element::UnbindFromTree"); 2542 } 2543 if (!aContext.IsMove() && mState.HasState(ElementState::FULLSCREEN)) { 2544 // The element being removed is an ancestor of the fullscreen element, 2545 // exit fullscreen state. 2546 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, 2547 OwnerDoc(), nsContentUtils::eDOM_PROPERTIES, 2548 "RemovedFullscreenElement"); 2549 // Fully exit fullscreen. 2550 Document::ExitFullscreenInDocTree(OwnerDoc()); 2551 } 2552 2553 MOZ_ASSERT_IF(HasServoData(), document); 2554 MOZ_ASSERT_IF(HasServoData() && !aContext.IsMove(), 2555 IsInNativeAnonymousSubtree()); 2556 if (document && !aContext.IsMove()) { 2557 ClearServoData(document); 2558 } 2559 2560 // Ensure that CSS transitions don't continue on an element at a 2561 // different place in the tree (even if reinserted before next 2562 // animation refresh). 2563 // 2564 // We need to delete the properties while we're still in document 2565 // (if we were in document) so that they can look up the 2566 // PendingAnimationTracker on the document and remove their animations, 2567 // and so they can find their pres context for dispatching cancel events. 2568 // 2569 // FIXME(bug 522599): Need a test for this. 2570 // FIXME(emilio): Why not clearing the effect set as well? 2571 if (!aContext.IsMove()) { 2572 if (auto* data = GetAnimationData()) { 2573 data->ClearAllAnimationCollections(); 2574 } 2575 } 2576 2577 if (nullParent) { 2578 if (GetParent()) { 2579 RefPtr<nsINode> p; 2580 p.swap(mParent); 2581 } else { 2582 mParent = nullptr; 2583 } 2584 SetParentIsContent(false); 2585 } 2586 2587 #ifdef DEBUG 2588 // If we can get access to the PresContext, then we sanity-check that 2589 // we're not leaving behind a pointer to ourselves as the PresContext's 2590 // cached provider of the viewport's scrollbar styles. 2591 if (document) { 2592 nsPresContext* presContext = document->GetPresContext(); 2593 if (presContext) { 2594 MOZ_ASSERT(this != presContext->GetViewportScrollStylesOverrideElement(), 2595 "Leaving behind a raw pointer to this element (as having " 2596 "propagated scrollbar styles) - that's dangerous..."); 2597 } 2598 } 2599 2600 # ifdef ACCESSIBILITY 2601 MOZ_ASSERT(!GetAccService() || !GetAccService()->HasAccessible(this), 2602 "An accessible for this element still exists!"); 2603 # endif 2604 #endif 2605 2606 ClearInDocument(); 2607 SetIsConnected(false); 2608 if (HasElementCreatedFromPrototypeAndHasUnmodifiedL10n()) { 2609 if (document) { 2610 document->mL10nProtoElements.Remove(this); 2611 } 2612 ClearElementCreatedFromPrototypeAndHasUnmodifiedL10n(); 2613 } 2614 2615 if (nullParent || !mParent->IsInShadowTree()) { 2616 UnsetFlags(NODE_IS_IN_SHADOW_TREE); 2617 2618 // Begin keeping track of our subtree root. 2619 SetSubtreeRootPointer(nullParent ? this : mParent->SubtreeRoot()); 2620 2621 if (nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) { 2622 slots->mContainingShadow = nullptr; 2623 } 2624 } 2625 2626 if (document) { 2627 // Disconnected must be enqueued whenever a connected custom element becomes 2628 // disconnected. 2629 if (CustomElementData* data = GetCustomElementData()) { 2630 if (data->mState == CustomElementData::State::eCustom) { 2631 if (!aContext.IsMove()) { 2632 nsContentUtils::EnqueueLifecycleCallback( 2633 ElementCallbackType::eDisconnected, this, {}); 2634 } 2635 } else { 2636 // Remove an unresolved custom element that is a candidate for upgrade 2637 // when a custom element is disconnected. 2638 nsContentUtils::UnregisterUnresolvedElement(this); 2639 } 2640 } 2641 2642 if (IsPendingMappedAttributeEvaluation()) { 2643 document->UnscheduleForPresAttrEvaluation(this); 2644 } 2645 2646 if (HasLastRememberedBSize() || HasLastRememberedISize()) { 2647 // Make sure the element is observed so that remembered sizes are kept 2648 // until the next time "ResizeObserver events are determined and 2649 // delivered". See "Disconnected element" tests from 2650 // css/css-sizing/contain-intrinsic-size/auto-006.html 2651 document->ObserveForLastRememberedSize(*this); 2652 } 2653 } 2654 2655 // This has to be here, rather than in nsGenericHTMLElement::UnbindFromTree, 2656 // because it has to happen after unsetting the parent pointer, but before 2657 // recursively unbinding the kids. 2658 ResetDir(this); 2659 2660 for (nsIContent* child = GetFirstChild(); child; 2661 child = child->GetNextSibling()) { 2662 child->UnbindFromTree(aContext); 2663 } 2664 2665 MutationObservers::NotifyParentChainChanged(this); 2666 2667 // Unbind children of shadow root. 2668 if (ShadowRoot* shadowRoot = GetShadowRoot()) { 2669 shadowRoot->Unbind(); 2670 } 2671 2672 MOZ_ASSERT_IF(!aContext.IsMove(), !HasAnyOfFlags(kAllServoDescendantBits)); 2673 MOZ_ASSERT_IF(!aContext.IsMove(), 2674 !document || document->GetServoRestyleRoot() != this); 2675 } 2676 2677 UniquePtr<SMILAttr> Element::GetAnimatedAttr(int32_t aNamespaceID, 2678 nsAtom* aName) { 2679 return nullptr; 2680 } 2681 2682 nsDOMCSSAttributeDeclaration* Element::SMILOverrideStyle() { 2683 Element::nsExtendedDOMSlots* slots = ExtendedDOMSlots(); 2684 2685 if (!slots->mSMILOverrideStyle) { 2686 slots->mSMILOverrideStyle = new nsDOMCSSAttributeDeclaration(this, true); 2687 } 2688 2689 return slots->mSMILOverrideStyle; 2690 } 2691 2692 DeclarationBlock* Element::GetSMILOverrideStyleDeclaration() { 2693 Element::nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots(); 2694 return slots ? slots->mSMILOverrideStyleDeclaration.get() : nullptr; 2695 } 2696 2697 void Element::SetSMILOverrideStyleDeclaration(DeclarationBlock& aDeclaration) { 2698 ExtendedDOMSlots()->mSMILOverrideStyleDeclaration = &aDeclaration; 2699 2700 // Only need to request a restyle if we're in a document. (We might not 2701 // be in a document, if we're clearing animation effects on a target node 2702 // that's been detached since the previous animation sample.) 2703 if (Document* doc = GetComposedDoc()) { 2704 if (PresShell* presShell = doc->GetPresShell()) { 2705 presShell->RestyleForAnimation(this, RestyleHint::RESTYLE_SMIL); 2706 } 2707 } 2708 } 2709 2710 bool Element::IsLabelable() const { return false; } 2711 2712 bool Element::IsInteractiveHTMLContent() const { return false; } 2713 2714 DeclarationBlock* Element::GetInlineStyleDeclaration() const { 2715 if (!MayHaveStyle()) { 2716 return nullptr; 2717 } 2718 const nsAttrValue* attrVal = mAttrs.GetAttr(nsGkAtoms::style); 2719 if (!attrVal || attrVal->Type() != nsAttrValue::eCSSDeclaration) { 2720 return nullptr; 2721 } 2722 return attrVal->GetCSSDeclarationValue(); 2723 } 2724 2725 void Element::InlineStyleDeclarationWillChange(MutationClosureData& aData) { 2726 MOZ_ASSERT_UNREACHABLE("Element::InlineStyleDeclarationWillChange"); 2727 } 2728 2729 nsresult Element::SetInlineStyleDeclaration(DeclarationBlock& aDeclaration, 2730 MutationClosureData& aData) { 2731 MOZ_ASSERT_UNREACHABLE("Element::SetInlineStyleDeclaration"); 2732 return NS_ERROR_NOT_IMPLEMENTED; 2733 } 2734 2735 NS_IMETHODIMP_(bool) 2736 Element::IsAttributeMapped(const nsAtom* aAttribute) const { return false; } 2737 2738 nsMapRuleToAttributesFunc Element::GetAttributeMappingFunction() const { 2739 return &MapNoAttributesInto; 2740 } 2741 2742 void Element::MapNoAttributesInto(mozilla::MappedDeclarationsBuilder&) {} 2743 2744 nsChangeHint Element::GetAttributeChangeHint(const nsAtom* aAttribute, 2745 AttrModType) const { 2746 return nsChangeHint(0); 2747 } 2748 2749 void Element::SetMappedDeclarationBlock( 2750 already_AddRefed<StyleLockedDeclarationBlock> aDeclarations) { 2751 MOZ_ASSERT(IsPendingMappedAttributeEvaluation()); 2752 mAttrs.SetMappedDeclarationBlock(std::move(aDeclarations)); 2753 MOZ_ASSERT(!IsPendingMappedAttributeEvaluation()); 2754 } 2755 2756 bool Element::FindAttributeDependence(const nsAtom* aAttribute, 2757 const MappedAttributeEntry* const aMaps[], 2758 uint32_t aMapCount) { 2759 for (uint32_t mapindex = 0; mapindex < aMapCount; ++mapindex) { 2760 for (const MappedAttributeEntry* map = aMaps[mapindex]; map->attribute; 2761 ++map) { 2762 if (aAttribute == map->attribute) { 2763 return true; 2764 } 2765 } 2766 } 2767 2768 return false; 2769 } 2770 2771 already_AddRefed<mozilla::dom::NodeInfo> Element::GetExistingAttrNameFromQName( 2772 const nsAString& aStr) const { 2773 const nsAttrName* name = InternalGetAttrNameFromQName(aStr); 2774 if (!name) { 2775 return nullptr; 2776 } 2777 2778 RefPtr<mozilla::dom::NodeInfo> nodeInfo; 2779 if (name->IsAtom()) { 2780 nodeInfo = mNodeInfo->NodeInfoManager()->GetNodeInfo( 2781 name->Atom(), nullptr, kNameSpaceID_None, ATTRIBUTE_NODE); 2782 } else { 2783 nodeInfo = name->NodeInfo(); 2784 } 2785 2786 return nodeInfo.forget(); 2787 } 2788 2789 // static 2790 bool Element::ShouldBlur(nsIContent* aContent) { 2791 // Determine if the current element is focused, if it is not focused 2792 // then we should not try to blur 2793 Document* document = aContent->GetComposedDoc(); 2794 if (!document) return false; 2795 2796 nsCOMPtr<nsPIDOMWindowOuter> window = document->GetWindow(); 2797 if (!window) return false; 2798 2799 nsCOMPtr<nsPIDOMWindowOuter> focusedFrame; 2800 nsIContent* contentToBlur = nsFocusManager::GetFocusedDescendant( 2801 window, nsFocusManager::eOnlyCurrentWindow, getter_AddRefs(focusedFrame)); 2802 2803 if (!contentToBlur) { 2804 return false; 2805 } 2806 2807 if (contentToBlur == aContent) { 2808 return true; 2809 } 2810 2811 ShadowRoot* root = aContent->GetShadowRoot(); 2812 if (root && root->DelegatesFocus() && 2813 contentToBlur->IsShadowIncludingInclusiveDescendantOf(root)) { 2814 return true; 2815 } 2816 return false; 2817 } 2818 2819 /* static */ 2820 nsresult Element::DispatchEvent(nsPresContext* aPresContext, 2821 WidgetEvent* aEvent, nsIContent* aTarget, 2822 bool aFullDispatch, nsEventStatus* aStatus) { 2823 MOZ_ASSERT(aTarget, "Must have target"); 2824 MOZ_ASSERT(aEvent, "Must have source event"); 2825 MOZ_ASSERT(aStatus, "Null out param?"); 2826 2827 if (!aPresContext) { 2828 return NS_OK; 2829 } 2830 2831 RefPtr<PresShell> presShell = aPresContext->GetPresShell(); 2832 if (!presShell) { 2833 return NS_OK; 2834 } 2835 2836 if (aFullDispatch) { 2837 return presShell->HandleEventWithTarget(aEvent, nullptr, aTarget, aStatus); 2838 } 2839 2840 return presShell->HandleDOMEventWithTarget(aTarget, aEvent, aStatus); 2841 } 2842 2843 /* static */ 2844 nsresult Element::DispatchClickEvent(nsPresContext* aPresContext, 2845 WidgetInputEvent* aSourceEvent, 2846 nsIContent* aTarget, bool aFullDispatch, 2847 const EventFlags* aExtraEventFlags, 2848 nsEventStatus* aStatus) { 2849 MOZ_ASSERT(aTarget, "Must have target"); 2850 MOZ_ASSERT(aSourceEvent, "Must have source event"); 2851 MOZ_ASSERT(aStatus, "Null out param?"); 2852 2853 WidgetPointerEvent event(aSourceEvent->IsTrusted(), ePointerClick, 2854 aSourceEvent->mWidget); 2855 event.mRefPoint = aSourceEvent->mRefPoint; 2856 uint32_t clickCount = 1; 2857 float pressure = 0; 2858 uint32_t pointerId = 0; // Use the default value here. 2859 uint16_t inputSource = 0; 2860 WidgetMouseEvent* sourceMouseEvent = aSourceEvent->AsMouseEvent(); 2861 if (sourceMouseEvent) { 2862 clickCount = sourceMouseEvent->mClickCount; 2863 pressure = sourceMouseEvent->mPressure; 2864 pointerId = sourceMouseEvent->pointerId; 2865 inputSource = sourceMouseEvent->mInputSource; 2866 } else if (aSourceEvent->mClass == eKeyboardEventClass) { 2867 event.mFlags.mIsPositionless = true; 2868 inputSource = MouseEvent_Binding::MOZ_SOURCE_KEYBOARD; 2869 // pointerId definition in Pointer Events: 2870 // > The pointerId value of -1 MUST be reserved and used to indicate events 2871 // > that were generated by something other than a pointing device. 2872 pointerId = -1; 2873 } 2874 event.mPressure = pressure; 2875 event.mClickCount = clickCount; 2876 event.pointerId = pointerId; 2877 event.mInputSource = inputSource; 2878 event.mModifiers = aSourceEvent->mModifiers; 2879 if (aExtraEventFlags) { 2880 // Be careful not to overwrite existing flags! 2881 event.mFlags.Union(*aExtraEventFlags); 2882 } 2883 2884 return DispatchEvent(aPresContext, &event, aTarget, aFullDispatch, aStatus); 2885 } 2886 2887 //---------------------------------------------------------------------- 2888 nsresult Element::LeaveLink(nsPresContext* aPresContext) { 2889 if (!aPresContext || !aPresContext->Document()->LinkHandlingEnabled()) { 2890 return NS_OK; 2891 } 2892 nsIDocShell* shell = aPresContext->Document()->GetDocShell(); 2893 if (!shell) { 2894 return NS_OK; 2895 } 2896 return nsDocShell::Cast(shell)->OnLeaveLink(); 2897 } 2898 2899 void Element::SetEventHandler(nsAtom* aEventName, const nsAString& aValue, 2900 bool aDefer) { 2901 Document* ownerDoc = OwnerDoc(); 2902 if (ownerDoc->IsLoadedAsData()) { 2903 // Make this a no-op rather than throwing an error to avoid 2904 // the error causing problems setting the attribute. 2905 return; 2906 } 2907 2908 MOZ_ASSERT(aEventName, "Must have event name!"); 2909 bool defer = true; 2910 EventListenerManager* manager = 2911 GetEventListenerManagerForAttr(aEventName, &defer); 2912 if (!manager) { 2913 return; 2914 } 2915 2916 defer = defer && aDefer; // only defer if everyone agrees... 2917 manager->SetEventHandler(aEventName, aValue, defer, 2918 !nsContentUtils::IsChromeDoc(ownerDoc), this); 2919 } 2920 2921 //---------------------------------------------------------------------- 2922 2923 const nsAttrName* Element::InternalGetAttrNameFromQName( 2924 const nsAString& aStr, nsAutoString* aNameToUse) const { 2925 MOZ_ASSERT(!aNameToUse || aNameToUse->IsEmpty()); 2926 const nsAttrName* val = nullptr; 2927 if (IsHTMLElement() && IsInHTMLDocument()) { 2928 nsAutoString lower; 2929 nsAutoString& outStr = aNameToUse ? *aNameToUse : lower; 2930 nsContentUtils::ASCIIToLower(aStr, outStr); 2931 val = mAttrs.GetExistingAttrNameFromQName(outStr); 2932 if (val) { 2933 outStr.Truncate(); 2934 } 2935 } else { 2936 val = mAttrs.GetExistingAttrNameFromQName(aStr); 2937 if (!val && aNameToUse) { 2938 *aNameToUse = aStr; 2939 } 2940 } 2941 2942 return val; 2943 } 2944 2945 bool Element::MaybeCheckSameAttrVal(int32_t aNamespaceID, const nsAtom* aName, 2946 const nsAtom* aPrefix, 2947 const nsAttrValueOrString& aValue, 2948 bool aNotify, nsAttrValue& aOldValue, 2949 AttrModType* aModType, bool* aOldValueSet) { 2950 bool modification = false; 2951 *aOldValueSet = false; 2952 2953 // If we have no listeners and aNotify is false, we are almost certainly 2954 // coming from the content sink and will almost certainly have no previous 2955 // value. Even if we do, setting the value is cheap when we don't plan to 2956 // notify. The check for aNotify here is an optimization. 2957 if (aNotify) { 2958 BorrowedAttrInfo info(GetAttrInfo(aNamespaceID, aName)); 2959 if (info.mValue) { 2960 // Check whether the old value is the same as the new one. Note that we 2961 // only need to actually _get_ the old value if the element is a custom 2962 // element (because it may have an attribute changed callback). 2963 if (GetCustomElementData()) { 2964 // Need to store the old value. 2965 // 2966 // If the current attribute value contains a pointer to some other data 2967 // structure that gets updated in the process of setting the attribute 2968 // we'll no longer have the old value of the attribute. Therefore, we 2969 // should serialize the attribute value now to keep a snapshot. 2970 aOldValue.SetToSerialized(*info.mValue); 2971 *aOldValueSet = true; 2972 } 2973 bool valueMatches = aValue.EqualsAsStrings(*info.mValue); 2974 if (valueMatches && aPrefix == info.mName->GetPrefix()) { 2975 return true; 2976 } 2977 modification = true; 2978 } 2979 } 2980 *aModType = modification ? AttrModType::Modification : AttrModType::Addition; 2981 return false; 2982 } 2983 2984 bool Element::OnlyNotifySameValueSet(int32_t aNamespaceID, nsAtom* aName, 2985 nsAtom* aPrefix, 2986 const nsAttrValueOrString& aValue, 2987 bool aNotify, nsAttrValue& aOldValue, 2988 AttrModType* aModType, 2989 bool* aOldValueSet) { 2990 if (!MaybeCheckSameAttrVal(aNamespaceID, aName, aPrefix, aValue, aNotify, 2991 aOldValue, aModType, aOldValueSet)) { 2992 return false; 2993 } 2994 2995 nsAutoScriptBlocker scriptBlocker; 2996 MutationObservers::NotifyAttributeSetToCurrentValue(this, aNamespaceID, 2997 aName); 2998 return true; 2999 } 3000 3001 nsresult Element::SetClassAttrFromParser(nsAtom* aValue) { 3002 // Keep this in sync with SetAttr and SetParsedAttr below. 3003 3004 nsAttrValue value; 3005 value.ParseAtomArray(aValue); 3006 3007 Document* document = GetComposedDoc(); 3008 mozAutoDocUpdate updateBatch(document, false); 3009 3010 // In principle, BeforeSetAttr should be called here if a node type 3011 // existed that wanted to do something special for class, but there 3012 // is no such node type, so calling SetMayHaveClass() directly. 3013 SetMayHaveClass(); 3014 3015 return SetAttrAndNotify(kNameSpaceID_None, nsGkAtoms::_class, 3016 nullptr, // prefix 3017 nullptr, // old value 3018 value, nullptr, AttrModType::Addition, 3019 false, // notify 3020 kCallAfterSetAttr, document, updateBatch); 3021 } 3022 3023 nsresult Element::SetAttr(int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix, 3024 const nsAString& aValue, 3025 nsIPrincipal* aSubjectPrincipal, bool aNotify) { 3026 // Keep this in sync with SetParsedAttr below and SetSingleClassFromParser 3027 // above. 3028 const nsAttrValueOrString valueForComparison(aValue); 3029 return SetAttrInternal(aNamespaceID, aName, aPrefix, valueForComparison, 3030 aSubjectPrincipal, aNotify, 3031 [&](nsAttrValue& attrValue) { 3032 if (!ParseAttribute(aNamespaceID, aName, aValue, 3033 aSubjectPrincipal, attrValue)) { 3034 attrValue.SetTo(aValue); 3035 } 3036 }); 3037 } 3038 3039 nsresult Element::SetAndSwapAttr(nsAtom* aLocalName, nsAttrValue& aValue, 3040 bool* aHadValue) { 3041 MOZ_TRY(mAttrs.SetAndSwapAttr(aLocalName, aValue, aHadValue)); 3042 3043 if (aLocalName == nsGkAtoms::_class) { 3044 UpdateSubtreeBloomFilterForClass(GetClasses()); 3045 } 3046 UpdateSubtreeBloomFilterForAttribute(aLocalName); 3047 PropagateBloomFilterToParents(); 3048 3049 return NS_OK; 3050 } 3051 3052 nsresult Element::SetAndSwapAttr(mozilla::dom::NodeInfo* aName, 3053 nsAttrValue& aValue, bool* aHadValue) { 3054 MOZ_TRY(mAttrs.SetAndSwapAttr(aName, aValue, aHadValue)); 3055 3056 // Only update bloom filter for null-namespace attributes, since the 3057 // querySelector bloom filter optimization only applies to those. 3058 if (aName->NamespaceEquals(kNameSpaceID_None)) { 3059 nsAtom* localName = aName->NameAtom(); 3060 if (localName == nsGkAtoms::_class) { 3061 UpdateSubtreeBloomFilterForClass(GetClasses()); 3062 } 3063 UpdateSubtreeBloomFilterForAttribute(localName); 3064 PropagateBloomFilterToParents(); 3065 } 3066 3067 return NS_OK; 3068 } 3069 3070 nsresult Element::SetAttr(int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix, 3071 nsAtom* aValue, nsIPrincipal* aSubjectPrincipal, 3072 bool aNotify) { 3073 // Keep this in sync with SetParsedAttr below and SetSingleClassFromParser 3074 // above. 3075 const nsDependentAtomString valueString(aValue); 3076 const nsAttrValueOrString valueForComparison(valueString); 3077 return SetAttrInternal(aNamespaceID, aName, aPrefix, valueForComparison, 3078 aSubjectPrincipal, aNotify, 3079 [&](nsAttrValue& attrValue) { 3080 if (!ParseAttribute(aNamespaceID, aName, valueString, 3081 aSubjectPrincipal, attrValue)) { 3082 attrValue.SetTo(aValue); 3083 } 3084 }); 3085 } 3086 3087 template <typename ParseFunc> 3088 nsresult Element::SetAttrInternal(int32_t aNamespaceID, nsAtom* aName, 3089 nsAtom* aPrefix, 3090 const nsAttrValueOrString& aValue, 3091 nsIPrincipal* aSubjectPrincipal, bool aNotify, 3092 ParseFunc&& aParseFn) { 3093 NS_ENSURE_ARG_POINTER(aName); 3094 NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown, 3095 "Don't call SetAttr with unknown namespace"); 3096 3097 AttrModType modType{0}; // NOTE: Initialized with invalid value. 3098 nsAttrValue oldValue; 3099 bool oldValueSet; 3100 3101 if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, aValue, aNotify, 3102 oldValue, &modType, &oldValueSet)) { 3103 OnAttrSetButNotChanged(aNamespaceID, aName, aValue, aNotify); 3104 return NS_OK; 3105 } 3106 3107 // Hold a script blocker while calling ParseAttribute since that can call 3108 // out to id-observers 3109 Document* document = GetComposedDoc(); 3110 mozAutoDocUpdate updateBatch(document, aNotify); 3111 3112 if (aNotify) { 3113 MutationObservers::NotifyAttributeWillChange(this, aNamespaceID, aName, 3114 modType); 3115 } 3116 3117 nsAttrValue attrValue; 3118 aParseFn(attrValue); 3119 3120 BeforeSetAttr(aNamespaceID, aName, &attrValue, aNotify); 3121 3122 PreIdMaybeChange(aNamespaceID, aName, &attrValue); 3123 3124 return SetAttrAndNotify(aNamespaceID, aName, aPrefix, 3125 oldValueSet ? &oldValue : nullptr, attrValue, 3126 aSubjectPrincipal, modType, aNotify, 3127 kCallAfterSetAttr, document, updateBatch); 3128 } 3129 3130 nsresult Element::SetParsedAttr(int32_t aNamespaceID, nsAtom* aName, 3131 nsAtom* aPrefix, nsAttrValue& aParsedValue, 3132 bool aNotify) { 3133 // Keep this in sync with SetAttr and SetSingleClassFromParser above 3134 3135 NS_ENSURE_ARG_POINTER(aName); 3136 NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown, 3137 "Don't call SetAttr with unknown namespace"); 3138 3139 AttrModType modType{0}; // NOTE: Initialized with invalid value. 3140 nsAttrValue oldValue; 3141 bool oldValueSet; 3142 3143 { 3144 const nsAttrValueOrString value(aParsedValue); 3145 if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify, 3146 oldValue, &modType, &oldValueSet)) { 3147 OnAttrSetButNotChanged(aNamespaceID, aName, value, aNotify); 3148 return NS_OK; 3149 } 3150 } 3151 3152 Document* document = GetComposedDoc(); 3153 mozAutoDocUpdate updateBatch(document, aNotify); 3154 3155 if (aNotify) { 3156 MutationObservers::NotifyAttributeWillChange(this, aNamespaceID, aName, 3157 modType); 3158 } 3159 3160 BeforeSetAttr(aNamespaceID, aName, &aParsedValue, aNotify); 3161 3162 PreIdMaybeChange(aNamespaceID, aName, &aParsedValue); 3163 3164 return SetAttrAndNotify(aNamespaceID, aName, aPrefix, 3165 oldValueSet ? &oldValue : nullptr, aParsedValue, 3166 nullptr, modType, aNotify, kCallAfterSetAttr, 3167 document, updateBatch); 3168 } 3169 3170 nsresult Element::SetAttrAndNotify( 3171 int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix, 3172 const nsAttrValue* aOldValue, nsAttrValue& aParsedValue, 3173 nsIPrincipal* aSubjectPrincipal, AttrModType aModType, bool aNotify, 3174 bool aCallAfterSetAttr, Document* aComposedDocument, 3175 const mozAutoDocUpdate& aGuard) { 3176 nsMutationGuard::DidMutate(); 3177 3178 // Copy aParsedValue for later use since it will be lost when we call 3179 // SetAndSwapAttr below 3180 nsAttrValue valueForAfterSetAttr; 3181 if (aCallAfterSetAttr || GetCustomElementData()) { 3182 valueForAfterSetAttr.SetTo(aParsedValue); 3183 } 3184 3185 bool hadValidDir = false; 3186 bool hadDirAuto = false; 3187 bool oldValueSet; 3188 3189 if (aNamespaceID == kNameSpaceID_None) { 3190 if (aName == nsGkAtoms::dir) { 3191 hadValidDir = HasValidDir() || IsHTMLElement(nsGkAtoms::bdi); 3192 hadDirAuto = HasDirAuto(); // already takes bdi into account 3193 } 3194 3195 MOZ_TRY(SetAndSwapAttr(aName, aParsedValue, &oldValueSet)); 3196 if (IsAttributeMapped(aName) && !IsPendingMappedAttributeEvaluation()) { 3197 mAttrs.InfallibleMarkAsPendingPresAttributeEvaluation(); 3198 if (Document* doc = GetComposedDoc()) { 3199 doc->ScheduleForPresAttrEvaluation(this); 3200 } 3201 } 3202 } else { 3203 RefPtr<mozilla::dom::NodeInfo> ni = 3204 mNodeInfo->NodeInfoManager()->GetNodeInfo(aName, aPrefix, aNamespaceID, 3205 ATTRIBUTE_NODE); 3206 MOZ_TRY(SetAndSwapAttr(ni, aParsedValue, &oldValueSet)); 3207 } 3208 3209 PostIdMaybeChange(aNamespaceID, aName, &valueForAfterSetAttr); 3210 3211 // If the old value owns its own data, we know it is OK to keep using it. 3212 // oldValue will be null if there was no previously set value 3213 const nsAttrValue* oldValue; 3214 if (aParsedValue.StoresOwnData()) { 3215 if (oldValueSet) { 3216 oldValue = &aParsedValue; 3217 } else { 3218 oldValue = nullptr; 3219 } 3220 } else { 3221 // No need to conditionally assign null here. If there was no previously 3222 // set value for the attribute, aOldValue will already be null. 3223 oldValue = aOldValue; 3224 } 3225 3226 if (HasElementCreatedFromPrototypeAndHasUnmodifiedL10n() && 3227 aNamespaceID == kNameSpaceID_None && 3228 (aName == nsGkAtoms::datal10nid || aName == nsGkAtoms::datal10nargs)) { 3229 ClearElementCreatedFromPrototypeAndHasUnmodifiedL10n(); 3230 if (aComposedDocument) { 3231 aComposedDocument->mL10nProtoElements.Remove(this); 3232 } 3233 } 3234 3235 const CustomElementData* data = GetCustomElementData(); 3236 if (data && data->mState == CustomElementData::State::eCustom) { 3237 CustomElementDefinition* definition = data->GetCustomElementDefinition(); 3238 MOZ_ASSERT(definition, "Should have a valid CustomElementDefinition"); 3239 3240 if (definition->IsInObservedAttributeList(aName)) { 3241 nsAutoString ns; 3242 nsNameSpaceManager::GetInstance()->GetNameSpaceURI(aNamespaceID, ns); 3243 3244 LifecycleCallbackArgs args; 3245 args.mName = aName; 3246 if (aModType == AttrModType::Addition) { 3247 args.mOldValue = VoidString(); 3248 } else { 3249 if (oldValue) { 3250 oldValue->ToString(args.mOldValue); 3251 } else { 3252 // If there is no old value, get the value of the uninitialized 3253 // attribute that was swapped with aParsedValue. 3254 aParsedValue.ToString(args.mOldValue); 3255 } 3256 } 3257 valueForAfterSetAttr.ToString(args.mNewValue); 3258 args.mNamespaceURI = ns.IsEmpty() ? VoidString() : ns; 3259 3260 nsContentUtils::EnqueueLifecycleCallback( 3261 ElementCallbackType::eAttributeChanged, this, args, definition); 3262 } 3263 } 3264 3265 if (aCallAfterSetAttr) { 3266 AfterSetAttr(aNamespaceID, aName, &valueForAfterSetAttr, oldValue, 3267 aSubjectPrincipal, aNotify); 3268 3269 if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) { 3270 OnSetDirAttr(this, &valueForAfterSetAttr, hadValidDir, hadDirAuto, 3271 aNotify); 3272 } 3273 } 3274 3275 if (aNotify) { 3276 // Don't pass aOldValue to AttributeChanged since it may not be reliable. 3277 // Callers only compute aOldValue under certain conditions which may not 3278 // be triggered by all nsIMutationObservers. 3279 MutationObservers::NotifyAttributeChanged( 3280 this, aNamespaceID, aName, aModType, 3281 aParsedValue.StoresOwnData() ? &aParsedValue : nullptr); 3282 } 3283 3284 return NS_OK; 3285 } 3286 3287 void Element::TryReserveAttributeCount(uint32_t aAttributeCount) { 3288 (void)mAttrs.GrowTo(aAttributeCount); 3289 } 3290 3291 bool Element::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute, 3292 const nsAString& aValue, 3293 nsIPrincipal* aMaybeScriptedPrincipal, 3294 nsAttrValue& aResult) { 3295 if (aAttribute == nsGkAtoms::lang) { 3296 aResult.ParseAtom(aValue); 3297 return true; 3298 } 3299 3300 if (aNamespaceID == kNameSpaceID_None) { 3301 if (aAttribute == nsGkAtoms::_class || aAttribute == nsGkAtoms::part || 3302 aAttribute == nsGkAtoms::aria_controls || 3303 aAttribute == nsGkAtoms::aria_describedby || 3304 aAttribute == nsGkAtoms::aria_details || 3305 aAttribute == nsGkAtoms::aria_errormessage || 3306 aAttribute == nsGkAtoms::aria_flowto || 3307 aAttribute == nsGkAtoms::aria_labelledby || 3308 aAttribute == nsGkAtoms::aria_owns) { 3309 aResult.ParseAtomArray(aValue); 3310 return true; 3311 } 3312 3313 if (aAttribute == nsGkAtoms::exportparts) { 3314 aResult.ParsePartMapping(aValue); 3315 return true; 3316 } 3317 3318 if (aAttribute == nsGkAtoms::aria_activedescendant) { 3319 // String in aria-activedescendant is an id, so store as an atom. 3320 aResult.ParseAtom(aValue); 3321 return true; 3322 } 3323 3324 if (aAttribute == nsGkAtoms::id) { 3325 // Store id as an atom. id="" means that the element has no id, 3326 // not that it has an emptystring as the id. 3327 if (aValue.IsEmpty()) { 3328 return false; 3329 } 3330 aResult.ParseAtom(aValue); 3331 return true; 3332 } 3333 } 3334 3335 return false; 3336 } 3337 3338 void Element::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName, 3339 const nsAttrValue* aValue, bool aNotify) { 3340 if (aNamespaceID == kNameSpaceID_None) { 3341 if (aName == nsGkAtoms::_class && aValue) { 3342 // Note: This flag is asymmetrical. It is never unset and isn't exact. 3343 // If it is ever made to be exact, we probably need to handle this 3344 // similarly to how ids are handled in PreIdMaybeChange and 3345 // PostIdMaybeChange. 3346 // Note that SetSingleClassFromParser inlines BeforeSetAttr and 3347 // calls SetMayHaveClass directly. Making a subclass take action 3348 // on the class attribute in a BeforeSetAttr override would 3349 // require revising SetSingleClassFromParser. 3350 SetMayHaveClass(); 3351 } 3352 } 3353 } 3354 3355 void Element::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, 3356 const nsAttrValue* aValue, 3357 const nsAttrValue* aOldValue, 3358 nsIPrincipal* aMaybeScriptedPrincipal, 3359 bool aNotify) { 3360 if (aNamespaceID == kNameSpaceID_None) { 3361 if (aName == nsGkAtoms::part) { 3362 bool isPart = !!aValue; 3363 if (HasPartAttribute() != isPart) { 3364 SetHasPartAttribute(isPart); 3365 if (ShadowRoot* shadow = GetContainingShadow()) { 3366 if (isPart) { 3367 shadow->PartAdded(*this); 3368 } else { 3369 shadow->PartRemoved(*this); 3370 } 3371 } 3372 } 3373 MOZ_ASSERT(HasPartAttribute() == isPart); 3374 } else if (aName == nsGkAtoms::slot && GetParent()) { 3375 if (ShadowRoot* shadow = GetParent()->GetShadowRoot()) { 3376 shadow->MaybeReassignContent(*this); 3377 } 3378 } else if (aName == nsGkAtoms::aria_activedescendant) { 3379 ClearExplicitlySetAttrElement(aName); 3380 } else if (aName == nsGkAtoms::aria_controls || 3381 aName == nsGkAtoms::aria_describedby || 3382 aName == nsGkAtoms::aria_details || 3383 aName == nsGkAtoms::aria_errormessage || 3384 aName == nsGkAtoms::aria_flowto || 3385 aName == nsGkAtoms::aria_labelledby || 3386 aName == nsGkAtoms::aria_owns) { 3387 ClearExplicitlySetAttrElements(aName); 3388 } 3389 } 3390 } 3391 3392 void Element::PreIdMaybeChange(int32_t aNamespaceID, nsAtom* aName, 3393 const nsAttrValue* aValue) { 3394 if (aNamespaceID != kNameSpaceID_None || aName != nsGkAtoms::id) { 3395 return; 3396 } 3397 RemoveFromIdTable(); 3398 } 3399 3400 void Element::PostIdMaybeChange(int32_t aNamespaceID, nsAtom* aName, 3401 const nsAttrValue* aValue) { 3402 if (aNamespaceID != kNameSpaceID_None || aName != nsGkAtoms::id) { 3403 return; 3404 } 3405 3406 // id="" means that the element has no id, not that it has an empty 3407 // string as the id. 3408 if (aValue && !aValue->IsEmptyString()) { 3409 SetHasID(); 3410 AddToIdTable(aValue->GetAtomValue()); 3411 } else { 3412 ClearHasID(); 3413 } 3414 } 3415 3416 void Element::OnAttrSetButNotChanged(int32_t aNamespaceID, nsAtom* aName, 3417 const nsAttrValueOrString& aValue, 3418 bool aNotify) { 3419 const CustomElementData* data = GetCustomElementData(); 3420 if (data && data->mState == CustomElementData::State::eCustom) { 3421 CustomElementDefinition* definition = data->GetCustomElementDefinition(); 3422 MOZ_ASSERT(definition, "Should have a valid CustomElementDefinition"); 3423 3424 if (definition->IsInObservedAttributeList(aName)) { 3425 nsAutoString ns; 3426 nsNameSpaceManager::GetInstance()->GetNameSpaceURI(aNamespaceID, ns); 3427 3428 nsAutoString value(aValue.String()); 3429 LifecycleCallbackArgs args; 3430 args.mName = aName; 3431 args.mOldValue = value; 3432 args.mNewValue = value; 3433 args.mNamespaceURI = ns.IsEmpty() ? VoidString() : ns; 3434 3435 nsContentUtils::EnqueueLifecycleCallback( 3436 ElementCallbackType::eAttributeChanged, this, args, definition); 3437 } 3438 } 3439 3440 if (aNamespaceID == kNameSpaceID_None && 3441 aName == nsGkAtoms::aria_activedescendant) { 3442 ClearExplicitlySetAttrElement(aName); 3443 } 3444 3445 if (aNamespaceID == kNameSpaceID_None && 3446 (aName == nsGkAtoms::aria_controls || 3447 aName == nsGkAtoms::aria_describedby || 3448 aName == nsGkAtoms::aria_details || 3449 aName == nsGkAtoms::aria_errormessage || 3450 aName == nsGkAtoms::aria_flowto || aName == nsGkAtoms::aria_labelledby || 3451 aName == nsGkAtoms::aria_owns)) { 3452 ClearExplicitlySetAttrElements(aName); 3453 } 3454 } 3455 3456 EventListenerManager* Element::GetEventListenerManagerForAttr(nsAtom* aAttrName, 3457 bool* aDefer) { 3458 *aDefer = true; 3459 return GetOrCreateListenerManager(); 3460 } 3461 3462 bool Element::GetAttr(const nsAtom* aName, nsAString& aResult) const { 3463 const nsAttrValue* val = mAttrs.GetAttr(aName); 3464 if (!val) { 3465 aResult.Truncate(); 3466 return false; 3467 } 3468 val->ToString(aResult); 3469 return true; 3470 } 3471 3472 bool Element::GetAttr(int32_t aNameSpaceID, const nsAtom* aName, 3473 nsAString& aResult) const { 3474 const nsAttrValue* val = mAttrs.GetAttr(aName, aNameSpaceID); 3475 if (!val) { 3476 aResult.Truncate(); 3477 return false; 3478 } 3479 val->ToString(aResult); 3480 return true; 3481 } 3482 3483 int32_t Element::FindAttrValueIn(int32_t aNameSpaceID, const nsAtom* aName, 3484 AttrArray::AttrValuesArray* aValues, 3485 nsCaseTreatment aCaseSensitive) const { 3486 return mAttrs.FindAttrValueIn(aNameSpaceID, aName, aValues, aCaseSensitive); 3487 } 3488 3489 nsresult Element::UnsetAttr(int32_t aNameSpaceID, nsAtom* aName, bool aNotify) { 3490 NS_ASSERTION(nullptr != aName, "must have attribute name"); 3491 3492 int32_t index = mAttrs.IndexOfAttr(aName, aNameSpaceID); 3493 if (index < 0) { 3494 return NS_OK; 3495 } 3496 3497 Document* document = GetComposedDoc(); 3498 mozAutoDocUpdate updateBatch(document, aNotify); 3499 3500 if (aNotify) { 3501 MutationObservers::NotifyAttributeWillChange(this, aNameSpaceID, aName, 3502 AttrModType::Removal); 3503 } 3504 3505 BeforeSetAttr(aNameSpaceID, aName, nullptr, aNotify); 3506 3507 PreIdMaybeChange(aNameSpaceID, aName, nullptr); 3508 3509 // Clear the attribute out from attribute map. 3510 nsDOMSlots* slots = GetExistingDOMSlots(); 3511 if (slots && slots->mAttributeMap) { 3512 slots->mAttributeMap->DropAttribute(aNameSpaceID, aName); 3513 } 3514 3515 // The id-handling code, and in the future possibly other code, need to 3516 // react to unexpected attribute changes. 3517 nsMutationGuard::DidMutate(); 3518 3519 bool hadValidDir = false; 3520 bool hadDirAuto = false; 3521 3522 if (aNameSpaceID == kNameSpaceID_None) { 3523 if (aName == nsGkAtoms::dir) { 3524 hadValidDir = HasValidDir() || IsHTMLElement(nsGkAtoms::bdi); 3525 hadDirAuto = HasDirAuto(); // already takes bdi into account 3526 } 3527 if (IsAttributeMapped(aName) && !IsPendingMappedAttributeEvaluation()) { 3528 mAttrs.InfallibleMarkAsPendingPresAttributeEvaluation(); 3529 if (Document* doc = GetComposedDoc()) { 3530 doc->ScheduleForPresAttrEvaluation(this); 3531 } 3532 } 3533 } 3534 3535 nsAttrValue oldValue; 3536 MOZ_TRY(mAttrs.RemoveAttrAt(index, oldValue)); 3537 3538 PostIdMaybeChange(aNameSpaceID, aName, nullptr); 3539 3540 const CustomElementData* data = GetCustomElementData(); 3541 if (data && data->mState == CustomElementData::State::eCustom) { 3542 CustomElementDefinition* definition = data->GetCustomElementDefinition(); 3543 MOZ_ASSERT(definition, "Should have a valid CustomElementDefinition"); 3544 if (definition->IsInObservedAttributeList(aName)) { 3545 nsAutoString ns; 3546 nsNameSpaceManager::GetInstance()->GetNameSpaceURI(aNameSpaceID, ns); 3547 LifecycleCallbackArgs args; 3548 args.mName = aName; 3549 oldValue.ToString(args.mOldValue); 3550 args.mNewValue = VoidString(); 3551 args.mNamespaceURI = ns.IsEmpty() ? VoidString() : ns; 3552 nsContentUtils::EnqueueLifecycleCallback( 3553 ElementCallbackType::eAttributeChanged, this, args, definition); 3554 } 3555 } 3556 3557 AfterSetAttr(aNameSpaceID, aName, nullptr, &oldValue, nullptr, aNotify); 3558 3559 if (aNotify) { 3560 // We can always pass oldValue here since there is no new value which could 3561 // have corrupted it. 3562 MutationObservers::NotifyAttributeChanged(this, aNameSpaceID, aName, 3563 AttrModType::Removal, &oldValue); 3564 } 3565 3566 if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) { 3567 OnSetDirAttr(this, nullptr, hadValidDir, hadDirAuto, aNotify); 3568 } 3569 3570 return NS_OK; 3571 } 3572 3573 void Element::DescribeAttribute(uint32_t index, 3574 nsAString& aOutDescription) const { 3575 // name 3576 mAttrs.AttrNameAt(index)->GetQualifiedName(aOutDescription); 3577 3578 // value 3579 aOutDescription.AppendLiteral("=\""); 3580 nsAutoString value; 3581 mAttrs.AttrAt(index)->ToString(value); 3582 for (uint32_t i = value.Length(); i > 0; --i) { 3583 if (value[i - 1] == char16_t('"')) value.Insert(char16_t('\\'), i - 1); 3584 } 3585 aOutDescription.Append(value); 3586 aOutDescription.Append('"'); 3587 } 3588 3589 #ifdef MOZ_DOM_LIST 3590 void Element::ListAttributes(FILE* out) const { 3591 uint32_t index, count = mAttrs.AttrCount(); 3592 for (index = 0; index < count; index++) { 3593 nsAutoString attributeDescription; 3594 DescribeAttribute(index, attributeDescription); 3595 3596 fputs(" ", out); 3597 fputs(NS_LossyConvertUTF16toASCII(attributeDescription).get(), out); 3598 } 3599 } 3600 3601 void Element::List(FILE* out, int32_t aIndent, const nsCString& aPrefix) const { 3602 int32_t indent; 3603 for (indent = aIndent; --indent >= 0;) fputs(" ", out); 3604 3605 fputs(aPrefix.get(), out); 3606 3607 fputs(NS_LossyConvertUTF16toASCII(mNodeInfo->QualifiedName()).get(), out); 3608 3609 fprintf(out, "@%p", (void*)this); 3610 3611 ListAttributes(out); 3612 3613 fprintf(out, " state=[%llx]", 3614 static_cast<unsigned long long>(State().GetInternalValue())); 3615 fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags())); 3616 fprintf(out, " selectorflags=[%08x]", 3617 static_cast<unsigned int>(GetSelectorFlags())); 3618 if (IsClosestCommonInclusiveAncestorForRangeInSelection()) { 3619 const LinkedList<AbstractRange>* ranges = 3620 GetExistingClosestCommonInclusiveAncestorRanges(); 3621 int32_t count = 0; 3622 if (ranges) { 3623 // Can't use range-based iteration on a const LinkedList, unfortunately. 3624 for (const AbstractRange* r = ranges->getFirst(); r; r = r->getNext()) { 3625 ++count; 3626 } 3627 } 3628 fprintf(out, " ranges:%d", count); 3629 } 3630 fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame())); 3631 fprintf(out, " refcount=%" PRIuPTR "<", mRefCnt.get()); 3632 3633 nsIContent* child = GetFirstChild(); 3634 if (child) { 3635 fputs("\n", out); 3636 3637 for (; child; child = child->GetNextSibling()) { 3638 child->List(out, aIndent + 1); 3639 } 3640 3641 for (indent = aIndent; --indent >= 0;) fputs(" ", out); 3642 } 3643 3644 fputs(">\n", out); 3645 } 3646 3647 void Element::DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const { 3648 int32_t indent; 3649 for (indent = aIndent; --indent >= 0;) fputs(" ", out); 3650 3651 const nsString& buf = mNodeInfo->QualifiedName(); 3652 fputs("<", out); 3653 fputs(NS_LossyConvertUTF16toASCII(buf).get(), out); 3654 3655 if (aDumpAll) ListAttributes(out); 3656 3657 fputs(">", out); 3658 3659 if (aIndent) fputs("\n", out); 3660 3661 for (nsIContent* child = GetFirstChild(); child; 3662 child = child->GetNextSibling()) { 3663 int32_t indent = aIndent ? aIndent + 1 : 0; 3664 child->DumpContent(out, indent, aDumpAll); 3665 } 3666 for (indent = aIndent; --indent >= 0;) fputs(" ", out); 3667 fputs("</", out); 3668 fputs(NS_LossyConvertUTF16toASCII(buf).get(), out); 3669 fputs(">", out); 3670 3671 if (aIndent) fputs("\n", out); 3672 } 3673 #endif 3674 3675 void Element::Describe(nsAString& aOutDescription, bool aShort) const { 3676 aOutDescription.Append(mNodeInfo->QualifiedName()); 3677 aOutDescription.AppendPrintf("@%p", (void*)this); 3678 3679 uint32_t index, count = mAttrs.AttrCount(); 3680 for (index = 0; index < count; index++) { 3681 if (aShort) { 3682 const nsAttrName* name = mAttrs.AttrNameAt(index); 3683 if (!name->Equals(nsGkAtoms::id) && !name->Equals(nsGkAtoms::_class)) { 3684 continue; 3685 } 3686 } 3687 aOutDescription.Append(' '); 3688 nsAutoString attributeDescription; 3689 DescribeAttribute(index, attributeDescription); 3690 aOutDescription.Append(attributeDescription); 3691 } 3692 } 3693 3694 bool Element::CheckHandleEventForLinksPrecondition( 3695 EventChainVisitor& aVisitor) const { 3696 // Make sure we actually are a link 3697 if (!IsLink()) { 3698 return false; 3699 } 3700 if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault || 3701 (!aVisitor.mEvent->IsTrusted() && 3702 (aVisitor.mEvent->mMessage != ePointerClick) && 3703 (aVisitor.mEvent->mMessage != eKeyPress) && 3704 (aVisitor.mEvent->mMessage != eLegacyDOMActivate)) || 3705 aVisitor.mEvent->mFlags.mMultipleActionsPrevented) { 3706 return false; 3707 } 3708 return true; 3709 } 3710 3711 void Element::GetEventTargetParentForLinks(EventChainPreVisitor& aVisitor) { 3712 // Optimisation: return early if this event doesn't interest us. 3713 // IMPORTANT: this switch and the switch below it must be kept in sync! 3714 switch (aVisitor.mEvent->mMessage) { 3715 case eMouseOver: 3716 case eFocus: 3717 case eMouseOut: 3718 case eBlur: 3719 break; 3720 default: 3721 return; 3722 } 3723 3724 // Make sure we meet the preconditions before continuing 3725 if (!CheckHandleEventForLinksPrecondition(aVisitor)) { 3726 return; 3727 } 3728 3729 // We try to handle everything we can even when the URI is invalid. Though of 3730 // course we can't do stuff like updating the status bar, so return early here 3731 // instead. 3732 nsCOMPtr<nsIURI> absURI = GetHrefURI(); 3733 if (!absURI) { 3734 return; 3735 } 3736 3737 // We do the status bar updates in GetEventTargetParent so that the status bar 3738 // gets updated even if the event is consumed before we have a chance to set 3739 // it. 3740 switch (aVisitor.mEvent->mMessage) { 3741 // Set the status bar similarly for mouseover and focus 3742 case eMouseOver: 3743 aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; 3744 [[fallthrough]]; 3745 case eFocus: { 3746 InternalFocusEvent* focusEvent = aVisitor.mEvent->AsFocusEvent(); 3747 if (!focusEvent || !focusEvent->mIsRefocus) { 3748 nsAutoString target; 3749 GetLinkTarget(target); 3750 nsContentUtils::TriggerLinkMouseOver(this, absURI, target); 3751 // Make sure any ancestor links don't also TriggerLink 3752 aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true; 3753 } 3754 break; 3755 } 3756 case eMouseOut: 3757 aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; 3758 [[fallthrough]]; 3759 case eBlur: { 3760 nsresult rv = LeaveLink(aVisitor.mPresContext); 3761 if (NS_SUCCEEDED(rv)) { 3762 aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true; 3763 } 3764 break; 3765 } 3766 3767 default: 3768 // switch not in sync with the optimization switch earlier in this 3769 // function 3770 MOZ_ASSERT_UNREACHABLE("switch statements not in sync"); 3771 } 3772 } 3773 3774 // This dispatches a 'chromelinkclick' CustomEvent to chrome-only listeners, 3775 // so that frontend can handle middle-clicks and ctrl/cmd/shift/etc.-clicks 3776 // on links, without getting a call for every single click the user makes. 3777 // Only supported for click or auxclick events. 3778 void Element::DispatchChromeOnlyLinkClickEvent( 3779 EventChainPostVisitor& aVisitor) { 3780 MOZ_ASSERT(aVisitor.mEvent->mMessage == ePointerAuxClick || 3781 aVisitor.mEvent->mMessage == ePointerClick, 3782 "DispatchChromeOnlyLinkClickEvent supports only click and " 3783 "auxclick source events"); 3784 Document* doc = OwnerDoc(); 3785 RefPtr<XULCommandEvent> event = 3786 new XULCommandEvent(doc, aVisitor.mPresContext, nullptr); 3787 RefPtr<dom::Event> mouseDOMEvent = aVisitor.mDOMEvent; 3788 if (!mouseDOMEvent) { 3789 mouseDOMEvent = EventDispatcher::CreateEvent( 3790 aVisitor.mEvent->mOriginalTarget, aVisitor.mPresContext, 3791 aVisitor.mEvent, u""_ns); 3792 NS_ADDREF(aVisitor.mDOMEvent = mouseDOMEvent); 3793 } 3794 3795 MouseEvent* mouseEvent = mouseDOMEvent->AsMouseEvent(); 3796 event->InitCommandEvent( 3797 u"chromelinkclick"_ns, /* CanBubble */ true, 3798 /* Cancelable */ true, nsGlobalWindowInner::Cast(doc->GetInnerWindow()), 3799 0, mouseEvent->CtrlKey(), mouseEvent->AltKey(), mouseEvent->ShiftKey(), 3800 mouseEvent->MetaKey(), mouseEvent->Button(), mouseDOMEvent, 3801 mouseEvent->InputSource(CallerType::System), IgnoreErrors()); 3802 // Note: we're always trusted, but the event we pass as the `sourceEvent` 3803 // might not be. Frontend code will check that event's trusted property to 3804 // make that determination; doing it this way means we don't also start 3805 // acting on web-generated custom 'chromelinkclick' events which would 3806 // provide additional attack surface for a malicious actor. 3807 event->SetTrusted(true); 3808 event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true; 3809 DispatchEvent(*event); 3810 } 3811 3812 nsresult Element::PostHandleEventForLinks(EventChainPostVisitor& aVisitor) { 3813 // Optimisation: return early if this event doesn't interest us. 3814 // IMPORTANT: this switch and the switch below it must be kept in sync! 3815 switch (aVisitor.mEvent->mMessage) { 3816 case eMouseDown: 3817 case ePointerClick: 3818 case ePointerAuxClick: 3819 case eLegacyDOMActivate: 3820 case eKeyPress: 3821 break; 3822 default: 3823 return NS_OK; 3824 } 3825 3826 // Make sure we meet the preconditions before continuing 3827 if (!CheckHandleEventForLinksPrecondition(aVisitor)) { 3828 return NS_OK; 3829 } 3830 3831 // We try to handle ~everything consistently even if the href is invalid 3832 // (GetHrefURI() returns null). 3833 nsresult rv = NS_OK; 3834 3835 switch (aVisitor.mEvent->mMessage) { 3836 case eMouseDown: { 3837 if (!OwnerDoc()->LinkHandlingEnabled()) { 3838 break; 3839 } 3840 3841 WidgetMouseEvent* const mouseEvent = aVisitor.mEvent->AsMouseEvent(); 3842 mouseEvent->mFlags.mMultipleActionsPrevented |= 3843 mouseEvent->mButton == MouseButton::ePrimary || 3844 mouseEvent->mButton == MouseButton::eMiddle; 3845 3846 if (mouseEvent->mButton == MouseButton::ePrimary) { 3847 // For avoiding focus popup opened by clicking this link to get blurred, 3848 // we need this to get focused now. However, if the mousedown occurs 3849 // in editable element in this link, we should not do this because its 3850 // editing host will get focus. 3851 if (IsInComposedDoc()) { 3852 Element* targetElement = Element::FromEventTargetOrNull( 3853 aVisitor.mEvent->GetDOMEventTarget()); 3854 if (targetElement && targetElement->IsInclusiveDescendantOf(this) && 3855 (!targetElement->IsEditable() || 3856 targetElement->GetEditingHost() == this)) { 3857 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) { 3858 RefPtr<Element> kungFuDeathGrip(this); 3859 fm->SetFocus(kungFuDeathGrip, nsIFocusManager::FLAG_BYMOUSE | 3860 nsIFocusManager::FLAG_NOSCROLL); 3861 } 3862 } 3863 } 3864 3865 if (aVisitor.mPresContext) { 3866 EventStateManager::SetActiveManager( 3867 aVisitor.mPresContext->EventStateManager(), this); 3868 } 3869 3870 // OK, we're pretty sure we're going to load, so warm up a speculative 3871 // connection to be sure we have one ready when we open the channel. 3872 if (nsIDocShell* shell = OwnerDoc()->GetDocShell()) { 3873 if (nsCOMPtr<nsIURI> absURI = GetHrefURI()) { 3874 if (nsCOMPtr<nsISpeculativeConnect> sc = 3875 mozilla::components::IO::Service()) { 3876 nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryInterface(shell); 3877 sc->SpeculativeConnect(absURI, NodePrincipal(), ir, false); 3878 } 3879 } 3880 } 3881 } 3882 } break; 3883 3884 case ePointerClick: { 3885 WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent(); 3886 if (mouseEvent->IsLeftClickEvent()) { 3887 if (!mouseEvent->IsControl() && !mouseEvent->IsMeta() && 3888 !mouseEvent->IsAlt() && !mouseEvent->IsShift()) { 3889 if (OwnerDoc()->MayHaveDOMActivateListeners()) { 3890 // The default action is simply to dispatch DOMActivate. 3891 // But dispatch that only if needed. 3892 nsEventStatus status = nsEventStatus_eIgnore; 3893 // DOMActivate event should be trusted since the activation is 3894 // actually occurred even if the cause is an untrusted click event. 3895 InternalUIEvent actEvent(true, eLegacyDOMActivate, mouseEvent); 3896 actEvent.mDetail = 1; 3897 rv = EventDispatcher::Dispatch(this, aVisitor.mPresContext, 3898 &actEvent, nullptr, &status); 3899 if (NS_SUCCEEDED(rv)) { 3900 aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; 3901 } 3902 } else { 3903 if (nsCOMPtr<nsIURI> absURI = GetHrefURI()) { 3904 // If you modify this code, tweak also the code handling 3905 // eLegacyDOMActivate. 3906 nsAutoString target; 3907 GetLinkTarget(target); 3908 UserNavigationInvolvement userInvolvement = 3909 mouseEvent->IsTrusted() 3910 ? UserNavigationInvolvement::Activation 3911 : UserNavigationInvolvement::None; 3912 nsContentUtils::TriggerLinkClick(this, absURI, target, 3913 userInvolvement); 3914 } 3915 // Since we didn't dispatch DOMActivate because there were no 3916 // listeners, do still set mEventStatus as if it was dispatched 3917 // successfully. 3918 aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; 3919 } 3920 } 3921 3922 DispatchChromeOnlyLinkClickEvent(aVisitor); 3923 } 3924 break; 3925 } 3926 case ePointerAuxClick: { 3927 DispatchChromeOnlyLinkClickEvent(aVisitor); 3928 break; 3929 } 3930 case eLegacyDOMActivate: { 3931 // If you modify this code, tweak also the code handling 3932 // ePointerClick. 3933 if (aVisitor.mEvent->mOriginalTarget == this) { 3934 if (nsCOMPtr<nsIURI> absURI = GetHrefURI()) { 3935 nsAutoString target; 3936 GetLinkTarget(target); 3937 UserNavigationInvolvement userInvolvement = 3938 aVisitor.mEvent->IsTrusted() 3939 ? UserNavigationInvolvement::Activation 3940 : UserNavigationInvolvement::None; 3941 nsContentUtils::TriggerLinkClick(this, absURI, target, 3942 userInvolvement); 3943 aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; 3944 } 3945 } 3946 } break; 3947 3948 case eKeyPress: { 3949 WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent(); 3950 if (keyEvent && keyEvent->mKeyCode == NS_VK_RETURN) { 3951 nsEventStatus status = nsEventStatus_eIgnore; 3952 rv = DispatchClickEvent(aVisitor.mPresContext, keyEvent, this, false, 3953 nullptr, &status); 3954 if (NS_SUCCEEDED(rv)) { 3955 aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; 3956 } 3957 } 3958 } break; 3959 3960 default: 3961 // switch not in sync with the optimization switch earlier in this 3962 // function 3963 MOZ_ASSERT_UNREACHABLE("switch statements not in sync"); 3964 return NS_ERROR_UNEXPECTED; 3965 } 3966 3967 return rv; 3968 } 3969 3970 // static 3971 void Element::SanitizeLinkOrFormTarget(nsAString& aTarget) { 3972 // <https://html.spec.whatwg.org/multipage/semantics.html#get-an-element's-target> 3973 // 2. If target is not null, and contains an ASCII tab or newline and a U+003C 3974 // (<), then set target to "_blank". 3975 if (!aTarget.IsEmpty() && aTarget.FindCharInSet(u"\t\n\r") != kNotFound && 3976 aTarget.Contains('<')) { 3977 aTarget.AssignLiteral("_blank"); 3978 } 3979 } 3980 3981 void Element::GetLinkTarget(nsAString& aTarget) { 3982 GetLinkTargetImpl(aTarget); 3983 SanitizeLinkOrFormTarget(aTarget); 3984 } 3985 3986 void Element::GetLinkTargetImpl(nsAString& aTarget) { aTarget.Truncate(); } 3987 3988 nsresult Element::CopyInnerTo(Element* aDst, ReparseAttributes aReparse) { 3989 nsresult rv = aDst->mAttrs.EnsureCapacityToClone(mAttrs); 3990 NS_ENSURE_SUCCESS(rv, rv); 3991 3992 const bool reparse = aReparse == ReparseAttributes::Yes; 3993 3994 uint32_t count = mAttrs.AttrCount(); 3995 for (uint32_t i = 0; i < count; ++i) { 3996 BorrowedAttrInfo info = mAttrs.AttrInfoAt(i); 3997 const nsAttrName* name = info.mName; 3998 const nsAttrValue* value = info.mValue; 3999 if (value->Type() == nsAttrValue::eCSSDeclaration) { 4000 MOZ_ASSERT(name->Equals(nsGkAtoms::style, kNameSpaceID_None)); 4001 // We still clone CSS attributes, even in the `reparse` (cross-document) 4002 // case. https://github.com/w3c/webappsec-csp/issues/212 4003 nsAttrValue valueCopy(*value); 4004 rv = aDst->SetParsedAttr(name->NamespaceID(), name->LocalName(), 4005 name->GetPrefix(), valueCopy, false); 4006 NS_ENSURE_SUCCESS(rv, rv); 4007 4008 value->GetCSSDeclarationValue()->SetImmutable(); 4009 } else if (reparse) { 4010 nsAutoString valStr; 4011 value->ToString(valStr); 4012 rv = aDst->SetAttr(name->NamespaceID(), name->LocalName(), 4013 name->GetPrefix(), valStr, false); 4014 NS_ENSURE_SUCCESS(rv, rv); 4015 } else { 4016 nsAttrValue valueCopy(*value); 4017 rv = aDst->SetParsedAttr(name->NamespaceID(), name->LocalName(), 4018 name->GetPrefix(), valueCopy, false); 4019 NS_ENSURE_SUCCESS(rv, rv); 4020 } 4021 } 4022 4023 dom::NodeInfo* dstNodeInfo = aDst->NodeInfo(); 4024 if (CustomElementData* data = GetCustomElementData()) { 4025 // The cloned node may be a custom element that may require 4026 // enqueing upgrade reaction. 4027 if (nsAtom* typeAtom = data->GetCustomElementType()) { 4028 aDst->SetCustomElementData(MakeUnique<CustomElementData>(typeAtom)); 4029 MOZ_ASSERT(dstNodeInfo->NameAtom()->Equals(dstNodeInfo->LocalName())); 4030 CustomElementDefinition* definition = 4031 nsContentUtils::LookupCustomElementDefinition( 4032 dstNodeInfo->GetDocument(), dstNodeInfo->NameAtom(), 4033 dstNodeInfo->NamespaceID(), typeAtom); 4034 if (definition) { 4035 nsContentUtils::EnqueueUpgradeReaction(aDst, definition); 4036 } 4037 } 4038 } 4039 4040 if (dstNodeInfo->GetDocument()->IsStaticDocument()) { 4041 // Propagate :defined state to the static clone. 4042 if (State().HasState(ElementState::DEFINED)) { 4043 aDst->SetDefined(true); 4044 } 4045 } 4046 4047 return NS_OK; 4048 } 4049 4050 Element* Element::Closest(const nsACString& aSelector, ErrorResult& aResult) { 4051 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("Element::Closest", 4052 LAYOUT_SelectorQuery, aSelector); 4053 const StyleSelectorList* list = ParseSelectorList(aSelector, aResult); 4054 if (!list) { 4055 return nullptr; 4056 } 4057 4058 return const_cast<Element*>(Servo_SelectorList_Closest(this, list)); 4059 } 4060 4061 bool Element::Matches(const nsACString& aSelector, ErrorResult& aResult) { 4062 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("Element::Matches", 4063 LAYOUT_SelectorQuery, aSelector); 4064 const StyleSelectorList* list = ParseSelectorList(aSelector, aResult); 4065 if (!list) { 4066 return false; 4067 } 4068 4069 return Servo_SelectorList_Matches(this, list); 4070 } 4071 4072 static constexpr nsAttrValue::EnumTableEntry kCORSAttributeTable[] = { 4073 // Order matters here 4074 // See ParseCORSValue 4075 {"anonymous", CORS_ANONYMOUS}, 4076 {"use-credentials", CORS_USE_CREDENTIALS}}; 4077 4078 /* static */ 4079 void Element::ParseCORSValue(const nsAString& aValue, nsAttrValue& aResult) { 4080 DebugOnly<bool> success = 4081 aResult.ParseEnumValue(aValue, kCORSAttributeTable, false, 4082 // default value is anonymous if aValue is 4083 // not a value we understand 4084 &kCORSAttributeTable[0]); 4085 MOZ_ASSERT(success); 4086 } 4087 4088 /* static */ 4089 CORSMode Element::StringToCORSMode(const nsAString& aValue) { 4090 if (aValue.IsVoid()) { 4091 return CORS_NONE; 4092 } 4093 4094 nsAttrValue val; 4095 Element::ParseCORSValue(aValue, val); 4096 return CORSMode(val.GetEnumValue()); 4097 } 4098 4099 /* static */ 4100 CORSMode Element::AttrValueToCORSMode(const nsAttrValue* aValue) { 4101 if (!aValue) { 4102 return CORS_NONE; 4103 } 4104 4105 return CORSMode(aValue->GetEnumValue()); 4106 } 4107 4108 /** 4109 * Returns nullptr if requests for fullscreen are allowed in the current 4110 * context. Requests are only allowed if the user initiated them (like with 4111 * a mouse-click or key press), unless this check has been disabled by 4112 * setting the pref "full-screen-api.allow-trusted-requests-only" to false 4113 * or if the caller is privileged. Feature policy may also deny requests. 4114 * If fullscreen is not allowed, a key for the error message is returned. 4115 */ 4116 static const char* GetFullscreenError(CallerType aCallerType, 4117 Document* aDocument) { 4118 MOZ_ASSERT(aDocument); 4119 4120 // Privileged callers can always request fullscreen 4121 if (aCallerType == CallerType::System) { 4122 return nullptr; 4123 } 4124 4125 if (nsContentUtils::IsPDFJS(aDocument->GetPrincipal())) { 4126 // The built-in pdf viewer can always request fullscreen 4127 return nullptr; 4128 } 4129 4130 if (const char* error = aDocument->GetFullscreenError(aCallerType)) { 4131 return error; 4132 } 4133 4134 // Bypass user interaction checks if preference is set 4135 if (!StaticPrefs::full_screen_api_allow_trusted_requests_only()) { 4136 return nullptr; 4137 } 4138 4139 if (!aDocument->ConsumeTransientUserGestureActivation()) { 4140 return "FullscreenDeniedNotInputDriven"; 4141 } 4142 4143 // Entering full-screen on mouse mouse event is only allowed with left mouse 4144 // button 4145 if (StaticPrefs::full_screen_api_mouse_event_allow_left_button_only() && 4146 (EventStateManager::sCurrentMouseBtn == MouseButton::eMiddle || 4147 EventStateManager::sCurrentMouseBtn == MouseButton::eSecondary)) { 4148 return "FullscreenDeniedMouseEventOnlyLeftBtn"; 4149 } 4150 4151 return nullptr; 4152 } 4153 4154 void Element::SetCapture(bool aRetargetToElement) { 4155 // If there is already an active capture, ignore this request. This would 4156 // occur if a splitter, frame resizer, etc had already captured and we don't 4157 // want to override those. 4158 if (!PresShell::GetCapturingContent()) { 4159 PresShell::SetCapturingContent( 4160 this, CaptureFlags::PreventDragStart | 4161 (aRetargetToElement ? CaptureFlags::RetargetToElement 4162 : CaptureFlags::None)); 4163 } 4164 } 4165 4166 void Element::SetCaptureAlways(bool aRetargetToElement) { 4167 PresShell::SetCapturingContent( 4168 this, CaptureFlags::PreventDragStart | CaptureFlags::IgnoreAllowedState | 4169 (aRetargetToElement ? CaptureFlags::RetargetToElement 4170 : CaptureFlags::None)); 4171 } 4172 4173 void Element::ReleaseCapture() { 4174 if (PresShell::GetCapturingContent() == this) { 4175 PresShell::ReleaseCapturingContent(); 4176 } 4177 } 4178 4179 already_AddRefed<Promise> Element::RequestFullscreen(CallerType aCallerType, 4180 ErrorResult& aRv) { 4181 auto request = FullscreenRequest::Create(this, aCallerType, aRv); 4182 RefPtr<Promise> promise = request->GetPromise(); 4183 4184 // Only grant fullscreen requests if this is called from inside a trusted 4185 // event handler (i.e. inside an event handler for a user initiated event). 4186 // This stops the fullscreen from being abused similar to the popups of old, 4187 // and it also makes it harder for bad guys' script to go fullscreen and 4188 // spoof the browser chrome/window and phish logins etc. 4189 // Note that requests for fullscreen inside a web app's origin are exempt 4190 // from this restriction. 4191 if (const char* error = GetFullscreenError(aCallerType, OwnerDoc())) { 4192 request->Reject(error); 4193 } else { 4194 OwnerDoc()->RequestFullscreen(std::move(request)); 4195 } 4196 return promise.forget(); 4197 } 4198 4199 void Element::RequestPointerLock(CallerType aCallerType) { 4200 PointerLockManager::RequestLock(this, aCallerType); 4201 } 4202 4203 already_AddRefed<Flex> Element::GetAsFlexContainer() { 4204 // We need the flex frame to compute additional info, and use 4205 // that annotated version of the frame. 4206 nsFlexContainerFrame* flexFrame = 4207 nsFlexContainerFrame::GetFlexFrameWithComputedInfo( 4208 GetPrimaryFrame(FlushType::Layout)); 4209 4210 if (flexFrame) { 4211 RefPtr<Flex> flex = new Flex(this, flexFrame); 4212 return flex.forget(); 4213 } 4214 return nullptr; 4215 } 4216 4217 void Element::GetGridFragments(nsTArray<RefPtr<Grid>>& aResult) { 4218 nsGridContainerFrame* frame = 4219 nsGridContainerFrame::GetGridFrameWithComputedInfo( 4220 GetPrimaryFrame(FlushType::Layout)); 4221 4222 // If we get a nsGridContainerFrame from the prior call, 4223 // all the next-in-flow frames will also be nsGridContainerFrames. 4224 while (frame) { 4225 // Get the existing Grid object, if it exists. This object is 4226 // guaranteed to be up-to-date because GetGridFrameWithComputedInfo 4227 // will delete an existing one when regenerating grid info. 4228 Grid* gridFragment = frame->GetGridFragmentInfo(); 4229 if (!gridFragment) { 4230 // Grid constructor will add itself as a property to frame, and 4231 // its unlink method will remove itself if the frame still exists. 4232 gridFragment = new Grid(this, frame); 4233 } 4234 aResult.AppendElement(gridFragment); 4235 frame = static_cast<nsGridContainerFrame*>(frame->GetNextInFlow()); 4236 } 4237 } 4238 4239 bool Element::HasGridFragments() { 4240 return !!nsGridContainerFrame::GetGridFrameWithComputedInfo( 4241 GetPrimaryFrame(FlushType::Layout)); 4242 } 4243 4244 already_AddRefed<DOMMatrixReadOnly> Element::GetTransformToAncestor( 4245 Element& aAncestor) { 4246 nsIFrame* primaryFrame = GetPrimaryFrame(); 4247 nsIFrame* ancestorFrame = aAncestor.GetPrimaryFrame(); 4248 4249 Matrix4x4 transform; 4250 if (primaryFrame) { 4251 // If aAncestor is not actually an ancestor of this (including nullptr), 4252 // then the call to GetTransformToAncestor will return the transform 4253 // all the way up through the parent chain. 4254 transform = nsLayoutUtils::GetTransformToAncestor(RelativeTo{primaryFrame}, 4255 RelativeTo{ancestorFrame}, 4256 nsIFrame::IN_CSS_UNITS) 4257 .GetMatrix(); 4258 } 4259 4260 DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform); 4261 RefPtr<DOMMatrixReadOnly> result(matrix); 4262 return result.forget(); 4263 } 4264 4265 already_AddRefed<DOMMatrixReadOnly> Element::GetTransformToParent() { 4266 nsIFrame* primaryFrame = GetPrimaryFrame(); 4267 4268 Matrix4x4 transform; 4269 if (primaryFrame) { 4270 nsIFrame* parentFrame = primaryFrame->GetParent(); 4271 transform = nsLayoutUtils::GetTransformToAncestor(RelativeTo{primaryFrame}, 4272 RelativeTo{parentFrame}, 4273 nsIFrame::IN_CSS_UNITS) 4274 .GetMatrix(); 4275 } 4276 4277 DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform); 4278 RefPtr<DOMMatrixReadOnly> result(matrix); 4279 return result.forget(); 4280 } 4281 4282 already_AddRefed<DOMMatrixReadOnly> Element::GetTransformToViewport() { 4283 nsIFrame* primaryFrame = GetPrimaryFrame(); 4284 Matrix4x4 transform; 4285 if (primaryFrame) { 4286 transform = 4287 nsLayoutUtils::GetTransformToAncestor( 4288 RelativeTo{primaryFrame}, 4289 RelativeTo{nsLayoutUtils::GetDisplayRootFrame(primaryFrame)}, 4290 nsIFrame::IN_CSS_UNITS) 4291 .GetMatrix(); 4292 } 4293 4294 DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform); 4295 RefPtr<DOMMatrixReadOnly> result(matrix); 4296 return result.forget(); 4297 } 4298 4299 already_AddRefed<Animation> Element::Animate( 4300 JSContext* aContext, JS::Handle<JSObject*> aKeyframes, 4301 const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions, 4302 ErrorResult& aError) { 4303 nsCOMPtr<nsIGlobalObject> ownerGlobal = GetOwnerGlobal(); 4304 if (!ownerGlobal) { 4305 aError.Throw(NS_ERROR_FAILURE); 4306 return nullptr; 4307 } 4308 GlobalObject global(aContext, ownerGlobal->GetGlobalJSObject()); 4309 MOZ_ASSERT(!global.Failed()); 4310 4311 // KeyframeEffect constructor doesn't follow the standard Xray calling 4312 // convention and needs to be called in caller's compartment. 4313 // This should match to RunConstructorInCallerCompartment attribute in 4314 // KeyframeEffect.webidl. 4315 RefPtr<KeyframeEffect> effect = 4316 KeyframeEffect::Constructor(global, this, aKeyframes, aOptions, aError); 4317 if (aError.Failed()) { 4318 return nullptr; 4319 } 4320 4321 // Animation constructor follows the standard Xray calling convention and 4322 // needs to be called in the target element's realm. 4323 JSAutoRealm ar(aContext, global.Get()); 4324 4325 AnimationTimeline* timeline = OwnerDoc()->Timeline(); 4326 RefPtr<Animation> animation = Animation::Constructor( 4327 global, effect, Optional<AnimationTimeline*>(timeline), aError); 4328 if (aError.Failed()) { 4329 return nullptr; 4330 } 4331 4332 if (aOptions.IsKeyframeAnimationOptions()) { 4333 animation->SetId(aOptions.GetAsKeyframeAnimationOptions().mId); 4334 } 4335 4336 animation->Play(aError, Animation::LimitBehavior::AutoRewind); 4337 if (aError.Failed()) { 4338 return nullptr; 4339 } 4340 4341 return animation.forget(); 4342 } 4343 4344 void Element::GetAnimations(const GetAnimationsOptions& aOptions, 4345 nsTArray<RefPtr<Animation>>& aAnimations) { 4346 if (Document* doc = GetComposedDoc()) { 4347 // We don't need to explicitly flush throttled animations here, since 4348 // updating the animation style of elements will never affect the set of 4349 // running animations and it's only the set of running animations that is 4350 // important here. 4351 // 4352 // NOTE: Any changes to the flags passed to the following call should 4353 // be reflected in the flags passed in DocumentOrShadowRoot::GetAnimations 4354 // too. 4355 doc->FlushPendingNotifications( 4356 ChangesToFlush(FlushType::Style, /* aFlushAnimations = */ false, 4357 /* aUpdateRelevancy = */ false)); 4358 } 4359 4360 GetAnimationsWithoutFlush(aOptions, aAnimations); 4361 } 4362 4363 static void GetAnimationsUnsorted(const Element* aElement, 4364 const PseudoStyleRequest& aPseudoRequest, 4365 nsTArray<RefPtr<Animation>>& aAnimations) { 4366 MOZ_ASSERT(aPseudoRequest.IsNotPseudo() || 4367 AnimationUtils::IsSupportedPseudoForAnimations(aPseudoRequest), 4368 "Unsupported pseudo type"); 4369 MOZ_ASSERT(aElement, "Null element"); 4370 4371 EffectSet* effects = EffectSet::Get(aElement, aPseudoRequest); 4372 if (!effects) { 4373 return; 4374 } 4375 4376 for (KeyframeEffect* effect : *effects) { 4377 MOZ_ASSERT(effect && effect->GetAnimation(), 4378 "Only effects associated with an animation should be " 4379 "added to an element's effect set"); 4380 Animation* animation = effect->GetAnimation(); 4381 4382 MOZ_ASSERT(animation->IsRelevant(), 4383 "Only relevant animations should be added to an element's " 4384 "effect set"); 4385 aAnimations.AppendElement(animation); 4386 } 4387 } 4388 4389 static inline bool IsSupportedForGetAnimationsSubtree(PseudoStyleType aType) { 4390 return aType == PseudoStyleType::NotPseudo || 4391 aType == PseudoStyleType::mozSnapshotContainingBlock || 4392 PseudoStyle::IsViewTransitionPseudoElement(aType); 4393 } 4394 4395 // This traverses the subtree from the root, |aRootElement|, to get the 4396 // animations. 4397 static void GetAnimationsUnsortedForSubtree( 4398 const Element* aRootElement, nsTArray<RefPtr<Animation>>& aAnimations) { 4399 const PseudoStyleType type = aRootElement->GetPseudoElementType(); 4400 // Only elements and view transition pseudo-elements get handled in this 4401 // function. 4402 if (MOZ_UNLIKELY(!IsSupportedForGetAnimationsSubtree(type))) { 4403 return; 4404 } 4405 4406 // For non pseudo-elements, we have to get the animations on the element 4407 // itself, ::before, ::after, and ::marker. 4408 if (type == PseudoStyleType::NotPseudo) { 4409 for (const nsIContent* node = aRootElement; node; 4410 node = node->GetNextNode(aRootElement)) { 4411 if (!node->IsElement()) { 4412 continue; 4413 } 4414 const Element* element = node->AsElement(); 4415 GetAnimationsUnsorted(element, PseudoStyleRequest::NotPseudo(), 4416 aAnimations); 4417 GetAnimationsUnsorted(element, PseudoStyleRequest::Before(), aAnimations); 4418 GetAnimationsUnsorted(element, PseudoStyleRequest::After(), aAnimations); 4419 GetAnimationsUnsorted(element, PseudoStyleRequest::Marker(), aAnimations); 4420 GetAnimationsUnsorted(element, PseudoStyleRequest::Backdrop(), 4421 aAnimations); 4422 } 4423 } 4424 4425 // If |aRootElement| is the document element, or it is a view transition 4426 // pseudo-element (including the snapshot containing block), we have to 4427 // traverse the view transition subtree. Otherwise, we can skip the traversal. 4428 if (!aRootElement->IsRootElement() && type == PseudoStyleType::NotPseudo) { 4429 return; 4430 } 4431 4432 const Document* doc = aRootElement->OwnerDoc(); 4433 const Element* originatingElement = doc->GetRootElement(); 4434 if (!originatingElement) { 4435 return; 4436 } 4437 4438 const Element* rootForTraversal = [&]() -> const Element* { 4439 if (!aRootElement->IsRootElement()) { 4440 // It is in the view transition pseudo-element tree already, so we use it 4441 // directly. 4442 return aRootElement; 4443 } 4444 // View transition pseudo-elements cannot be accessed directly from the 4445 // document element, so we have to retrieve its tree root from the active 4446 // view transition object. 4447 const ViewTransition* vt = doc->GetActiveViewTransition(); 4448 return vt ? vt->GetViewTransitionTreeRoot() : nullptr; 4449 }(); 4450 4451 for (const nsIContent* node = rootForTraversal; node; 4452 node = node->GetNextNode(rootForTraversal)) { 4453 if (!node->IsElement()) { 4454 continue; 4455 } 4456 const Element* pseudo = node->AsElement(); 4457 const PseudoStyleRequest request( 4458 pseudo->GetPseudoElementType(), 4459 pseudo->HasName() 4460 ? pseudo->GetParsedAttr(nsGkAtoms::name)->GetAtomValue() 4461 : nullptr); 4462 GetAnimationsUnsorted(originatingElement, request, aAnimations); 4463 } 4464 } 4465 4466 void Element::GetAnimationsWithoutFlush( 4467 const GetAnimationsOptions& aOptions, 4468 nsTArray<RefPtr<Animation>>& aAnimations) { 4469 Element* elem = this; 4470 PseudoStyleRequest pseudoRequest; 4471 // For animations on generated-content elements, the animations are stored 4472 // on the parent element. 4473 if (IsGeneratedContentContainerForBefore()) { 4474 elem = GetParentElement(); 4475 pseudoRequest.mType = PseudoStyleType::before; 4476 } else if (IsGeneratedContentContainerForAfter()) { 4477 elem = GetParentElement(); 4478 pseudoRequest.mType = PseudoStyleType::after; 4479 } else if (IsGeneratedContentContainerForMarker()) { 4480 elem = GetParentElement(); 4481 pseudoRequest.mType = PseudoStyleType::marker; 4482 } else if (IsGeneratedContentContainerForBackdrop()) { 4483 elem = GetParentElement(); 4484 pseudoRequest.mType = PseudoStyleType::backdrop; 4485 } 4486 4487 if (!elem) { 4488 return; 4489 } 4490 4491 // FIXME: Bug 1935557. Rewrite this to support pseudoElement option. 4492 if (!aOptions.mSubtree || (pseudoRequest.mType == PseudoStyleType::before || 4493 pseudoRequest.mType == PseudoStyleType::after || 4494 pseudoRequest.mType == PseudoStyleType::backdrop || 4495 pseudoRequest.mType == PseudoStyleType::marker)) { 4496 // Case 1: Non-subtree, or |this| is ::before, ::after, or ::marker. 4497 // 4498 // ::before, ::after, and ::marker doesn't have subtree on themself, so we 4499 // just simply get the animations of the element itself even if mSubtree is 4500 // true. 4501 GetAnimationsUnsorted(elem, pseudoRequest, aAnimations); 4502 } else { 4503 // Case 2: Subtree. |this| is an element or a view transition 4504 // pseudo-element. 4505 GetAnimationsUnsortedForSubtree(elem, aAnimations); 4506 } 4507 aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>()); 4508 } 4509 4510 void Element::CloneAnimationsFrom(const Element& aOther) { 4511 AnimationTimeline* const timeline = OwnerDoc()->Timeline(); 4512 MOZ_ASSERT(timeline, "Timeline has not been set on the document yet"); 4513 // Iterate through all pseudo types and copy the effects from each of the 4514 // other element's effect sets into this element's effect set. 4515 // FIXME: Bug 1929470. This function is for printing, and it may be tricky to 4516 // support view transitions. We have to revisit here after we support view 4517 // transitions to make sure we clone the animations properly. 4518 for (PseudoStyleType pseudoType : 4519 {PseudoStyleType::NotPseudo, PseudoStyleType::before, 4520 PseudoStyleType::after, PseudoStyleType::marker, 4521 PseudoStyleType::backdrop}) { 4522 // If the element has an effect set for this pseudo type (or not pseudo) 4523 // then copy the effects and animation properties. 4524 const PseudoStyleRequest request(pseudoType); 4525 if (auto* const effects = EffectSet::Get(&aOther, request)) { 4526 auto* const clonedEffects = EffectSet::GetOrCreate(this, request); 4527 for (KeyframeEffect* const effect : *effects) { 4528 auto* animation = effect->GetAnimation(); 4529 if (animation->AsCSSTransition()) { 4530 // Don't clone transitions, for compat with other browsers. 4531 continue; 4532 } 4533 // Clone the effect. 4534 RefPtr<KeyframeEffect> clonedEffect = new KeyframeEffect( 4535 OwnerDoc(), OwningAnimationTarget{this, request}, *effect); 4536 4537 // Clone the animation 4538 RefPtr<Animation> clonedAnimation = Animation::ClonePausedAnimation( 4539 OwnerDoc()->GetParentObject(), *animation, *clonedEffect, 4540 *timeline); 4541 if (!clonedAnimation) { 4542 continue; 4543 } 4544 clonedEffects->AddEffect(*clonedEffect); 4545 } 4546 } 4547 } 4548 } 4549 4550 void Element::GetInnerHTML(nsAString& aInnerHTML, OOMReporter& aError) { 4551 GetMarkup(false, aInnerHTML); 4552 } 4553 4554 void Element::GetInnerHTML(OwningTrustedHTMLOrNullIsEmptyString& aInnerHTML, 4555 OOMReporter& aError) { 4556 GetInnerHTML(aInnerHTML.SetAsNullIsEmptyString(), aError); 4557 } 4558 4559 void Element::SetInnerHTML(const TrustedHTMLOrNullIsEmptyString& aInnerHTML, 4560 nsIPrincipal* aSubjectPrincipal, 4561 ErrorResult& aError) { 4562 constexpr nsLiteralString sink = u"Element innerHTML"_ns; 4563 4564 Maybe<nsAutoString> compliantStringHolder; 4565 const nsAString* compliantString = 4566 TrustedTypeUtils::GetTrustedTypesCompliantString( 4567 aInnerHTML, sink, kTrustedTypesOnlySinkGroup, *this, 4568 aSubjectPrincipal, compliantStringHolder, aError); 4569 4570 if (aError.Failed()) { 4571 return; 4572 } 4573 4574 SetInnerHTMLTrusted(*compliantString, aSubjectPrincipal, aError); 4575 } 4576 4577 void Element::SetInnerHTMLTrusted(const nsAString& aInnerHTML, 4578 nsIPrincipal* aSubjectPrincipal, 4579 ErrorResult& aError) { 4580 SetInnerHTMLInternal(aInnerHTML, aError); 4581 } 4582 4583 void Element::GetOuterHTML(OwningTrustedHTMLOrNullIsEmptyString& aOuterHTML) { 4584 GetMarkup(true, aOuterHTML.SetAsNullIsEmptyString()); 4585 } 4586 4587 void Element::SetOuterHTML(const TrustedHTMLOrNullIsEmptyString& aOuterHTML, 4588 nsIPrincipal* aSubjectPrincipal, 4589 ErrorResult& aError) { 4590 constexpr nsLiteralString sink = u"Element outerHTML"_ns; 4591 4592 Maybe<nsAutoString> compliantStringHolder; 4593 const nsAString* compliantString = 4594 TrustedTypeUtils::GetTrustedTypesCompliantString( 4595 aOuterHTML, sink, kTrustedTypesOnlySinkGroup, *this, 4596 aSubjectPrincipal, compliantStringHolder, aError); 4597 if (aError.Failed()) { 4598 return; 4599 } 4600 4601 nsCOMPtr<nsINode> parent = GetParentNode(); 4602 if (!parent) { 4603 return; 4604 } 4605 4606 if (parent->NodeType() == DOCUMENT_NODE) { 4607 aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); 4608 return; 4609 } 4610 4611 if (OwnerDoc()->IsHTMLDocument()) { 4612 nsAtom* localName; 4613 int32_t namespaceID; 4614 if (parent->IsElement()) { 4615 localName = parent->NodeInfo()->NameAtom(); 4616 namespaceID = parent->NodeInfo()->NamespaceID(); 4617 } else { 4618 NS_ASSERTION( 4619 parent->NodeType() == DOCUMENT_FRAGMENT_NODE, 4620 "How come the parent isn't a document, a fragment or an element?"); 4621 localName = nsGkAtoms::body; 4622 namespaceID = kNameSpaceID_XHTML; 4623 } 4624 RefPtr<DocumentFragment> fragment = new (OwnerDoc()->NodeInfoManager()) 4625 DocumentFragment(OwnerDoc()->NodeInfoManager()); 4626 nsContentUtils::ParseFragmentHTML( 4627 *compliantString, fragment, localName, namespaceID, 4628 OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks, true); 4629 parent->ReplaceChild(*fragment, *this, aError); 4630 return; 4631 } 4632 4633 nsCOMPtr<nsINode> context; 4634 if (parent->IsElement()) { 4635 context = parent; 4636 } else { 4637 NS_ASSERTION( 4638 parent->NodeType() == DOCUMENT_FRAGMENT_NODE, 4639 "How come the parent isn't a document, a fragment or an element?"); 4640 RefPtr<mozilla::dom::NodeInfo> info = 4641 OwnerDoc()->NodeInfoManager()->GetNodeInfo( 4642 nsGkAtoms::body, nullptr, kNameSpaceID_XHTML, ELEMENT_NODE); 4643 context = NS_NewHTMLBodyElement(info.forget(), FROM_PARSER_FRAGMENT); 4644 } 4645 4646 RefPtr<DocumentFragment> fragment = nsContentUtils::CreateContextualFragment( 4647 context, *compliantString, true, aError); 4648 if (aError.Failed()) { 4649 return; 4650 } 4651 parent->ReplaceChild(*fragment, *this, aError); 4652 } 4653 4654 enum nsAdjacentPosition { eBeforeBegin, eAfterBegin, eBeforeEnd, eAfterEnd }; 4655 4656 void Element::InsertAdjacentHTML( 4657 const nsAString& aPosition, const TrustedHTMLOrString& aTrustedHTMLOrString, 4658 nsIPrincipal* aSubjectPrincipal, ErrorResult& aError) { 4659 constexpr nsLiteralString kSink = u"Element insertAdjacentHTML"_ns; 4660 4661 Maybe<nsAutoString> compliantStringHolder; 4662 const nsAString* compliantString = 4663 TrustedTypeUtils::GetTrustedTypesCompliantString( 4664 aTrustedHTMLOrString, kSink, kTrustedTypesOnlySinkGroup, *this, 4665 aSubjectPrincipal, compliantStringHolder, aError); 4666 4667 if (aError.Failed()) { 4668 return; 4669 } 4670 4671 nsAdjacentPosition position; 4672 if (aPosition.LowerCaseEqualsLiteral("beforebegin")) { 4673 position = eBeforeBegin; 4674 } else if (aPosition.LowerCaseEqualsLiteral("afterbegin")) { 4675 position = eAfterBegin; 4676 } else if (aPosition.LowerCaseEqualsLiteral("beforeend")) { 4677 position = eBeforeEnd; 4678 } else if (aPosition.LowerCaseEqualsLiteral("afterend")) { 4679 position = eAfterEnd; 4680 } else { 4681 aError.Throw(NS_ERROR_DOM_SYNTAX_ERR); 4682 return; 4683 } 4684 4685 nsCOMPtr<nsIContent> destination; 4686 if (position == eBeforeBegin || position == eAfterEnd) { 4687 destination = GetParent(); 4688 if (!destination) { 4689 aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); 4690 return; 4691 } 4692 } else { 4693 destination = this; 4694 } 4695 4696 // mozAutoDocUpdate keeps the owner document alive. Therefore, using a raw 4697 // pointer here is safe. 4698 Document* const doc = OwnerDoc(); 4699 4700 // Needed when insertAdjacentHTML is used in combination with contenteditable 4701 mozAutoDocUpdate updateBatch(doc, true); 4702 nsAutoScriptLoaderDisabler sld(doc); 4703 4704 // Parse directly into destination if possible 4705 nsIContent* oldLastChild = destination->GetLastChild(); 4706 bool oldLastChildIsText = oldLastChild && oldLastChild->IsText(); 4707 if (doc->IsHTMLDocument() && !OwnerDoc()->MayHaveDOMMutationObservers() && 4708 ((position == eBeforeEnd && !oldLastChildIsText) || 4709 (position == eAfterEnd && !GetNextSibling()) || 4710 (position == eAfterBegin && !GetFirstChild()))) { 4711 doc->SuspendDOMNotifications(); 4712 int32_t contextNs = destination->GetNameSpaceID(); 4713 nsAtom* contextLocal = destination->NodeInfo()->NameAtom(); 4714 if (contextLocal == nsGkAtoms::html && contextNs == kNameSpaceID_XHTML) { 4715 // For compat with IE6 through IE9. Willful violation of HTML5 as of 4716 // 2011-04-06. CreateContextualFragment does the same already. 4717 // Spec bug: http://www.w3.org/Bugs/Public/show_bug.cgi?id=12434 4718 contextLocal = nsGkAtoms::body; 4719 } 4720 aError = nsContentUtils::ParseFragmentHTML( 4721 *compliantString, destination, contextLocal, contextNs, 4722 doc->GetCompatibilityMode() == eCompatibility_NavQuirks, true); 4723 doc->ResumeDOMNotifications(); 4724 nsIContent* firstNewChild = oldLastChild ? oldLastChild->GetNextSibling() 4725 : destination->GetFirstChild(); 4726 if (firstNewChild) { 4727 MutationObservers::NotifyContentAppended(destination, firstNewChild, {}); 4728 } 4729 return; 4730 } 4731 4732 // couldn't parse directly 4733 RefPtr<DocumentFragment> fragment = nsContentUtils::CreateContextualFragment( 4734 destination, *compliantString, true, aError); 4735 if (aError.Failed()) { 4736 return; 4737 } 4738 4739 // Suppress assertion about node removal mutation events that can't have 4740 // listeners anyway, because no one has had the chance to register mutation 4741 // listeners on the fragment that comes from the parser. 4742 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker; 4743 4744 switch (position) { 4745 case eBeforeBegin: 4746 destination->InsertBefore(*fragment, this, aError); 4747 break; 4748 case eAfterBegin: 4749 static_cast<nsINode*>(this)->InsertBefore(*fragment, GetFirstChild(), 4750 aError); 4751 break; 4752 case eBeforeEnd: 4753 static_cast<nsINode*>(this)->AppendChild(*fragment, aError); 4754 break; 4755 case eAfterEnd: 4756 destination->InsertBefore(*fragment, GetNextSibling(), aError); 4757 break; 4758 } 4759 } 4760 4761 nsINode* Element::InsertAdjacent(const nsAString& aWhere, nsINode* aNode, 4762 ErrorResult& aError) { 4763 if (aWhere.LowerCaseEqualsLiteral("beforebegin")) { 4764 nsCOMPtr<nsINode> parent = GetParentNode(); 4765 if (!parent) { 4766 return nullptr; 4767 } 4768 parent->InsertBefore(*aNode, this, aError); 4769 } else if (aWhere.LowerCaseEqualsLiteral("afterbegin")) { 4770 nsCOMPtr<nsINode> refNode = GetFirstChild(); 4771 static_cast<nsINode*>(this)->InsertBefore(*aNode, refNode, aError); 4772 } else if (aWhere.LowerCaseEqualsLiteral("beforeend")) { 4773 static_cast<nsINode*>(this)->AppendChild(*aNode, aError); 4774 } else if (aWhere.LowerCaseEqualsLiteral("afterend")) { 4775 nsCOMPtr<nsINode> parent = GetParentNode(); 4776 if (!parent) { 4777 return nullptr; 4778 } 4779 nsCOMPtr<nsINode> refNode = GetNextSibling(); 4780 parent->InsertBefore(*aNode, refNode, aError); 4781 } else { 4782 aError.Throw(NS_ERROR_DOM_SYNTAX_ERR); 4783 return nullptr; 4784 } 4785 4786 return aError.Failed() ? nullptr : aNode; 4787 } 4788 4789 Element* Element::InsertAdjacentElement(const nsAString& aWhere, 4790 Element& aElement, 4791 ErrorResult& aError) { 4792 nsINode* newNode = InsertAdjacent(aWhere, &aElement, aError); 4793 MOZ_ASSERT(!newNode || newNode->IsElement()); 4794 4795 return newNode ? newNode->AsElement() : nullptr; 4796 } 4797 4798 void Element::InsertAdjacentText(const nsAString& aWhere, 4799 const nsAString& aData, ErrorResult& aError) { 4800 RefPtr<nsTextNode> textNode = OwnerDoc()->CreateTextNode(aData); 4801 InsertAdjacent(aWhere, textNode, aError); 4802 } 4803 4804 TextEditor* Element::GetTextEditorInternal() { 4805 TextControlElement* textControlElement = TextControlElement::FromNode(this); 4806 return textControlElement ? MOZ_KnownLive(textControlElement)->GetTextEditor() 4807 : nullptr; 4808 } 4809 4810 nsresult Element::SetBoolAttr(nsAtom* aAttr, bool aValue) { 4811 if (aValue) { 4812 return SetAttr(kNameSpaceID_None, aAttr, u""_ns, true); 4813 } 4814 4815 return UnsetAttr(kNameSpaceID_None, aAttr, true); 4816 } 4817 4818 void Element::GetEnumAttr(nsAtom* aAttr, const char* aDefault, 4819 nsAString& aResult) const { 4820 GetEnumAttr(aAttr, aDefault, aDefault, aResult); 4821 } 4822 4823 void Element::GetEnumAttr(nsAtom* aAttr, const char* aDefaultMissing, 4824 const char* aDefaultInvalid, 4825 nsAString& aResult) const { 4826 const nsAttrValue* attrVal = mAttrs.GetAttr(aAttr); 4827 4828 aResult.Truncate(); 4829 4830 if (!attrVal) { 4831 if (aDefaultMissing) { 4832 AppendASCIItoUTF16(nsDependentCString(aDefaultMissing), aResult); 4833 } else { 4834 SetDOMStringToNull(aResult); 4835 } 4836 } else { 4837 if (attrVal->Type() == nsAttrValue::eEnum) { 4838 attrVal->GetEnumString(aResult, true); 4839 } else if (aDefaultInvalid) { 4840 AppendASCIItoUTF16(nsDependentCString(aDefaultInvalid), aResult); 4841 } 4842 } 4843 } 4844 4845 void Element::SetOrRemoveNullableStringAttr(nsAtom* aName, 4846 const nsAString& aValue, 4847 ErrorResult& aError) { 4848 if (DOMStringIsNull(aValue)) { 4849 UnsetAttr(aName, aError); 4850 } else { 4851 SetAttr(aName, aValue, aError); 4852 } 4853 } 4854 4855 Directionality Element::GetComputedDirectionality() const { 4856 if (nsIFrame* frame = GetPrimaryFrame()) { 4857 return frame->StyleVisibility()->mDirection == StyleDirection::Ltr 4858 ? Directionality::Ltr 4859 : Directionality::Rtl; 4860 } 4861 4862 return GetDirectionality(); 4863 } 4864 4865 float Element::FontSizeInflation() { 4866 nsIFrame* frame = GetPrimaryFrame(); 4867 if (!frame) { 4868 return -1.0; 4869 } 4870 4871 if (nsLayoutUtils::FontSizeInflationEnabled(frame->PresContext())) { 4872 return nsLayoutUtils::FontSizeInflationFor(frame); 4873 } 4874 4875 return 1.0; 4876 } 4877 4878 void Element::GetImplementedPseudoElement(nsAString& aPseudo) const { 4879 PseudoStyleType pseudoType = GetPseudoElementType(); 4880 if (pseudoType == PseudoStyleType::NotPseudo) { 4881 return SetDOMStringToNull(aPseudo); 4882 } 4883 nsDependentAtomString pseudo(nsCSSPseudoElements::GetPseudoAtom(pseudoType)); 4884 4885 // We want to use the modern syntax (::placeholder, etc), but the atoms only 4886 // contain one semi-colon. 4887 MOZ_ASSERT(pseudo.Length() > 2 && pseudo[0] == ':' && pseudo[1] != ':'); 4888 4889 aPseudo.Truncate(); 4890 aPseudo.SetCapacity(pseudo.Length() + 1); 4891 aPseudo.Append(':'); 4892 aPseudo.Append(pseudo); 4893 } 4894 4895 // This function traverses the view transition pseudo-elements tree and finds 4896 // the pseudo-element matched with |aRequest|. 4897 static Element* SearchViewTransitionPseudo(const Element* aElement, 4898 const PseudoStyleRequest& aRequest) { 4899 // If |aElement| is not the root. 4900 if (!aElement->IsRootElement()) { 4901 return nullptr; 4902 } 4903 4904 const Document* doc = aElement->OwnerDoc(); 4905 const ViewTransition* vt = doc->GetActiveViewTransition(); 4906 if (!vt) { 4907 return nullptr; 4908 } 4909 4910 return vt->FindPseudo(aRequest); 4911 } 4912 4913 Element* Element::GetPseudoElement(const PseudoStyleRequest& aRequest) const { 4914 switch (aRequest.mType) { 4915 case PseudoStyleType::NotPseudo: 4916 // It's unfortunate we have to do const cast, so we don't have to write 4917 // the almost duplicate function for the non-const function. 4918 return const_cast<Element*>(this); 4919 case PseudoStyleType::before: 4920 return nsLayoutUtils::GetBeforePseudo(this); 4921 case PseudoStyleType::after: 4922 return nsLayoutUtils::GetAfterPseudo(this); 4923 case PseudoStyleType::marker: 4924 return nsLayoutUtils::GetMarkerPseudo(this); 4925 case PseudoStyleType::backdrop: 4926 return nsLayoutUtils::GetBackdropPseudo(this); 4927 case PseudoStyleType::viewTransition: 4928 case PseudoStyleType::viewTransitionGroup: 4929 case PseudoStyleType::viewTransitionImagePair: 4930 case PseudoStyleType::viewTransitionOld: 4931 case PseudoStyleType::viewTransitionNew: { 4932 Element* result = SearchViewTransitionPseudo(this, aRequest); 4933 MOZ_ASSERT(!result || result->GetPseudoElementType() == aRequest.mType, 4934 "The type should match"); 4935 MOZ_ASSERT(!result || !result->HasName() || 4936 result->GetParsedAttr(nsGkAtoms::name)->GetAtomValue() == 4937 aRequest.mIdentifier, 4938 "The identifier should match"); 4939 return result; 4940 } 4941 default: 4942 return nullptr; 4943 } 4944 } 4945 4946 ReferrerPolicy Element::GetReferrerPolicyAsEnum() const { 4947 if (IsHTMLElement()) { 4948 return ReferrerPolicyFromAttr(GetParsedAttr(nsGkAtoms::referrerpolicy)); 4949 } 4950 return ReferrerPolicy::_empty; 4951 } 4952 4953 ReferrerPolicy Element::ReferrerPolicyFromAttr( 4954 const nsAttrValue* aValue) const { 4955 if (aValue && aValue->Type() == nsAttrValue::eEnum) { 4956 return ReferrerPolicy(aValue->GetEnumValue()); 4957 } 4958 return ReferrerPolicy::_empty; 4959 } 4960 4961 already_AddRefed<nsDOMStringMap> Element::Dataset() { 4962 nsExtendedDOMSlots* slots = ExtendedDOMSlots(); 4963 if (!slots->mDataset) { 4964 // mDataset is a weak reference so assignment will not AddRef. 4965 // AddRef is called before returning the pointer. 4966 slots->mDataset = new nsDOMStringMap(this); 4967 } 4968 return do_AddRef(slots->mDataset); 4969 } 4970 4971 void Element::ClearDataset() { 4972 nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots(); 4973 MOZ_ASSERT(slots && slots->mDataset, 4974 "Slots should exist and dataset should not be null."); 4975 slots->mDataset = nullptr; 4976 } 4977 4978 template <class T> 4979 void Element::GetCustomInterface(nsGetterAddRefs<T> aResult) { 4980 nsCOMPtr<nsISupports> iface = 4981 CustomElementRegistry::CallGetCustomInterface(this, NS_GET_IID(T)); 4982 if (iface) { 4983 if (NS_SUCCEEDED(CallQueryInterface(iface, static_cast<T**>(aResult)))) { 4984 return; 4985 } 4986 } 4987 } 4988 4989 void Element::ClearServoData(Document* aDoc) { 4990 MOZ_ASSERT(aDoc); 4991 if (HasServoData()) { 4992 Servo_Element_ClearData(this); 4993 } else { 4994 UnsetFlags(kAllServoDescendantBits | NODE_NEEDS_FRAME); 4995 } 4996 // Since this element is losing its servo data, nothing under it may have 4997 // servo data either, so we can forget restyles rooted at this element. This 4998 // is necessary for correctness, since we invoke ClearServoData in various 4999 // places where an element's flattened tree parent changes, and such a change 5000 // may also make an element invalid to be used as a restyle root. 5001 if (aDoc->GetServoRestyleRoot() == this) { 5002 aDoc->ClearServoRestyleRoot(); 5003 } 5004 } 5005 5006 bool Element::IsPopoverOpenedInMode(PopoverAttributeState aMode) const { 5007 const auto* htmlElement = nsGenericHTMLElement::FromNode(this); 5008 return htmlElement && htmlElement->PopoverOpen() && 5009 htmlElement->GetPopoverData()->GetOpenedInMode() == aMode; 5010 } 5011 5012 bool Element::IsPopoverOpen() const { 5013 const auto* htmlElement = nsGenericHTMLElement::FromNode(this); 5014 return htmlElement && htmlElement->PopoverOpen(); 5015 } 5016 5017 void Element::SetAssociatedPopover(nsGenericHTMLElement& aPopover) { 5018 MOZ_ASSERT(IsHTMLElement()); 5019 MOZ_ASSERT(aPopover.IsHTMLElement()); 5020 auto* slots = ExtendedDOMSlots(); 5021 slots->mAssociatedPopover = do_GetWeakReference(&aPopover); 5022 } 5023 5024 nsGenericHTMLElement* Element::GetAssociatedPopover() const { 5025 if (const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) { 5026 if (nsCOMPtr<nsGenericHTMLElement> popover = 5027 do_QueryReferent(slots->mAssociatedPopover)) { 5028 if (popover->GetPopoverData() && 5029 popover->GetPopoverData()->GetInvoker() == this) { 5030 return popover; 5031 } 5032 } 5033 } 5034 return nullptr; 5035 } 5036 5037 Element* Element::GetTopmostPopoverAncestor(PopoverAttributeState aMode, 5038 const Element* aInvoker, 5039 bool isPopover) const { 5040 const Element* newPopover = this; 5041 5042 nsTHashMap<nsPtrHashKey<const Element>, size_t> popoverPositions; 5043 size_t index = 0; 5044 for (Element* popover : OwnerDoc()->PopoverListOf(aMode)) { 5045 popoverPositions.LookupOrInsert(popover, index++); 5046 } 5047 5048 if (isPopover) { 5049 popoverPositions.LookupOrInsert(newPopover, index); 5050 } 5051 5052 Element* topmostPopoverAncestor = nullptr; 5053 5054 auto checkAncestor = [&](const Element* candidate) { 5055 if (!candidate) { 5056 return; 5057 } 5058 Element* candidateAncestor = candidate->GetNearestInclusiveOpenPopover(); 5059 if (!candidateAncestor) { 5060 return; 5061 } 5062 size_t candidatePosition; 5063 if (popoverPositions.Get(candidateAncestor, &candidatePosition)) { 5064 size_t topmostPosition; 5065 if (!topmostPopoverAncestor || 5066 (popoverPositions.Get(topmostPopoverAncestor, &topmostPosition) && 5067 topmostPosition < candidatePosition)) { 5068 topmostPopoverAncestor = candidateAncestor; 5069 } 5070 } 5071 }; 5072 5073 checkAncestor(newPopover->GetFlattenedTreeParentElement()); 5074 checkAncestor(aInvoker); 5075 5076 return topmostPopoverAncestor; 5077 } 5078 5079 ElementAnimationData& Element::CreateAnimationData() { 5080 MOZ_ASSERT(!GetAnimationData()); 5081 SetMayHaveAnimations(); 5082 auto* slots = ExtendedDOMSlots(); 5083 slots->mAnimations = MakeUnique<ElementAnimationData>(); 5084 return *slots->mAnimations; 5085 } 5086 5087 PopoverData& Element::CreatePopoverData() { 5088 MOZ_ASSERT(!GetPopoverData()); 5089 auto* slots = ExtendedDOMSlots(); 5090 slots->mPopoverData = MakeUnique<PopoverData>(); 5091 return *slots->mPopoverData; 5092 } 5093 5094 void Element::ClearPopoverData() { 5095 nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots(); 5096 if (slots) { 5097 slots->mPopoverData = nullptr; 5098 } 5099 } 5100 5101 void Element::SetCustomElementData(UniquePtr<CustomElementData> aData) { 5102 SetHasCustomElementData(); 5103 5104 if (aData->mState != CustomElementData::State::eCustom) { 5105 SetDefined(false); 5106 } 5107 5108 nsExtendedDOMSlots* slots = ExtendedDOMSlots(); 5109 MOZ_ASSERT(!slots->mCustomElementData, 5110 "Custom element data may not be changed once set."); 5111 #if DEBUG 5112 // We assert only XUL usage, since web may pass whatever as 'is' value 5113 if (NodeInfo()->NamespaceID() == kNameSpaceID_XUL) { 5114 nsAtom* name = NodeInfo()->NameAtom(); 5115 nsAtom* type = aData->GetCustomElementType(); 5116 // Check to see if the tag name is a dashed name. 5117 if (nsContentUtils::IsNameWithDash(name)) { 5118 // Assert that a tag name with dashes is always an autonomous custom 5119 // element. 5120 MOZ_ASSERT(type == name); 5121 } else { 5122 // Could still be an autonomous custom element with a non-dashed tag name. 5123 // Need the check below for sure. 5124 if (type != name) { 5125 // Assert that the name of the built-in custom element type is always 5126 // a dashed name. 5127 MOZ_ASSERT(nsContentUtils::IsNameWithDash(type)); 5128 } 5129 } 5130 } 5131 #endif 5132 slots->mCustomElementData = std::move(aData); 5133 } 5134 5135 nsTArray<RefPtr<nsAtom>>& Element::EnsureCustomStates() { 5136 MOZ_ASSERT(IsHTMLElement()); 5137 nsExtendedDOMSlots* slots = ExtendedDOMSlots(); 5138 return slots->mCustomStates; 5139 } 5140 5141 CustomElementDefinition* Element::GetCustomElementDefinition() const { 5142 CustomElementData* data = GetCustomElementData(); 5143 if (!data) { 5144 return nullptr; 5145 } 5146 5147 return data->GetCustomElementDefinition(); 5148 } 5149 5150 void Element::SetCustomElementDefinition(CustomElementDefinition* aDefinition) { 5151 CustomElementData* data = GetCustomElementData(); 5152 MOZ_ASSERT(data); 5153 5154 data->SetCustomElementDefinition(aDefinition); 5155 } 5156 5157 already_AddRefed<nsIDOMXULButtonElement> Element::AsXULButton() { 5158 nsCOMPtr<nsIDOMXULButtonElement> value; 5159 GetCustomInterface(getter_AddRefs(value)); 5160 return value.forget(); 5161 } 5162 5163 already_AddRefed<nsIDOMXULContainerElement> Element::AsXULContainer() { 5164 nsCOMPtr<nsIDOMXULContainerElement> value; 5165 GetCustomInterface(getter_AddRefs(value)); 5166 return value.forget(); 5167 } 5168 5169 already_AddRefed<nsIDOMXULContainerItemElement> Element::AsXULContainerItem() { 5170 nsCOMPtr<nsIDOMXULContainerItemElement> value; 5171 GetCustomInterface(getter_AddRefs(value)); 5172 return value.forget(); 5173 } 5174 5175 already_AddRefed<nsIDOMXULControlElement> Element::AsXULControl() { 5176 nsCOMPtr<nsIDOMXULControlElement> value; 5177 GetCustomInterface(getter_AddRefs(value)); 5178 return value.forget(); 5179 } 5180 5181 already_AddRefed<nsIDOMXULMenuListElement> Element::AsXULMenuList() { 5182 nsCOMPtr<nsIDOMXULMenuListElement> value; 5183 GetCustomInterface(getter_AddRefs(value)); 5184 return value.forget(); 5185 } 5186 5187 already_AddRefed<nsIDOMXULMultiSelectControlElement> 5188 Element::AsXULMultiSelectControl() { 5189 nsCOMPtr<nsIDOMXULMultiSelectControlElement> value; 5190 GetCustomInterface(getter_AddRefs(value)); 5191 return value.forget(); 5192 } 5193 5194 already_AddRefed<nsIDOMXULRadioGroupElement> Element::AsXULRadioGroup() { 5195 nsCOMPtr<nsIDOMXULRadioGroupElement> value; 5196 GetCustomInterface(getter_AddRefs(value)); 5197 return value.forget(); 5198 } 5199 5200 already_AddRefed<nsIDOMXULRelatedElement> Element::AsXULRelated() { 5201 nsCOMPtr<nsIDOMXULRelatedElement> value; 5202 GetCustomInterface(getter_AddRefs(value)); 5203 return value.forget(); 5204 } 5205 5206 already_AddRefed<nsIDOMXULSelectControlElement> Element::AsXULSelectControl() { 5207 nsCOMPtr<nsIDOMXULSelectControlElement> value; 5208 GetCustomInterface(getter_AddRefs(value)); 5209 return value.forget(); 5210 } 5211 5212 already_AddRefed<nsIDOMXULSelectControlItemElement> 5213 Element::AsXULSelectControlItem() { 5214 nsCOMPtr<nsIDOMXULSelectControlItemElement> value; 5215 GetCustomInterface(getter_AddRefs(value)); 5216 return value.forget(); 5217 } 5218 5219 already_AddRefed<nsIBrowser> Element::AsBrowser() { 5220 nsCOMPtr<nsIBrowser> value; 5221 GetCustomInterface(getter_AddRefs(value)); 5222 return value.forget(); 5223 } 5224 5225 already_AddRefed<nsIAutoCompletePopup> Element::AsAutoCompletePopup() { 5226 nsCOMPtr<nsIAutoCompletePopup> value; 5227 GetCustomInterface(getter_AddRefs(value)); 5228 return value.forget(); 5229 } 5230 5231 nsPresContext* Element::GetPresContext(PresContextFor aFor) const { 5232 // Get the document 5233 Document* doc = 5234 (aFor == eForComposedDoc) ? GetComposedDoc() : GetUncomposedDoc(); 5235 if (doc) { 5236 return doc->GetPresContext(); 5237 } 5238 5239 return nullptr; 5240 } 5241 5242 MOZ_DEFINE_MALLOC_SIZE_OF(ServoElementMallocSizeOf) 5243 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoElementMallocEnclosingSizeOf) 5244 5245 void Element::AddSizeOfExcludingThis(nsWindowSizes& aSizes, 5246 size_t* aNodeSize) const { 5247 FragmentOrElement::AddSizeOfExcludingThis(aSizes, aNodeSize); 5248 *aNodeSize += mAttrs.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf); 5249 5250 if (HasServoData()) { 5251 // Measure the ElementData object itself. 5252 aSizes.mLayoutElementDataObjects += 5253 aSizes.mState.mMallocSizeOf(mServoData.Get()); 5254 5255 // Measure mServoData, excluding the ComputedValues. This measurement 5256 // counts towards the element's size. We use ServoElementMallocSizeOf and 5257 // ServoElementMallocEnclosingSizeOf rather than |aState.mMallocSizeOf| to 5258 // better distinguish in DMD's output the memory measured within Servo 5259 // code. 5260 *aNodeSize += Servo_Element_SizeOfExcludingThisAndCVs( 5261 ServoElementMallocSizeOf, ServoElementMallocEnclosingSizeOf, 5262 &aSizes.mState.mSeenPtrs, this); 5263 5264 // Now measure just the ComputedValues (and style structs) under 5265 // mServoData. This counts towards the relevant fields in |aSizes|. 5266 if (auto* style = Servo_Element_GetMaybeOutOfDateStyle(this)) { 5267 if (!aSizes.mState.HaveSeenPtr(style)) { 5268 style->AddSizeOfIncludingThis(aSizes, &aSizes.mLayoutComputedValuesDom); 5269 } 5270 5271 for (size_t i = 0; i < PseudoStyle::kEagerPseudoCount; i++) { 5272 if (auto* style = Servo_Element_GetMaybeOutOfDatePseudoStyle(this, i)) { 5273 if (!aSizes.mState.HaveSeenPtr(style)) { 5274 style->AddSizeOfIncludingThis(aSizes, 5275 &aSizes.mLayoutComputedValuesDom); 5276 } 5277 } 5278 } 5279 } 5280 } 5281 } 5282 5283 #ifdef DEBUG 5284 static bool BitsArePropagated(const Element* aElement, uint32_t aBits, 5285 nsINode* aRestyleRoot) { 5286 const Element* curr = aElement; 5287 while (curr) { 5288 if (curr == aRestyleRoot) { 5289 return true; 5290 } 5291 if (!curr->HasAllFlags(aBits)) { 5292 return false; 5293 } 5294 nsINode* parentNode = curr->GetParentNode(); 5295 curr = curr->GetFlattenedTreeParentElementForStyle(); 5296 MOZ_ASSERT_IF(!curr, 5297 parentNode == aElement->OwnerDoc() || 5298 parentNode == parentNode->OwnerDoc()->GetRootElement()); 5299 } 5300 return true; 5301 } 5302 #endif 5303 5304 static inline void AssertNoBitsPropagatedFrom(nsINode* aRoot) { 5305 #ifdef DEBUG 5306 if (!aRoot || !aRoot->IsElement()) { 5307 return; 5308 } 5309 5310 auto* element = aRoot->GetFlattenedTreeParentElementForStyle(); 5311 while (element) { 5312 MOZ_ASSERT(!element->HasAnyOfFlags(Element::kAllServoDescendantBits)); 5313 element = element->GetFlattenedTreeParentElementForStyle(); 5314 } 5315 #endif 5316 } 5317 5318 // Sets `aBits` on `aElement` and all of its flattened-tree ancestors up to and 5319 // including aStopAt or the root element (whichever is encountered first), and 5320 // as long as `aBitsToStopAt` isn't found anywhere in the chain. 5321 static inline Element* PropagateBits(Element* aElement, uint32_t aBits, 5322 nsINode* aStopAt, uint32_t aBitsToStopAt) { 5323 Element* curr = aElement; 5324 while (curr && !curr->HasAllFlags(aBitsToStopAt)) { 5325 curr->SetFlags(aBits); 5326 if (curr == aStopAt) { 5327 break; 5328 } 5329 curr = curr->GetFlattenedTreeParentElementForStyle(); 5330 } 5331 5332 if (aBitsToStopAt != aBits && curr) { 5333 curr->SetFlags(aBits); 5334 } 5335 5336 return curr; 5337 } 5338 5339 // Notes that a given element is "dirty" with respect to the given descendants 5340 // bit (which may be one of dirty descendants, dirty animation descendants, or 5341 // need frame construction for descendants). 5342 // 5343 // This function operates on the dirty element itself, despite the fact that the 5344 // bits are generally used to describe descendants. This allows restyle roots 5345 // to be scoped as tightly as possible. On the first call to NoteDirtyElement 5346 // since the last restyle, we don't set any descendant bits at all, and just set 5347 // the element as the restyle root. 5348 // 5349 // Because the style traversal handles multiple tasks (styling, 5350 // animation-ticking, and lazy frame construction), there are potentially three 5351 // separate kinds of dirtiness to track. Rather than maintaining three separate 5352 // restyle roots, we use a single root, and always bubble it up to be the 5353 // nearest common ancestor of all the dirty content in the tree. This means that 5354 // we need to track the types of dirtiness that the restyle root corresponds to, 5355 // so SetServoRestyleRoot accepts a bitfield along with an element. 5356 // 5357 // The overall algorithm is as follows: 5358 // * When the first dirty element is noted, we just set as the restyle root. 5359 // * When additional dirty elements are noted, we propagate the given bit up 5360 // the tree, until we either reach the restyle root or the document root. 5361 // * If we reach the document root, we then propagate the bits associated with 5362 // the restyle root up the tree until we cross the path of the new root. Once 5363 // we find this common ancestor, we record it as the restyle root, and then 5364 // clear the bits between the new restyle root and the document root. 5365 // * If we have dirty content beneath multiple "document style traversal roots" 5366 // (which are the main DOM + each piece of document-level native-anoymous 5367 // content), we set the restyle root to the nsINode of the document itself. 5368 // This is the bail-out case where we traverse everything. 5369 // 5370 // Note that, since we track a root, we try to optimize the case where an 5371 // element under the current root is dirtied, that's why we don't trivially use 5372 // `nsContentUtils::GetCommonFlattenedTreeAncestorForStyle`. 5373 static void NoteDirtyElement(Element* aElement, uint32_t aBits) { 5374 MOZ_ASSERT(aElement->IsInComposedDoc()); 5375 5376 // Check the existing root early on, since it may allow us to short-circuit 5377 // before examining the parent chain. 5378 Document* doc = aElement->GetComposedDoc(); 5379 nsINode* existingRoot = doc->GetServoRestyleRoot(); 5380 if (existingRoot == aElement) { 5381 doc->SetServoRestyleRootDirtyBits(doc->GetServoRestyleRootDirtyBits() | 5382 aBits); 5383 return; 5384 } 5385 5386 nsINode* parent = aElement->GetFlattenedTreeParentNodeForStyle(); 5387 if (!parent) { 5388 // The element is not in the flattened tree, bail. 5389 return; 5390 } 5391 5392 if (MOZ_LIKELY(parent->IsElement())) { 5393 // If our parent is unstyled, we can inductively assume that it will be 5394 // traversed when the time is right, and that the traversal will reach us 5395 // when it happens. Nothing left to do. 5396 if (!parent->AsElement()->HasServoData()) { 5397 return; 5398 } 5399 5400 // Similarly, if our parent already has the bit we're propagating, we can 5401 // assume everything is already set up. 5402 if (parent->HasAllFlags(aBits)) { 5403 return; 5404 } 5405 5406 // If the parent is styled but is display:none, we're done. 5407 // 5408 // We can't check for a frame here, since <frame> elements inside <frameset> 5409 // still need to generate a frame, even if they're display: none. :( 5410 // 5411 // The servo traversal doesn't keep style data under display: none subtrees, 5412 // so in order for it to not need to cleanup each time anything happens in a 5413 // display: none subtree, we keep it clean. 5414 // 5415 // Also, we can't be much more smarter about using the parent's frame in 5416 // order to avoid work here, because since the style system keeps style data 5417 // in, e.g., subtrees under a leaf frame, missing restyles and such in there 5418 // has observable behavior via getComputedStyle, for example. 5419 if (Servo_Element_IsDisplayNone(parent->AsElement())) { 5420 return; 5421 } 5422 } 5423 5424 if (PresShell* presShell = doc->GetPresShell()) { 5425 presShell->EnsureStyleFlush(); 5426 } 5427 5428 MOZ_ASSERT(parent->IsElement() || parent == doc); 5429 5430 // The bit checks below rely on this to arrive to useful conclusions about the 5431 // shape of the tree. 5432 AssertNoBitsPropagatedFrom(existingRoot); 5433 5434 // If there's no existing restyle root, or if the root is already aElement, 5435 // just note the root+bits and return. 5436 if (!existingRoot) { 5437 doc->SetServoRestyleRoot(aElement, aBits); 5438 return; 5439 } 5440 5441 // There is an existing restyle root - walk up the tree from our element, 5442 // propagating bits as we go. 5443 const bool reachedDocRoot = 5444 !parent->IsElement() || 5445 !PropagateBits(parent->AsElement(), aBits, existingRoot, aBits); 5446 5447 uint32_t existingBits = doc->GetServoRestyleRootDirtyBits(); 5448 if (!reachedDocRoot || existingRoot == doc) { 5449 // We're a descendant of the existing root. All that's left to do is to 5450 // make sure the bit we propagated is also registered on the root. 5451 doc->SetServoRestyleRoot(existingRoot, existingBits | aBits); 5452 } else { 5453 // We reached the root without crossing the pre-existing restyle root. We 5454 // now need to find the nearest common ancestor, so climb up from the 5455 // existing root, extending bits along the way. 5456 Element* rootParent = existingRoot->GetFlattenedTreeParentElementForStyle(); 5457 // We can stop at the first occurrence of `aBits` in order to find the 5458 // common ancestor. 5459 if (Element* commonAncestor = 5460 PropagateBits(rootParent, existingBits, aElement, aBits)) { 5461 MOZ_ASSERT(commonAncestor == aElement || 5462 commonAncestor == 5463 nsContentUtils::GetCommonFlattenedTreeAncestorForStyle( 5464 aElement, rootParent)); 5465 5466 // We found a common ancestor. Make that the new style root, and clear the 5467 // bits between the new style root and the document root. 5468 doc->SetServoRestyleRoot(commonAncestor, existingBits | aBits); 5469 Element* curr = commonAncestor; 5470 while ((curr = curr->GetFlattenedTreeParentElementForStyle())) { 5471 MOZ_ASSERT(curr->HasAllFlags(aBits)); 5472 curr->UnsetFlags(aBits); 5473 } 5474 AssertNoBitsPropagatedFrom(commonAncestor); 5475 } else { 5476 // We didn't find a common ancestor element. That means we're descended 5477 // from two different document style roots, so the common ancestor is the 5478 // document. 5479 doc->SetServoRestyleRoot(doc, existingBits | aBits); 5480 } 5481 } 5482 5483 // See the comment in Document::SetServoRestyleRoot about the !IsElement() 5484 // check there. Same justification here. 5485 MOZ_ASSERT(aElement == doc->GetServoRestyleRoot() || 5486 !doc->GetServoRestyleRoot()->IsElement() || 5487 nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle( 5488 aElement, doc->GetServoRestyleRoot())); 5489 MOZ_ASSERT(aElement == doc->GetServoRestyleRoot() || 5490 !doc->GetServoRestyleRoot()->IsElement() || !parent->IsElement() || 5491 BitsArePropagated(parent->AsElement(), aBits, 5492 doc->GetServoRestyleRoot())); 5493 MOZ_ASSERT(doc->GetServoRestyleRootDirtyBits() & aBits); 5494 } 5495 5496 void Element::NoteDirtySubtreeForServo() { 5497 MOZ_ASSERT(IsInComposedDoc()); 5498 MOZ_ASSERT(HasServoData()); 5499 5500 Document* doc = GetComposedDoc(); 5501 nsINode* existingRoot = doc->GetServoRestyleRoot(); 5502 uint32_t existingBits = 5503 existingRoot ? doc->GetServoRestyleRootDirtyBits() : 0; 5504 5505 if (existingRoot && existingRoot->IsElement() && existingRoot != this && 5506 nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle( 5507 existingRoot->AsElement(), this)) { 5508 PropagateBits( 5509 existingRoot->AsElement()->GetFlattenedTreeParentElementForStyle(), 5510 existingBits, this, existingBits); 5511 5512 doc->ClearServoRestyleRoot(); 5513 } 5514 5515 NoteDirtyElement(this, 5516 existingBits | ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO); 5517 } 5518 5519 void Element::NoteDirtyForServo() { 5520 NoteDirtyElement(this, ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO); 5521 } 5522 5523 void Element::NoteAnimationOnlyDirtyForServo() { 5524 NoteDirtyElement(this, 5525 ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO); 5526 } 5527 5528 void Element::NoteDescendantsNeedFramesForServo() { 5529 // Since lazy frame construction can be required for non-element nodes, this 5530 // Note() method operates on the parent of the frame-requiring content, unlike 5531 // the other Note() methods above (which operate directly on the element that 5532 // needs processing). 5533 NoteDirtyElement(this, NODE_DESCENDANTS_NEED_FRAMES); 5534 SetFlags(NODE_DESCENDANTS_NEED_FRAMES); 5535 } 5536 5537 double Element::FirstLineBoxBSize() const { 5538 const nsBlockFrame* frame = do_QueryFrame(GetPrimaryFrame()); 5539 if (!frame) { 5540 return 0.0; 5541 } 5542 nsBlockFrame::ConstLineIterator line = frame->LinesBegin(); 5543 nsBlockFrame::ConstLineIterator lineEnd = frame->LinesEnd(); 5544 return line != lineEnd 5545 ? nsPresContext::AppUnitsToDoubleCSSPixels(line->BSize()) 5546 : 0.0; 5547 } 5548 5549 // static 5550 nsAtom* Element::GetEventNameForAttr(nsAtom* aAttr) { 5551 if (aAttr == nsGkAtoms::onwebkitanimationend) { 5552 return nsGkAtoms::onwebkitAnimationEnd; 5553 } 5554 if (aAttr == nsGkAtoms::onwebkitanimationiteration) { 5555 return nsGkAtoms::onwebkitAnimationIteration; 5556 } 5557 if (aAttr == nsGkAtoms::onwebkitanimationstart) { 5558 return nsGkAtoms::onwebkitAnimationStart; 5559 } 5560 if (aAttr == nsGkAtoms::onwebkittransitionend) { 5561 return nsGkAtoms::onwebkitTransitionEnd; 5562 } 5563 return aAttr; 5564 } 5565 5566 void Element::RegUnRegAccessKey(bool aDoReg) { 5567 // first check to see if we have an access key 5568 nsAutoString accessKey; 5569 GetAttr(nsGkAtoms::accesskey, accessKey); 5570 if (accessKey.IsEmpty()) { 5571 return; 5572 } 5573 5574 // We have an access key, so get the ESM from the pres context. 5575 if (nsPresContext* presContext = GetPresContext(eForComposedDoc)) { 5576 EventStateManager* esm = presContext->EventStateManager(); 5577 5578 // Register or unregister as appropriate. 5579 if (aDoReg) { 5580 esm->RegisterAccessKey(this, (uint32_t)accessKey.First()); 5581 } else { 5582 esm->UnregisterAccessKey(this, (uint32_t)accessKey.First()); 5583 } 5584 } 5585 } 5586 5587 void Element::SetHTML(const nsAString& aHTML, const SetHTMLOptions& aOptions, 5588 ErrorResult& aError) { 5589 nsContentUtils::SetHTML(this, this, aHTML, aOptions, aError); 5590 } 5591 5592 void Element::GetHTML(const GetHTMLOptions& aOptions, nsAString& aResult) { 5593 if (aOptions.mSerializableShadowRoots || !aOptions.mShadowRoots.IsEmpty()) { 5594 nsContentUtils::SerializeNodeToMarkup<SerializeShadowRoots::Yes>( 5595 this, true, aResult, aOptions.mSerializableShadowRoots, 5596 aOptions.mShadowRoots); 5597 } else { 5598 nsContentUtils::SerializeNodeToMarkup<SerializeShadowRoots::No>( 5599 this, true, aResult, aOptions.mSerializableShadowRoots, 5600 aOptions.mShadowRoots); 5601 } 5602 } 5603 5604 StylePropertyMapReadOnly* Element::ComputedStyleMap() { 5605 nsDOMSlots* slots = DOMSlots(); 5606 5607 if (!slots->mComputedStyleMap) { 5608 slots->mComputedStyleMap = 5609 MakeRefPtr<StylePropertyMapReadOnly>(this, /* aComputed */ true); 5610 } 5611 5612 return slots->mComputedStyleMap; 5613 } 5614 5615 bool Element::Translate() const { 5616 if (const auto* parent = Element::FromNodeOrNull(mParent)) { 5617 return parent->Translate(); 5618 } 5619 return true; 5620 } 5621 5622 EditorBase* Element::GetExtantEditor() const { 5623 if (!IsInComposedDoc()) { 5624 return nullptr; 5625 } 5626 const bool isInDesignMode = IsInDesignMode(); 5627 // Even if a text control element is an editing host, TextEditor handles 5628 // user input. Therefore, we should return TextEditor (or nullptr) in this 5629 // case. Note that text control element in the design mode does not work as 5630 // a text control. Therefore, in that case, we should return HTMLEditor. 5631 if (!isInDesignMode) { 5632 if (const auto* textControlElement = TextControlElement::FromNode(this)) { 5633 if (textControlElement->IsSingleLineTextControlOrTextArea()) { 5634 return textControlElement->GetExtantTextEditor(); 5635 } 5636 } 5637 } 5638 5639 if (!isInDesignMode && !IsEditable()) { 5640 return nullptr; 5641 } 5642 // FYI: This never creates HTMLEditor immediately. 5643 nsDocShell* const docShell = nsDocShell::Cast(OwnerDoc()->GetDocShell()); 5644 return docShell ? docShell->GetHTMLEditorInternal() : nullptr; 5645 } 5646 5647 void Element::SetHTMLUnsafe(const TrustedHTMLOrString& aHTML, 5648 const SetHTMLUnsafeOptions& aOptions, 5649 nsIPrincipal* aSubjectPrincipal, 5650 ErrorResult& aError) { 5651 nsContentUtils::SetHTMLUnsafe(this, this, aHTML, aOptions, 5652 false /*aIsShadowRoot*/, aSubjectPrincipal, 5653 aError); 5654 } 5655 5656 // https://html.spec.whatwg.org/#event-beforematch 5657 void Element::FireBeforematchEvent(ErrorResult& aRv) { 5658 RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr); 5659 event->InitEvent(u"beforematch"_ns, 5660 /*aCanBubble=*/true, 5661 /*aCancelable=*/false); 5662 5663 event->SetTrusted(true); 5664 DispatchEvent(*event, aRv); 5665 } 5666 5667 bool Element::BlockingContainsRender() const { 5668 const nsAttrValue* attrValue = GetParsedAttr(nsGkAtoms::blocking); 5669 if (!attrValue || !StaticPrefs::dom_element_blocking_enabled()) { 5670 return false; 5671 } 5672 MOZ_ASSERT(attrValue->Type() == nsAttrValue::eAtomArray, 5673 "Checking blocking attribute on element that doesn't parse it?"); 5674 return attrValue->Contains(nsGkAtoms::render, eIgnoreCase); 5675 } 5676 5677 static bool IsOffsetParent(nsIFrame* aFrame) { 5678 LayoutFrameType frameType = aFrame->Type(); 5679 5680 if (frameType == LayoutFrameType::TableCell || 5681 frameType == LayoutFrameType::TableWrapper) { 5682 // Per the IDL for Element, only td, th, and table are acceptable 5683 // offsetParents apart from body or positioned elements; we need to check 5684 // the content type as well as the frame type so we ignore anonymous tables 5685 // created by an element with display: table-cell with no actual table 5686 nsIContent* content = aFrame->GetContent(); 5687 5688 return content->IsAnyOfHTMLElements(nsGkAtoms::table, nsGkAtoms::td, 5689 nsGkAtoms::th); 5690 } 5691 return false; 5692 } 5693 5694 struct OffsetResult { 5695 Element* mParent = nullptr; 5696 nsRect mRect; 5697 }; 5698 5699 static OffsetResult GetUnretargetedOffsetsFor(const Element& aElement) { 5700 nsIFrame* frame = aElement.GetPrimaryFrame(); 5701 if (!frame) { 5702 return {}; 5703 } 5704 5705 nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(frame); 5706 5707 nsIFrame* parent = frame->GetParent(); 5708 nsPoint origin(0, 0); 5709 5710 nsIContent* offsetParent = nullptr; 5711 Element* docElement = aElement.GetComposedDoc()->GetRootElement(); 5712 nsIContent* content = frame->GetContent(); 5713 const auto effectiveZoom = frame->Style()->EffectiveZoom(); 5714 5715 if (content && 5716 (content->IsHTMLElement(nsGkAtoms::body) || content == docElement)) { 5717 parent = frame; 5718 } else { 5719 const bool isPositioned = styleFrame->IsAbsPosContainingBlock(); 5720 const bool isAbsolutelyPositioned = frame->IsAbsolutelyPositioned(); 5721 origin += frame->GetPositionIgnoringScrolling(); 5722 5723 for (; parent; parent = parent->GetParent()) { 5724 content = parent->GetContent(); 5725 5726 // Stop at the first ancestor that is positioned. 5727 if (parent->IsAbsPosContainingBlock()) { 5728 offsetParent = content; 5729 break; 5730 } 5731 5732 // WebKit-ism: offsetParent stops at zoom changes. 5733 // See https://github.com/w3c/csswg-drafts/issues/10252 5734 if (effectiveZoom != parent->Style()->EffectiveZoom()) { 5735 offsetParent = content; 5736 break; 5737 } 5738 5739 // Add the parent's origin to our own to get to the 5740 // right coordinate system. 5741 const bool isOffsetParent = !isPositioned && IsOffsetParent(parent); 5742 if (!isOffsetParent) { 5743 origin += parent->GetPositionIgnoringScrolling(); 5744 } 5745 5746 if (content) { 5747 // If we've hit the document element, break here. 5748 if (content == docElement) { 5749 break; 5750 } 5751 5752 // Break if the ancestor frame type makes it suitable as offset parent 5753 // and this element is *not* positioned or if we found the body element. 5754 if (isOffsetParent || content->IsHTMLElement(nsGkAtoms::body)) { 5755 offsetParent = content; 5756 break; 5757 } 5758 } 5759 } 5760 5761 if (isAbsolutelyPositioned && !offsetParent && 5762 !frame->GetParent()->IsViewportFrame()) { 5763 // If this element is absolutely positioned, but we don't have 5764 // an offset parent it means this element is an absolutely 5765 // positioned child that's not nested inside another positioned 5766 // element, in this case the element's frame's parent is the 5767 // frame for the HTML element so we fail to find the body in the 5768 // parent chain. We want the offset parent in this case to be 5769 // the body, so we just get the body element from the document. 5770 // 5771 // We use GetBodyElement() here, not GetBody(), because we don't want to 5772 // end up with framesets here. 5773 offsetParent = aElement.GetComposedDoc()->GetBodyElement(); 5774 } 5775 } 5776 5777 // Make the position relative to the padding edge. 5778 if (parent) { 5779 const nsStyleBorder* border = parent->StyleBorder(); 5780 origin.x -= border->GetComputedBorderWidth(eSideLeft); 5781 origin.y -= border->GetComputedBorderWidth(eSideTop); 5782 } 5783 5784 // Get the union of all rectangles in this and continuation frames. 5785 // It doesn't really matter what we use as aRelativeTo here, since 5786 // we only care about the size. We just have to use something non-null. 5787 nsRect rcFrame = nsLayoutUtils::GetAllInFlowRectsUnion(frame, frame); 5788 rcFrame.MoveTo(origin); 5789 return {Element::FromNodeOrNull(offsetParent), rcFrame}; 5790 } 5791 5792 static bool ShouldBeRetargeted(const Element& aReferenceElement, 5793 const Element& aElementToMaybeRetarget) { 5794 ShadowRoot* shadow = aElementToMaybeRetarget.GetContainingShadow(); 5795 if (!shadow) { 5796 return false; 5797 } 5798 for (ShadowRoot* scope = aReferenceElement.GetContainingShadow(); scope; 5799 scope = scope->Host()->GetContainingShadow()) { 5800 if (scope == shadow) { 5801 return false; 5802 } 5803 } 5804 5805 return true; 5806 } 5807 5808 Element* Element::GetOffsetRect(CSSIntRect& aRect) { 5809 aRect = CSSIntRect(); 5810 5811 nsIFrame* frame = GetPrimaryFrame(FlushType::Layout); 5812 if (!frame) { 5813 return nullptr; 5814 } 5815 5816 OffsetResult thisResult = GetUnretargetedOffsetsFor(*this); 5817 nsRect rect = thisResult.mRect; 5818 Element* parent = thisResult.mParent; 5819 while (parent && ShouldBeRetargeted(*this, *parent)) { 5820 OffsetResult result = GetUnretargetedOffsetsFor(*parent); 5821 rect += result.mRect.TopLeft(); 5822 parent = result.mParent; 5823 } 5824 5825 aRect = CSSIntRect::FromAppUnitsRounded( 5826 frame->Style()->EffectiveZoom().Unzoom(rect)); 5827 return parent; 5828 } 5829 5830 } // namespace mozilla::dom