Backed out changeset 1d9301697aa0 (bug 1887752) for causing failures on browser_all_f...
[gecko.git] / dom / html / HTMLFrameSetElement.cpp
blobd6a794698b51038bf2151762a6fa67f5dc6448cf
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,
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 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
41 * be fine.
43 mCurrentRowColHint = NS_STYLE_HINT_REFLOW;
44 if (aNamespaceID == kNameSpaceID_None) {
45 if (aName == nsGkAtoms::rows) {
46 if (aValue) {
47 int32_t oldRows = mNumRows;
48 ParseRowCol(*aValue, mNumRows, &mRowSpecs);
49 if (mNumRows != oldRows) {
50 mCurrentRowColHint = nsChangeHint_ReconstructFrame;
53 } else if (aName == nsGkAtoms::cols) {
54 if (aValue) {
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,
65 aNotify);
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");
72 *aNumValues = 0;
73 *aSpecs = nullptr;
75 if (!mRowSpecs) {
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);
82 mNumRows = 1;
83 mRowSpecs[0].mUnit = eFramesetUnit_Relative;
84 mRowSpecs[0].mValue = 1;
88 *aSpecs = mRowSpecs.get();
89 *aNumValues = mNumRows;
90 return NS_OK;
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");
97 *aNumValues = 0;
98 *aSpecs = nullptr;
100 if (!mColSpecs) {
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);
107 mNumCols = 1;
108 mColSpecs[0].mUnit = eFramesetUnit_Relative;
109 mColSpecs[0].mValue = 1;
113 *aSpecs = mColSpecs.get();
114 *aNumValues = mNumCols;
115 return NS_OK;
118 bool HTMLFrameSetElement::ParseAttribute(int32_t aNamespaceID,
119 nsAtom* aAttribute,
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;
146 return retval;
150 * Translate a "rows" or "cols" spec into an array of nsFramesetSpecs
152 nsresult HTMLFrameSetElement::ParseRowCol(const nsAttrValue& aValue,
153 int32_t& aNumSpecs,
154 UniquePtr<nsFramesetSpec[]>* aSpecs) {
155 if (aValue.IsEmptyString()) {
156 aNumSpecs = 0;
157 *aSpecs = nullptr;
158 return NS_OK;
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\"\'");
171 spec.Trim(",");
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);
177 int32_t count = 1;
178 while (commaX != kNotFound && count < NS_MAX_FRAMESET_SPEC_COUNT) {
179 count++;
180 commaX = spec.FindChar(sComma, commaX + 1);
183 auto specs = MakeUniqueFallible<nsFramesetSpec[]>(count);
184 if (!specs) {
185 *aSpecs = nullptr;
186 aNumSpecs = 0;
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
195 int32_t start = 0;
196 int32_t specLen = spec.Length();
198 for (int32_t i = 0; i < count; i++) {
199 // Find our comma
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;
209 specs[i].mValue = 0;
210 if (end > start) {
211 int32_t numberEnd = end;
212 char16_t ch = spec.CharAt(numberEnd - 1);
213 if (sAster == ch) {
214 specs[i].mUnit = eFramesetUnit_Relative;
215 numberEnd--;
216 } else if (sPercent == ch) {
217 specs[i].mUnit = eFramesetUnit_Percent;
218 numberEnd--;
219 // check for "*%"
220 if (numberEnd > start) {
221 ch = spec.CharAt(numberEnd - 1);
222 if (sAster == ch) {
223 specs[i].mUnit = eFramesetUnit_Relative;
224 numberEnd--;
229 // Translate value to an integer
230 nsAutoString token;
231 spec.Mid(token, start, numberEnd - start);
233 // Treat * as 1*
234 if ((eFramesetUnit_Relative == specs[i].mUnit) && (0 == token.Length())) {
235 specs[i].mValue = 1;
236 } else {
237 // Otherwise just convert to integer.
238 nsresult err;
239 specs[i].mValue = token.ToInteger(&err);
240 if (NS_FAILED(err)) {
241 specs[i].mValue = 0;
245 // Treat 0* as 1* in quirks mode (bug 40383)
246 if (isInQuirks) {
247 if ((eFramesetUnit_Relative == specs[i].mUnit) &&
248 (0 == specs[i].mValue)) {
249 specs[i].mValue = 1;
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;
260 // } else {
261 // specs[i].mValue = 1;
262 // }
263 //} else {
265 // In standards mode, just set negative sizes to zero
266 if (specs[i].mValue < 0) {
267 specs[i].mValue = 0;
269 start = end + 1;
273 aNumSpecs = count;
274 // Transfer ownership to caller here
275 *aSpecs = std::move(specs);
277 return NS_OK;
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_(); \
295 return nullptr; \
297 void HTMLFrameSetElement::SetOn##name_(type_* handler) { \
298 nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow(); \
299 if (!win) { \
300 return; \
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
312 #undef WINDOW_EVENT
313 #undef WINDOW_EVENT_HELPER
314 #undef EVENT
316 } // namespace mozilla::dom