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/Try.h"
9 #include "mozilla/dom/HTMLFrameSetElementBinding.h"
10 #include "mozilla/dom/Document.h"
11 #include "mozilla/dom/EventHandlerBinding.h"
12 #include "nsGlobalWindowInner.h"
13 #include "mozilla/UniquePtrExtensions.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 void HTMLFrameSetElement::BeforeSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
29 const nsAttrValue
* 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
, mNumRows
, &mRowSpecs
);
49 if (mNumRows
!= oldRows
) {
50 mCurrentRowColHint
= nsChangeHint_ReconstructFrame
;
53 } else if (aName
== nsGkAtoms::cols
) {
55 int32_t oldCols
= mNumCols
;
56 ParseRowCol(*aValue
, mNumCols
, &mColSpecs
);
57 if (mNumCols
!= oldCols
) {
58 mCurrentRowColHint
= nsChangeHint_ReconstructFrame
;
64 return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID
, aName
, aValue
,
68 nsresult
HTMLFrameSetElement::GetRowSpec(int32_t* aNumValues
,
69 const nsFramesetSpec
** aSpecs
) {
70 MOZ_ASSERT(aNumValues
, "Must have a pointer to an integer here!");
71 MOZ_ASSERT(aSpecs
, "Must have a pointer to an array of nsFramesetSpecs");
76 if (const nsAttrValue
* value
= GetParsedAttr(nsGkAtoms::rows
)) {
77 MOZ_TRY(ParseRowCol(*value
, mNumRows
, &mRowSpecs
));
80 if (!mRowSpecs
) { // we may not have had an attr or had an empty attr
81 mRowSpecs
= MakeUnique
<nsFramesetSpec
[]>(1);
83 mRowSpecs
[0].mUnit
= eFramesetUnit_Relative
;
84 mRowSpecs
[0].mValue
= 1;
88 *aSpecs
= mRowSpecs
.get();
89 *aNumValues
= mNumRows
;
93 nsresult
HTMLFrameSetElement::GetColSpec(int32_t* aNumValues
,
94 const nsFramesetSpec
** aSpecs
) {
95 MOZ_ASSERT(aNumValues
, "Must have a pointer to an integer here!");
96 MOZ_ASSERT(aSpecs
, "Must have a pointer to an array of nsFramesetSpecs");
101 if (const nsAttrValue
* value
= GetParsedAttr(nsGkAtoms::cols
)) {
102 MOZ_TRY(ParseRowCol(*value
, mNumCols
, &mColSpecs
));
105 if (!mColSpecs
) { // we may not have had an attr or had an empty attr
106 mColSpecs
= MakeUnique
<nsFramesetSpec
[]>(1);
108 mColSpecs
[0].mUnit
= eFramesetUnit_Relative
;
109 mColSpecs
[0].mValue
= 1;
113 *aSpecs
= mColSpecs
.get();
114 *aNumValues
= mNumCols
;
118 bool HTMLFrameSetElement::ParseAttribute(int32_t aNamespaceID
,
120 const nsAString
& aValue
,
121 nsIPrincipal
* aMaybeScriptedPrincipal
,
122 nsAttrValue
& aResult
) {
123 if (aNamespaceID
== kNameSpaceID_None
) {
124 if (aAttribute
== nsGkAtoms::bordercolor
) {
125 return aResult
.ParseColor(aValue
);
127 if (aAttribute
== nsGkAtoms::frameborder
) {
128 return nsGenericHTMLElement::ParseFrameborderValue(aValue
, aResult
);
130 if (aAttribute
== nsGkAtoms::border
) {
131 return aResult
.ParseIntWithBounds(aValue
, 0, 100);
135 return nsGenericHTMLElement::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
136 aMaybeScriptedPrincipal
, aResult
);
139 nsChangeHint
HTMLFrameSetElement::GetAttributeChangeHint(
140 const nsAtom
* aAttribute
, int32_t aModType
) const {
141 nsChangeHint retval
=
142 nsGenericHTMLElement::GetAttributeChangeHint(aAttribute
, aModType
);
143 if (aAttribute
== nsGkAtoms::rows
|| aAttribute
== nsGkAtoms::cols
) {
144 retval
|= mCurrentRowColHint
;
150 * Translate a "rows" or "cols" spec into an array of nsFramesetSpecs
152 nsresult
HTMLFrameSetElement::ParseRowCol(const nsAttrValue
& aValue
,
154 UniquePtr
<nsFramesetSpec
[]>* aSpecs
) {
155 if (aValue
.IsEmptyString()) {
161 MOZ_ASSERT(aValue
.Type() == nsAttrValue::eString
);
163 static const char16_t
sAster('*');
164 static const char16_t
sPercent('%');
165 static const char16_t
sComma(',');
167 nsAutoString
spec(aValue
.GetStringValue());
168 // remove whitespace (Bug 33699) and quotation marks (bug 224598)
169 // also remove leading/trailing commas (bug 31482)
170 spec
.StripChars(u
" \n\r\t\"\'");
173 // Count the commas. Don't count more than X commas (bug 576447).
174 static_assert(NS_MAX_FRAMESET_SPEC_COUNT
* sizeof(nsFramesetSpec
) < (1 << 30),
175 "Too many frameset specs allowed to allocate");
176 int32_t commaX
= spec
.FindChar(sComma
);
178 while (commaX
!= kNotFound
&& count
< NS_MAX_FRAMESET_SPEC_COUNT
) {
180 commaX
= spec
.FindChar(sComma
, commaX
+ 1);
183 auto specs
= MakeUniqueFallible
<nsFramesetSpec
[]>(count
);
187 return NS_ERROR_OUT_OF_MEMORY
;
190 // Pre-grab the compat mode; we may need it later in the loop.
191 bool isInQuirks
= InNavQuirksMode(OwnerDoc());
193 // Parse each comma separated token
196 int32_t specLen
= spec
.Length();
198 for (int32_t i
= 0; i
< count
; i
++) {
200 commaX
= spec
.FindChar(sComma
, start
);
201 NS_ASSERTION(i
== count
- 1 || commaX
!= kNotFound
,
202 "Failed to find comma, somehow");
203 int32_t end
= (commaX
== kNotFound
) ? specLen
: commaX
;
205 // Note: If end == start then it means that the token has no
206 // data in it other than a terminating comma (or the end of the spec).
207 // So default to a fixed width of 0.
208 specs
[i
].mUnit
= eFramesetUnit_Fixed
;
211 int32_t numberEnd
= end
;
212 char16_t ch
= spec
.CharAt(numberEnd
- 1);
214 specs
[i
].mUnit
= eFramesetUnit_Relative
;
216 } else if (sPercent
== ch
) {
217 specs
[i
].mUnit
= eFramesetUnit_Percent
;
220 if (numberEnd
> start
) {
221 ch
= spec
.CharAt(numberEnd
- 1);
223 specs
[i
].mUnit
= eFramesetUnit_Relative
;
229 // Translate value to an integer
231 spec
.Mid(token
, start
, numberEnd
- start
);
234 if ((eFramesetUnit_Relative
== specs
[i
].mUnit
) && (0 == token
.Length())) {
237 // Otherwise just convert to integer.
239 specs
[i
].mValue
= token
.ToInteger(&err
);
240 if (NS_FAILED(err
)) {
245 // Treat 0* as 1* in quirks mode (bug 40383)
247 if ((eFramesetUnit_Relative
== specs
[i
].mUnit
) &&
248 (0 == specs
[i
].mValue
)) {
253 // Catch zero and negative frame sizes for Nav compatibility
254 // Nav resized absolute and relative frames to "1" and
255 // percent frames to an even percentage of the width
257 // if (isInQuirks && (specs[i].mValue <= 0)) {
258 // if (eFramesetUnit_Percent == specs[i].mUnit) {
259 // specs[i].mValue = 100 / count;
261 // specs[i].mValue = 1;
265 // In standards mode, just set negative sizes to zero
266 if (specs
[i
].mValue
< 0) {
274 // Transfer ownership to caller here
275 *aSpecs
= std::move(specs
);
280 bool HTMLFrameSetElement::IsEventAttributeNameInternal(nsAtom
* aName
) {
281 return nsContentUtils::IsEventAttributeName(
282 aName
, EventNameType_HTML
| EventNameType_HTMLBodyOrFramesetOnly
);
285 #define EVENT(name_, id_, type_, struct_) /* nothing; handled by the shim */
286 // nsGenericHTMLElement::GetOnError returns
287 // already_AddRefed<EventHandlerNonNull> while other getters return
288 // EventHandlerNonNull*, so allow passing in the type to use here.
289 #define WINDOW_EVENT_HELPER(name_, type_) \
290 type_* HTMLFrameSetElement::GetOn##name_() { \
291 if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) { \
292 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \
293 return globalWin->GetOn##name_(); \
297 void HTMLFrameSetElement::SetOn##name_(type_* handler) { \
298 nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow(); \
303 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \
304 return globalWin->SetOn##name_(handler); \
306 #define WINDOW_EVENT(name_, id_, type_, struct_) \
307 WINDOW_EVENT_HELPER(name_, EventHandlerNonNull)
308 #define BEFOREUNLOAD_EVENT(name_, id_, type_, struct_) \
309 WINDOW_EVENT_HELPER(name_, OnBeforeUnloadEventHandlerNonNull)
310 #include "mozilla/EventNameList.h" // IWYU pragma: keep
311 #undef BEFOREUNLOAD_EVENT
313 #undef WINDOW_EVENT_HELPER
316 } // namespace mozilla::dom