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 "HTMLFrameSetElement.h"
8 #include "mozilla/dom/HTMLFrameSetElementBinding.h"
9 #include "mozilla/dom/EventHandlerBinding.h"
10 #include "nsGlobalWindow.h"
11 #include "mozilla/UniquePtrExtensions.h"
12 #include "nsAttrValueOrString.h"
13
14 NS_IMPL_NS_NEW_HTML_ELEMENT(FrameSet)
15
16 namespace mozilla {
17 namespace dom {
18
~HTMLFrameSetElement()19 HTMLFrameSetElement::~HTMLFrameSetElement() {}
20
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)21 JSObject* HTMLFrameSetElement::WrapNode(JSContext* aCx,
22 JS::Handle<JSObject*> aGivenProto) {
23 return HTMLFrameSetElementBinding::Wrap(aCx, this, aGivenProto);
24 }
25
NS_IMPL_ELEMENT_CLONE(HTMLFrameSetElement)26 NS_IMPL_ELEMENT_CLONE(HTMLFrameSetElement)
27
28 nsresult HTMLFrameSetElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
29 const nsAttrValueOrString* aValue,
30 bool aNotify) {
31 /* The main goal here is to see whether the _number_ of rows or
32 * columns has changed. If it has, we need to reframe; otherwise
33 * we want to reflow.
34 * Ideally, the style hint would be changed back to reflow after the reframe
35 * has been performed. Unfortunately, however, the reframe will be performed
36 * by the call to nsNodeUtils::AttributeChanged, which occurs *after*
37 * AfterSetAttr is called, leaving us with no convenient way of changing the
38 * value back to reflow afterwards. However, nsNodeUtils::AttributeChanged is
39 * effectively the only consumer of this value, so as long as we always set
40 * the value correctly here, we should be fine.
41 */
42 mCurrentRowColHint = NS_STYLE_HINT_REFLOW;
43 if (aNamespaceID == kNameSpaceID_None) {
44 if (aName == nsGkAtoms::rows) {
45 if (aValue) {
46 int32_t oldRows = mNumRows;
47 ParseRowCol(aValue->String(), mNumRows, &mRowSpecs);
48
49 if (mNumRows != oldRows) {
50 mCurrentRowColHint = nsChangeHint_ReconstructFrame;
51 }
52 }
53 } else if (aName == nsGkAtoms::cols) {
54 if (aValue) {
55 int32_t oldCols = mNumCols;
56 ParseRowCol(aValue->String(), mNumCols, &mColSpecs);
57
58 if (mNumCols != oldCols) {
59 mCurrentRowColHint = nsChangeHint_ReconstructFrame;
60 }
61 }
62 }
63 }
64
65 return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, aValue,
66 aNotify);
67 }
68
GetRowSpec(int32_t * aNumValues,const nsFramesetSpec ** aSpecs)69 nsresult HTMLFrameSetElement::GetRowSpec(int32_t* aNumValues,
70 const nsFramesetSpec** aSpecs) {
71 NS_PRECONDITION(aNumValues, "Must have a pointer to an integer here!");
72 NS_PRECONDITION(aSpecs, "Must have a pointer to an array of nsFramesetSpecs");
73 *aNumValues = 0;
74 *aSpecs = nullptr;
75
76 if (!mRowSpecs) {
77 const nsAttrValue* value = GetParsedAttr(nsGkAtoms::rows);
78 if (value && value->Type() == nsAttrValue::eString) {
79 nsresult rv = ParseRowCol(value->GetStringValue(), mNumRows, &mRowSpecs);
80 NS_ENSURE_SUCCESS(rv, rv);
81 }
82
83 if (!mRowSpecs) { // we may not have had an attr or had an empty attr
84 mRowSpecs = MakeUnique<nsFramesetSpec[]>(1);
85 mNumRows = 1;
86 mRowSpecs[0].mUnit = eFramesetUnit_Relative;
87 mRowSpecs[0].mValue = 1;
88 }
89 }
90
91 *aSpecs = mRowSpecs.get();
92 *aNumValues = mNumRows;
93 return NS_OK;
94 }
95
GetColSpec(int32_t * aNumValues,const nsFramesetSpec ** aSpecs)96 nsresult HTMLFrameSetElement::GetColSpec(int32_t* aNumValues,
97 const nsFramesetSpec** aSpecs) {
98 NS_PRECONDITION(aNumValues, "Must have a pointer to an integer here!");
99 NS_PRECONDITION(aSpecs, "Must have a pointer to an array of nsFramesetSpecs");
100 *aNumValues = 0;
101 *aSpecs = nullptr;
102
103 if (!mColSpecs) {
104 const nsAttrValue* value = GetParsedAttr(nsGkAtoms::cols);
105 if (value && value->Type() == nsAttrValue::eString) {
106 nsresult rv = ParseRowCol(value->GetStringValue(), mNumCols, &mColSpecs);
107 NS_ENSURE_SUCCESS(rv, rv);
108 }
109
110 if (!mColSpecs) { // we may not have had an attr or had an empty attr
111 mColSpecs = MakeUnique<nsFramesetSpec[]>(1);
112 mNumCols = 1;
113 mColSpecs[0].mUnit = eFramesetUnit_Relative;
114 mColSpecs[0].mValue = 1;
115 }
116 }
117
118 *aSpecs = mColSpecs.get();
119 *aNumValues = mNumCols;
120 return NS_OK;
121 }
122
ParseAttribute(int32_t aNamespaceID,nsAtom * aAttribute,const nsAString & aValue,nsIPrincipal * aMaybeScriptedPrincipal,nsAttrValue & aResult)123 bool HTMLFrameSetElement::ParseAttribute(int32_t aNamespaceID,
124 nsAtom* aAttribute,
125 const nsAString& aValue,
126 nsIPrincipal* aMaybeScriptedPrincipal,
127 nsAttrValue& aResult) {
128 if (aNamespaceID == kNameSpaceID_None) {
129 if (aAttribute == nsGkAtoms::bordercolor) {
130 return aResult.ParseColor(aValue);
131 }
132 if (aAttribute == nsGkAtoms::frameborder) {
133 return nsGenericHTMLElement::ParseFrameborderValue(aValue, aResult);
134 }
135 if (aAttribute == nsGkAtoms::border) {
136 return aResult.ParseIntWithBounds(aValue, 0, 100);
137 }
138 }
139
140 return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
141 aMaybeScriptedPrincipal, aResult);
142 }
143
GetAttributeChangeHint(const nsAtom * aAttribute,int32_t aModType) const144 nsChangeHint HTMLFrameSetElement::GetAttributeChangeHint(
145 const nsAtom* aAttribute, int32_t aModType) const {
146 nsChangeHint retval =
147 nsGenericHTMLElement::GetAttributeChangeHint(aAttribute, aModType);
148 if (aAttribute == nsGkAtoms::rows || aAttribute == nsGkAtoms::cols) {
149 retval |= mCurrentRowColHint;
150 }
151 return retval;
152 }
153
154 /**
155 * Translate a "rows" or "cols" spec into an array of nsFramesetSpecs
156 */
ParseRowCol(const nsAString & aValue,int32_t & aNumSpecs,UniquePtr<nsFramesetSpec[]> * aSpecs)157 nsresult HTMLFrameSetElement::ParseRowCol(const nsAString& aValue,
158 int32_t& aNumSpecs,
159 UniquePtr<nsFramesetSpec[]>* aSpecs) {
160 if (aValue.IsEmpty()) {
161 aNumSpecs = 0;
162 *aSpecs = nullptr;
163 return NS_OK;
164 }
165
166 static const char16_t sAster('*');
167 static const char16_t sPercent('%');
168 static const char16_t sComma(',');
169
170 nsAutoString spec(aValue);
171 // remove whitespace (Bug 33699) and quotation marks (bug 224598)
172 // also remove leading/trailing commas (bug 31482)
173 spec.StripChars(" \n\r\t\"\'");
174 spec.Trim(",");
175
176 // Count the commas. Don't count more than X commas (bug 576447).
177 static_assert(NS_MAX_FRAMESET_SPEC_COUNT * sizeof(nsFramesetSpec) < (1 << 30),
178 "Too many frameset specs allowed to allocate");
179 int32_t commaX = spec.FindChar(sComma);
180 int32_t count = 1;
181 while (commaX != kNotFound && count < NS_MAX_FRAMESET_SPEC_COUNT) {
182 count++;
183 commaX = spec.FindChar(sComma, commaX + 1);
184 }
185
186 auto specs = MakeUniqueFallible<nsFramesetSpec[]>(count);
187 if (!specs) {
188 *aSpecs = nullptr;
189 aNumSpecs = 0;
190 return NS_ERROR_OUT_OF_MEMORY;
191 }
192
193 // Pre-grab the compat mode; we may need it later in the loop.
194 bool isInQuirks = InNavQuirksMode(OwnerDoc());
195
196 // Parse each comma separated token
197
198 int32_t start = 0;
199 int32_t specLen = spec.Length();
200
201 for (int32_t i = 0; i < count; i++) {
202 // Find our comma
203 commaX = spec.FindChar(sComma, start);
204 NS_ASSERTION(i == count - 1 || commaX != kNotFound,
205 "Failed to find comma, somehow");
206 int32_t end = (commaX == kNotFound) ? specLen : commaX;
207
208 // Note: If end == start then it means that the token has no
209 // data in it other than a terminating comma (or the end of the spec).
210 // So default to a fixed width of 0.
211 specs[i].mUnit = eFramesetUnit_Fixed;
212 specs[i].mValue = 0;
213 if (end > start) {
214 int32_t numberEnd = end;
215 char16_t ch = spec.CharAt(numberEnd - 1);
216 if (sAster == ch) {
217 specs[i].mUnit = eFramesetUnit_Relative;
218 numberEnd--;
219 } else if (sPercent == ch) {
220 specs[i].mUnit = eFramesetUnit_Percent;
221 numberEnd--;
222 // check for "*%"
223 if (numberEnd > start) {
224 ch = spec.CharAt(numberEnd - 1);
225 if (sAster == ch) {
226 specs[i].mUnit = eFramesetUnit_Relative;
227 numberEnd--;
228 }
229 }
230 }
231
232 // Translate value to an integer
233 nsAutoString token;
234 spec.Mid(token, start, numberEnd - start);
235
236 // Treat * as 1*
237 if ((eFramesetUnit_Relative == specs[i].mUnit) && (0 == token.Length())) {
238 specs[i].mValue = 1;
239 } else {
240 // Otherwise just convert to integer.
241 nsresult err;
242 specs[i].mValue = token.ToInteger(&err);
243 if (NS_FAILED(err)) {
244 specs[i].mValue = 0;
245 }
246 }
247
248 // Treat 0* as 1* in quirks mode (bug 40383)
249 if (isInQuirks) {
250 if ((eFramesetUnit_Relative == specs[i].mUnit) &&
251 (0 == specs[i].mValue)) {
252 specs[i].mValue = 1;
253 }
254 }
255
256 // Catch zero and negative frame sizes for Nav compatibility
257 // Nav resized absolute and relative frames to "1" and
258 // percent frames to an even percentage of the width
259 //
260 // if (isInQuirks && (specs[i].mValue <= 0)) {
261 // if (eFramesetUnit_Percent == specs[i].mUnit) {
262 // specs[i].mValue = 100 / count;
263 // } else {
264 // specs[i].mValue = 1;
265 // }
266 //} else {
267
268 // In standards mode, just set negative sizes to zero
269 if (specs[i].mValue < 0) {
270 specs[i].mValue = 0;
271 }
272 start = end + 1;
273 }
274 }
275
276 aNumSpecs = count;
277 // Transfer ownership to caller here
278 *aSpecs = Move(specs);
279
280 return NS_OK;
281 }
282
IsEventAttributeNameInternal(nsAtom * aName)283 bool HTMLFrameSetElement::IsEventAttributeNameInternal(nsAtom* aName) {
284 return nsContentUtils::IsEventAttributeName(
285 aName, EventNameType_HTML | EventNameType_HTMLBodyOrFramesetOnly);
286 }
287
288 #define EVENT(name_, id_, type_, struct_) /* nothing; handled by the shim */
289 // nsGenericHTMLElement::GetOnError returns
290 // already_AddRefed<EventHandlerNonNull> while other getters return
291 // EventHandlerNonNull*, so allow passing in the type to use here.
292 #define WINDOW_EVENT_HELPER(name_, type_) \
293 type_* HTMLFrameSetElement::GetOn##name_() { \
294 if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) { \
295 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \
296 return globalWin->GetOn##name_(); \
297 } \
298 return nullptr; \
299 } \
300 void HTMLFrameSetElement::SetOn##name_(type_* handler) { \
301 nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow(); \
302 if (!win) { \
303 return; \
304 } \
305 \
306 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \
307 return globalWin->SetOn##name_(handler); \
308 }
309 #define WINDOW_EVENT(name_, id_, type_, struct_) \
310 WINDOW_EVENT_HELPER(name_, EventHandlerNonNull)
311 #define BEFOREUNLOAD_EVENT(name_, id_, type_, struct_) \
312 WINDOW_EVENT_HELPER(name_, OnBeforeUnloadEventHandlerNonNull)
313 #include "mozilla/EventNameList.h" // IWYU pragma: keep
314 #undef BEFOREUNLOAD_EVENT
315 #undef WINDOW_EVENT
316 #undef WINDOW_EVENT_HELPER
317 #undef EVENT
318
319 } // namespace dom
320 } // namespace mozilla
321