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 "nsGlobalWindowInner.h"
12 #include "mozilla/UniquePtrExtensions.h"
14 NS_IMPL_NS_NEW_HTML_ELEMENT(FrameSet
)
16 namespace mozilla::dom
{
18 HTMLFrameSetElement::~HTMLFrameSetElement() = default;
20 JSObject
* HTMLFrameSetElement::WrapNode(JSContext
* aCx
,
21 JS::Handle
<JSObject
*> aGivenProto
) {
22 return HTMLFrameSetElement_Binding::Wrap(aCx
, this, aGivenProto
);
25 NS_IMPL_ELEMENT_CLONE(HTMLFrameSetElement
)
27 void HTMLFrameSetElement::BeforeSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
28 const nsAttrValue
* aValue
,
30 /* The main goal here is to see whether the _number_ of rows or
31 * columns has changed. If it has, we need to reframe; otherwise
33 * Ideally, the style hint would be changed back to reflow after the reframe
34 * has been performed. Unfortunately, however, the reframe will be performed
35 * by the call to MutationObservers::AttributeChanged, which occurs *after*
36 * AfterSetAttr is called, leaving us with no convenient way of changing the
37 * value back to reflow afterwards. However,
38 * MutationObservers::AttributeChanged is effectively the only consumer of
39 * this value, so as long as we always set the value correctly here, we should
42 mCurrentRowColHint
= NS_STYLE_HINT_REFLOW
;
43 if (aNamespaceID
== kNameSpaceID_None
) {
44 if (aName
== nsGkAtoms::rows
) {
46 int32_t oldRows
= mNumRows
;
47 ParseRowCol(*aValue
, mNumRows
, &mRowSpecs
);
48 if (mNumRows
!= oldRows
) {
49 mCurrentRowColHint
= nsChangeHint_ReconstructFrame
;
52 } else if (aName
== nsGkAtoms::cols
) {
54 int32_t oldCols
= mNumCols
;
55 ParseRowCol(*aValue
, mNumCols
, &mColSpecs
);
56 if (mNumCols
!= oldCols
) {
57 mCurrentRowColHint
= nsChangeHint_ReconstructFrame
;
63 return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID
, aName
, aValue
,
67 nsresult
HTMLFrameSetElement::GetRowSpec(int32_t* aNumValues
,
68 const nsFramesetSpec
** aSpecs
) {
69 MOZ_ASSERT(aNumValues
, "Must have a pointer to an integer here!");
70 MOZ_ASSERT(aSpecs
, "Must have a pointer to an array of nsFramesetSpecs");
75 if (const nsAttrValue
* value
= GetParsedAttr(nsGkAtoms::rows
)) {
76 MOZ_TRY(ParseRowCol(*value
, mNumRows
, &mRowSpecs
));
79 if (!mRowSpecs
) { // we may not have had an attr or had an empty attr
80 mRowSpecs
= MakeUnique
<nsFramesetSpec
[]>(1);
82 mRowSpecs
[0].mUnit
= eFramesetUnit_Relative
;
83 mRowSpecs
[0].mValue
= 1;
87 *aSpecs
= mRowSpecs
.get();
88 *aNumValues
= mNumRows
;
92 nsresult
HTMLFrameSetElement::GetColSpec(int32_t* aNumValues
,
93 const nsFramesetSpec
** aSpecs
) {
94 MOZ_ASSERT(aNumValues
, "Must have a pointer to an integer here!");
95 MOZ_ASSERT(aSpecs
, "Must have a pointer to an array of nsFramesetSpecs");
100 if (const nsAttrValue
* value
= GetParsedAttr(nsGkAtoms::cols
)) {
101 MOZ_TRY(ParseRowCol(*value
, mNumCols
, &mColSpecs
));
104 if (!mColSpecs
) { // we may not have had an attr or had an empty attr
105 mColSpecs
= MakeUnique
<nsFramesetSpec
[]>(1);
107 mColSpecs
[0].mUnit
= eFramesetUnit_Relative
;
108 mColSpecs
[0].mValue
= 1;
112 *aSpecs
= mColSpecs
.get();
113 *aNumValues
= mNumCols
;
117 bool HTMLFrameSetElement::ParseAttribute(int32_t aNamespaceID
,
119 const nsAString
& aValue
,
120 nsIPrincipal
* aMaybeScriptedPrincipal
,
121 nsAttrValue
& aResult
) {
122 if (aNamespaceID
== kNameSpaceID_None
) {
123 if (aAttribute
== nsGkAtoms::bordercolor
) {
124 return aResult
.ParseColor(aValue
);
126 if (aAttribute
== nsGkAtoms::frameborder
) {
127 return nsGenericHTMLElement::ParseFrameborderValue(aValue
, aResult
);
129 if (aAttribute
== nsGkAtoms::border
) {
130 return aResult
.ParseIntWithBounds(aValue
, 0, 100);
134 return nsGenericHTMLElement::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
135 aMaybeScriptedPrincipal
, aResult
);
138 nsChangeHint
HTMLFrameSetElement::GetAttributeChangeHint(
139 const nsAtom
* aAttribute
, int32_t aModType
) const {
140 nsChangeHint retval
=
141 nsGenericHTMLElement::GetAttributeChangeHint(aAttribute
, aModType
);
142 if (aAttribute
== nsGkAtoms::rows
|| aAttribute
== nsGkAtoms::cols
) {
143 retval
|= mCurrentRowColHint
;
149 * Translate a "rows" or "cols" spec into an array of nsFramesetSpecs
151 nsresult
HTMLFrameSetElement::ParseRowCol(const nsAttrValue
& aValue
,
153 UniquePtr
<nsFramesetSpec
[]>* aSpecs
) {
154 if (aValue
.IsEmptyString()) {
160 MOZ_ASSERT(aValue
.Type() == nsAttrValue::eString
);
162 static const char16_t
sAster('*');
163 static const char16_t
sPercent('%');
164 static const char16_t
sComma(',');
166 nsAutoString
spec(aValue
.GetStringValue());
167 // remove whitespace (Bug 33699) and quotation marks (bug 224598)
168 // also remove leading/trailing commas (bug 31482)
169 spec
.StripChars(u
" \n\r\t\"\'");
172 // Count the commas. Don't count more than X commas (bug 576447).
173 static_assert(NS_MAX_FRAMESET_SPEC_COUNT
* sizeof(nsFramesetSpec
) < (1 << 30),
174 "Too many frameset specs allowed to allocate");
175 int32_t commaX
= spec
.FindChar(sComma
);
177 while (commaX
!= kNotFound
&& count
< NS_MAX_FRAMESET_SPEC_COUNT
) {
179 commaX
= spec
.FindChar(sComma
, commaX
+ 1);
182 auto specs
= MakeUniqueFallible
<nsFramesetSpec
[]>(count
);
186 return NS_ERROR_OUT_OF_MEMORY
;
189 // Pre-grab the compat mode; we may need it later in the loop.
190 bool isInQuirks
= InNavQuirksMode(OwnerDoc());
192 // Parse each comma separated token
195 int32_t specLen
= spec
.Length();
197 for (int32_t i
= 0; i
< count
; i
++) {
199 commaX
= spec
.FindChar(sComma
, start
);
200 NS_ASSERTION(i
== count
- 1 || commaX
!= kNotFound
,
201 "Failed to find comma, somehow");
202 int32_t end
= (commaX
== kNotFound
) ? specLen
: commaX
;
204 // Note: If end == start then it means that the token has no
205 // data in it other than a terminating comma (or the end of the spec).
206 // So default to a fixed width of 0.
207 specs
[i
].mUnit
= eFramesetUnit_Fixed
;
210 int32_t numberEnd
= end
;
211 char16_t ch
= spec
.CharAt(numberEnd
- 1);
213 specs
[i
].mUnit
= eFramesetUnit_Relative
;
215 } else if (sPercent
== ch
) {
216 specs
[i
].mUnit
= eFramesetUnit_Percent
;
219 if (numberEnd
> start
) {
220 ch
= spec
.CharAt(numberEnd
- 1);
222 specs
[i
].mUnit
= eFramesetUnit_Relative
;
228 // Translate value to an integer
230 spec
.Mid(token
, start
, numberEnd
- start
);
233 if ((eFramesetUnit_Relative
== specs
[i
].mUnit
) && (0 == token
.Length())) {
236 // Otherwise just convert to integer.
238 specs
[i
].mValue
= token
.ToInteger(&err
);
239 if (NS_FAILED(err
)) {
244 // Treat 0* as 1* in quirks mode (bug 40383)
246 if ((eFramesetUnit_Relative
== specs
[i
].mUnit
) &&
247 (0 == specs
[i
].mValue
)) {
252 // Catch zero and negative frame sizes for Nav compatibility
253 // Nav resized absolute and relative frames to "1" and
254 // percent frames to an even percentage of the width
256 // if (isInQuirks && (specs[i].mValue <= 0)) {
257 // if (eFramesetUnit_Percent == specs[i].mUnit) {
258 // specs[i].mValue = 100 / count;
260 // specs[i].mValue = 1;
264 // In standards mode, just set negative sizes to zero
265 if (specs
[i
].mValue
< 0) {
273 // Transfer ownership to caller here
274 *aSpecs
= std::move(specs
);
279 bool HTMLFrameSetElement::IsEventAttributeNameInternal(nsAtom
* aName
) {
280 return nsContentUtils::IsEventAttributeName(
281 aName
, EventNameType_HTML
| EventNameType_HTMLBodyOrFramesetOnly
);
284 #define EVENT(name_, id_, type_, struct_) /* nothing; handled by the shim */
285 // nsGenericHTMLElement::GetOnError returns
286 // already_AddRefed<EventHandlerNonNull> while other getters return
287 // EventHandlerNonNull*, so allow passing in the type to use here.
288 #define WINDOW_EVENT_HELPER(name_, type_) \
289 type_* HTMLFrameSetElement::GetOn##name_() { \
290 if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) { \
291 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \
292 return globalWin->GetOn##name_(); \
296 void HTMLFrameSetElement::SetOn##name_(type_* handler) { \
297 nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow(); \
302 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \
303 return globalWin->SetOn##name_(handler); \
305 #define WINDOW_EVENT(name_, id_, type_, struct_) \
306 WINDOW_EVENT_HELPER(name_, EventHandlerNonNull)
307 #define BEFOREUNLOAD_EVENT(name_, id_, type_, struct_) \
308 WINDOW_EVENT_HELPER(name_, OnBeforeUnloadEventHandlerNonNull)
309 #include "mozilla/EventNameList.h" // IWYU pragma: keep
310 #undef BEFOREUNLOAD_EVENT
312 #undef WINDOW_EVENT_HELPER
315 } // namespace mozilla::dom