Bug 1841281 - Disable test_basics.html on mac debug and windows for frequent failures...
[gecko.git] / dom / html / HTMLFrameSetElement.cpp
blobd2b4a0b9ec1f9a529ae4fcbb2997e466fe8cadc3
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,
29 bool aNotify) {
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
32 * we want to reflow.
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
40 * be fine.
42 mCurrentRowColHint = NS_STYLE_HINT_REFLOW;
43 if (aNamespaceID == kNameSpaceID_None) {
44 if (aName == nsGkAtoms::rows) {
45 if (aValue) {
46 int32_t oldRows = mNumRows;
47 ParseRowCol(*aValue, mNumRows, &mRowSpecs);
48 if (mNumRows != oldRows) {
49 mCurrentRowColHint = nsChangeHint_ReconstructFrame;
52 } else if (aName == nsGkAtoms::cols) {
53 if (aValue) {
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,
64 aNotify);
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");
71 *aNumValues = 0;
72 *aSpecs = nullptr;
74 if (!mRowSpecs) {
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);
81 mNumRows = 1;
82 mRowSpecs[0].mUnit = eFramesetUnit_Relative;
83 mRowSpecs[0].mValue = 1;
87 *aSpecs = mRowSpecs.get();
88 *aNumValues = mNumRows;
89 return NS_OK;
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");
96 *aNumValues = 0;
97 *aSpecs = nullptr;
99 if (!mColSpecs) {
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);
106 mNumCols = 1;
107 mColSpecs[0].mUnit = eFramesetUnit_Relative;
108 mColSpecs[0].mValue = 1;
112 *aSpecs = mColSpecs.get();
113 *aNumValues = mNumCols;
114 return NS_OK;
117 bool HTMLFrameSetElement::ParseAttribute(int32_t aNamespaceID,
118 nsAtom* aAttribute,
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;
145 return retval;
149 * Translate a "rows" or "cols" spec into an array of nsFramesetSpecs
151 nsresult HTMLFrameSetElement::ParseRowCol(const nsAttrValue& aValue,
152 int32_t& aNumSpecs,
153 UniquePtr<nsFramesetSpec[]>* aSpecs) {
154 if (aValue.IsEmptyString()) {
155 aNumSpecs = 0;
156 *aSpecs = nullptr;
157 return NS_OK;
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\"\'");
170 spec.Trim(",");
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);
176 int32_t count = 1;
177 while (commaX != kNotFound && count < NS_MAX_FRAMESET_SPEC_COUNT) {
178 count++;
179 commaX = spec.FindChar(sComma, commaX + 1);
182 auto specs = MakeUniqueFallible<nsFramesetSpec[]>(count);
183 if (!specs) {
184 *aSpecs = nullptr;
185 aNumSpecs = 0;
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
194 int32_t start = 0;
195 int32_t specLen = spec.Length();
197 for (int32_t i = 0; i < count; i++) {
198 // Find our comma
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;
208 specs[i].mValue = 0;
209 if (end > start) {
210 int32_t numberEnd = end;
211 char16_t ch = spec.CharAt(numberEnd - 1);
212 if (sAster == ch) {
213 specs[i].mUnit = eFramesetUnit_Relative;
214 numberEnd--;
215 } else if (sPercent == ch) {
216 specs[i].mUnit = eFramesetUnit_Percent;
217 numberEnd--;
218 // check for "*%"
219 if (numberEnd > start) {
220 ch = spec.CharAt(numberEnd - 1);
221 if (sAster == ch) {
222 specs[i].mUnit = eFramesetUnit_Relative;
223 numberEnd--;
228 // Translate value to an integer
229 nsAutoString token;
230 spec.Mid(token, start, numberEnd - start);
232 // Treat * as 1*
233 if ((eFramesetUnit_Relative == specs[i].mUnit) && (0 == token.Length())) {
234 specs[i].mValue = 1;
235 } else {
236 // Otherwise just convert to integer.
237 nsresult err;
238 specs[i].mValue = token.ToInteger(&err);
239 if (NS_FAILED(err)) {
240 specs[i].mValue = 0;
244 // Treat 0* as 1* in quirks mode (bug 40383)
245 if (isInQuirks) {
246 if ((eFramesetUnit_Relative == specs[i].mUnit) &&
247 (0 == specs[i].mValue)) {
248 specs[i].mValue = 1;
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;
259 // } else {
260 // specs[i].mValue = 1;
261 // }
262 //} else {
264 // In standards mode, just set negative sizes to zero
265 if (specs[i].mValue < 0) {
266 specs[i].mValue = 0;
268 start = end + 1;
272 aNumSpecs = count;
273 // Transfer ownership to caller here
274 *aSpecs = std::move(specs);
276 return NS_OK;
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_(); \
294 return nullptr; \
296 void HTMLFrameSetElement::SetOn##name_(type_* handler) { \
297 nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow(); \
298 if (!win) { \
299 return; \
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
311 #undef WINDOW_EVENT
312 #undef WINDOW_EVENT_HELPER
313 #undef EVENT
315 } // namespace mozilla::dom