Bug 1795723 - Unified extensions UI should support High Contrast Mode. r=ayeddi,deskt...
[gecko.git] / dom / html / HTMLBodyElement.cpp
blob55dcc539aea6cb2e5c0ba80ec52c4288ab92cc18
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 "HTMLBodyElement.h"
8 #include "mozilla/dom/BindContext.h"
9 #include "mozilla/dom/HTMLBodyElementBinding.h"
10 #include "mozilla/EditorBase.h"
11 #include "mozilla/MappedDeclarations.h"
12 #include "mozilla/HTMLEditor.h"
13 #include "mozilla/TextEditor.h"
14 #include "nsAttrValueInlines.h"
15 #include "nsGkAtoms.h"
16 #include "nsStyleConsts.h"
17 #include "nsPresContext.h"
18 #include "mozilla/dom/Document.h"
19 #include "DocumentInlines.h"
20 #include "nsDocShell.h"
21 #include "nsHTMLStyleSheet.h"
22 #include "nsMappedAttributes.h"
23 #include "nsIDocShell.h"
24 #include "nsGlobalWindow.h"
26 NS_IMPL_NS_NEW_HTML_ELEMENT(Body)
28 namespace mozilla::dom {
30 //----------------------------------------------------------------------
32 HTMLBodyElement::~HTMLBodyElement() = default;
34 JSObject* HTMLBodyElement::WrapNode(JSContext* aCx,
35 JS::Handle<JSObject*> aGivenProto) {
36 return HTMLBodyElement_Binding::Wrap(aCx, this, aGivenProto);
39 NS_IMPL_ELEMENT_CLONE(HTMLBodyElement)
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);
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);
61 return nsGenericHTMLElement::ParseBackgroundAttribute(
62 aNamespaceID, aAttribute, aValue, aResult) ||
63 nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
64 aMaybeScriptedPrincipal, aResult);
67 void HTMLBodyElement::MapAttributesIntoRule(
68 const nsMappedAttributes* aAttributes, MappedDeclarations& aDecls) {
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)
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;
84 const nsAttrValue* value;
85 // if marginwidth/marginheight are set, reflect them as 'margin'
86 value = aAttributes->GetAttr(nsGkAtoms::marginwidth);
87 if (value && value->Type() == nsAttrValue::eInteger) {
88 bodyMarginWidth = value->GetIntegerValue();
89 if (bodyMarginWidth < 0) {
90 bodyMarginWidth = 0;
92 aDecls.SetPixelValueIfUnset(eCSSProperty_margin_left,
93 (float)bodyMarginWidth);
94 aDecls.SetPixelValueIfUnset(eCSSProperty_margin_right,
95 (float)bodyMarginWidth);
98 value = aAttributes->GetAttr(nsGkAtoms::marginheight);
99 if (value && value->Type() == nsAttrValue::eInteger) {
100 bodyMarginHeight = value->GetIntegerValue();
101 if (bodyMarginHeight < 0) {
102 bodyMarginHeight = 0;
104 aDecls.SetPixelValueIfUnset(eCSSProperty_margin_top,
105 (float)bodyMarginHeight);
106 aDecls.SetPixelValueIfUnset(eCSSProperty_margin_bottom,
107 (float)bodyMarginHeight);
110 // topmargin (IE-attribute)
111 if (bodyMarginHeight == -1) {
112 value = aAttributes->GetAttr(nsGkAtoms::topmargin);
113 if (value && value->Type() == nsAttrValue::eInteger) {
114 bodyTopMargin = value->GetIntegerValue();
115 if (bodyTopMargin < 0) {
116 bodyTopMargin = 0;
118 aDecls.SetPixelValueIfUnset(eCSSProperty_margin_top,
119 (float)bodyTopMargin);
122 // bottommargin (IE-attribute)
124 if (bodyMarginHeight == -1) {
125 value = aAttributes->GetAttr(nsGkAtoms::bottommargin);
126 if (value && value->Type() == nsAttrValue::eInteger) {
127 bodyBottomMargin = value->GetIntegerValue();
128 if (bodyBottomMargin < 0) {
129 bodyBottomMargin = 0;
131 aDecls.SetPixelValueIfUnset(eCSSProperty_margin_bottom,
132 (float)bodyBottomMargin);
136 // leftmargin (IE-attribute)
137 if (bodyMarginWidth == -1) {
138 value = aAttributes->GetAttr(nsGkAtoms::leftmargin);
139 if (value && value->Type() == nsAttrValue::eInteger) {
140 bodyLeftMargin = value->GetIntegerValue();
141 if (bodyLeftMargin < 0) {
142 bodyLeftMargin = 0;
144 aDecls.SetPixelValueIfUnset(eCSSProperty_margin_left,
145 (float)bodyLeftMargin);
148 // rightmargin (IE-attribute)
149 if (bodyMarginWidth == -1) {
150 value = aAttributes->GetAttr(nsGkAtoms::rightmargin);
151 if (value && value->Type() == nsAttrValue::eInteger) {
152 bodyRightMargin = value->GetIntegerValue();
153 if (bodyRightMargin < 0) {
154 bodyRightMargin = 0;
156 aDecls.SetPixelValueIfUnset(eCSSProperty_margin_right,
157 (float)bodyRightMargin);
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(aDecls.Document()->GetDocShell())) {
165 CSSIntSize margins = ds->GetFrameMargins();
166 int32_t frameMarginWidth = margins.width;
167 int32_t frameMarginHeight = margins.height;
169 if (bodyMarginWidth == -1 && frameMarginWidth >= 0) {
170 if (bodyLeftMargin == -1) {
171 aDecls.SetPixelValueIfUnset(eCSSProperty_margin_left,
172 (float)frameMarginWidth);
174 if (bodyRightMargin == -1) {
175 aDecls.SetPixelValueIfUnset(eCSSProperty_margin_right,
176 (float)frameMarginWidth);
180 if (bodyMarginHeight == -1 && frameMarginHeight >= 0) {
181 if (bodyTopMargin == -1) {
182 aDecls.SetPixelValueIfUnset(eCSSProperty_margin_top,
183 (float)frameMarginHeight);
185 if (bodyBottomMargin == -1) {
186 aDecls.SetPixelValueIfUnset(eCSSProperty_margin_bottom,
187 (float)frameMarginHeight);
193 // When display if first asked for, go ahead and get our colors set up.
194 if (nsHTMLStyleSheet* styleSheet =
195 aDecls.Document()->GetAttributeStyleSheet()) {
196 nscolor color;
197 value = aAttributes->GetAttr(nsGkAtoms::link);
198 if (value && value->GetColorValue(color)) {
199 styleSheet->SetLinkColor(color);
202 value = aAttributes->GetAttr(nsGkAtoms::alink);
203 if (value && value->GetColorValue(color)) {
204 styleSheet->SetActiveLinkColor(color);
207 value = aAttributes->GetAttr(nsGkAtoms::vlink);
208 if (value && value->GetColorValue(color)) {
209 styleSheet->SetVisitedLinkColor(color);
213 if (!aDecls.PropertyIsSet(eCSSProperty_color)) {
214 // color: color
215 nscolor color;
216 value = aAttributes->GetAttr(nsGkAtoms::text);
217 if (value && value->GetColorValue(color)) {
218 aDecls.SetColorValue(eCSSProperty_color, color);
222 nsGenericHTMLElement::MapBackgroundAttributesInto(aAttributes, aDecls);
223 nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aDecls);
226 nsMapRuleToAttributesFunc HTMLBodyElement::GetAttributeMappingFunction() const {
227 return &MapAttributesIntoRule;
230 NS_IMETHODIMP_(bool)
231 HTMLBodyElement::IsAttributeMapped(const nsAtom* aAttribute) const {
232 static const MappedAttributeEntry attributes[] = {
233 {nsGkAtoms::link},
234 {nsGkAtoms::vlink},
235 {nsGkAtoms::alink},
236 {nsGkAtoms::text},
237 {nsGkAtoms::marginwidth},
238 {nsGkAtoms::marginheight},
239 {nsGkAtoms::topmargin},
240 {nsGkAtoms::rightmargin},
241 {nsGkAtoms::bottommargin},
242 {nsGkAtoms::leftmargin},
243 {nullptr},
246 static const MappedAttributeEntry* const map[] = {
247 attributes,
248 sCommonAttributeMap,
249 sBackgroundAttributeMap,
252 return FindAttributeDependence(aAttribute, map);
255 already_AddRefed<EditorBase> HTMLBodyElement::GetAssociatedEditor() {
256 MOZ_ASSERT(!GetTextEditorInternal());
258 // Make sure this is the actual body of the document
259 if (this != OwnerDoc()->GetBodyElement()) {
260 return nullptr;
263 // For designmode, try to get document's editor
264 nsPresContext* presContext = GetPresContext(eForComposedDoc);
265 if (!presContext) {
266 return nullptr;
269 nsCOMPtr<nsIDocShell> docShell = presContext->GetDocShell();
270 if (!docShell) {
271 return nullptr;
274 RefPtr<HTMLEditor> htmlEditor = docShell->GetHTMLEditor();
275 return htmlEditor.forget();
278 bool HTMLBodyElement::IsEventAttributeNameInternal(nsAtom* aName) {
279 return nsContentUtils::IsEventAttributeName(
280 aName, EventNameType_HTML | EventNameType_HTMLBodyOrFramesetOnly);
283 nsresult HTMLBodyElement::BindToTree(BindContext& aContext, nsINode& aParent) {
284 nsresult rv = nsGenericHTMLElement::BindToTree(aContext, aParent);
285 NS_ENSURE_SUCCESS(rv, rv);
286 return mAttrs.ForceMapped(this, &aContext.OwnerDoc());
289 nsresult HTMLBodyElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
290 const nsAttrValue* aValue,
291 const nsAttrValue* aOldValue,
292 nsIPrincipal* aSubjectPrincipal,
293 bool aNotify) {
294 nsresult rv = nsGenericHTMLElement::AfterSetAttr(
295 aNameSpaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify);
296 NS_ENSURE_SUCCESS(rv, rv);
297 // if the last mapped attribute was removed, don't clear the
298 // nsMappedAttributes, our style can still depend on the containing frame
299 // element
300 if (!aValue && IsAttributeMapped(aName)) {
301 nsresult rv = mAttrs.ForceMapped(this, OwnerDoc());
302 NS_ENSURE_SUCCESS(rv, rv);
305 return NS_OK;
308 #define EVENT(name_, id_, type_, \
309 struct_) /* nothing; handled by the superclass */
310 // nsGenericHTMLElement::GetOnError returns
311 // already_AddRefed<EventHandlerNonNull> while other getters return
312 // EventHandlerNonNull*, so allow passing in the type to use here.
313 #define WINDOW_EVENT_HELPER(name_, type_) \
314 type_* HTMLBodyElement::GetOn##name_() { \
315 if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) { \
316 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \
317 return globalWin->GetOn##name_(); \
319 return nullptr; \
321 void HTMLBodyElement::SetOn##name_(type_* handler) { \
322 nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow(); \
323 if (!win) { \
324 return; \
327 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \
328 return globalWin->SetOn##name_(handler); \
330 #define WINDOW_EVENT(name_, id_, type_, struct_) \
331 WINDOW_EVENT_HELPER(name_, EventHandlerNonNull)
332 #define BEFOREUNLOAD_EVENT(name_, id_, type_, struct_) \
333 WINDOW_EVENT_HELPER(name_, OnBeforeUnloadEventHandlerNonNull)
334 #include "mozilla/EventNameList.h" // IWYU pragma: keep
335 #undef BEFOREUNLOAD_EVENT
336 #undef WINDOW_EVENT
337 #undef WINDOW_EVENT_HELPER
338 #undef EVENT
340 } // namespace mozilla::dom