HTMLBodyElement.cpp (12090B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "HTMLBodyElement.h" 8 9 #include "DocumentInlines.h" 10 #include "mozilla/AttributeStyles.h" 11 #include "mozilla/EditorBase.h" 12 #include "mozilla/HTMLEditor.h" 13 #include "mozilla/MappedDeclarationsBuilder.h" 14 #include "mozilla/TextEditor.h" 15 #include "mozilla/dom/BindContext.h" 16 #include "mozilla/dom/Document.h" 17 #include "mozilla/dom/HTMLBodyElementBinding.h" 18 #include "nsAttrValueInlines.h" 19 #include "nsDocShell.h" 20 #include "nsGkAtoms.h" 21 #include "nsGlobalWindowInner.h" 22 #include "nsIDocShell.h" 23 #include "nsPresContext.h" 24 #include "nsStyleConsts.h" 25 26 NS_IMPL_NS_NEW_HTML_ELEMENT(Body) 27 28 namespace mozilla::dom { 29 30 //---------------------------------------------------------------------- 31 32 HTMLBodyElement::~HTMLBodyElement() = default; 33 34 JSObject* HTMLBodyElement::WrapNode(JSContext* aCx, 35 JS::Handle<JSObject*> aGivenProto) { 36 return HTMLBodyElement_Binding::Wrap(aCx, this, aGivenProto); 37 } 38 39 NS_IMPL_ELEMENT_CLONE(HTMLBodyElement) 40 41 bool HTMLBodyElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute, 42 const nsAString& aValue, 43 nsIPrincipal* aMaybeScriptedPrincipal, 44 nsAttrValue& aResult) { 45 if (aNamespaceID == kNameSpaceID_None) { 46 if (aAttribute == nsGkAtoms::bgcolor || aAttribute == nsGkAtoms::text || 47 aAttribute == nsGkAtoms::link || aAttribute == nsGkAtoms::alink || 48 aAttribute == nsGkAtoms::vlink) { 49 return aResult.ParseColor(aValue); 50 } 51 if (aAttribute == nsGkAtoms::marginwidth || 52 aAttribute == nsGkAtoms::marginheight || 53 aAttribute == nsGkAtoms::topmargin || 54 aAttribute == nsGkAtoms::bottommargin || 55 aAttribute == nsGkAtoms::leftmargin || 56 aAttribute == nsGkAtoms::rightmargin) { 57 return aResult.ParseNonNegativeIntValue(aValue); 58 } 59 } 60 61 return nsGenericHTMLElement::ParseBackgroundAttribute( 62 aNamespaceID, aAttribute, aValue, aResult) || 63 nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue, 64 aMaybeScriptedPrincipal, aResult); 65 } 66 67 void HTMLBodyElement::MapAttributesIntoRule( 68 MappedDeclarationsBuilder& aBuilder) { 69 // This is the one place where we try to set the same property 70 // multiple times in presentation attributes. Servo does not support 71 // querying if a property is set (because that is O(n) behavior 72 // in ServoSpecifiedValues). Instead, we use the below values to keep 73 // track of whether we have already set a property, and if so, what value 74 // we set it to (which is used when handling margin 75 // attributes from the containing frame element) 76 77 int32_t bodyMarginWidth = -1; 78 int32_t bodyMarginHeight = -1; 79 int32_t bodyTopMargin = -1; 80 int32_t bodyBottomMargin = -1; 81 int32_t bodyLeftMargin = -1; 82 int32_t bodyRightMargin = -1; 83 84 const nsAttrValue* value; 85 // if marginwidth/marginheight are set, reflect them as 'margin' 86 value = aBuilder.GetAttr(nsGkAtoms::marginwidth); 87 if (value && value->Type() == nsAttrValue::eInteger) { 88 bodyMarginWidth = value->GetIntegerValue(); 89 if (bodyMarginWidth < 0) { 90 bodyMarginWidth = 0; 91 } 92 aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_left, 93 (float)bodyMarginWidth); 94 aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_right, 95 (float)bodyMarginWidth); 96 } 97 98 value = aBuilder.GetAttr(nsGkAtoms::marginheight); 99 if (value && value->Type() == nsAttrValue::eInteger) { 100 bodyMarginHeight = value->GetIntegerValue(); 101 if (bodyMarginHeight < 0) { 102 bodyMarginHeight = 0; 103 } 104 aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_top, 105 (float)bodyMarginHeight); 106 aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_bottom, 107 (float)bodyMarginHeight); 108 } 109 110 // topmargin (IE-attribute) 111 if (bodyMarginHeight == -1) { 112 value = aBuilder.GetAttr(nsGkAtoms::topmargin); 113 if (value && value->Type() == nsAttrValue::eInteger) { 114 bodyTopMargin = value->GetIntegerValue(); 115 if (bodyTopMargin < 0) { 116 bodyTopMargin = 0; 117 } 118 aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_top, 119 (float)bodyTopMargin); 120 } 121 } 122 // bottommargin (IE-attribute) 123 124 if (bodyMarginHeight == -1) { 125 value = aBuilder.GetAttr(nsGkAtoms::bottommargin); 126 if (value && value->Type() == nsAttrValue::eInteger) { 127 bodyBottomMargin = value->GetIntegerValue(); 128 if (bodyBottomMargin < 0) { 129 bodyBottomMargin = 0; 130 } 131 aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_bottom, 132 (float)bodyBottomMargin); 133 } 134 } 135 136 // leftmargin (IE-attribute) 137 if (bodyMarginWidth == -1) { 138 value = aBuilder.GetAttr(nsGkAtoms::leftmargin); 139 if (value && value->Type() == nsAttrValue::eInteger) { 140 bodyLeftMargin = value->GetIntegerValue(); 141 if (bodyLeftMargin < 0) { 142 bodyLeftMargin = 0; 143 } 144 aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_left, 145 (float)bodyLeftMargin); 146 } 147 } 148 // rightmargin (IE-attribute) 149 if (bodyMarginWidth == -1) { 150 value = aBuilder.GetAttr(nsGkAtoms::rightmargin); 151 if (value && value->Type() == nsAttrValue::eInteger) { 152 bodyRightMargin = value->GetIntegerValue(); 153 if (bodyRightMargin < 0) { 154 bodyRightMargin = 0; 155 } 156 aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_right, 157 (float)bodyRightMargin); 158 } 159 } 160 161 // if marginwidth or marginheight is set in the <frame> and not set in the 162 // <body> reflect them as margin in the <body> 163 if (bodyMarginWidth == -1 || bodyMarginHeight == -1) { 164 if (nsDocShell* ds = nsDocShell::Cast(aBuilder.Document().GetDocShell())) { 165 CSSIntSize margins = ds->GetFrameMargins(); 166 int32_t frameMarginWidth = margins.width; 167 int32_t frameMarginHeight = margins.height; 168 169 if (bodyMarginWidth == -1 && frameMarginWidth >= 0) { 170 if (bodyLeftMargin == -1) { 171 aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_left, 172 (float)frameMarginWidth); 173 } 174 if (bodyRightMargin == -1) { 175 aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_right, 176 (float)frameMarginWidth); 177 } 178 } 179 180 if (bodyMarginHeight == -1 && frameMarginHeight >= 0) { 181 if (bodyTopMargin == -1) { 182 aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_top, 183 (float)frameMarginHeight); 184 } 185 if (bodyBottomMargin == -1) { 186 aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_bottom, 187 (float)frameMarginHeight); 188 } 189 } 190 } 191 } 192 193 // When display if first asked for, go ahead and get our colors set up. 194 if (AttributeStyles* attrStyles = aBuilder.Document().GetAttributeStyles()) { 195 nscolor color; 196 value = aBuilder.GetAttr(nsGkAtoms::link); 197 if (value && value->GetColorValue(color)) { 198 attrStyles->SetLinkColor(color); 199 } 200 201 value = aBuilder.GetAttr(nsGkAtoms::alink); 202 if (value && value->GetColorValue(color)) { 203 attrStyles->SetActiveLinkColor(color); 204 } 205 206 value = aBuilder.GetAttr(nsGkAtoms::vlink); 207 if (value && value->GetColorValue(color)) { 208 attrStyles->SetVisitedLinkColor(color); 209 } 210 } 211 212 if (!aBuilder.PropertyIsSet(eCSSProperty_color)) { 213 // color: color 214 nscolor color; 215 value = aBuilder.GetAttr(nsGkAtoms::text); 216 if (value && value->GetColorValue(color)) { 217 aBuilder.SetColorValue(eCSSProperty_color, color); 218 } 219 } 220 221 nsGenericHTMLElement::MapBackgroundAttributesInto(aBuilder); 222 nsGenericHTMLElement::MapCommonAttributesInto(aBuilder); 223 } 224 225 nsMapRuleToAttributesFunc HTMLBodyElement::GetAttributeMappingFunction() const { 226 return &MapAttributesIntoRule; 227 } 228 229 NS_IMETHODIMP_(bool) 230 HTMLBodyElement::IsAttributeMapped(const nsAtom* aAttribute) const { 231 static const MappedAttributeEntry attributes[] = { 232 {nsGkAtoms::link}, 233 {nsGkAtoms::vlink}, 234 {nsGkAtoms::alink}, 235 {nsGkAtoms::text}, 236 {nsGkAtoms::marginwidth}, 237 {nsGkAtoms::marginheight}, 238 {nsGkAtoms::topmargin}, 239 {nsGkAtoms::rightmargin}, 240 {nsGkAtoms::bottommargin}, 241 {nsGkAtoms::leftmargin}, 242 {nullptr}, 243 }; 244 245 static const MappedAttributeEntry* const map[] = { 246 attributes, 247 sCommonAttributeMap, 248 sBackgroundAttributeMap, 249 }; 250 251 return FindAttributeDependence(aAttribute, map); 252 } 253 254 already_AddRefed<EditorBase> HTMLBodyElement::GetAssociatedEditor() { 255 MOZ_ASSERT(!GetTextEditorInternal()); 256 257 // Make sure this is the actual body of the document 258 if (this != OwnerDoc()->GetBodyElement()) { 259 return nullptr; 260 } 261 262 // For designmode, try to get document's editor 263 nsPresContext* presContext = GetPresContext(eForComposedDoc); 264 if (!presContext) { 265 return nullptr; 266 } 267 268 nsCOMPtr<nsIDocShell> docShell = presContext->GetDocShell(); 269 if (!docShell) { 270 return nullptr; 271 } 272 273 RefPtr<HTMLEditor> htmlEditor = docShell->GetHTMLEditor(); 274 return htmlEditor.forget(); 275 } 276 277 bool HTMLBodyElement::IsEventAttributeNameInternal(nsAtom* aName) { 278 return nsContentUtils::IsEventAttributeName( 279 aName, EventNameType_HTML | EventNameType_HTMLBodyOrFramesetOnly); 280 } 281 282 nsresult HTMLBodyElement::BindToTree(BindContext& aContext, nsINode& aParent) { 283 mAttrs.MarkAsPendingPresAttributeEvaluation(); 284 return nsGenericHTMLElement::BindToTree(aContext, aParent); 285 } 286 287 void HTMLBodyElement::FrameMarginsChanged() { 288 MOZ_ASSERT(IsInComposedDoc()); 289 if (IsPendingMappedAttributeEvaluation()) { 290 return; 291 } 292 if (mAttrs.MarkAsPendingPresAttributeEvaluation()) { 293 OwnerDoc()->ScheduleForPresAttrEvaluation(this); 294 } 295 } 296 297 #define EVENT(name_, id_, type_, \ 298 struct_) /* nothing; handled by the superclass */ 299 // nsGenericHTMLElement::GetOnError returns 300 // already_AddRefed<EventHandlerNonNull> while other getters return 301 // EventHandlerNonNull*, so allow passing in the type to use here. 302 #define WINDOW_EVENT_HELPER(name_, type_) \ 303 type_* HTMLBodyElement::GetOn##name_() { \ 304 if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) { \ 305 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \ 306 return globalWin->GetOn##name_(); \ 307 } \ 308 return nullptr; \ 309 } \ 310 void HTMLBodyElement::SetOn##name_(type_* handler) { \ 311 nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow(); \ 312 if (!win) { \ 313 return; \ 314 } \ 315 \ 316 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \ 317 return globalWin->SetOn##name_(handler); \ 318 } 319 #define WINDOW_EVENT(name_, id_, type_, struct_) \ 320 WINDOW_EVENT_HELPER(name_, EventHandlerNonNull) 321 #define BEFOREUNLOAD_EVENT(name_, id_, type_, struct_) \ 322 WINDOW_EVENT_HELPER(name_, OnBeforeUnloadEventHandlerNonNull) 323 #include "mozilla/EventNameList.h" // IWYU pragma: keep 324 #undef BEFOREUNLOAD_EVENT 325 #undef WINDOW_EVENT 326 #undef WINDOW_EVENT_HELPER 327 #undef EVENT 328 329 } // namespace mozilla::dom