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/. */
7 #include "mozilla/ArrayUtils.h"
8 #include "mozilla/DeclarationBlock.h"
9 #include "mozilla/EventDispatcher.h"
10 #include "mozilla/EventListenerManager.h"
11 #include "mozilla/EventStateManager.h"
12 #include "mozilla/EventStates.h"
13 #include "mozilla/HTMLEditor.h"
14 #include "mozilla/MappedDeclarations.h"
15 #include "mozilla/Likely.h"
16 #include "mozilla/MouseEvents.h"
17 #include "mozilla/PresShell.h"
18 #include "mozilla/TextEditor.h"
19 #include "mozilla/StaticPrefs.h"
22 #include "nsGenericHTMLElement.h"
23 #include "nsAttrValueInlines.h"
26 #include "nsQueryObject.h"
27 #include "nsIContentInlines.h"
28 #include "nsIContentViewer.h"
29 #include "mozilla/dom/BindContext.h"
30 #include "mozilla/dom/Document.h"
31 #include "nsIDocumentEncoder.h"
32 #include "nsIDOMWindow.h"
33 #include "nsMappedAttributes.h"
34 #include "nsHTMLStyleSheet.h"
35 #include "nsPIDOMWindow.h"
38 #include "nsIFrameInlines.h"
39 #include "nsIScrollableFrame.h"
41 #include "nsViewManager.h"
42 #include "nsIWidget.h"
44 #include "nsPresContext.h"
45 #include "nsIDocShell.h"
46 #include "nsNameSpaceManager.h"
48 #include "nsIPrincipal.h"
49 #include "nsContainerFrame.h"
50 #include "nsStyleUtil.h"
52 #include "mozilla/PresState.h"
53 #include "nsILayoutHistoryState.h"
55 #include "nsHTMLParts.h"
56 #include "nsContentUtils.h"
57 #include "mozilla/dom/DirectionalityUtils.h"
58 #include "mozilla/dom/DocumentOrShadowRoot.h"
60 #include "nsUnicharUtils.h"
61 #include "nsGkAtoms.h"
62 #include "nsDOMCSSDeclaration.h"
63 #include "nsITextControlFrame.h"
65 #include "nsIFormControl.h"
66 #include "mozilla/dom/HTMLFormElement.h"
67 #include "nsFocusManager.h"
68 #include "nsAttrValueOrString.h"
70 #include "mozilla/InternalMutationEvent.h"
71 #include "nsDOMStringMap.h"
73 #include "nsLayoutUtils.h"
74 #include "mozAutoDocUpdate.h"
75 #include "nsHtml5Module.h"
76 #include "nsITextControlElement.h"
77 #include "mozilla/dom/ElementInlines.h"
78 #include "HTMLFieldSetElement.h"
79 #include "nsTextNode.h"
80 #include "HTMLBRElement.h"
81 #include "HTMLMenuElement.h"
82 #include "nsDOMMutationObserver.h"
83 #include "mozilla/Preferences.h"
84 #include "mozilla/dom/FromParser.h"
85 #include "mozilla/dom/Link.h"
86 #include "mozilla/BloomFilter.h"
87 #include "mozilla/dom/ScriptLoader.h"
89 #include "nsVariant.h"
90 #include "nsDOMTokenList.h"
91 #include "nsThreadUtils.h"
92 #include "nsTextFragment.h"
93 #include "mozilla/dom/BindingUtils.h"
94 #include "mozilla/dom/MouseEventBinding.h"
95 #include "mozilla/dom/TouchEvent.h"
96 #include "mozilla/ErrorResult.h"
97 #include "nsHTMLDocument.h"
98 #include "nsGlobalWindow.h"
99 #include "mozilla/dom/HTMLBodyElement.h"
100 #include "imgIContainer.h"
101 #include "nsComputedDOMStyle.h"
102 #include "ReferrerPolicy.h"
103 #include "mozilla/dom/HTMLLabelElement.h"
104 #include "mozilla/dom/HTMLInputElement.h"
106 using namespace mozilla
;
107 using namespace mozilla::dom
;
109 nsresult
nsGenericHTMLElement::CopyInnerTo(Element
* aDst
) {
110 MOZ_ASSERT(!aDst
->GetUncomposedDoc(),
111 "Should not CopyInnerTo an Element in a document");
113 bool reparse
= (aDst
->OwnerDoc() != OwnerDoc());
116 static_cast<nsGenericHTMLElement
*>(aDst
)->mAttrs
.EnsureCapacityToClone(
118 NS_ENSURE_SUCCESS(rv
, rv
);
120 int32_t i
, count
= GetAttrCount();
121 for (i
= 0; i
< count
; ++i
) {
122 const nsAttrName
* name
= mAttrs
.AttrNameAt(i
);
123 const nsAttrValue
* value
= mAttrs
.AttrAt(i
);
125 if (name
->Equals(nsGkAtoms::style
, kNameSpaceID_None
) &&
126 value
->Type() == nsAttrValue::eCSSDeclaration
) {
127 // We still clone CSS attributes, even in the cross-document case.
128 // https://github.com/w3c/webappsec-csp/issues/212
130 // We can't just set this as a string, because that will fail
131 // to reparse the string into style data until the node is
132 // inserted into the document. Clone the Rule instead.
133 nsAttrValue
valueCopy(*value
);
134 rv
= aDst
->SetParsedAttr(name
->NamespaceID(), name
->LocalName(),
135 name
->GetPrefix(), valueCopy
, false);
136 NS_ENSURE_SUCCESS(rv
, rv
);
138 DeclarationBlock
* cssDeclaration
= value
->GetCSSDeclarationValue();
139 cssDeclaration
->SetImmutable();
140 } else if (reparse
) {
142 value
->ToString(valStr
);
144 rv
= aDst
->SetAttr(name
->NamespaceID(), name
->LocalName(),
145 name
->GetPrefix(), valStr
, false);
146 NS_ENSURE_SUCCESS(rv
, rv
);
148 nsAttrValue
valueCopy(*value
);
149 rv
= aDst
->SetParsedAttr(name
->NamespaceID(), name
->LocalName(),
150 name
->GetPrefix(), valueCopy
, false);
151 NS_ENSURE_SUCCESS(rv
, rv
);
158 static const nsAttrValue::EnumTable kDirTable
[] = {
159 {"ltr", eDir_LTR
}, {"rtl", eDir_RTL
}, {"auto", eDir_Auto
}, {nullptr, 0}};
161 void nsGenericHTMLElement::AddToNameTable(nsAtom
* aName
) {
162 MOZ_ASSERT(HasName(), "Node doesn't have name?");
163 Document
* doc
= GetUncomposedDoc();
164 if (doc
&& !IsInAnonymousSubtree()) {
165 doc
->AddToNameTable(this, aName
);
169 void nsGenericHTMLElement::RemoveFromNameTable() {
170 if (HasName() && CanHaveName(NodeInfo()->NameAtom())) {
171 if (Document
* doc
= GetUncomposedDoc()) {
172 doc
->RemoveFromNameTable(this,
173 GetParsedAttr(nsGkAtoms::name
)->GetAtomValue());
178 void nsGenericHTMLElement::GetAccessKeyLabel(nsString
& aLabel
) {
180 GetAccessKey(suffix
);
181 if (!suffix
.IsEmpty()) {
182 EventStateManager::GetAccessKeyLabelPrefix(this, aLabel
);
183 aLabel
.Append(suffix
);
187 static bool IsTableCell(LayoutFrameType frameType
) {
188 return LayoutFrameType::TableCell
== frameType
||
189 LayoutFrameType::BCTableCell
== frameType
;
192 static bool IsOffsetParent(nsIFrame
* aFrame
) {
193 LayoutFrameType frameType
= aFrame
->Type();
195 if (IsTableCell(frameType
) || frameType
== LayoutFrameType::Table
) {
196 // Per the IDL for Element, only td, th, and table are acceptable
197 // offsetParents apart from body or positioned elements; we need to check
198 // the content type as well as the frame type so we ignore anonymous tables
199 // created by an element with display: table-cell with no actual table
200 nsIContent
* content
= aFrame
->GetContent();
202 return content
->IsAnyOfHTMLElements(nsGkAtoms::table
, nsGkAtoms::td
,
208 struct OffsetResult
{
209 Element
* mParent
= nullptr;
213 static OffsetResult
GetUnretargetedOffsetsFor(const Element
& aElement
) {
214 nsIFrame
* frame
= aElement
.GetPrimaryFrame();
219 nsIFrame
* styleFrame
= nsLayoutUtils::GetStyleFrame(frame
);
221 nsIFrame
* parent
= frame
->GetParent();
222 nsPoint
origin(0, 0);
224 nsIContent
* offsetParent
= nullptr;
225 Element
* docElement
= aElement
.GetComposedDoc()->GetRootElement();
226 nsIContent
* content
= frame
->GetContent();
229 (content
->IsHTMLElement(nsGkAtoms::body
) || content
== docElement
)) {
232 const bool isPositioned
= styleFrame
->IsAbsPosContainingBlock();
233 const bool isAbsolutelyPositioned
= styleFrame
->IsAbsolutelyPositioned();
234 origin
+= frame
->GetPositionIgnoringScrolling();
236 for (; parent
; parent
= parent
->GetParent()) {
237 content
= parent
->GetContent();
239 // Stop at the first ancestor that is positioned.
240 if (parent
->IsAbsPosContainingBlock()) {
241 offsetParent
= content
;
245 // Add the parent's origin to our own to get to the
246 // right coordinate system.
247 const bool isOffsetParent
= !isPositioned
&& IsOffsetParent(parent
);
248 if (!isOffsetParent
) {
249 origin
+= parent
->GetPositionIgnoringScrolling();
253 // If we've hit the document element, break here.
254 if (content
== docElement
) {
258 // Break if the ancestor frame type makes it suitable as offset parent
259 // and this element is *not* positioned or if we found the body element.
260 if (isOffsetParent
|| content
->IsHTMLElement(nsGkAtoms::body
)) {
261 offsetParent
= content
;
267 if (isAbsolutelyPositioned
&& !offsetParent
) {
268 // If this element is absolutely positioned, but we don't have
269 // an offset parent it means this element is an absolutely
270 // positioned child that's not nested inside another positioned
271 // element, in this case the element's frame's parent is the
272 // frame for the HTML element so we fail to find the body in the
273 // parent chain. We want the offset parent in this case to be
274 // the body, so we just get the body element from the document.
276 // We use GetBodyElement() here, not GetBody(), because we don't want to
277 // end up with framesets here.
278 offsetParent
= aElement
.GetComposedDoc()->GetBodyElement();
282 // Subtract the parent border unless it uses border-box sizing.
283 if (parent
&& parent
->StylePosition()->mBoxSizing
!= StyleBoxSizing::Border
) {
284 const nsStyleBorder
* border
= parent
->StyleBorder();
285 origin
.x
-= border
->GetComputedBorderWidth(eSideLeft
);
286 origin
.y
-= border
->GetComputedBorderWidth(eSideTop
);
289 // XXX We should really consider subtracting out padding for
290 // content-box sizing, but we should see what IE does....
292 // Get the union of all rectangles in this and continuation frames.
293 // It doesn't really matter what we use as aRelativeTo here, since
294 // we only care about the size. We just have to use something non-null.
295 nsRect rcFrame
= nsLayoutUtils::GetAllInFlowRectsUnion(frame
, frame
);
296 rcFrame
.MoveTo(origin
);
297 return {Element::FromNodeOrNull(offsetParent
),
298 CSSIntRect::FromAppUnitsRounded(rcFrame
)};
301 static bool ShouldBeRetargeted(const Element
& aReferenceElement
,
302 const Element
& aElementToMaybeRetarget
) {
303 ShadowRoot
* shadow
= aElementToMaybeRetarget
.GetContainingShadow();
307 for (ShadowRoot
* scope
= aReferenceElement
.GetContainingShadow(); scope
;
308 scope
= scope
->Host()->GetContainingShadow()) {
309 if (scope
== shadow
) {
317 Element
* nsGenericHTMLElement::GetOffsetRect(CSSIntRect
& aRect
) {
318 aRect
= CSSIntRect();
320 if (!GetPrimaryFrame(FlushType::Layout
)) {
324 OffsetResult thisResult
= GetUnretargetedOffsetsFor(*this);
325 aRect
= thisResult
.mRect
;
327 Element
* parent
= thisResult
.mParent
;
328 while (parent
&& ShouldBeRetargeted(*this, *parent
)) {
329 OffsetResult result
= GetUnretargetedOffsetsFor(*parent
);
330 aRect
+= result
.mRect
.TopLeft();
331 parent
= result
.mParent
;
337 bool nsGenericHTMLElement::Spellcheck() {
338 // Has the state has been explicitly set?
340 for (node
= this; node
; node
= node
->GetParent()) {
341 if (node
->IsHTMLElement()) {
342 static Element::AttrValuesArray strings
[] = {nsGkAtoms::_true
,
343 nsGkAtoms::_false
, nullptr};
344 switch (node
->AsElement()->FindAttrValueIn(
345 kNameSpaceID_None
, nsGkAtoms::spellcheck
, strings
, eCaseMatters
)) {
346 case 0: // spellcheck = "true"
348 case 1: // spellcheck = "false"
354 // contenteditable/designMode are spellchecked by default
359 // Is this a chrome element?
360 if (nsContentUtils::IsChromeDoc(OwnerDoc())) {
361 return false; // Not spellchecked by default
364 // Anything else that's not a form control is not spellchecked by default
365 nsCOMPtr
<nsIFormControl
> formControl
= do_QueryObject(this);
367 return false; // Not spellchecked by default
370 // Is this a multiline plaintext input?
371 int32_t controlType
= formControl
->ControlType();
372 if (controlType
== NS_FORM_TEXTAREA
) {
373 return true; // Spellchecked by default
376 // Is this anything other than an input text?
377 // Other inputs are not spellchecked.
378 if (controlType
!= NS_FORM_INPUT_TEXT
) {
379 return false; // Not spellchecked by default
382 // Does the user want input text spellchecked by default?
383 // NOTE: Do not reflect a pref value of 0 back to the DOM getter.
384 // The web page should not know if the user has disabled spellchecking.
385 // We'll catch this in the editor itself.
386 int32_t spellcheckLevel
= Preferences::GetInt("layout.spellcheckDefault", 1);
387 return spellcheckLevel
== 2; // "Spellcheck multi- and single-line"
390 bool nsGenericHTMLElement::InNavQuirksMode(Document
* aDoc
) {
391 return aDoc
&& aDoc
->GetCompatibilityMode() == eCompatibility_NavQuirks
;
394 void nsGenericHTMLElement::UpdateEditableState(bool aNotify
) {
395 // XXX Should we do this only when in a document?
396 ContentEditableTristate value
= GetContentEditableValue();
397 if (value
!= eInherit
) {
398 DoSetEditableFlag(!!value
, aNotify
);
402 nsStyledElement::UpdateEditableState(aNotify
);
405 EventStates
nsGenericHTMLElement::IntrinsicState() const {
406 EventStates state
= nsGenericHTMLElementBase::IntrinsicState();
408 if (GetDirectionality() == eDir_RTL
) {
409 state
|= NS_EVENT_STATE_RTL
;
410 state
&= ~NS_EVENT_STATE_LTR
;
411 } else { // at least for HTML, directionality is exclusively LTR or RTL
412 NS_ASSERTION(GetDirectionality() == eDir_LTR
,
413 "HTML element's directionality must be either RTL or LTR");
414 state
|= NS_EVENT_STATE_LTR
;
415 state
&= ~NS_EVENT_STATE_RTL
;
421 nsresult
nsGenericHTMLElement::BindToTree(BindContext
& aContext
,
423 nsresult rv
= nsGenericHTMLElementBase::BindToTree(aContext
, aParent
);
424 NS_ENSURE_SUCCESS(rv
, rv
);
426 if (IsInUncomposedDoc()) {
428 if (HasName() && CanHaveName(NodeInfo()->NameAtom())) {
429 aContext
.OwnerDoc().AddToNameTable(
430 this, GetParsedAttr(nsGkAtoms::name
)->GetAtomValue());
434 if (HasFlag(NODE_IS_EDITABLE
) && GetContentEditableValue() == eTrue
&&
436 aContext
.OwnerDoc().ChangeContentEditableCount(this, +1);
439 // We need to consider a labels element is moved to another subtree
440 // with different root, it needs to update labels list and its root
442 nsExtendedDOMSlots
* slots
= GetExistingExtendedDOMSlots();
443 if (slots
&& slots
->mLabelsList
) {
444 slots
->mLabelsList
->MaybeResetRoot(SubtreeRoot());
450 void nsGenericHTMLElement::UnbindFromTree(bool aNullParent
) {
451 if (IsInUncomposedDoc()) {
455 RemoveFromNameTable();
457 if (GetContentEditableValue() == eTrue
) {
458 Document
* doc
= GetComposedDoc();
460 doc
->ChangeContentEditableCount(this, -1);
464 nsStyledElement::UnbindFromTree(aNullParent
);
466 // Invalidate .labels list. It will be repopulated when used the next time.
467 nsExtendedDOMSlots
* slots
= GetExistingExtendedDOMSlots();
468 if (slots
&& slots
->mLabelsList
) {
469 slots
->mLabelsList
->MaybeResetRoot(SubtreeRoot());
473 HTMLFormElement
* nsGenericHTMLElement::FindAncestorForm(
474 HTMLFormElement
* aCurrentForm
) {
475 NS_ASSERTION(!HasAttr(kNameSpaceID_None
, nsGkAtoms::form
) ||
476 IsHTMLElement(nsGkAtoms::img
),
477 "FindAncestorForm should not be called if @form is set!");
479 // Make sure we don't end up finding a form that's anonymous from
480 // our point of view. See also nsGenericHTMLFormElement::UpdateFieldSet.
481 nsIContent
* bindingParent
= GetBindingParent();
483 nsIContent
* content
= this;
484 while (content
!= bindingParent
&& content
) {
485 // If the current ancestor is a form, return it as our form
486 if (content
->IsHTMLElement(nsGkAtoms::form
)) {
488 if (!nsContentUtils::IsInSameAnonymousTree(this, content
)) {
489 // It's possible that we started unbinding at |content| or
490 // some ancestor of it, and |content| and |this| used to all be
491 // anonymous. Check for this the hard way.
492 for (nsIContent
* child
= this; child
!= content
;
493 child
= child
->GetParent()) {
494 NS_ASSERTION(child
->GetParent()->ComputeIndexOf(child
) != -1,
499 return static_cast<HTMLFormElement
*>(content
);
502 nsIContent
* prevContent
= content
;
503 content
= prevContent
->GetParent();
505 if (!content
&& aCurrentForm
) {
506 // We got to the root of the subtree we're in, and we're being removed
507 // from the DOM (the only time we get into this method with a non-null
508 // aCurrentForm). Check whether aCurrentForm is in the same subtree. If
509 // it is, we want to return aCurrentForm, since this case means that
510 // we're one of those inputs-in-a-table that have a hacked mForm pointer
511 // and a subtree containing both us and the form got removed from the
513 if (aCurrentForm
->IsInclusiveDescendantOf(prevContent
)) {
522 bool nsGenericHTMLElement::CheckHandleEventForAnchorsPreconditions(
523 EventChainVisitor
& aVisitor
) {
524 MOZ_ASSERT(nsCOMPtr
<Link
>(do_QueryObject(this)),
525 "should be called only when |this| implements |Link|");
526 // When disconnected, only <a> should navigate away per
527 // https://html.spec.whatwg.org/#cannot-navigate
528 return IsInComposedDoc() || IsHTMLElement(nsGkAtoms::a
);
531 void nsGenericHTMLElement::GetEventTargetParentForAnchors(
532 EventChainPreVisitor
& aVisitor
) {
533 nsGenericHTMLElementBase::GetEventTargetParent(aVisitor
);
535 if (!CheckHandleEventForAnchorsPreconditions(aVisitor
)) {
539 GetEventTargetParentForLinks(aVisitor
);
542 nsresult
nsGenericHTMLElement::PostHandleEventForAnchors(
543 EventChainPostVisitor
& aVisitor
) {
544 if (!CheckHandleEventForAnchorsPreconditions(aVisitor
)) {
548 return PostHandleEventForLinks(aVisitor
);
551 bool nsGenericHTMLElement::IsHTMLLink(nsIURI
** aURI
) const {
552 MOZ_ASSERT(aURI
, "Must provide aURI out param");
554 *aURI
= GetHrefURIForAnchors().take();
555 // We promise out param is non-null if we return true, so base rv on it
556 return *aURI
!= nullptr;
559 already_AddRefed
<nsIURI
> nsGenericHTMLElement::GetHrefURIForAnchors() const {
560 // This is used by the three Link implementations and
561 // nsHTMLStyleElement.
563 // Get href= attribute (relative URI).
565 // We use the nsAttrValue's copy of the URI string to avoid copying.
566 nsCOMPtr
<nsIURI
> uri
;
567 GetURIAttr(nsGkAtoms::href
, nullptr, getter_AddRefs(uri
));
572 nsresult
nsGenericHTMLElement::BeforeSetAttr(int32_t aNamespaceID
,
574 const nsAttrValueOrString
* aValue
,
576 if (aNamespaceID
== kNameSpaceID_None
) {
577 if (aName
== nsGkAtoms::accesskey
) {
578 // Have to unregister before clearing flag. See UnregAccessKey
581 UnsetFlags(NODE_HAS_ACCESSKEY
);
583 } else if (aName
== nsGkAtoms::name
) {
584 // Have to do this before clearing flag. See RemoveFromNameTable
585 RemoveFromNameTable();
586 if (!aValue
|| aValue
->IsEmpty()) {
589 } else if (aName
== nsGkAtoms::contenteditable
) {
591 // Set this before the attribute is set so that any subclass code that
592 // runs before the attribute is set won't think we're missing a
593 // contenteditable attr when we actually have one.
594 SetMayHaveContentEditableAttr();
597 if (!aValue
&& IsEventAttributeName(aName
)) {
598 if (EventListenerManager
* manager
= GetExistingListenerManager()) {
599 manager
->RemoveEventHandler(aName
);
604 return nsGenericHTMLElementBase::BeforeSetAttr(aNamespaceID
, aName
, aValue
,
608 nsresult
nsGenericHTMLElement::AfterSetAttr(
609 int32_t aNamespaceID
, nsAtom
* aName
, const nsAttrValue
* aValue
,
610 const nsAttrValue
* aOldValue
, nsIPrincipal
* aMaybeScriptedPrincipal
,
612 if (aNamespaceID
== kNameSpaceID_None
) {
613 if (IsEventAttributeName(aName
) && aValue
) {
614 MOZ_ASSERT(aValue
->Type() == nsAttrValue::eString
,
615 "Expected string value for script body");
616 nsresult rv
= SetEventHandler(aName
, aValue
->GetStringValue());
617 NS_ENSURE_SUCCESS(rv
, rv
);
618 } else if (aNotify
&& aName
== nsGkAtoms::spellcheck
) {
619 SyncEditorsOnSubtree(this);
620 } else if (aName
== nsGkAtoms::dir
) {
621 Directionality dir
= eDir_LTR
;
622 // A boolean tracking whether we need to recompute our directionality.
623 // This needs to happen after we update our internal "dir" attribute
624 // state but before we call SetDirectionalityOnDescendants.
625 bool recomputeDirectionality
= false;
626 // We don't want to have to keep getting the "dir" attribute in
627 // IntrinsicState, so we manually recompute our dir-related event states
628 // here and send the relevant update notifications.
629 EventStates dirStates
;
630 if (aValue
&& aValue
->Type() == nsAttrValue::eEnum
) {
632 dirStates
|= NS_EVENT_STATE_HAS_DIR_ATTR
;
633 Directionality dirValue
= (Directionality
)aValue
->GetEnumValue();
634 if (dirValue
== eDir_Auto
) {
635 dirStates
|= NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO
;
638 SetDirectionality(dir
, aNotify
);
639 if (dirValue
== eDir_LTR
) {
640 dirStates
|= NS_EVENT_STATE_DIR_ATTR_LTR
;
642 MOZ_ASSERT(dirValue
== eDir_RTL
);
643 dirStates
|= NS_EVENT_STATE_DIR_ATTR_RTL
;
648 // We have a value, just not a valid one.
649 dirStates
|= NS_EVENT_STATE_HAS_DIR_ATTR
;
652 if (NodeInfo()->Equals(nsGkAtoms::bdi
)) {
653 dirStates
|= NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO
;
655 recomputeDirectionality
= true;
658 // Now figure out what's changed about our dir states.
659 EventStates oldDirStates
= State() & DIR_ATTR_STATES
;
660 EventStates changedStates
= dirStates
^ oldDirStates
;
661 ToggleStates(changedStates
, aNotify
);
662 if (recomputeDirectionality
) {
663 dir
= RecomputeDirectionality(this, aNotify
);
665 SetDirectionalityOnDescendants(this, dir
, aNotify
);
666 } else if (aName
== nsGkAtoms::contenteditable
) {
667 int32_t editableCountDelta
= 0;
669 (aOldValue
->Equals(NS_LITERAL_STRING("true"), eIgnoreCase
) ||
670 aOldValue
->Equals(EmptyString(), eIgnoreCase
))) {
671 editableCountDelta
= -1;
673 if (aValue
&& (aValue
->Equals(NS_LITERAL_STRING("true"), eIgnoreCase
) ||
674 aValue
->Equals(EmptyString(), eIgnoreCase
))) {
675 ++editableCountDelta
;
677 ChangeEditableState(editableCountDelta
);
678 } else if (aName
== nsGkAtoms::accesskey
) {
679 if (aValue
&& !aValue
->Equals(EmptyString(), eIgnoreCase
)) {
680 SetFlags(NODE_HAS_ACCESSKEY
);
683 } else if (aName
== nsGkAtoms::name
) {
684 if (aValue
&& !aValue
->Equals(EmptyString(), eIgnoreCase
)) {
685 // This may not be quite right because we can have subclass code run
686 // before here. But in practice subclasses don't care about this flag,
687 // and in particular selector matching does not care. Otherwise we'd
688 // want to handle it like we handle id attributes (in PreIdMaybeChange
689 // and PostIdMaybeChange).
691 if (CanHaveName(NodeInfo()->NameAtom())) {
692 AddToNameTable(aValue
->GetAtomValue());
698 return nsGenericHTMLElementBase::AfterSetAttr(
699 aNamespaceID
, aName
, aValue
, aOldValue
, aMaybeScriptedPrincipal
, aNotify
);
702 EventListenerManager
* nsGenericHTMLElement::GetEventListenerManagerForAttr(
703 nsAtom
* aAttrName
, bool* aDefer
) {
704 // Attributes on the body and frameset tags get set on the global object
705 if ((mNodeInfo
->Equals(nsGkAtoms::body
) ||
706 mNodeInfo
->Equals(nsGkAtoms::frameset
)) &&
707 // We only forward some event attributes from body/frameset to window
709 #define EVENT(name_, id_, type_, struct_) /* nothing */
710 #define FORWARDED_EVENT(name_, id_, type_, struct_) \
711 || nsGkAtoms::on##name_ == aAttrName
712 #define WINDOW_EVENT FORWARDED_EVENT
713 #include "mozilla/EventNameList.h" // IWYU pragma: keep
715 #undef FORWARDED_EVENT
718 nsPIDOMWindowInner
* win
;
720 // If we have a document, and it has a window, add the event
721 // listener on the window (the inner window). If not, proceed as
723 // XXXbz sXBL/XBL2 issue: should we instead use GetComposedDoc() here,
724 // override BindToTree for those classes and munge event listeners there?
725 Document
* document
= OwnerDoc();
728 if ((win
= document
->GetInnerWindow())) {
729 nsCOMPtr
<EventTarget
> piTarget(do_QueryInterface(win
));
731 return piTarget
->GetOrCreateListenerManager();
737 return nsGenericHTMLElementBase::GetEventListenerManagerForAttr(aAttrName
,
741 #define EVENT(name_, id_, type_, struct_) /* nothing; handled by nsINode */
742 #define FORWARDED_EVENT(name_, id_, type_, struct_) \
743 EventHandlerNonNull* nsGenericHTMLElement::GetOn##name_() { \
744 if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) { \
745 /* XXXbz note to self: add tests for this! */ \
746 if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) { \
747 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \
748 return globalWin->GetOn##name_(); \
753 return nsINode::GetOn##name_(); \
755 void nsGenericHTMLElement::SetOn##name_(EventHandlerNonNull* handler) { \
756 if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) { \
757 nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow(); \
762 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \
763 return globalWin->SetOn##name_(handler); \
766 return nsINode::SetOn##name_(handler); \
768 #define ERROR_EVENT(name_, id_, type_, struct_) \
769 already_AddRefed<EventHandlerNonNull> nsGenericHTMLElement::GetOn##name_() { \
770 if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) { \
771 /* XXXbz note to self: add tests for this! */ \
772 if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) { \
773 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \
774 OnErrorEventHandlerNonNull* errorHandler = globalWin->GetOn##name_(); \
775 if (errorHandler) { \
776 RefPtr<EventHandlerNonNull> handler = \
777 new EventHandlerNonNull(errorHandler); \
778 return handler.forget(); \
784 RefPtr<EventHandlerNonNull> handler = nsINode::GetOn##name_(); \
785 return handler.forget(); \
787 void nsGenericHTMLElement::SetOn##name_(EventHandlerNonNull* handler) { \
788 if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) { \
789 nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow(); \
794 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \
795 RefPtr<OnErrorEventHandlerNonNull> errorHandler; \
797 errorHandler = new OnErrorEventHandlerNonNull(handler); \
799 return globalWin->SetOn##name_(errorHandler); \
802 return nsINode::SetOn##name_(handler); \
804 #include "mozilla/EventNameList.h" // IWYU pragma: keep
806 #undef FORWARDED_EVENT
809 void nsGenericHTMLElement::GetBaseTarget(nsAString
& aBaseTarget
) const {
810 OwnerDoc()->GetBaseTarget(aBaseTarget
);
813 //----------------------------------------------------------------------
815 bool nsGenericHTMLElement::ParseAttribute(int32_t aNamespaceID
,
817 const nsAString
& aValue
,
818 nsIPrincipal
* aMaybeScriptedPrincipal
,
819 nsAttrValue
& aResult
) {
820 if (aNamespaceID
== kNameSpaceID_None
) {
821 if (aAttribute
== nsGkAtoms::dir
) {
822 return aResult
.ParseEnumValue(aValue
, kDirTable
, false);
825 if (aAttribute
== nsGkAtoms::tabindex
) {
826 return aResult
.ParseIntValue(aValue
);
829 if (aAttribute
== nsGkAtoms::referrerpolicy
) {
830 return ParseReferrerAttribute(aValue
, aResult
);
833 if (aAttribute
== nsGkAtoms::name
) {
834 // Store name as an atom. name="" means that the element has no name,
835 // not that it has an empty string as the name.
836 if (aValue
.IsEmpty()) {
839 aResult
.ParseAtom(aValue
);
843 if (aAttribute
== nsGkAtoms::contenteditable
) {
844 aResult
.ParseAtom(aValue
);
848 if (aAttribute
== nsGkAtoms::rel
) {
849 aResult
.ParseAtomArray(aValue
);
854 return nsGenericHTMLElementBase::ParseAttribute(
855 aNamespaceID
, aAttribute
, aValue
, aMaybeScriptedPrincipal
, aResult
);
858 bool nsGenericHTMLElement::ParseBackgroundAttribute(int32_t aNamespaceID
,
860 const nsAString
& aValue
,
861 nsAttrValue
& aResult
) {
862 if (aNamespaceID
== kNameSpaceID_None
&&
863 aAttribute
== nsGkAtoms::background
&& !aValue
.IsEmpty()) {
864 // Resolve url to an absolute url
865 Document
* doc
= OwnerDoc();
866 nsCOMPtr
<nsIURI
> uri
;
867 nsresult rv
= nsContentUtils::NewURIWithDocumentCharset(
868 getter_AddRefs(uri
), aValue
, doc
, GetBaseURI());
872 aResult
.SetTo(uri
, &aValue
);
879 bool nsGenericHTMLElement::IsAttributeMapped(const nsAtom
* aAttribute
) const {
880 static const MappedAttributeEntry
* const map
[] = {sCommonAttributeMap
};
882 return FindAttributeDependence(aAttribute
, map
);
885 nsMapRuleToAttributesFunc
nsGenericHTMLElement::GetAttributeMappingFunction()
887 return &MapCommonAttributesInto
;
890 nsIFormControlFrame
* nsGenericHTMLElement::GetFormControlFrame(
892 if (aFlushFrames
&& IsInComposedDoc()) {
893 // Cause a flush of the frames, so we get up-to-date frame information
894 GetComposedDoc()->FlushPendingNotifications(FlushType::Frames
);
896 nsIFrame
* frame
= GetPrimaryFrame();
898 nsIFormControlFrame
* form_frame
= do_QueryFrame(frame
);
903 // If we have generated content, the primary frame will be a
904 // wrapper frame.. out real frame will be in its child list.
905 for (frame
= frame
->PrincipalChildList().FirstChild(); frame
;
906 frame
= frame
->GetNextSibling()) {
907 form_frame
= do_QueryFrame(frame
);
917 nsPresContext
* nsGenericHTMLElement::GetPresContext(PresContextFor aFor
) {
920 (aFor
== eForComposedDoc
) ? GetComposedDoc() : GetUncomposedDoc();
922 return doc
->GetPresContext();
928 static const nsAttrValue::EnumTable kDivAlignTable
[] = {
929 {"left", NS_STYLE_TEXT_ALIGN_MOZ_LEFT
},
930 {"right", NS_STYLE_TEXT_ALIGN_MOZ_RIGHT
},
931 {"center", NS_STYLE_TEXT_ALIGN_MOZ_CENTER
},
932 {"middle", NS_STYLE_TEXT_ALIGN_MOZ_CENTER
},
933 {"justify", NS_STYLE_TEXT_ALIGN_JUSTIFY
},
936 static const nsAttrValue::EnumTable kFrameborderTable
[] = {
937 {"yes", NS_STYLE_FRAME_YES
},
938 {"no", NS_STYLE_FRAME_NO
},
939 {"1", NS_STYLE_FRAME_1
},
940 {"0", NS_STYLE_FRAME_0
},
943 static const nsAttrValue::EnumTable kScrollingTable
[] = {
944 {"yes", NS_STYLE_FRAME_YES
}, {"no", NS_STYLE_FRAME_NO
},
945 {"on", NS_STYLE_FRAME_ON
}, {"off", NS_STYLE_FRAME_OFF
},
946 {"scroll", NS_STYLE_FRAME_SCROLL
}, {"noscroll", NS_STYLE_FRAME_NOSCROLL
},
947 {"auto", NS_STYLE_FRAME_AUTO
}, {nullptr, 0}};
949 static const nsAttrValue::EnumTable kTableVAlignTable
[] = {
950 {"top", StyleVerticalAlignKeyword::Top
},
951 {"middle", StyleVerticalAlignKeyword::Middle
},
952 {"bottom", StyleVerticalAlignKeyword::Bottom
},
953 {"baseline", StyleVerticalAlignKeyword::Baseline
},
956 bool nsGenericHTMLElement::ParseAlignValue(const nsAString
& aString
,
957 nsAttrValue
& aResult
) {
958 static const nsAttrValue::EnumTable kAlignTable
[] = {
959 {"left", NS_STYLE_TEXT_ALIGN_LEFT
},
960 {"right", NS_STYLE_TEXT_ALIGN_RIGHT
},
962 {"top", StyleVerticalAlignKeyword::Top
},
963 {"middle", StyleVerticalAlignKeyword::MozMiddleWithBaseline
},
964 {"bottom", StyleVerticalAlignKeyword::Bottom
},
966 {"center", StyleVerticalAlignKeyword::MozMiddleWithBaseline
},
967 {"baseline", StyleVerticalAlignKeyword::Baseline
},
969 {"texttop", StyleVerticalAlignKeyword::TextTop
},
970 {"absmiddle", StyleVerticalAlignKeyword::Middle
},
971 {"abscenter", StyleVerticalAlignKeyword::Middle
},
972 {"absbottom", StyleVerticalAlignKeyword::Bottom
},
975 return aResult
.ParseEnumValue(aString
, kAlignTable
, false);
978 //----------------------------------------
980 static const nsAttrValue::EnumTable kTableHAlignTable
[] = {
981 {"left", NS_STYLE_TEXT_ALIGN_LEFT
},
982 {"right", NS_STYLE_TEXT_ALIGN_RIGHT
},
983 {"center", NS_STYLE_TEXT_ALIGN_CENTER
},
984 {"char", NS_STYLE_TEXT_ALIGN_CHAR
},
985 {"justify", NS_STYLE_TEXT_ALIGN_JUSTIFY
},
988 bool nsGenericHTMLElement::ParseTableHAlignValue(const nsAString
& aString
,
989 nsAttrValue
& aResult
) {
990 return aResult
.ParseEnumValue(aString
, kTableHAlignTable
, false);
993 //----------------------------------------
995 // This table is used for td, th, tr, col, thead, tbody and tfoot.
996 static const nsAttrValue::EnumTable kTableCellHAlignTable
[] = {
997 {"left", NS_STYLE_TEXT_ALIGN_MOZ_LEFT
},
998 {"right", NS_STYLE_TEXT_ALIGN_MOZ_RIGHT
},
999 {"center", NS_STYLE_TEXT_ALIGN_MOZ_CENTER
},
1000 {"char", NS_STYLE_TEXT_ALIGN_CHAR
},
1001 {"justify", NS_STYLE_TEXT_ALIGN_JUSTIFY
},
1002 {"middle", NS_STYLE_TEXT_ALIGN_MOZ_CENTER
},
1003 {"absmiddle", NS_STYLE_TEXT_ALIGN_CENTER
},
1006 bool nsGenericHTMLElement::ParseTableCellHAlignValue(const nsAString
& aString
,
1007 nsAttrValue
& aResult
) {
1008 return aResult
.ParseEnumValue(aString
, kTableCellHAlignTable
, false);
1011 //----------------------------------------
1013 bool nsGenericHTMLElement::ParseTableVAlignValue(const nsAString
& aString
,
1014 nsAttrValue
& aResult
) {
1015 return aResult
.ParseEnumValue(aString
, kTableVAlignTable
, false);
1018 bool nsGenericHTMLElement::ParseDivAlignValue(const nsAString
& aString
,
1019 nsAttrValue
& aResult
) {
1020 return aResult
.ParseEnumValue(aString
, kDivAlignTable
, false);
1023 bool nsGenericHTMLElement::ParseImageAttribute(nsAtom
* aAttribute
,
1024 const nsAString
& aString
,
1025 nsAttrValue
& aResult
) {
1026 if (aAttribute
== nsGkAtoms::width
|| aAttribute
== nsGkAtoms::height
||
1027 aAttribute
== nsGkAtoms::hspace
|| aAttribute
== nsGkAtoms::vspace
) {
1028 return aResult
.ParseHTMLDimension(aString
);
1030 if (aAttribute
== nsGkAtoms::border
) {
1031 return aResult
.ParseNonNegativeIntValue(aString
);
1036 bool nsGenericHTMLElement::ParseReferrerAttribute(const nsAString
& aString
,
1037 nsAttrValue
& aResult
) {
1038 static const nsAttrValue::EnumTable kReferrerTable
[] = {
1039 {ReferrerPolicyToString(net::RP_No_Referrer
),
1040 static_cast<int16_t>(net::RP_No_Referrer
)},
1041 {ReferrerPolicyToString(net::RP_Origin
),
1042 static_cast<int16_t>(net::RP_Origin
)},
1043 {ReferrerPolicyToString(net::RP_Origin_When_Crossorigin
),
1044 static_cast<int16_t>(net::RP_Origin_When_Crossorigin
)},
1045 {ReferrerPolicyToString(net::RP_No_Referrer_When_Downgrade
),
1046 static_cast<int16_t>(net::RP_No_Referrer_When_Downgrade
)},
1047 {ReferrerPolicyToString(net::RP_Unsafe_URL
),
1048 static_cast<int16_t>(net::RP_Unsafe_URL
)},
1049 {ReferrerPolicyToString(net::RP_Strict_Origin
),
1050 static_cast<int16_t>(net::RP_Strict_Origin
)},
1051 {ReferrerPolicyToString(net::RP_Same_Origin
),
1052 static_cast<int16_t>(net::RP_Same_Origin
)},
1053 {ReferrerPolicyToString(net::RP_Strict_Origin_When_Cross_Origin
),
1054 static_cast<int16_t>(net::RP_Strict_Origin_When_Cross_Origin
)},
1056 return aResult
.ParseEnumValue(aString
, kReferrerTable
, false);
1059 bool nsGenericHTMLElement::ParseFrameborderValue(const nsAString
& aString
,
1060 nsAttrValue
& aResult
) {
1061 return aResult
.ParseEnumValue(aString
, kFrameborderTable
, false);
1064 bool nsGenericHTMLElement::ParseScrollingValue(const nsAString
& aString
,
1065 nsAttrValue
& aResult
) {
1066 return aResult
.ParseEnumValue(aString
, kScrollingTable
, false);
1069 static inline void MapLangAttributeInto(const nsMappedAttributes
* aAttributes
,
1070 MappedDeclarations
& aDecls
) {
1071 const nsAttrValue
* langValue
= aAttributes
->GetAttr(nsGkAtoms::lang
);
1075 MOZ_ASSERT(langValue
->Type() == nsAttrValue::eAtom
);
1076 aDecls
.SetIdentAtomValueIfUnset(eCSSProperty__x_lang
,
1077 langValue
->GetAtomValue());
1078 if (!aDecls
.PropertyIsSet(eCSSProperty_text_emphasis_position
)) {
1079 const nsAtom
* lang
= langValue
->GetAtomValue();
1080 if (nsStyleUtil::MatchesLanguagePrefix(lang
, u
"zh")) {
1081 aDecls
.SetKeywordValue(eCSSProperty_text_emphasis_position
,
1082 NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT_ZH
);
1083 } else if (nsStyleUtil::MatchesLanguagePrefix(lang
, u
"ja") ||
1084 nsStyleUtil::MatchesLanguagePrefix(lang
, u
"mn")) {
1085 // This branch is currently no part of the spec.
1086 // See bug 1040668 comment 69 and comment 75.
1087 aDecls
.SetKeywordValue(eCSSProperty_text_emphasis_position
,
1088 NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT
);
1094 * Handle attributes common to all html elements
1096 void nsGenericHTMLElement::MapCommonAttributesIntoExceptHidden(
1097 const nsMappedAttributes
* aAttributes
, MappedDeclarations
& aDecls
) {
1098 if (!aDecls
.PropertyIsSet(eCSSProperty__moz_user_modify
)) {
1099 const nsAttrValue
* value
= aAttributes
->GetAttr(nsGkAtoms::contenteditable
);
1101 if (value
->Equals(nsGkAtoms::_empty
, eCaseMatters
) ||
1102 value
->Equals(nsGkAtoms::_true
, eIgnoreCase
)) {
1103 aDecls
.SetKeywordValue(eCSSProperty__moz_user_modify
,
1104 StyleUserModify::ReadWrite
);
1105 } else if (value
->Equals(nsGkAtoms::_false
, eIgnoreCase
)) {
1106 aDecls
.SetKeywordValue(eCSSProperty__moz_user_modify
,
1107 StyleUserModify::ReadOnly
);
1112 MapLangAttributeInto(aAttributes
, aDecls
);
1115 void nsGenericHTMLElement::MapCommonAttributesInto(
1116 const nsMappedAttributes
* aAttributes
, MappedDeclarations
& aDecls
) {
1117 MapCommonAttributesIntoExceptHidden(aAttributes
, aDecls
);
1119 if (!aDecls
.PropertyIsSet(eCSSProperty_display
)) {
1120 if (aAttributes
->IndexOfAttr(nsGkAtoms::hidden
) >= 0) {
1121 aDecls
.SetKeywordValue(eCSSProperty_display
, StyleDisplay::None
);
1127 const nsGenericHTMLElement::MappedAttributeEntry
1128 nsGenericHTMLElement::sCommonAttributeMap
[] = {{nsGkAtoms::contenteditable
},
1130 {nsGkAtoms::hidden
},
1134 const Element::MappedAttributeEntry
1135 nsGenericHTMLElement::sImageMarginSizeAttributeMap
[] = {{nsGkAtoms::width
},
1136 {nsGkAtoms::height
},
1137 {nsGkAtoms::hspace
},
1138 {nsGkAtoms::vspace
},
1142 const Element::MappedAttributeEntry
1143 nsGenericHTMLElement::sImageAlignAttributeMap
[] = {{nsGkAtoms::align
},
1147 const Element::MappedAttributeEntry
1148 nsGenericHTMLElement::sDivAlignAttributeMap
[] = {{nsGkAtoms::align
},
1152 const Element::MappedAttributeEntry
1153 nsGenericHTMLElement::sImageBorderAttributeMap
[] = {{nsGkAtoms::border
},
1157 const Element::MappedAttributeEntry
1158 nsGenericHTMLElement::sBackgroundAttributeMap
[] = {
1159 {nsGkAtoms::background
}, {nsGkAtoms::bgcolor
}, {nullptr}};
1162 const Element::MappedAttributeEntry
1163 nsGenericHTMLElement::sBackgroundColorAttributeMap
[] = {
1164 {nsGkAtoms::bgcolor
}, {nullptr}};
1166 void nsGenericHTMLElement::MapImageAlignAttributeInto(
1167 const nsMappedAttributes
* aAttributes
, MappedDeclarations
& aDecls
) {
1168 const nsAttrValue
* value
= aAttributes
->GetAttr(nsGkAtoms::align
);
1169 if (value
&& value
->Type() == nsAttrValue::eEnum
) {
1170 int32_t align
= value
->GetEnumValue();
1171 if (!aDecls
.PropertyIsSet(eCSSProperty_float
)) {
1172 if (align
== NS_STYLE_TEXT_ALIGN_LEFT
) {
1173 aDecls
.SetKeywordValue(eCSSProperty_float
, StyleFloat::Left
);
1174 } else if (align
== NS_STYLE_TEXT_ALIGN_RIGHT
) {
1175 aDecls
.SetKeywordValue(eCSSProperty_float
, StyleFloat::Right
);
1178 if (!aDecls
.PropertyIsSet(eCSSProperty_vertical_align
)) {
1180 case NS_STYLE_TEXT_ALIGN_LEFT
:
1181 case NS_STYLE_TEXT_ALIGN_RIGHT
:
1184 aDecls
.SetKeywordValue(eCSSProperty_vertical_align
, align
);
1191 void nsGenericHTMLElement::MapDivAlignAttributeInto(
1192 const nsMappedAttributes
* aAttributes
, MappedDeclarations
& aDecls
) {
1193 if (!aDecls
.PropertyIsSet(eCSSProperty_text_align
)) {
1195 const nsAttrValue
* value
= aAttributes
->GetAttr(nsGkAtoms::align
);
1196 if (value
&& value
->Type() == nsAttrValue::eEnum
)
1197 aDecls
.SetKeywordValue(eCSSProperty_text_align
, value
->GetEnumValue());
1201 void nsGenericHTMLElement::MapVAlignAttributeInto(
1202 const nsMappedAttributes
* aAttributes
, MappedDeclarations
& aDecls
) {
1203 if (!aDecls
.PropertyIsSet(eCSSProperty_vertical_align
)) {
1205 const nsAttrValue
* value
= aAttributes
->GetAttr(nsGkAtoms::valign
);
1206 if (value
&& value
->Type() == nsAttrValue::eEnum
)
1207 aDecls
.SetKeywordValue(eCSSProperty_vertical_align
,
1208 value
->GetEnumValue());
1212 static void MapDimensionAttributeInto(MappedDeclarations
& aDecls
,
1213 nsCSSPropertyID aProp
,
1214 const nsAttrValue
& aValue
) {
1215 MOZ_ASSERT(!aDecls
.PropertyIsSet(aProp
),
1216 "Why mapping the same property twice?");
1217 if (aValue
.Type() == nsAttrValue::eInteger
) {
1218 return aDecls
.SetPixelValue(aProp
, aValue
.GetIntegerValue());
1220 if (aValue
.Type() == nsAttrValue::ePercent
) {
1221 return aDecls
.SetPercentValue(aProp
, aValue
.GetPercentValue());
1223 if (aValue
.Type() == nsAttrValue::eDoubleValue
) {
1224 return aDecls
.SetPixelValue(aProp
, aValue
.GetDoubleValue());
1228 void nsGenericHTMLElement::MapImageMarginAttributeInto(
1229 const nsMappedAttributes
* aAttributes
, MappedDeclarations
& aDecls
) {
1230 const nsAttrValue
* value
;
1233 value
= aAttributes
->GetAttr(nsGkAtoms::hspace
);
1235 MapDimensionAttributeInto(aDecls
, eCSSProperty_margin_left
, *value
);
1236 MapDimensionAttributeInto(aDecls
, eCSSProperty_margin_right
, *value
);
1240 value
= aAttributes
->GetAttr(nsGkAtoms::vspace
);
1242 MapDimensionAttributeInto(aDecls
, eCSSProperty_margin_top
, *value
);
1243 MapDimensionAttributeInto(aDecls
, eCSSProperty_margin_bottom
, *value
);
1247 void nsGenericHTMLElement::MapWidthAttributeInto(
1248 const nsMappedAttributes
* aAttributes
, MappedDeclarations
& aDecls
) {
1249 if (const nsAttrValue
* value
= aAttributes
->GetAttr(nsGkAtoms::width
)) {
1250 MapDimensionAttributeInto(aDecls
, eCSSProperty_width
, *value
);
1254 void nsGenericHTMLElement::MapHeightAttributeInto(
1255 const nsMappedAttributes
* aAttributes
, MappedDeclarations
& aDecls
) {
1256 if (const nsAttrValue
* value
= aAttributes
->GetAttr(nsGkAtoms::height
)) {
1257 MapDimensionAttributeInto(aDecls
, eCSSProperty_height
, *value
);
1261 void nsGenericHTMLElement::MapImageSizeAttributesInto(
1262 const nsMappedAttributes
* aAttributes
, MappedDeclarations
& aDecls
) {
1263 auto* width
= aAttributes
->GetAttr(nsGkAtoms::width
);
1264 auto* height
= aAttributes
->GetAttr(nsGkAtoms::height
);
1266 MapDimensionAttributeInto(aDecls
, eCSSProperty_width
, *width
);
1269 MapDimensionAttributeInto(aDecls
, eCSSProperty_height
, *height
);
1271 // NOTE(emilio): If we implement the unrestricted aspect-ratio proposal, we
1272 // probably need to make this attribute mapping not apply to things like
1273 // <marquee> and <table>, which right now can go through this path.
1274 if (StaticPrefs::layout_css_width_and_height_map_to_aspect_ratio_enabled() &&
1277 if (width
->Type() == nsAttrValue::eInteger
) {
1278 w
.emplace(width
->GetIntegerValue());
1279 } else if (width
->Type() == nsAttrValue::eDoubleValue
) {
1280 w
.emplace(width
->GetDoubleValue());
1284 if (height
->Type() == nsAttrValue::eInteger
) {
1285 h
.emplace(height
->GetIntegerValue());
1286 } else if (height
->Type() == nsAttrValue::eDoubleValue
) {
1287 h
.emplace(height
->GetDoubleValue());
1290 if (w
&& h
&& *w
!= 0 && *h
!= 0) {
1291 aDecls
.SetNumberValue(eCSSProperty_aspect_ratio
, *w
/ *h
);
1296 void nsGenericHTMLElement::MapImageBorderAttributeInto(
1297 const nsMappedAttributes
* aAttributes
, MappedDeclarations
& aDecls
) {
1299 const nsAttrValue
* value
= aAttributes
->GetAttr(nsGkAtoms::border
);
1303 if (value
->Type() == nsAttrValue::eInteger
) val
= value
->GetIntegerValue();
1305 aDecls
.SetPixelValueIfUnset(eCSSProperty_border_top_width
, (float)val
);
1306 aDecls
.SetPixelValueIfUnset(eCSSProperty_border_right_width
, (float)val
);
1307 aDecls
.SetPixelValueIfUnset(eCSSProperty_border_bottom_width
, (float)val
);
1308 aDecls
.SetPixelValueIfUnset(eCSSProperty_border_left_width
, (float)val
);
1310 aDecls
.SetKeywordValueIfUnset(eCSSProperty_border_top_style
,
1311 StyleBorderStyle::Solid
);
1312 aDecls
.SetKeywordValueIfUnset(eCSSProperty_border_right_style
,
1313 StyleBorderStyle::Solid
);
1314 aDecls
.SetKeywordValueIfUnset(eCSSProperty_border_bottom_style
,
1315 StyleBorderStyle::Solid
);
1316 aDecls
.SetKeywordValueIfUnset(eCSSProperty_border_left_style
,
1317 StyleBorderStyle::Solid
);
1319 aDecls
.SetCurrentColorIfUnset(eCSSProperty_border_top_color
);
1320 aDecls
.SetCurrentColorIfUnset(eCSSProperty_border_right_color
);
1321 aDecls
.SetCurrentColorIfUnset(eCSSProperty_border_bottom_color
);
1322 aDecls
.SetCurrentColorIfUnset(eCSSProperty_border_left_color
);
1325 void nsGenericHTMLElement::MapBackgroundInto(
1326 const nsMappedAttributes
* aAttributes
, MappedDeclarations
& aDecls
) {
1327 if (!aDecls
.PropertyIsSet(eCSSProperty_background_image
)) {
1329 nsAttrValue
* value
=
1330 const_cast<nsAttrValue
*>(aAttributes
->GetAttr(nsGkAtoms::background
));
1332 aDecls
.SetBackgroundImage(*value
);
1337 void nsGenericHTMLElement::MapBGColorInto(const nsMappedAttributes
* aAttributes
,
1338 MappedDeclarations
& aDecls
) {
1339 if (!aDecls
.PropertyIsSet(eCSSProperty_background_color
)) {
1340 const nsAttrValue
* value
= aAttributes
->GetAttr(nsGkAtoms::bgcolor
);
1342 if (value
&& value
->GetColorValue(color
)) {
1343 aDecls
.SetColorValue(eCSSProperty_background_color
, color
);
1348 void nsGenericHTMLElement::MapBackgroundAttributesInto(
1349 const nsMappedAttributes
* aAttributes
, MappedDeclarations
& aDecls
) {
1350 MapBackgroundInto(aAttributes
, aDecls
);
1351 MapBGColorInto(aAttributes
, aDecls
);
1354 //----------------------------------------------------------------------
1356 int32_t nsGenericHTMLElement::GetIntAttr(nsAtom
* aAttr
,
1357 int32_t aDefault
) const {
1358 const nsAttrValue
* attrVal
= mAttrs
.GetAttr(aAttr
);
1359 if (attrVal
&& attrVal
->Type() == nsAttrValue::eInteger
) {
1360 return attrVal
->GetIntegerValue();
1365 nsresult
nsGenericHTMLElement::SetIntAttr(nsAtom
* aAttr
, int32_t aValue
) {
1367 value
.AppendInt(aValue
);
1369 return SetAttr(kNameSpaceID_None
, aAttr
, value
, true);
1372 uint32_t nsGenericHTMLElement::GetUnsignedIntAttr(nsAtom
* aAttr
,
1373 uint32_t aDefault
) const {
1374 const nsAttrValue
* attrVal
= mAttrs
.GetAttr(aAttr
);
1375 if (!attrVal
|| attrVal
->Type() != nsAttrValue::eInteger
) {
1379 return attrVal
->GetIntegerValue();
1382 uint32_t nsGenericHTMLElement::GetDimensionAttrAsUnsignedInt(
1383 nsAtom
* aAttr
, uint32_t aDefault
) const {
1384 const nsAttrValue
* attrVal
= mAttrs
.GetAttr(aAttr
);
1389 if (attrVal
->Type() == nsAttrValue::eInteger
) {
1390 return attrVal
->GetIntegerValue();
1393 if (attrVal
->Type() == nsAttrValue::ePercent
) {
1394 // This is a nasty hack. When we parsed the value, we stored it as an
1395 // ePercent, not eInteger, because there was a '%' after it in the string.
1396 // But the spec says to basically re-parse the string as an integer.
1397 // Luckily, we can just return the value we have stored. But
1398 // GetPercentValue() divides it by 100, so we need to multiply it back.
1399 return uint32_t(attrVal
->GetPercentValue() * 100.0f
);
1402 if (attrVal
->Type() == nsAttrValue::eDoubleValue
) {
1403 return uint32_t(attrVal
->GetDoubleValue());
1406 // Unfortunately, the set of values that are valid dimensions is not a
1407 // superset of values that are valid unsigned ints. In particular "+100" is
1408 // not a valid dimension, but should parse as the unsigned int "100". So if
1409 // we got here and we don't have a valid dimension value, just try re-parsing
1410 // the string we have as an integer.
1412 attrVal
->ToString(val
);
1413 nsContentUtils::ParseHTMLIntegerResultFlags result
;
1414 int32_t parsedInt
= nsContentUtils::ParseHTMLInteger(val
, &result
);
1415 if ((result
& nsContentUtils::eParseHTMLInteger_Error
) || parsedInt
< 0) {
1422 void nsGenericHTMLElement::GetURIAttr(nsAtom
* aAttr
, nsAtom
* aBaseAttr
,
1423 nsAString
& aResult
) const {
1424 nsCOMPtr
<nsIURI
> uri
;
1425 bool hadAttr
= GetURIAttr(aAttr
, aBaseAttr
, getter_AddRefs(uri
));
1432 // Just return the attr value
1433 GetAttr(kNameSpaceID_None
, aAttr
, aResult
);
1439 CopyUTF8toUTF16(spec
, aResult
);
1442 bool nsGenericHTMLElement::GetURIAttr(nsAtom
* aAttr
, nsAtom
* aBaseAttr
,
1443 nsIURI
** aURI
) const {
1446 const nsAttrValue
* attr
= mAttrs
.GetAttr(aAttr
);
1451 nsCOMPtr
<nsIURI
> baseURI
= GetBaseURI();
1454 nsAutoString baseAttrValue
;
1455 if (GetAttr(kNameSpaceID_None
, aBaseAttr
, baseAttrValue
)) {
1456 nsCOMPtr
<nsIURI
> baseAttrURI
;
1457 nsresult rv
= nsContentUtils::NewURIWithDocumentCharset(
1458 getter_AddRefs(baseAttrURI
), baseAttrValue
, OwnerDoc(), baseURI
);
1459 if (NS_FAILED(rv
)) {
1462 baseURI
.swap(baseAttrURI
);
1466 // Don't care about return value. If it fails, we still want to
1467 // return true, and *aURI will be null.
1468 nsContentUtils::NewURIWithDocumentCharset(aURI
, attr
->GetStringValue(),
1469 OwnerDoc(), baseURI
);
1473 HTMLMenuElement
* nsGenericHTMLElement::GetContextMenu() const {
1475 GetHTMLAttr(nsGkAtoms::contextmenu
, value
);
1476 if (!value
.IsEmpty()) {
1477 // XXXsmaug How should this work in Shadow DOM?
1478 Document
* doc
= GetUncomposedDoc();
1480 return HTMLMenuElement::FromNodeOrNull(doc
->GetElementById(value
));
1486 bool nsGenericHTMLElement::IsLabelable() const {
1487 return IsAnyOfHTMLElements(nsGkAtoms::progress
, nsGkAtoms::meter
);
1491 bool nsGenericHTMLElement::MatchLabelsElement(Element
* aElement
,
1492 int32_t aNamespaceID
,
1493 nsAtom
* aAtom
, void* aData
) {
1494 HTMLLabelElement
* element
= HTMLLabelElement::FromNode(aElement
);
1495 return element
&& element
->GetControl() == aData
;
1498 already_AddRefed
<nsINodeList
> nsGenericHTMLElement::Labels() {
1499 MOZ_ASSERT(IsLabelable(),
1500 "Labels() only allow labelable elements to use it.");
1501 nsExtendedDOMSlots
* slots
= ExtendedDOMSlots();
1503 if (!slots
->mLabelsList
) {
1504 slots
->mLabelsList
=
1505 new nsLabelsNodeList(SubtreeRoot(), MatchLabelsElement
, nullptr, this);
1508 RefPtr
<nsLabelsNodeList
> labels
= slots
->mLabelsList
;
1509 return labels
.forget();
1512 bool nsGenericHTMLElement::IsInteractiveHTMLContent(
1513 bool aIgnoreTabindex
) const {
1514 return IsAnyOfHTMLElements(nsGkAtoms::embed
) ||
1515 (!aIgnoreTabindex
&& HasAttr(kNameSpaceID_None
, nsGkAtoms::tabindex
));
1519 bool nsGenericHTMLElement::LegacyTouchAPIEnabled(JSContext
* aCx
,
1520 JSObject
* aGlobal
) {
1521 return TouchEvent::LegacyAPIEnabled(aCx
, aGlobal
);
1524 //----------------------------------------------------------------------
1526 nsGenericHTMLFormElement::nsGenericHTMLFormElement(
1527 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
, uint8_t aType
)
1528 : nsGenericHTMLElement(std::move(aNodeInfo
)),
1529 nsIFormControl(aType
),
1531 mFieldSet(nullptr) {
1532 // We should add the NS_EVENT_STATE_ENABLED bit here as needed, but
1533 // that depends on our type, which is not initialized yet. So we
1534 // have to do this in subclasses.
1537 nsGenericHTMLFormElement::~nsGenericHTMLFormElement() {
1539 mFieldSet
->RemoveElement(this);
1542 // Check that this element doesn't know anything about its form at this point.
1543 NS_ASSERTION(!mForm
, "mForm should be null at this point!");
1546 NS_IMPL_ISUPPORTS_INHERITED(nsGenericHTMLFormElement
, nsGenericHTMLElement
,
1549 nsINode
* nsGenericHTMLFormElement::GetScopeChainParent() const {
1550 return mForm
? mForm
: nsGenericHTMLElement::GetScopeChainParent();
1553 bool nsGenericHTMLFormElement::IsNodeOfType(uint32_t aFlags
) const {
1554 return !(aFlags
& ~eHTML_FORM_CONTROL
);
1557 void nsGenericHTMLFormElement::SaveSubtreeState() {
1560 nsGenericHTMLElement::SaveSubtreeState();
1563 void nsGenericHTMLFormElement::SetForm(HTMLFormElement
* aForm
) {
1564 MOZ_ASSERT(aForm
, "Don't pass null here");
1565 NS_ASSERTION(!mForm
,
1566 "We don't support switching from one non-null form to another.");
1568 SetForm(aForm
, false);
1571 void nsGenericHTMLFormElement::SetForm(HTMLFormElement
* aForm
,
1574 BeforeSetForm(aBindToTree
);
1577 // keep a *weak* ref to the form here
1581 void nsGenericHTMLFormElement::ClearForm(bool aRemoveFromForm
,
1582 bool aUnbindOrDelete
) {
1583 NS_ASSERTION((mForm
!= nullptr) == HasFlag(ADDED_TO_FORM
),
1584 "Form control should have had flag set correctly");
1590 if (aRemoveFromForm
) {
1591 nsAutoString nameVal
, idVal
;
1592 GetAttr(kNameSpaceID_None
, nsGkAtoms::name
, nameVal
);
1593 GetAttr(kNameSpaceID_None
, nsGkAtoms::id
, idVal
);
1595 mForm
->RemoveElement(this, true);
1597 if (!nameVal
.IsEmpty()) {
1598 mForm
->RemoveElementFromTable(this, nameVal
);
1601 if (!idVal
.IsEmpty()) {
1602 mForm
->RemoveElementFromTable(this, idVal
);
1606 UnsetFlags(ADDED_TO_FORM
);
1609 AfterClearForm(aUnbindOrDelete
);
1612 HTMLFormElement
* nsGenericHTMLFormElement::GetFormElement() { return mForm
; }
1614 HTMLFieldSetElement
* nsGenericHTMLFormElement::GetFieldSet() {
1618 nsIContent::IMEState
nsGenericHTMLFormElement::GetDesiredIMEState() {
1619 TextEditor
* textEditor
= GetTextEditorInternal();
1621 return nsGenericHTMLElement::GetDesiredIMEState();
1624 nsresult rv
= textEditor
->GetPreferredIMEState(&state
);
1625 if (NS_FAILED(rv
)) {
1626 return nsGenericHTMLElement::GetDesiredIMEState();
1631 nsresult
nsGenericHTMLFormElement::BindToTree(BindContext
& aContext
,
1633 nsresult rv
= nsGenericHTMLElement::BindToTree(aContext
, aParent
);
1634 NS_ENSURE_SUCCESS(rv
, rv
);
1636 // An autofocus event has to be launched if the autofocus attribute is
1637 // specified and the element accept the autofocus attribute. In addition,
1638 // the document should not be already loaded and the "browser.autofocus"
1639 // preference should be 'true'.
1640 if (IsAutofocusable() && HasAttr(kNameSpaceID_None
, nsGkAtoms::autofocus
) &&
1641 StaticPrefs::browser_autofocus() && IsInUncomposedDoc()) {
1642 aContext
.OwnerDoc().SetAutoFocusElement(this);
1645 // If @form is set, the element *has* to be in a composed document, otherwise
1646 // it wouldn't be possible to find an element with the corresponding id.
1647 // If @form isn't set, the element *has* to have a parent, otherwise it
1648 // wouldn't be possible to find a form ancestor.
1649 // We should not call UpdateFormOwner if none of these conditions are
1651 if (HasAttr(kNameSpaceID_None
, nsGkAtoms::form
) ? IsInComposedDoc()
1652 : aParent
.IsContent()) {
1653 UpdateFormOwner(true, nullptr);
1656 // Set parent fieldset which should be used for the disabled state.
1657 UpdateFieldSet(false);
1662 void nsGenericHTMLFormElement::UnbindFromTree(bool aNullParent
) {
1663 // Save state before doing anything
1667 // Might need to unset mForm
1669 // No more parent means no more form
1670 ClearForm(true, true);
1672 // Recheck whether we should still have an mForm.
1673 if (HasAttr(kNameSpaceID_None
, nsGkAtoms::form
) ||
1674 !FindAncestorForm(mForm
)) {
1675 ClearForm(true, true);
1677 UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT
);
1682 // Our novalidate state might have changed
1687 // We have to remove the form id observer if there was one.
1688 // We will re-add one later if needed (during bind to tree).
1689 if (nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None
,
1691 RemoveFormIdObserver();
1694 nsGenericHTMLElement::UnbindFromTree(aNullParent
);
1696 // The element might not have a fieldset anymore.
1697 UpdateFieldSet(false);
1700 nsresult
nsGenericHTMLFormElement::BeforeSetAttr(
1701 int32_t aNameSpaceID
, nsAtom
* aName
, const nsAttrValueOrString
* aValue
,
1703 if (aNameSpaceID
== kNameSpaceID_None
) {
1706 // remove the control from the hashtable as needed
1708 if (mForm
&& (aName
== nsGkAtoms::name
|| aName
== nsGkAtoms::id
)) {
1709 GetAttr(kNameSpaceID_None
, aName
, tmp
);
1711 if (!tmp
.IsEmpty()) {
1712 mForm
->RemoveElementFromTable(this, tmp
);
1716 if (mForm
&& aName
== nsGkAtoms::type
) {
1717 GetAttr(kNameSpaceID_None
, nsGkAtoms::name
, tmp
);
1719 if (!tmp
.IsEmpty()) {
1720 mForm
->RemoveElementFromTable(this, tmp
);
1723 GetAttr(kNameSpaceID_None
, nsGkAtoms::id
, tmp
);
1725 if (!tmp
.IsEmpty()) {
1726 mForm
->RemoveElementFromTable(this, tmp
);
1729 mForm
->RemoveElement(this, false);
1732 if (aName
== nsGkAtoms::form
) {
1733 // If @form isn't set or set to the empty string, there were no observer
1734 // so we don't have to remove it.
1735 if (nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None
,
1737 // The current form id observer is no longer needed.
1738 // A new one may be added in AfterSetAttr.
1739 RemoveFormIdObserver();
1744 return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID
, aName
, aValue
,
1748 nsresult
nsGenericHTMLFormElement::AfterSetAttr(
1749 int32_t aNameSpaceID
, nsAtom
* aName
, const nsAttrValue
* aValue
,
1750 const nsAttrValue
* aOldValue
, nsIPrincipal
* aMaybeScriptedPrincipal
,
1752 if (aNameSpaceID
== kNameSpaceID_None
) {
1753 // add the control to the hashtable as needed
1755 if (mForm
&& (aName
== nsGkAtoms::name
|| aName
== nsGkAtoms::id
) &&
1756 aValue
&& !aValue
->IsEmptyString()) {
1757 MOZ_ASSERT(aValue
->Type() == nsAttrValue::eAtom
,
1758 "Expected atom value for name/id");
1759 mForm
->AddElementToTable(this,
1760 nsDependentAtomString(aValue
->GetAtomValue()));
1763 if (mForm
&& aName
== nsGkAtoms::type
) {
1766 GetAttr(kNameSpaceID_None
, nsGkAtoms::name
, tmp
);
1768 if (!tmp
.IsEmpty()) {
1769 mForm
->AddElementToTable(this, tmp
);
1772 GetAttr(kNameSpaceID_None
, nsGkAtoms::id
, tmp
);
1774 if (!tmp
.IsEmpty()) {
1775 mForm
->AddElementToTable(this, tmp
);
1778 mForm
->AddElement(this, false, aNotify
);
1781 if (aName
== nsGkAtoms::form
) {
1782 // We need a new form id observer.
1783 DocumentOrShadowRoot
* docOrShadow
=
1784 GetUncomposedDocOrConnectedShadowRoot();
1786 Element
* formIdElement
= nullptr;
1787 if (aValue
&& !aValue
->IsEmptyString()) {
1788 formIdElement
= AddFormIdObserver();
1791 // Because we have a new @form value (or no more @form), we have to
1792 // update our form owner.
1793 UpdateFormOwner(false, formIdElement
);
1798 return nsGenericHTMLElement::AfterSetAttr(
1799 aNameSpaceID
, aName
, aValue
, aOldValue
, aMaybeScriptedPrincipal
, aNotify
);
1802 void nsGenericHTMLFormElement::GetEventTargetParent(
1803 EventChainPreVisitor
& aVisitor
) {
1804 if (aVisitor
.mEvent
->IsTrusted() && (aVisitor
.mEvent
->mMessage
== eFocus
||
1805 aVisitor
.mEvent
->mMessage
== eBlur
)) {
1806 // We have to handle focus/blur event to change focus states in
1807 // PreHandleEvent to prevent it breaks event target chain creation.
1808 aVisitor
.mWantsPreHandleEvent
= true;
1810 nsGenericHTMLElement::GetEventTargetParent(aVisitor
);
1813 nsresult
nsGenericHTMLFormElement::PreHandleEvent(EventChainVisitor
& aVisitor
) {
1814 if (aVisitor
.mEvent
->IsTrusted()) {
1815 switch (aVisitor
.mEvent
->mMessage
) {
1817 // Check to see if focus has bubbled up from a form control's
1818 // child textfield or button. If that's the case, don't focus
1819 // this parent file control -- leave focus on the child.
1820 nsIFormControlFrame
* formControlFrame
= GetFormControlFrame(true);
1821 if (formControlFrame
&&
1822 aVisitor
.mEvent
->mOriginalTarget
== static_cast<nsINode
*>(this))
1823 formControlFrame
->SetFocus(true, true);
1827 nsIFormControlFrame
* formControlFrame
= GetFormControlFrame(true);
1828 if (formControlFrame
) formControlFrame
->SetFocus(false, false);
1835 return nsGenericHTMLElement::PreHandleEvent(aVisitor
);
1838 void nsGenericHTMLFormElement::ForgetFieldSet(nsIContent
* aFieldset
) {
1839 if (mFieldSet
== aFieldset
) {
1840 mFieldSet
= nullptr;
1844 bool nsGenericHTMLFormElement::CanBeDisabled() const {
1845 int32_t type
= ControlType();
1846 // It's easier to test the types that _cannot_ be disabled
1847 return type
!= NS_FORM_OBJECT
&& type
!= NS_FORM_OUTPUT
;
1850 bool nsGenericHTMLFormElement::DoesReadOnlyApply() const {
1851 int32_t type
= ControlType();
1852 if (!(type
& NS_FORM_INPUT_ELEMENT
) && type
!= NS_FORM_TEXTAREA
) {
1857 case NS_FORM_INPUT_HIDDEN
:
1858 case NS_FORM_INPUT_BUTTON
:
1859 case NS_FORM_INPUT_IMAGE
:
1860 case NS_FORM_INPUT_RESET
:
1861 case NS_FORM_INPUT_SUBMIT
:
1862 case NS_FORM_INPUT_RADIO
:
1863 case NS_FORM_INPUT_FILE
:
1864 case NS_FORM_INPUT_CHECKBOX
:
1865 case NS_FORM_INPUT_RANGE
:
1866 case NS_FORM_INPUT_COLOR
:
1869 case NS_FORM_TEXTAREA
:
1870 case NS_FORM_INPUT_TEXT
:
1871 case NS_FORM_INPUT_PASSWORD
:
1872 case NS_FORM_INPUT_SEARCH
:
1873 case NS_FORM_INPUT_TEL
:
1874 case NS_FORM_INPUT_EMAIL
:
1875 case NS_FORM_INPUT_URL
:
1876 case NS_FORM_INPUT_NUMBER
:
1877 case NS_FORM_INPUT_DATE
:
1878 case NS_FORM_INPUT_TIME
:
1879 case NS_FORM_INPUT_MONTH
:
1880 case NS_FORM_INPUT_WEEK
:
1881 case NS_FORM_INPUT_DATETIME_LOCAL
:
1884 MOZ_ASSERT_UNREACHABLE("Unexpected input type in DoesReadOnlyApply()");
1893 bool nsGenericHTMLFormElement::IsHTMLFocusable(bool aWithMouse
,
1895 int32_t* aTabIndex
) {
1896 if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse
, aIsFocusable
,
1902 *aIsFocusable
= (!aWithMouse
|| nsFocusManager::sMouseFocusesFormControl
) &&
1908 EventStates
nsGenericHTMLFormElement::IntrinsicState() const {
1909 // If you add attribute-dependent states here, you need to add them them to
1910 // AfterSetAttr too. And add them to AfterSetAttr for all subclasses that
1911 // implement IntrinsicState() and are affected by that attribute.
1912 EventStates state
= nsGenericHTMLElement::IntrinsicState();
1914 if (mForm
&& mForm
->IsDefaultSubmitElement(this)) {
1915 NS_ASSERTION(IsSubmitControl(),
1916 "Default submit element that isn't a submit control.");
1917 // We are the default submit element (:default)
1918 state
|= NS_EVENT_STATE_DEFAULT
;
1921 // Make the text controls read-write
1922 if (!state
.HasState(NS_EVENT_STATE_MOZ_READWRITE
) && DoesReadOnlyApply()) {
1923 if (!GetBoolAttr(nsGkAtoms::readonly
)) {
1924 state
|= NS_EVENT_STATE_MOZ_READWRITE
;
1925 state
&= ~NS_EVENT_STATE_MOZ_READONLY
;
1932 nsGenericHTMLFormElement::FocusTristate
nsGenericHTMLFormElement::FocusState() {
1933 // We can't be focused if we aren't in a (composed) document
1934 Document
* doc
= GetComposedDoc();
1935 if (!doc
) return eUnfocusable
;
1937 // first see if we are disabled or not. If disabled then do nothing.
1939 return eUnfocusable
;
1942 // If the window is not active, do not allow the focus to bring the
1943 // window to the front. We update the focus controller, but do
1945 if (nsPIDOMWindowOuter
* win
= doc
->GetWindow()) {
1946 nsCOMPtr
<nsPIDOMWindowOuter
> rootWindow
= win
->GetPrivateRoot();
1948 nsCOMPtr
<nsIFocusManager
> fm
= do_GetService(FOCUSMANAGER_CONTRACTID
);
1949 if (fm
&& rootWindow
) {
1950 nsCOMPtr
<mozIDOMWindowProxy
> activeWindow
;
1951 fm
->GetActiveWindow(getter_AddRefs(activeWindow
));
1952 if (activeWindow
== rootWindow
) {
1953 return eActiveWindow
;
1958 return eInactiveWindow
;
1961 Element
* nsGenericHTMLFormElement::AddFormIdObserver() {
1962 nsAutoString formId
;
1963 DocumentOrShadowRoot
* docOrShadow
= GetUncomposedDocOrConnectedShadowRoot();
1964 GetAttr(kNameSpaceID_None
, nsGkAtoms::form
, formId
);
1965 NS_ASSERTION(!formId
.IsEmpty(),
1966 "@form value should not be the empty string!");
1967 RefPtr
<nsAtom
> atom
= NS_Atomize(formId
);
1969 return docOrShadow
->AddIDTargetObserver(atom
, FormIdUpdated
, this, false);
1972 void nsGenericHTMLFormElement::RemoveFormIdObserver() {
1973 DocumentOrShadowRoot
* docOrShadow
= GetUncomposedDocOrConnectedShadowRoot();
1978 nsAutoString formId
;
1979 GetAttr(kNameSpaceID_None
, nsGkAtoms::form
, formId
);
1980 NS_ASSERTION(!formId
.IsEmpty(),
1981 "@form value should not be the empty string!");
1982 RefPtr
<nsAtom
> atom
= NS_Atomize(formId
);
1984 docOrShadow
->RemoveIDTargetObserver(atom
, FormIdUpdated
, this, false);
1988 bool nsGenericHTMLFormElement::FormIdUpdated(Element
* aOldElement
,
1989 Element
* aNewElement
,
1991 nsGenericHTMLFormElement
* element
=
1992 static_cast<nsGenericHTMLFormElement
*>(aData
);
1994 NS_ASSERTION(element
->IsHTMLElement(), "aData should be an HTML element");
1996 element
->UpdateFormOwner(false, aNewElement
);
2001 bool nsGenericHTMLFormElement::IsElementDisabledForEvents(WidgetEvent
* aEvent
,
2005 // Allow dispatch of CustomEvent and untrusted Events.
2006 if (!aEvent
->IsTrusted()) {
2010 switch (aEvent
->mMessage
) {
2011 case eAnimationStart
:
2013 case eAnimationIteration
:
2014 case eAnimationCancel
:
2025 case eTransitionCancel
:
2026 case eTransitionEnd
:
2027 case eTransitionRun
:
2028 case eTransitionStart
:
2030 case eLegacyMouseLineOrPageScroll
:
2031 case eLegacyMousePixelScroll
:
2037 // FIXME(emilio): This poking at the style of the frame is slightly bogus
2038 // unless we flush before every event, which we don't really want to do.
2039 if (aFrame
&& aFrame
->StyleUI()->mUserInput
== StyleUserInput::None
) {
2043 return IsDisabled();
2046 void nsGenericHTMLFormElement::UpdateFormOwner(bool aBindToTree
,
2047 Element
* aFormIdElement
) {
2048 MOZ_ASSERT(!aBindToTree
|| !aFormIdElement
,
2049 "aFormIdElement shouldn't be set if aBindToTree is true!");
2051 bool needStateUpdate
= false;
2053 needStateUpdate
= mForm
&& mForm
->IsDefaultSubmitElement(this);
2054 ClearForm(true, false);
2057 HTMLFormElement
* oldForm
= mForm
;
2060 // If @form is set, we have to use that to find the form.
2061 nsAutoString formId
;
2062 if (GetAttr(kNameSpaceID_None
, nsGkAtoms::form
, formId
)) {
2063 if (!formId
.IsEmpty()) {
2064 Element
* element
= nullptr;
2067 element
= AddFormIdObserver();
2069 element
= aFormIdElement
;
2072 NS_ASSERTION(!IsInComposedDoc() ||
2073 element
== GetUncomposedDocOrConnectedShadowRoot()
2074 ->GetElementById(formId
),
2075 "element should be equals to the current element "
2076 "associated with the id in @form!");
2078 if (element
&& element
->IsHTMLElement(nsGkAtoms::form
) &&
2079 nsContentUtils::IsInSameAnonymousTree(this, element
)) {
2080 SetForm(static_cast<HTMLFormElement
*>(element
), aBindToTree
);
2084 // We now have a parent, so we may have picked up an ancestor form. Search
2085 // for it. Note that if mForm is already set we don't want to do this,
2086 // because that means someone (probably the content sink) has already set
2087 // it to the right value. Also note that even if being bound here didn't
2088 // change our parent, we still need to search, since our parent chain
2089 // probably changed _somewhere_.
2090 SetForm(FindAncestorForm(), aBindToTree
);
2094 if (mForm
&& !HasFlag(ADDED_TO_FORM
)) {
2095 // Now we need to add ourselves to the form
2096 nsAutoString nameVal
, idVal
;
2097 GetAttr(kNameSpaceID_None
, nsGkAtoms::name
, nameVal
);
2098 GetAttr(kNameSpaceID_None
, nsGkAtoms::id
, idVal
);
2100 SetFlags(ADDED_TO_FORM
);
2102 // Notify only if we just found this mForm.
2103 mForm
->AddElement(this, true, oldForm
== nullptr);
2105 if (!nameVal
.IsEmpty()) {
2106 mForm
->AddElementToTable(this, nameVal
);
2109 if (!idVal
.IsEmpty()) {
2110 mForm
->AddElementToTable(this, idVal
);
2114 if (mForm
!= oldForm
|| needStateUpdate
) {
2119 void nsGenericHTMLFormElement::UpdateFieldSet(bool aNotify
) {
2120 nsIContent
* parent
= nullptr;
2121 nsIContent
* prev
= nullptr;
2123 // Don't walk out of anonymous subtrees. Note the similar code in
2124 // nsGenericHTMLElement::FindAncestorForm.
2125 nsIContent
* bindingParent
= GetBindingParent();
2127 for (parent
= GetParent(); parent
&& parent
!= bindingParent
;
2128 prev
= parent
, parent
= parent
->GetParent()) {
2129 HTMLFieldSetElement
* fieldset
= HTMLFieldSetElement::FromNode(parent
);
2130 if (fieldset
&& (!prev
|| fieldset
->GetFirstLegend() != prev
)) {
2131 if (mFieldSet
== fieldset
) {
2132 // We already have the right fieldset;
2137 mFieldSet
->RemoveElement(this);
2139 mFieldSet
= fieldset
;
2140 fieldset
->AddElement(this);
2142 // The disabled state may have changed
2143 FieldSetDisabledChanged(aNotify
);
2148 // No fieldset found.
2150 mFieldSet
->RemoveElement(this);
2151 mFieldSet
= nullptr;
2152 // The disabled state may have changed
2153 FieldSetDisabledChanged(aNotify
);
2157 void nsGenericHTMLFormElement::UpdateDisabledState(bool aNotify
) {
2158 if (!CanBeDisabled()) {
2162 bool isDisabled
= HasAttr(kNameSpaceID_None
, nsGkAtoms::disabled
);
2163 if (!isDisabled
&& mFieldSet
) {
2164 isDisabled
= mFieldSet
->IsDisabled();
2167 EventStates disabledStates
;
2169 disabledStates
|= NS_EVENT_STATE_DISABLED
;
2171 disabledStates
|= NS_EVENT_STATE_ENABLED
;
2174 EventStates oldDisabledStates
= State() & DISABLED_STATES
;
2175 EventStates changedStates
= disabledStates
^ oldDisabledStates
;
2177 if (!changedStates
.IsEmpty()) {
2178 ToggleStates(changedStates
, aNotify
);
2182 void nsGenericHTMLFormElement::UpdateRequiredState(bool aIsRequired
,
2185 int32_t type
= ControlType();
2187 MOZ_ASSERT((type
& NS_FORM_INPUT_ELEMENT
) || type
== NS_FORM_SELECT
||
2188 type
== NS_FORM_TEXTAREA
,
2189 "This should be called only on types that @required applies");
2192 HTMLInputElement
* input
= HTMLInputElement::FromNode(this);
2195 input
->DoesRequiredApply(),
2196 "This should be called only on input types that @required applies");
2200 EventStates requiredStates
;
2202 requiredStates
|= NS_EVENT_STATE_REQUIRED
;
2204 requiredStates
|= NS_EVENT_STATE_OPTIONAL
;
2207 EventStates oldRequiredStates
= State() & REQUIRED_STATES
;
2208 EventStates changedStates
= requiredStates
^ oldRequiredStates
;
2210 if (!changedStates
.IsEmpty()) {
2211 ToggleStates(changedStates
, aNotify
);
2215 void nsGenericHTMLFormElement::FieldSetDisabledChanged(bool aNotify
) {
2216 UpdateDisabledState(aNotify
);
2219 bool nsGenericHTMLFormElement::IsLabelable() const {
2220 uint32_t type
= ControlType();
2221 return (type
& NS_FORM_INPUT_ELEMENT
&& type
!= NS_FORM_INPUT_HIDDEN
) ||
2222 type
& NS_FORM_BUTTON_ELEMENT
|| type
== NS_FORM_OUTPUT
||
2223 type
== NS_FORM_SELECT
|| type
== NS_FORM_TEXTAREA
;
2226 void nsGenericHTMLFormElement::GetFormAction(nsString
& aValue
) {
2227 uint32_t type
= ControlType();
2228 if (!(type
& NS_FORM_INPUT_ELEMENT
) && !(type
& NS_FORM_BUTTON_ELEMENT
)) {
2232 if (!GetAttr(kNameSpaceID_None
, nsGkAtoms::formaction
, aValue
) ||
2234 Document
* document
= OwnerDoc();
2235 nsIURI
* docURI
= document
->GetDocumentURI();
2238 nsresult rv
= docURI
->GetSpec(spec
);
2239 if (NS_FAILED(rv
)) {
2243 CopyUTF8toUTF16(spec
, aValue
);
2246 GetURIAttr(nsGkAtoms::formaction
, nullptr, aValue
);
2250 //----------------------------------------------------------------------
2252 void nsGenericHTMLElement::Click(CallerType aCallerType
) {
2253 if (IsDisabled() || HandlingClick()) {
2257 // Strong in case the event kills it
2258 nsCOMPtr
<Document
> doc
= GetComposedDoc();
2260 RefPtr
<nsPresContext
> context
;
2262 context
= doc
->GetPresContext();
2267 // Mark this event trusted if Click() is called from system code.
2268 WidgetMouseEvent
event(aCallerType
== CallerType::System
, eMouseClick
,
2269 nullptr, WidgetMouseEvent::eReal
);
2270 event
.mFlags
.mIsPositionless
= true;
2271 event
.mInputSource
= MouseEvent_Binding::MOZ_SOURCE_UNKNOWN
;
2273 EventDispatcher::Dispatch(static_cast<nsIContent
*>(this), context
, &event
);
2275 ClearHandlingClick();
2278 bool nsGenericHTMLElement::IsHTMLFocusable(bool aWithMouse
, bool* aIsFocusable
,
2279 int32_t* aTabIndex
) {
2280 Document
* doc
= GetComposedDoc();
2281 if (!doc
|| doc
->HasFlag(NODE_IS_EDITABLE
)) {
2282 // In designMode documents we only allow focusing the document.
2287 *aIsFocusable
= false;
2292 int32_t tabIndex
= TabIndex();
2293 bool disabled
= false;
2294 bool disallowOverridingFocusability
= true;
2296 if (IsEditableRoot()) {
2297 // Editable roots should always be focusable.
2298 disallowOverridingFocusability
= true;
2300 // Ignore the disabled attribute in editable contentEditable/designMode
2302 if (!HasAttr(kNameSpaceID_None
, nsGkAtoms::tabindex
)) {
2303 // The default value for tabindex should be 0 for editable
2304 // contentEditable roots.
2308 disallowOverridingFocusability
= false;
2310 // Just check for disabled attribute on form controls
2311 disabled
= IsDisabled();
2318 *aTabIndex
= tabIndex
;
2321 // If a tabindex is specified at all, or the default tabindex is 0, we're
2325 (!disabled
&& HasAttr(kNameSpaceID_None
, nsGkAtoms::tabindex
)));
2327 return disallowOverridingFocusability
;
2330 void nsGenericHTMLElement::RegUnRegAccessKey(bool aDoReg
) {
2331 // first check to see if we have an access key
2332 nsAutoString accessKey
;
2333 GetAttr(kNameSpaceID_None
, nsGkAtoms::accesskey
, accessKey
);
2334 if (accessKey
.IsEmpty()) {
2338 // We have an access key, so get the ESM from the pres context.
2339 nsPresContext
* presContext
= GetPresContext(eForUncomposedDoc
);
2342 EventStateManager
* esm
= presContext
->EventStateManager();
2344 // Register or unregister as appropriate.
2346 esm
->RegisterAccessKey(this, (uint32_t)accessKey
.First());
2348 esm
->UnregisterAccessKey(this, (uint32_t)accessKey
.First());
2353 bool nsGenericHTMLElement::PerformAccesskey(bool aKeyCausesActivation
,
2354 bool aIsTrustedEvent
) {
2355 nsPresContext
* presContext
= GetPresContext(eForUncomposedDoc
);
2360 // It's hard to say what HTML4 wants us to do in all cases.
2361 bool focused
= true;
2362 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
2364 fm
->SetFocus(this, nsIFocusManager::FLAG_BYKEY
|
2365 nsIFocusManager::FLAG_BYELEMENTFOCUS
);
2367 // Return true if the element became the current focus within its window.
2368 nsPIDOMWindowOuter
* window
= OwnerDoc()->GetWindow();
2369 focused
= (window
&& window
->GetFocusedElement());
2372 if (aKeyCausesActivation
) {
2373 // Click on it if the users prefs indicate to do so.
2374 AutoPopupStatePusher
popupStatePusher(
2375 aIsTrustedEvent
? PopupBlocker::openAllowed
: PopupBlocker::openAbused
);
2376 DispatchSimulatedClick(this, aIsTrustedEvent
, presContext
);
2382 nsresult
nsGenericHTMLElement::DispatchSimulatedClick(
2383 nsGenericHTMLElement
* aElement
, bool aIsTrusted
,
2384 nsPresContext
* aPresContext
) {
2385 WidgetMouseEvent
event(aIsTrusted
, eMouseClick
, nullptr,
2386 WidgetMouseEvent::eReal
);
2387 event
.mInputSource
= MouseEvent_Binding::MOZ_SOURCE_KEYBOARD
;
2388 event
.mFlags
.mIsPositionless
= true;
2389 return EventDispatcher::Dispatch(ToSupports(aElement
), aPresContext
, &event
);
2392 already_AddRefed
<TextEditor
> nsGenericHTMLElement::GetAssociatedEditor() {
2393 // If contenteditable is ever implemented, it might need to do something
2396 RefPtr
<TextEditor
> textEditor
= GetTextEditorInternal();
2397 return textEditor
.forget();
2401 void nsGenericHTMLElement::SyncEditorsOnSubtree(nsIContent
* content
) {
2402 /* Sync this node */
2403 nsGenericHTMLElement
* element
= FromNode(content
);
2405 RefPtr
<TextEditor
> textEditor
= element
->GetAssociatedEditor();
2407 textEditor
->SyncRealTimeSpell();
2411 /* Sync all children */
2412 for (nsIContent
* child
= content
->GetFirstChild(); child
;
2413 child
= child
->GetNextSibling()) {
2414 SyncEditorsOnSubtree(child
);
2418 void nsGenericHTMLElement::RecompileScriptEventListeners() {
2419 int32_t i
, count
= mAttrs
.AttrCount();
2420 for (i
= 0; i
< count
; ++i
) {
2421 const nsAttrName
* name
= mAttrs
.AttrNameAt(i
);
2423 // Eventlistenener-attributes are always in the null namespace
2424 if (!name
->IsAtom()) {
2428 nsAtom
* attr
= name
->Atom();
2429 if (!IsEventAttributeName(attr
)) {
2434 GetAttr(kNameSpaceID_None
, attr
, value
);
2435 SetEventHandler(attr
, value
, true);
2439 bool nsGenericHTMLElement::IsEditableRoot() const {
2440 Document
* document
= GetComposedDoc();
2445 if (document
->HasFlag(NODE_IS_EDITABLE
)) {
2449 if (GetContentEditableValue() != eTrue
) {
2453 nsIContent
* parent
= GetParent();
2455 return !parent
|| !parent
->HasFlag(NODE_IS_EDITABLE
);
2458 static void MakeContentDescendantsEditable(nsIContent
* aContent
,
2459 Document
* aDocument
) {
2460 // If aContent is not an element, we just need to update its
2461 // internal editable state and don't need to notify anyone about
2462 // that. For elements, we need to send a ContentStateChanged
2464 if (!aContent
->IsElement()) {
2465 aContent
->UpdateEditableState(false);
2469 Element
* element
= aContent
->AsElement();
2471 element
->UpdateEditableState(true);
2473 for (nsIContent
* child
= aContent
->GetFirstChild(); child
;
2474 child
= child
->GetNextSibling()) {
2475 if (!child
->IsElement() ||
2476 !child
->AsElement()->HasAttr(kNameSpaceID_None
,
2477 nsGkAtoms::contenteditable
)) {
2478 MakeContentDescendantsEditable(child
, aDocument
);
2483 void nsGenericHTMLElement::ChangeEditableState(int32_t aChange
) {
2484 Document
* document
= GetComposedDoc();
2489 Document::EditingState previousEditingState
= Document::EditingState::eOff
;
2491 document
->ChangeContentEditableCount(this, aChange
);
2492 previousEditingState
= document
->GetEditingState();
2495 if (document
->HasFlag(NODE_IS_EDITABLE
)) {
2499 // MakeContentDescendantsEditable is going to call ContentStateChanged for
2500 // this element and all descendants if editable state has changed.
2501 // We might as well wrap it all in one script blocker.
2502 nsAutoScriptBlocker scriptBlocker
;
2503 MakeContentDescendantsEditable(this, document
);
2505 // If the document already had contenteditable and JS adds new
2506 // contenteditable, that might cause changing editing host to current editing
2507 // host's ancestor. In such case, HTMLEditor needs to know that
2508 // synchronously to update selection limitter.
2509 if (document
&& aChange
> 0 &&
2510 previousEditingState
== Document::EditingState::eContentEditable
) {
2511 if (HTMLEditor
* htmlEditor
=
2512 nsContentUtils::GetHTMLEditor(document
->GetPresContext())) {
2513 htmlEditor
->NotifyEditingHostMaybeChanged();
2518 //----------------------------------------------------------------------
2520 nsGenericHTMLFormElementWithState::nsGenericHTMLFormElementWithState(
2521 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
,
2522 FromParser aFromParser
, uint8_t aType
)
2523 : nsGenericHTMLFormElement(std::move(aNodeInfo
), aType
),
2524 mControlNumber(!!(aFromParser
& FROM_PARSER_NETWORK
)
2525 ? OwnerDoc()->GetNextControlNumber()
2527 mStateKey
.SetIsVoid(true);
2530 void nsGenericHTMLFormElementWithState::GenerateStateKey() {
2531 // Keep the key if already computed
2532 if (!mStateKey
.IsVoid()) {
2536 Document
* doc
= GetUncomposedDoc();
2538 mStateKey
.Truncate();
2542 // Generate the state key
2543 nsContentUtils::GenerateStateKey(this, doc
, mStateKey
);
2545 // If the state key is blank, this is anonymous content or for whatever
2546 // reason we are not supposed to save/restore state: keep it as such.
2547 if (!mStateKey
.IsEmpty()) {
2548 // Add something unique to content so layout doesn't muck us up.
2553 PresState
* nsGenericHTMLFormElementWithState::GetPrimaryPresState() {
2554 if (mStateKey
.IsEmpty()) {
2558 nsCOMPtr
<nsILayoutHistoryState
> history
= GetLayoutHistory(false);
2564 // Get the pres state for this key, if it doesn't exist, create one.
2565 PresState
* result
= history
->GetState(mStateKey
);
2567 UniquePtr
<PresState
> newState
= NewPresState();
2568 result
= newState
.get();
2569 history
->AddState(mStateKey
, std::move(newState
));
2575 already_AddRefed
<nsILayoutHistoryState
>
2576 nsGenericHTMLFormElementWithState::GetLayoutHistory(bool aRead
) {
2577 nsCOMPtr
<Document
> doc
= GetUncomposedDoc();
2585 nsCOMPtr
<nsILayoutHistoryState
> history
= doc
->GetLayoutHistoryState();
2590 if (aRead
&& !history
->HasStates()) {
2594 return history
.forget();
2597 bool nsGenericHTMLFormElementWithState::RestoreFormControlState() {
2598 MOZ_ASSERT(!mStateKey
.IsVoid(),
2599 "GenerateStateKey must already have been called");
2601 if (mStateKey
.IsEmpty()) {
2605 nsCOMPtr
<nsILayoutHistoryState
> history
= GetLayoutHistory(true);
2610 // Get the pres state for this key
2611 PresState
* state
= history
->GetState(mStateKey
);
2613 bool result
= RestoreState(state
);
2614 history
->RemoveState(mStateKey
);
2621 void nsGenericHTMLFormElementWithState::NodeInfoChanged(Document
* aOldDoc
) {
2622 nsGenericHTMLElement::NodeInfoChanged(aOldDoc
);
2624 // We need to regenerate the state key now we're in a new document. Clearing
2625 // mControlNumber means we stop considering this control to be parser
2626 // inserted, and we'll generate a state key based on its position in the
2627 // document rather than the order it was inserted into the document.
2628 mControlNumber
= -1;
2629 mStateKey
.SetIsVoid(true);
2632 nsSize
nsGenericHTMLElement::GetWidthHeightForImage(
2633 RefPtr
<imgRequestProxy
>& aImageRequest
) {
2636 nsIFrame
* frame
= GetPrimaryFrame(FlushType::Layout
);
2639 size
= frame
->GetContentRect().Size();
2641 size
.width
= nsPresContext::AppUnitsToIntCSSPixels(size
.width
);
2642 size
.height
= nsPresContext::AppUnitsToIntCSSPixels(size
.height
);
2644 const nsAttrValue
* value
;
2645 nsCOMPtr
<imgIContainer
> image
;
2646 if (aImageRequest
) {
2647 aImageRequest
->GetImage(getter_AddRefs(image
));
2650 if ((value
= GetParsedAttr(nsGkAtoms::width
)) &&
2651 value
->Type() == nsAttrValue::eInteger
) {
2652 size
.width
= value
->GetIntegerValue();
2654 image
->GetWidth(&size
.width
);
2657 if ((value
= GetParsedAttr(nsGkAtoms::height
)) &&
2658 value
->Type() == nsAttrValue::eInteger
) {
2659 size
.height
= value
->GetIntegerValue();
2661 image
->GetHeight(&size
.height
);
2665 NS_ASSERTION(size
.width
>= 0, "negative width");
2666 NS_ASSERTION(size
.height
>= 0, "negative height");
2670 bool nsGenericHTMLElement::IsEventAttributeNameInternal(nsAtom
* aName
) {
2671 return nsContentUtils::IsEventAttributeName(aName
, EventNameType_HTML
);
2675 * Construct a URI from a string, as an element.src attribute
2676 * would be set to. Helper for the media elements.
2678 nsresult
nsGenericHTMLElement::NewURIFromString(const nsAString
& aURISpec
,
2680 NS_ENSURE_ARG_POINTER(aURI
);
2684 nsCOMPtr
<Document
> doc
= OwnerDoc();
2687 nsContentUtils::NewURIWithDocumentCharset(aURI
, aURISpec
, doc
,
2689 NS_ENSURE_SUCCESS(rv
, rv
);
2692 if (aURISpec
.IsEmpty() && doc
->GetDocumentURI() &&
2693 NS_SUCCEEDED(doc
->GetDocumentURI()->Equals(*aURI
, &equal
)) && equal
) {
2694 // Assume an element can't point to a fragment of its embedding
2695 // document. Fail here instead of returning the recursive URI
2696 // and waiting for the subsequent load to fail.
2698 return NS_ERROR_DOM_INVALID_STATE_ERR
;
2704 void nsGenericHTMLElement::GetInnerText(mozilla::dom::DOMString
& aValue
,
2705 mozilla::ErrorResult
& aError
) {
2706 // innerText depends on layout. For example, white space processing is
2707 // something that happens during reflow and which must be reflected by
2708 // innerText. So for:
2710 // <div style="white-space:normal"> A B C </div>
2712 // innerText should give "A B C".
2714 // The approach taken here to avoid the expense of reflow is to flush style
2715 // and then see whether it's necessary to flush layout afterwards. Flushing
2716 // layout can be skipped if we can detect that the element or its descendants
2719 // Obtain the composed doc to handle elements in Shadow DOM.
2720 Document
* doc
= GetComposedDoc();
2722 doc
->FlushPendingNotifications(FlushType::Style
);
2725 // Elements with `display: content` will not have a frame. To handle Shadow
2726 // DOM, walk the flattened tree looking for parent frame.
2727 nsIFrame
* frame
= GetPrimaryFrame();
2728 if (IsDisplayContents()) {
2729 for (Element
* parent
= GetFlattenedTreeParentElement(); parent
;
2730 parent
= parent
->GetFlattenedTreeParentElement()) {
2731 frame
= parent
->GetPrimaryFrame();
2738 // Check for dirty reflow roots in the subtree from targetFrame; this requires
2740 bool dirty
= frame
&& frame
->PresShell()->FrameIsAncestorOfDirtyRoot(frame
);
2742 // The way we do that is by checking whether the element has either of the two
2743 // dirty bits (NS_FRAME_IS_DIRTY or NS_FRAME_HAS_DIRTY_DESCENDANTS) or if any
2744 // ancestor has NS_FRAME_IS_DIRTY. We need to check for NS_FRAME_IS_DIRTY on
2745 // ancestors since that is something that implies NS_FRAME_IS_DIRTY on all
2747 dirty
|= frame
&& frame
->HasAnyStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
2748 while (!dirty
&& frame
) {
2749 dirty
|= frame
->HasAnyStateBits(NS_FRAME_IS_DIRTY
);
2750 frame
= frame
->GetInFlowParent();
2753 // Flush layout if we determined a reflow is required.
2755 doc
->FlushPendingNotifications(FlushType::Layout
);
2758 if (!IsRendered()) {
2759 GetTextContentInternal(aValue
, aError
);
2761 nsRange::GetInnerTextNoFlush(aValue
, aError
, this);
2765 void nsGenericHTMLElement::SetInnerText(const nsAString
& aValue
) {
2766 // Batch possible DOMSubtreeModified events.
2767 mozAutoSubtreeModified
subtree(OwnerDoc(), nullptr);
2768 FireNodeRemovedForChildren();
2770 // Might as well stick a batch around this since we're performing several
2772 mozAutoDocUpdate
updateBatch(GetComposedDoc(), true);
2773 nsAutoMutationBatch mb
;
2775 mb
.Init(this, true, false);
2777 while (HasChildren()) {
2778 RemoveChildNode(nsINode::GetFirstChild(), true);
2784 const char16_t
* s
= aValue
.BeginReading();
2785 const char16_t
* end
= aValue
.EndReading();
2787 if (s
!= end
&& *s
== '\r' && s
+ 1 != end
&& s
[1] == '\n') {
2788 // a \r\n pair should only generate one <br>, so just skip the \r
2791 if (s
== end
|| *s
== '\r' || *s
== '\n') {
2792 if (!str
.IsEmpty()) {
2793 RefPtr
<nsTextNode
> textContent
=
2794 new nsTextNode(NodeInfo()->NodeInfoManager());
2795 textContent
->SetText(str
, true);
2796 AppendChildTo(textContent
, true);
2802 RefPtr
<mozilla::dom::NodeInfo
> ni
=
2803 NodeInfo()->NodeInfoManager()->GetNodeInfo(
2804 nsGkAtoms::br
, nullptr, kNameSpaceID_XHTML
, ELEMENT_NODE
);
2805 RefPtr
<HTMLBRElement
> br
= new HTMLBRElement(ni
.forget());
2806 AppendChildTo(br
, true);