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 "mozilla/dom/HTMLHRElement.h"
8 #include "mozilla/dom/HTMLHRElementBinding.h"
9 
10 #include "nsCSSProps.h"
11 #include "nsStyleConsts.h"
12 #include "mozilla/MappedDeclarations.h"
13 
14 NS_IMPL_NS_NEW_HTML_ELEMENT(HR)
15 
16 namespace mozilla::dom {
17 
HTMLHRElement(already_AddRefed<mozilla::dom::NodeInfo> && aNodeInfo)18 HTMLHRElement::HTMLHRElement(
19     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
20     : nsGenericHTMLElement(std::move(aNodeInfo)) {}
21 
22 HTMLHRElement::~HTMLHRElement() = default;
23 
NS_IMPL_ELEMENT_CLONE(HTMLHRElement)24 NS_IMPL_ELEMENT_CLONE(HTMLHRElement)
25 
26 bool HTMLHRElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
27                                    const nsAString& aValue,
28                                    nsIPrincipal* aMaybeScriptedPrincipal,
29                                    nsAttrValue& aResult) {
30   static const nsAttrValue::EnumTable kAlignTable[] = {
31       {"left", StyleTextAlign::Left},
32       {"right", StyleTextAlign::Right},
33       {"center", StyleTextAlign::Center},
34       {nullptr, 0}};
35 
36   if (aNamespaceID == kNameSpaceID_None) {
37     if (aAttribute == nsGkAtoms::width) {
38       return aResult.ParseHTMLDimension(aValue);
39     }
40     if (aAttribute == nsGkAtoms::size) {
41       return aResult.ParseIntWithBounds(aValue, 1, 1000);
42     }
43     if (aAttribute == nsGkAtoms::align) {
44       return aResult.ParseEnumValue(aValue, kAlignTable, false);
45     }
46     if (aAttribute == nsGkAtoms::color) {
47       return aResult.ParseColor(aValue);
48     }
49   }
50 
51   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
52                                               aMaybeScriptedPrincipal, aResult);
53 }
54 
MapAttributesIntoRule(const nsMappedAttributes * aAttributes,MappedDeclarations & aDecls)55 void HTMLHRElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
56                                           MappedDeclarations& aDecls) {
57   bool noshade = false;
58 
59   const nsAttrValue* colorValue = aAttributes->GetAttr(nsGkAtoms::color);
60   nscolor color;
61   bool colorIsSet = colorValue && colorValue->GetColorValue(color);
62 
63   if (colorIsSet) {
64     noshade = true;
65   } else {
66     noshade = !!aAttributes->GetAttr(nsGkAtoms::noshade);
67   }
68 
69   // align: enum
70   const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
71   if (value && value->Type() == nsAttrValue::eEnum) {
72     // Map align attribute into auto side margins
73     switch (StyleTextAlign(value->GetEnumValue())) {
74       case StyleTextAlign::Left:
75         aDecls.SetPixelValueIfUnset(eCSSProperty_margin_left, 0.0f);
76         aDecls.SetAutoValueIfUnset(eCSSProperty_margin_right);
77         break;
78       case StyleTextAlign::Right:
79         aDecls.SetAutoValueIfUnset(eCSSProperty_margin_left);
80         aDecls.SetPixelValueIfUnset(eCSSProperty_margin_right, 0.0f);
81         break;
82       case StyleTextAlign::Center:
83         aDecls.SetAutoValueIfUnset(eCSSProperty_margin_left);
84         aDecls.SetAutoValueIfUnset(eCSSProperty_margin_right);
85         break;
86       default:
87         MOZ_ASSERT_UNREACHABLE("Unknown <hr align> value");
88         break;
89     }
90   }
91   if (!aDecls.PropertyIsSet(eCSSProperty_height)) {
92     // size: integer
93     if (noshade) {
94       // noshade case: size is set using the border
95       aDecls.SetAutoValue(eCSSProperty_height);
96     } else {
97       // normal case
98       // the height includes the top and bottom borders that are initially 1px.
99       // for size=1, html.css has a special case rule that makes this work by
100       // removing all but the top border.
101       const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::size);
102       if (value && value->Type() == nsAttrValue::eInteger) {
103         aDecls.SetPixelValue(eCSSProperty_height,
104                              (float)value->GetIntegerValue());
105       }  // else use default value from html.css
106     }
107   }
108 
109   // if not noshade, border styles are dealt with by html.css
110   if (noshade) {
111     // size: integer
112     // if a size is set, use half of it per side, otherwise, use 1px per side
113     float sizePerSide;
114     bool allSides = true;
115     value = aAttributes->GetAttr(nsGkAtoms::size);
116     if (value && value->Type() == nsAttrValue::eInteger) {
117       sizePerSide = (float)value->GetIntegerValue() / 2.0f;
118       if (sizePerSide < 1.0f) {
119         // XXX When the pixel bug is fixed, all the special casing for
120         // subpixel borders should be removed.
121         // In the meantime, this makes http://www.microsoft.com/ look right.
122         sizePerSide = 1.0f;
123         allSides = false;
124       }
125     } else {
126       sizePerSide = 1.0f;  // default to a 2px high line
127     }
128     aDecls.SetPixelValueIfUnset(eCSSProperty_border_top_width, sizePerSide);
129     if (allSides) {
130       aDecls.SetPixelValueIfUnset(eCSSProperty_border_right_width, sizePerSide);
131       aDecls.SetPixelValueIfUnset(eCSSProperty_border_bottom_width,
132                                   sizePerSide);
133       aDecls.SetPixelValueIfUnset(eCSSProperty_border_left_width, sizePerSide);
134     }
135 
136     if (!aDecls.PropertyIsSet(eCSSProperty_border_top_style))
137       aDecls.SetKeywordValue(eCSSProperty_border_top_style,
138                              StyleBorderStyle::Solid);
139     if (allSides) {
140       aDecls.SetKeywordValueIfUnset(eCSSProperty_border_right_style,
141                                     StyleBorderStyle::Solid);
142       aDecls.SetKeywordValueIfUnset(eCSSProperty_border_bottom_style,
143                                     StyleBorderStyle::Solid);
144       aDecls.SetKeywordValueIfUnset(eCSSProperty_border_left_style,
145                                     StyleBorderStyle::Solid);
146 
147       // If it would be noticeable, set the border radius to
148       // 10000px on all corners; this triggers the clamping to make
149       // circular ends.  This assumes the <hr> isn't larger than
150       // that in *both* dimensions.
151       for (const nsCSSPropertyID* props =
152                nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_radius);
153            *props != eCSSProperty_UNKNOWN; ++props) {
154         aDecls.SetPixelValueIfUnset(*props, 10000.0f);
155       }
156     }
157   }
158   // color: a color
159   // (we got the color attribute earlier)
160   if (colorIsSet) {
161     aDecls.SetColorValueIfUnset(eCSSProperty_color, color);
162   }
163 
164   nsGenericHTMLElement::MapWidthAttributeInto(aAttributes, aDecls);
165   nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aDecls);
166 }
167 
NS_IMETHODIMP_(bool)168 NS_IMETHODIMP_(bool)
169 HTMLHRElement::IsAttributeMapped(const nsAtom* aAttribute) const {
170   static const MappedAttributeEntry attributes[] = {
171       {nsGkAtoms::align}, {nsGkAtoms::width},   {nsGkAtoms::size},
172       {nsGkAtoms::color}, {nsGkAtoms::noshade}, {nullptr},
173   };
174 
175   static const MappedAttributeEntry* const map[] = {
176       attributes,
177       sCommonAttributeMap,
178   };
179 
180   return FindAttributeDependence(aAttribute, map);
181 }
182 
GetAttributeMappingFunction() const183 nsMapRuleToAttributesFunc HTMLHRElement::GetAttributeMappingFunction() const {
184   return &MapAttributesIntoRule;
185 }
186 
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)187 JSObject* HTMLHRElement::WrapNode(JSContext* aCx,
188                                   JS::Handle<JSObject*> aGivenProto) {
189   return HTMLHRElement_Binding::Wrap(aCx, this, aGivenProto);
190 }
191 
192 }  // namespace mozilla::dom
193