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