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) {
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) {
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) {
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) {
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()) {
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
)) {
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
;
231 HTMLBodyElement::IsAttributeMapped(const nsAtom
* aAttribute
) const {
232 static const MappedAttributeEntry attributes
[] = {
237 {nsGkAtoms::marginwidth
},
238 {nsGkAtoms::marginheight
},
239 {nsGkAtoms::topmargin
},
240 {nsGkAtoms::rightmargin
},
241 {nsGkAtoms::bottommargin
},
242 {nsGkAtoms::leftmargin
},
246 static const MappedAttributeEntry
* const map
[] = {
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()) {
263 // For designmode, try to get document's editor
264 nsPresContext
* presContext
= GetPresContext(eForComposedDoc
);
269 nsCOMPtr
<nsIDocShell
> docShell
= presContext
->GetDocShell();
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
,
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
300 if (!aValue
&& IsAttributeMapped(aName
)) {
301 nsresult rv
= mAttrs
.ForceMapped(this, OwnerDoc());
302 NS_ENSURE_SUCCESS(rv
, rv
);
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_(); \
321 void HTMLBodyElement::SetOn##name_(type_* handler) { \
322 nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow(); \
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
337 #undef WINDOW_EVENT_HELPER
340 } // namespace mozilla::dom