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 "HTMLFrameSetElement.h"
8 #include "mozilla/dom/HTMLFrameSetElementBinding.h"
9 #include "mozilla/dom/Document.h"
10 #include "mozilla/dom/EventHandlerBinding.h"
11 #include "nsGlobalWindow.h"
12 #include "mozilla/UniquePtrExtensions.h"
13 #include "nsAttrValueOrString.h"
15 NS_IMPL_NS_NEW_HTML_ELEMENT(FrameSet
)
17 namespace mozilla::dom
{
19 HTMLFrameSetElement::~HTMLFrameSetElement() = default;
21 JSObject
* HTMLFrameSetElement::WrapNode(JSContext
* aCx
,
22 JS::Handle
<JSObject
*> aGivenProto
) {
23 return HTMLFrameSetElement_Binding::Wrap(aCx
, this, aGivenProto
);
26 NS_IMPL_ELEMENT_CLONE(HTMLFrameSetElement
)
28 nsresult
HTMLFrameSetElement::BeforeSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
29 const nsAttrValueOrString
* aValue
,
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
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 MutationObservers::AttributeChanged, which occurs *after*
37 * AfterSetAttr is called, leaving us with no convenient way of changing the
38 * value back to reflow afterwards. However,
39 * MutationObservers::AttributeChanged is effectively the only consumer of
40 * this value, so as long as we always set the value correctly here, we should
43 mCurrentRowColHint
= NS_STYLE_HINT_REFLOW
;
44 if (aNamespaceID
== kNameSpaceID_None
) {
45 if (aName
== nsGkAtoms::rows
) {
47 int32_t oldRows
= mNumRows
;
48 ParseRowCol(aValue
->String(), mNumRows
, &mRowSpecs
);
50 if (mNumRows
!= oldRows
) {
51 mCurrentRowColHint
= nsChangeHint_ReconstructFrame
;
54 } else if (aName
== nsGkAtoms::cols
) {
56 int32_t oldCols
= mNumCols
;
57 ParseRowCol(aValue
->String(), mNumCols
, &mColSpecs
);
59 if (mNumCols
!= oldCols
) {
60 mCurrentRowColHint
= nsChangeHint_ReconstructFrame
;
66 return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID
, aName
, aValue
,
70 nsresult
HTMLFrameSetElement::GetRowSpec(int32_t* aNumValues
,
71 const nsFramesetSpec
** aSpecs
) {
72 MOZ_ASSERT(aNumValues
, "Must have a pointer to an integer here!");
73 MOZ_ASSERT(aSpecs
, "Must have a pointer to an array of nsFramesetSpecs");
78 const nsAttrValue
* value
= GetParsedAttr(nsGkAtoms::rows
);
79 if (value
&& value
->Type() == nsAttrValue::eString
) {
80 nsresult rv
= ParseRowCol(value
->GetStringValue(), mNumRows
, &mRowSpecs
);
81 NS_ENSURE_SUCCESS(rv
, rv
);
84 if (!mRowSpecs
) { // we may not have had an attr or had an empty attr
85 mRowSpecs
= MakeUnique
<nsFramesetSpec
[]>(1);
87 mRowSpecs
[0].mUnit
= eFramesetUnit_Relative
;
88 mRowSpecs
[0].mValue
= 1;
92 *aSpecs
= mRowSpecs
.get();
93 *aNumValues
= mNumRows
;
97 nsresult
HTMLFrameSetElement::GetColSpec(int32_t* aNumValues
,
98 const nsFramesetSpec
** aSpecs
) {
99 MOZ_ASSERT(aNumValues
, "Must have a pointer to an integer here!");
100 MOZ_ASSERT(aSpecs
, "Must have a pointer to an array of nsFramesetSpecs");
105 const nsAttrValue
* value
= GetParsedAttr(nsGkAtoms::cols
);
106 if (value
&& value
->Type() == nsAttrValue::eString
) {
107 nsresult rv
= ParseRowCol(value
->GetStringValue(), mNumCols
, &mColSpecs
);
108 NS_ENSURE_SUCCESS(rv
, rv
);
111 if (!mColSpecs
) { // we may not have had an attr or had an empty attr
112 mColSpecs
= MakeUnique
<nsFramesetSpec
[]>(1);
114 mColSpecs
[0].mUnit
= eFramesetUnit_Relative
;
115 mColSpecs
[0].mValue
= 1;
119 *aSpecs
= mColSpecs
.get();
120 *aNumValues
= mNumCols
;
124 bool HTMLFrameSetElement::ParseAttribute(int32_t aNamespaceID
,
126 const nsAString
& aValue
,
127 nsIPrincipal
* aMaybeScriptedPrincipal
,
128 nsAttrValue
& aResult
) {
129 if (aNamespaceID
== kNameSpaceID_None
) {
130 if (aAttribute
== nsGkAtoms::bordercolor
) {
131 return aResult
.ParseColor(aValue
);
133 if (aAttribute
== nsGkAtoms::frameborder
) {
134 return nsGenericHTMLElement::ParseFrameborderValue(aValue
, aResult
);
136 if (aAttribute
== nsGkAtoms::border
) {
137 return aResult
.ParseIntWithBounds(aValue
, 0, 100);
141 return nsGenericHTMLElement::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
142 aMaybeScriptedPrincipal
, aResult
);
145 nsChangeHint
HTMLFrameSetElement::GetAttributeChangeHint(
146 const nsAtom
* aAttribute
, int32_t aModType
) const {
147 nsChangeHint retval
=
148 nsGenericHTMLElement::GetAttributeChangeHint(aAttribute
, aModType
);
149 if (aAttribute
== nsGkAtoms::rows
|| aAttribute
== nsGkAtoms::cols
) {
150 retval
|= mCurrentRowColHint
;
156 * Translate a "rows" or "cols" spec into an array of nsFramesetSpecs
158 nsresult
HTMLFrameSetElement::ParseRowCol(const nsAString
& aValue
,
160 UniquePtr
<nsFramesetSpec
[]>* aSpecs
) {
161 if (aValue
.IsEmpty()) {
167 static const char16_t
sAster('*');
168 static const char16_t
sPercent('%');
169 static const char16_t
sComma(',');
171 nsAutoString
spec(aValue
);
172 // remove whitespace (Bug 33699) and quotation marks (bug 224598)
173 // also remove leading/trailing commas (bug 31482)
174 spec
.StripChars(u
" \n\r\t\"\'");
177 // Count the commas. Don't count more than X commas (bug 576447).
178 static_assert(NS_MAX_FRAMESET_SPEC_COUNT
* sizeof(nsFramesetSpec
) < (1 << 30),
179 "Too many frameset specs allowed to allocate");
180 int32_t commaX
= spec
.FindChar(sComma
);
182 while (commaX
!= kNotFound
&& count
< NS_MAX_FRAMESET_SPEC_COUNT
) {
184 commaX
= spec
.FindChar(sComma
, commaX
+ 1);
187 auto specs
= MakeUniqueFallible
<nsFramesetSpec
[]>(count
);
191 return NS_ERROR_OUT_OF_MEMORY
;
194 // Pre-grab the compat mode; we may need it later in the loop.
195 bool isInQuirks
= InNavQuirksMode(OwnerDoc());
197 // Parse each comma separated token
200 int32_t specLen
= spec
.Length();
202 for (int32_t i
= 0; i
< count
; i
++) {
204 commaX
= spec
.FindChar(sComma
, start
);
205 NS_ASSERTION(i
== count
- 1 || commaX
!= kNotFound
,
206 "Failed to find comma, somehow");
207 int32_t end
= (commaX
== kNotFound
) ? specLen
: commaX
;
209 // Note: If end == start then it means that the token has no
210 // data in it other than a terminating comma (or the end of the spec).
211 // So default to a fixed width of 0.
212 specs
[i
].mUnit
= eFramesetUnit_Fixed
;
215 int32_t numberEnd
= end
;
216 char16_t ch
= spec
.CharAt(numberEnd
- 1);
218 specs
[i
].mUnit
= eFramesetUnit_Relative
;
220 } else if (sPercent
== ch
) {
221 specs
[i
].mUnit
= eFramesetUnit_Percent
;
224 if (numberEnd
> start
) {
225 ch
= spec
.CharAt(numberEnd
- 1);
227 specs
[i
].mUnit
= eFramesetUnit_Relative
;
233 // Translate value to an integer
235 spec
.Mid(token
, start
, numberEnd
- start
);
238 if ((eFramesetUnit_Relative
== specs
[i
].mUnit
) && (0 == token
.Length())) {
241 // Otherwise just convert to integer.
243 specs
[i
].mValue
= token
.ToInteger(&err
);
244 if (NS_FAILED(err
)) {
249 // Treat 0* as 1* in quirks mode (bug 40383)
251 if ((eFramesetUnit_Relative
== specs
[i
].mUnit
) &&
252 (0 == specs
[i
].mValue
)) {
257 // Catch zero and negative frame sizes for Nav compatibility
258 // Nav resized absolute and relative frames to "1" and
259 // percent frames to an even percentage of the width
261 // if (isInQuirks && (specs[i].mValue <= 0)) {
262 // if (eFramesetUnit_Percent == specs[i].mUnit) {
263 // specs[i].mValue = 100 / count;
265 // specs[i].mValue = 1;
269 // In standards mode, just set negative sizes to zero
270 if (specs
[i
].mValue
< 0) {
278 // Transfer ownership to caller here
279 *aSpecs
= std::move(specs
);
284 bool HTMLFrameSetElement::IsEventAttributeNameInternal(nsAtom
* aName
) {
285 return nsContentUtils::IsEventAttributeName(
286 aName
, EventNameType_HTML
| EventNameType_HTMLBodyOrFramesetOnly
);
289 #define EVENT(name_, id_, type_, struct_) /* nothing; handled by the shim */
290 // nsGenericHTMLElement::GetOnError returns
291 // already_AddRefed<EventHandlerNonNull> while other getters return
292 // EventHandlerNonNull*, so allow passing in the type to use here.
293 #define WINDOW_EVENT_HELPER(name_, type_) \
294 type_* HTMLFrameSetElement::GetOn##name_() { \
295 if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) { \
296 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \
297 return globalWin->GetOn##name_(); \
301 void HTMLFrameSetElement::SetOn##name_(type_* handler) { \
302 nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow(); \
307 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \
308 return globalWin->SetOn##name_(handler); \
310 #define WINDOW_EVENT(name_, id_, type_, struct_) \
311 WINDOW_EVENT_HELPER(name_, EventHandlerNonNull)
312 #define BEFOREUNLOAD_EVENT(name_, id_, type_, struct_) \
313 WINDOW_EVENT_HELPER(name_, OnBeforeUnloadEventHandlerNonNull)
314 #include "mozilla/EventNameList.h" // IWYU pragma: keep
315 #undef BEFOREUNLOAD_EVENT
317 #undef WINDOW_EVENT_HELPER
320 } // namespace mozilla::dom