/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
if (frameType == LayoutFrameType::TableCell ||
frameType == LayoutFrameType::TableWrapper) { // Per the IDL for Element, only td, th, and table are acceptable // offsetParents apart from body or positioned elements; we need to check // the content type as well as the frame type so we ignore anonymous tables // created by an element with display: table-cell with no actual table
nsIContent* content = aFrame->GetContent();
for (; parent; parent = parent->GetParent()) {
content = parent->GetContent();
// Stop at the first ancestor that is positioned. if (parent->IsAbsPosContainingBlock()) {
offsetParent = content; break;
}
// WebKit-ism: offsetParent stops at zoom changes. // See https://github.com/w3c/csswg-drafts/issues/10252 if (effectiveZoom != parent->Style()->EffectiveZoom()) {
offsetParent = content; break;
}
// Add the parent's origin to our own to get to the // right coordinate system. constbool isOffsetParent = !isPositioned && IsOffsetParent(parent); if (!isOffsetParent) {
origin += parent->GetPositionIgnoringScrolling();
}
if (content) { // If we've hit the document element, break here. if (content == docElement) { break;
}
// Break if the ancestor frame type makes it suitable as offset parent // and this element is *not* positioned or if we found the body element. if (isOffsetParent || content->IsHTMLElement(nsGkAtoms::body)) {
offsetParent = content; break;
}
}
}
if (isAbsolutelyPositioned && !offsetParent) { // If this element is absolutely positioned, but we don't have // an offset parent it means this element is an absolutely // positioned child that's not nested inside another positioned // element, in this case the element's frame's parent is the // frame for the HTML element so we fail to find the body in the // parent chain. We want the offset parent in this case to be // the body, so we just get the body element from the document. // // We use GetBodyElement() here, not GetBody(), because we don't want to // end up with framesets here.
offsetParent = aElement.GetComposedDoc()->GetBodyElement();
}
}
// Make the position relative to the padding edge. if (parent) { const nsStyleBorder* border = parent->StyleBorder();
origin.x -= border->GetComputedBorderWidth(eSideLeft);
origin.y -= border->GetComputedBorderWidth(eSideTop);
}
// Get the union of all rectangles in this and continuation frames. // It doesn't really matter what we use as aRelativeTo here, since // we only care about the size. We just have to use something non-null.
nsRect rcFrame = nsLayoutUtils::GetAllInFlowRectsUnion(frame, frame);
rcFrame.MoveTo(origin); return {Element::FromNodeOrNull(offsetParent), rcFrame};
}
bool nsGenericHTMLElement::Spellcheck() { // Has the state has been explicitly set?
nsIContent* node; for (node = this; node; node = node->GetParent()) { if (node->IsHTMLElement()) { static Element::AttrValuesArray strings[] = {nsGkAtoms::_true,
nsGkAtoms::_false, nullptr}; switch (node->AsElement()->FindAttrValueIn(
kNameSpaceID_None, nsGkAtoms::spellcheck, strings, eCaseMatters)) { case 0: // spellcheck = "true" returntrue; case 1: // spellcheck = "false" returnfalse;
}
}
}
// contenteditable/designMode are spellchecked by default if (IsEditable()) { returntrue;
}
// Is this a chrome element? if (nsContentUtils::IsChromeDoc(OwnerDoc())) { returnfalse; // Not spellchecked by default
}
// Anything else that's not a form control is not spellchecked by default const nsIFormControl* formControl = GetAsFormControl(); if (!formControl) { returnfalse; // Not spellchecked by default
}
// Is this a multiline plaintext input? auto controlType = formControl->ControlType(); if (controlType == FormControlType::Textarea) { returntrue; // Spellchecked by default
}
// Is this anything other than an input text? // Other inputs are not spellchecked. if (controlType != FormControlType::InputText) { returnfalse; // Not spellchecked by default
}
// Does the user want input text spellchecked by default? // NOTE: Do not reflect a pref value of 0 back to the DOM getter. // The web page should not know if the user has disabled spellchecking. // We'll catch this in the editor itself.
int32_t spellcheckLevel = StaticPrefs::layout_spellcheckDefault(); return spellcheckLevel == 2; // "Spellcheck multi- and single-line"
}
void nsGenericHTMLElement::UpdateEditableState(bool aNotify) { // XXX Should we do this only when in a document?
ContentEditableState state = GetContentEditableState(); if (state != ContentEditableState::Inherit) {
SetEditableFlag(IsEditableState(state));
UpdateReadOnlyState(aNotify); return;
}
nsStyledElement::UpdateEditableState(aNotify);
}
if (IsInComposedDoc()) {
RegUnRegAccessKey(true);
}
if (IsInUncomposedDoc()) { if (HasName() && CanHaveName(NodeInfo()->NameAtom())) {
aContext.OwnerDoc().AddToNameTable( this, GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
}
}
if (HasFlag(NODE_IS_EDITABLE) &&
HasContentEditableAttrTrueOrPlainTextOnly() && IsInComposedDoc()) {
aContext.OwnerDoc().ChangeContentEditableCount(this, +1);
}
// Hide any nonce from the DOM, but keep the internal value of the // nonce by copying and resetting the internal nonce value. if (HasFlag(NODE_HAS_NONCE_AND_HEADER_CSP) && IsInComposedDoc() &&
OwnerDoc()->GetBrowsingContext()) {
nsContentUtils::AddScriptRunner(NS_NewRunnableFunction( "nsGenericHTMLElement::ResetNonce::Runnable",
[self = RefPtr<nsGenericHTMLElement>(this)]() {
nsAutoString nonce;
self->GetNonce(nonce);
self->SetAttr(kNameSpaceID_None, nsGkAtoms::nonce, u""_ns, true);
self->SetNonce(nonce);
}));
}
// We need to consider a labels element is moved to another subtree // with different root, it needs to update labels list and its root // as well.
nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots(); if (slots && slots->mLabelsList) {
slots->mLabelsList->MaybeResetRoot(SubtreeRoot());
}
return rv;
}
void nsGenericHTMLElement::UnbindFromTree(UnbindContext& aContext) { if (IsInComposedDoc()) { // https://html.spec.whatwg.org/#dom-trees:hide-popover-algorithm // If removedNode's popover attribute is not in the no popover state, then // run the hide popover algorithm given removedNode, false, false, and // false. if (GetPopoverData()) {
HidePopoverWithoutRunningScript();
}
RegUnRegAccessKey(false);
}
RemoveFromNameTable();
if (HasContentEditableAttrTrueOrPlainTextOnly()) { if (Document* doc = GetComposedDoc()) {
doc->ChangeContentEditableCount(this, -1);
}
}
nsStyledElement::UnbindFromTree(aContext);
// Invalidate .labels list. It will be repopulated when used the next time.
nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots(); if (slots && slots->mLabelsList) {
slots->mLabelsList->MaybeResetRoot(SubtreeRoot());
}
}
HTMLFormElement* nsGenericHTMLElement::FindAncestorForm(
HTMLFormElement* aCurrentForm) {
NS_ASSERTION(!HasAttr(nsGkAtoms::form) || IsHTMLElement(nsGkAtoms::img), "FindAncestorForm should not be called if @form is set!"); if (IsInNativeAnonymousSubtree()) { return nullptr;
}
nsIContent* content = this; while (content) { // If the current ancestor is a form, return it as our form if (content->IsHTMLElement(nsGkAtoms::form)) { #ifdef DEBUG if (!nsContentUtils::IsInSameAnonymousTree(this, content)) { // It's possible that we started unbinding at |content| or // some ancestor of it, and |content| and |this| used to all be // anonymous. Check for this the hard way. for (nsIContent* child = this; child != content;
child = child->GetParent()) {
NS_ASSERTION(child->ComputeIndexInParentContent().isSome(), "Walked too far?");
}
} #endif returnstatic_cast<HTMLFormElement*>(content);
}
if (!content && aCurrentForm) { // We got to the root of the subtree we're in, and we're being removed // from the DOM (the only time we get into this method with a non-null // aCurrentForm). Check whether aCurrentForm is in the same subtree. If // it is, we want to return aCurrentForm, since this case means that // we're one of those inputs-in-a-table that have a hacked mForm pointer // and a subtree containing both us and the form got removed from the // DOM. if (aCurrentForm->IsInclusiveDescendantOf(prevContent)) { return aCurrentForm;
}
}
}
return nullptr;
}
bool nsGenericHTMLElement::CheckHandleEventForAnchorsPreconditions(
EventChainVisitor& aVisitor) {
MOZ_ASSERT(nsCOMPtr<Link>(do_QueryObject(this)), "should be called only when |this| implements |Link|"); // When disconnected, only <a> should navigate away per // https://html.spec.whatwg.org/#cannot-navigate return IsInComposedDoc() || IsHTMLElement(nsGkAtoms::a);
}
bool nsGenericHTMLElement::IsHTMLLink(nsIURI** aURI) const {
MOZ_ASSERT(aURI, "Must provide aURI out param");
*aURI = GetHrefURIForAnchors().take(); // We promise out param is non-null if we return true, so base rv on it return *aURI != nullptr;
}
already_AddRefed<nsIURI> nsGenericHTMLElement::GetHrefURIForAnchors() const { // This is used by the three Link implementations and // nsHTMLStyleElement.
// Get href= attribute (relative URI).
// We use the nsAttrValue's copy of the URI string to avoid copying.
nsCOMPtr<nsIURI> uri;
GetURIAttr(nsGkAtoms::href, nullptr, getter_AddRefs(uri)); return uri.forget();
}
void nsGenericHTMLElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName, const nsAttrValue* aValue, bool aNotify) { if (aNamespaceID == kNameSpaceID_None) { if (aName == nsGkAtoms::accesskey) { // Have to unregister before clearing flag. See UnregAccessKey
RegUnRegAccessKey(false); if (!aValue) {
UnsetFlags(NODE_HAS_ACCESSKEY);
}
} elseif (aName == nsGkAtoms::name) { // Have to do this before clearing flag. See RemoveFromNameTable
RemoveFromNameTable(); if (!aValue || aValue->IsEmptyString()) {
ClearHasName();
}
} elseif (aName == nsGkAtoms::contenteditable) { if (aValue) { // Set this before the attribute is set so that any subclass code that // runs before the attribute is set won't think we're missing a // contenteditable attr when we actually have one.
SetMayHaveContentEditableAttr();
}
} if (!aValue && IsEventAttributeName(aName)) { if (EventListenerManager* manager = GetExistingListenerManager()) {
manager->RemoveEventHandler(GetEventNameForAttr(aName));
}
}
}
void nsGenericHTMLElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, const nsAttrValue* aValue, const nsAttrValue* aOldValue,
nsIPrincipal* aMaybeScriptedPrincipal, bool aNotify) { if (aNamespaceID == kNameSpaceID_None) { if (IsEventAttributeName(aName) && aValue) {
MOZ_ASSERT(aValue->Type() == nsAttrValue::eString, "Expected string value for script body");
SetEventHandler(GetEventNameForAttr(aName), aValue->GetStringValue());
} elseif (aNotify && aName == nsGkAtoms::spellcheck) {
SyncEditorsOnSubtree(this);
} elseif (aName == nsGkAtoms::popover) {
nsContentUtils::AddScriptRunner(
NewRunnableMethod("nsGenericHTMLElement::AfterSetPopoverAttr", this,
&nsGenericHTMLElement::AfterSetPopoverAttr));
} elseif (aName == nsGkAtoms::popovertarget) {
ClearExplicitlySetAttrElement(aName);
} elseif (aName == nsGkAtoms::dir) { auto dir = Directionality::Ltr; // A boolean tracking whether we need to recompute our directionality. // This needs to happen after we update our internal "dir" attribute // state but before we call SetDirectionalityOnDescendants. bool recomputeDirectionality = false;
ElementState dirStates; if (aValue && aValue->Type() == nsAttrValue::eEnum) {
SetHasValidDir();
dirStates |= ElementState::HAS_DIR_ATTR; auto dirValue = Directionality(aValue->GetEnumValue()); if (dirValue == Directionality::Auto) {
dirStates |= ElementState::HAS_DIR_ATTR_LIKE_AUTO;
} else {
dir = dirValue;
SetDirectionality(dir, aNotify); if (dirValue == Directionality::Ltr) {
dirStates |= ElementState::HAS_DIR_ATTR_LTR;
} else {
MOZ_ASSERT(dirValue == Directionality::Rtl);
dirStates |= ElementState::HAS_DIR_ATTR_RTL;
}
}
} else { if (aValue) { // We have a value, just not a valid one.
dirStates |= ElementState::HAS_DIR_ATTR;
}
ClearHasValidDir(); if (NodeInfo()->Equals(nsGkAtoms::bdi)) {
dirStates |= ElementState::HAS_DIR_ATTR_LIKE_AUTO;
} else {
recomputeDirectionality = true;
}
} // Now figure out what's changed about our dir states.
ElementState oldDirStates = State() & ElementState::DIR_ATTR_STATES;
ElementState changedStates = dirStates ^ oldDirStates; if (!changedStates.IsEmpty()) {
ToggleStates(changedStates, aNotify);
} if (recomputeDirectionality) {
dir = RecomputeDirectionality(this, aNotify);
}
SetDirectionalityOnDescendants(this, dir, aNotify);
} elseif (aName == nsGkAtoms::contenteditable) { constauto IsEditableExceptInherit = [](const nsAttrValue& aValue) { return aValue.Equals(EmptyString(), eCaseMatters) ||
aValue.Equals(u"true"_ns, eIgnoreCase) ||
(StaticPrefs::
dom_element_contenteditable_plaintext_only_enabled() &&
aValue.Equals(u"plaintext-only"_ns, eIgnoreCase));
}; // FYI: Now, both HasContentEditableAttrTrueOrPlainTextOnly() and // HasContentEditableAttrFalse() return true. Therefore, we need to clear // one of them or both of them.
int32_t editableCountDelta = 0; if (aOldValue && IsEditableExceptInherit(*aOldValue)) {
editableCountDelta = -1;
ClearHasContentEditableAttrTrueOrPlainTextOnly();
} if (!aValue) {
ClearMayHaveContentEditableAttr();
} elseif (IsEditableExceptInherit(*aValue)) {
++editableCountDelta;
SetHasContentEditableAttrTrueOrPlainTextOnly();
}
ChangeEditableState(editableCountDelta);
} elseif (aName == nsGkAtoms::accesskey) { if (aValue && !aValue->Equals(u""_ns, eIgnoreCase)) {
SetFlags(NODE_HAS_ACCESSKEY);
RegUnRegAccessKey(true);
}
} elseif (aName == nsGkAtoms::inert) { if (aValue) {
AddStates(ElementState::INERT);
} else {
RemoveStates(ElementState::INERT);
}
} elseif (aName == nsGkAtoms::name) { if (aValue && !aValue->Equals(u""_ns, eIgnoreCase)) { // This may not be quite right because we can have subclass code run // before here. But in practice subclasses don't care about this flag, // and in particular selector matching does not care. Otherwise we'd // want to handle it like we handle id attributes (in PreIdMaybeChange // and PostIdMaybeChange).
SetHasName(); if (CanHaveName(NodeInfo()->NameAtom())) {
AddToNameTable(aValue->GetAtomValue());
}
}
} elseif (aName == nsGkAtoms::inputmode ||
aName == nsGkAtoms::enterkeyhint) { if (nsFocusManager::GetFocusedElementStatic() == this) { if (const nsPresContext* presContext =
GetPresContext(eForComposedDoc)) {
IMEContentObserver* observer =
IMEStateManager::GetActiveContentObserver(); if (observer && observer->IsObserving(*presContext, this)) { if (const RefPtr<EditorBase> editorBase = GetExtantEditor()) {
IMEState newState;
editorBase->GetPreferredIMEState(&newState);
OwningNonNull<nsGenericHTMLElement> kungFuDeathGrip(*this);
IMEStateManager::UpdateIMEState(
newState, kungFuDeathGrip, *editorBase,
{IMEStateManager::UpdateIMEStateOption::ForceUpdate,
IMEStateManager::UpdateIMEStateOption::
DontCommitComposition});
}
}
}
}
}
// The nonce will be copied over to an internal slot and cleared from the // Element within BindToTree to avoid CSS Selector nonce exfiltration if // the CSP list contains a header-delivered CSP. if (nsGkAtoms::nonce == aName) { if (aValue) {
SetNonce(aValue->GetStringValue()); if (OwnerDoc()->GetHasCSPDeliveredThroughHeader()) {
SetFlags(NODE_HAS_NONCE_AND_HEADER_CSP);
}
} else {
RemoveNonce();
}
}
}
EventListenerManager* nsGenericHTMLElement::GetEventListenerManagerForAttr(
nsAtom* aAttrName, bool* aDefer) { // Attributes on the body and frameset tags get set on the global object if ((mNodeInfo->Equals(nsGkAtoms::body) ||
mNodeInfo->Equals(nsGkAtoms::frameset)) && // We only forward some event attributes from body/frameset to window
(0 #define EVENT(name_, id_, type_, struct_) /* nothing */ #define FORWARDED_EVENT(name_, id_, type_, struct_) \
|| nsGkAtoms::on##name_ == aAttrName #define WINDOW_EVENT FORWARDED_EVENT #include"mozilla/EventNameList.h"// IWYU pragma: keep #undef WINDOW_EVENT #undef FORWARDED_EVENT #undef EVENT
)) {
nsPIDOMWindowInner* win;
// If we have a document, and it has a window, add the event // listener on the window (the inner window). If not, proceed as // normal. // XXXbz sXBL/XBL2 issue: should we instead use GetComposedDoc() here, // override BindToTree for those classes and munge event listeners there?
Document* document = OwnerDoc();
*aDefer = false; if ((win = document->GetInnerWindow())) {
nsCOMPtr<EventTarget> piTarget(do_QueryInterface(win));
if (aAttribute == nsGkAtoms::tabindex) { return aResult.ParseIntValue(aValue);
}
if (aAttribute == nsGkAtoms::referrerpolicy) { return ParseReferrerAttribute(aValue, aResult);
}
if (aAttribute == nsGkAtoms::name) { // Store name as an atom. name="" means that the element has no name, // not that it has an empty string as the name. if (aValue.IsEmpty()) { returnfalse;
}
aResult.ParseAtom(aValue); returntrue;
}
staticinlinevoid MapLangAttributeInto(MappedDeclarationsBuilder& aBuilder) { const nsAttrValue* langValue = aBuilder.GetAttr(nsGkAtoms::lang); if (!langValue) { return;
}
MOZ_ASSERT(langValue->Type() == nsAttrValue::eAtom);
aBuilder.SetIdentAtomValueIfUnset(eCSSProperty__x_lang,
langValue->GetAtomValue()); if (!aBuilder.PropertyIsSet(eCSSProperty_text_emphasis_position)) { const nsAtom* lang = langValue->GetAtomValue(); if (nsStyleUtil::MatchesLanguagePrefix(lang, u"zh")) {
aBuilder.SetKeywordValue(eCSSProperty_text_emphasis_position,
StyleTextEmphasisPosition::UNDER._0);
} elseif (nsStyleUtil::MatchesLanguagePrefix(lang, u"ja") ||
nsStyleUtil::MatchesLanguagePrefix(lang, u"mn")) { // This branch is currently no part of the spec. // See bug 1040668 comment 69 and comment 75.
aBuilder.SetKeywordValue(eCSSProperty_text_emphasis_position,
StyleTextEmphasisPosition::OVER._0);
}
}
}
/** * Handle attributes common to all html elements
*/ void nsGenericHTMLElement::MapCommonAttributesIntoExceptHidden(
MappedDeclarationsBuilder& aBuilder) {
MapLangAttributeInto(aBuilder);
}
void nsGenericHTMLElement::MapCommonAttributesInto(
MappedDeclarationsBuilder& aBuilder) {
MapCommonAttributesIntoExceptHidden(aBuilder); if (!aBuilder.PropertyIsSet(eCSSProperty_display)) { if (aBuilder.GetAttr(nsGkAtoms::hidden)) {
aBuilder.SetKeywordValue(eCSSProperty_display, StyleDisplay::None._0);
}
}
}
if (attrVal->Type() == nsAttrValue::eInteger) { return attrVal->GetIntegerValue();
}
if (attrVal->Type() == nsAttrValue::ePercent) { // This is a nasty hack. When we parsed the value, we stored it as an // ePercent, not eInteger, because there was a '%' after it in the string. // But the spec says to basically re-parse the string as an integer. // Luckily, we can just return the value we have stored. But // GetPercentValue() divides it by 100, so we need to multiply it back. return uint32_t(attrVal->GetPercentValue() * 100.0f);
}
if (attrVal->Type() == nsAttrValue::eDoubleValue) { return uint32_t(attrVal->GetDoubleValue());
}
// Unfortunately, the set of values that are valid dimensions is not a // superset of values that are valid unsigned ints. In particular "+100" is // not a valid dimension, but should parse as the unsigned int "100". So if // we got here and we don't have a valid dimension value, just try re-parsing // the string we have as an integer.
nsAutoString val;
attrVal->ToString(val);
nsContentUtils::ParseHTMLIntegerResultFlags result;
int32_t parsedInt = nsContentUtils::ParseHTMLInteger(val, &result); if ((result & nsContentUtils::eParseHTMLInteger_Error) || parsedInt < 0) { return aDefault;
}
return parsedInt;
}
void nsGenericHTMLElement::GetURIAttr(nsAtom* aAttr, nsAtom* aBaseAttr,
nsAString& aResult) const {
nsCOMPtr<nsIURI> uri; const nsAttrValue* attr = GetURIAttr(aAttr, aBaseAttr, getter_AddRefs(uri)); if (!attr) {
aResult.Truncate(); return;
} if (!uri) { // Just return the attr value
attr->ToString(aResult); return;
}
nsAutoCString spec;
uri->GetSpec(spec);
CopyUTF8toUTF16(spec, aResult);
}
void nsGenericHTMLElement::GetURIAttr(nsAtom* aAttr, nsAtom* aBaseAttr,
nsACString& aResult) const {
nsCOMPtr<nsIURI> uri; const nsAttrValue* attr = GetURIAttr(aAttr, aBaseAttr, getter_AddRefs(uri)); if (!attr) {
aResult.Truncate(); return;
} if (!uri) { // Just return the attr value
nsAutoString value;
attr->ToString(value);
CopyUTF16toUTF8(value, aResult); return;
}
uri->GetSpec(aResult);
}
// Don't care about return value. If it fails, we still want to // return true, and *aURI will be null.
nsContentUtils::NewURIWithDocumentCharset(aURI, attr->GetStringValue(),
OwnerDoc(), baseURI); return attr;
}
bool nsGenericHTMLElement::IsContentEditable() const { for (constauto* element : InclusiveAncestorsOfType<nsGenericHTMLElement>()) {
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.65 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.