HTMLFrameSetElement.cpp (10191B)
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/. */ 6 7 #include "HTMLFrameSetElement.h" 8 9 #include "mozilla/dom/Document.h" 10 #include "mozilla/dom/EventHandlerBinding.h" 11 #include "mozilla/dom/HTMLFrameSetElementBinding.h" 12 #include "nsAttrValueOrString.h" 13 #include "nsGlobalWindowInner.h" 14 15 NS_IMPL_NS_NEW_HTML_ELEMENT(FrameSet) 16 17 namespace mozilla::dom { 18 19 HTMLFrameSetElement::~HTMLFrameSetElement() = default; 20 21 JSObject* HTMLFrameSetElement::WrapNode(JSContext* aCx, 22 JS::Handle<JSObject*> aGivenProto) { 23 return HTMLFrameSetElement_Binding::Wrap(aCx, this, aGivenProto); 24 } 25 26 NS_IMPL_ELEMENT_CLONE(HTMLFrameSetElement) 27 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. 42 */ 43 mCurrentRowColHint = NS_STYLE_HINT_REFLOW; 44 if (aNamespaceID == kNameSpaceID_None) { 45 if (aName == nsGkAtoms::rows) { 46 if (aValue) { 47 size_t oldNumRows = mRowSpecs.Length(); 48 ParseRowCol(*aValue, mRowSpecs); 49 if (mRowSpecs.Length() != oldNumRows) { 50 mCurrentRowColHint = nsChangeHint_ReconstructFrame; 51 } 52 } 53 } else if (aName == nsGkAtoms::cols) { 54 if (aValue) { 55 size_t oldNumCols = mColSpecs.Length(); 56 ParseRowCol(*aValue, mColSpecs); 57 if (mColSpecs.Length() != oldNumCols) { 58 mCurrentRowColHint = nsChangeHint_ReconstructFrame; 59 } 60 } 61 } 62 } 63 64 return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, aValue, 65 aNotify); 66 } 67 68 Span<const nsFramesetSpec> HTMLFrameSetElement::GetRowSpec() { 69 if (mRowSpecs.IsEmpty()) { 70 if (const nsAttrValue* value = GetParsedAttr(nsGkAtoms::rows)) { 71 if (NS_FAILED(ParseRowCol(*value, mRowSpecs))) { 72 return {}; 73 } 74 } 75 76 if (mRowSpecs.IsEmpty()) { 77 // We may not have had an attr or had an empty attr. 78 mRowSpecs.SetLength(1); 79 mRowSpecs[0].mUnit = eFramesetUnit_Relative; 80 mRowSpecs[0].mValue = 1; 81 } 82 } 83 84 return Span(mRowSpecs); 85 } 86 87 Span<const nsFramesetSpec> HTMLFrameSetElement::GetColSpec() { 88 if (mColSpecs.IsEmpty()) { 89 if (const nsAttrValue* value = GetParsedAttr(nsGkAtoms::cols)) { 90 if (NS_FAILED(ParseRowCol(*value, mColSpecs))) { 91 return {}; 92 } 93 } 94 95 if (mColSpecs.IsEmpty()) { 96 // We may not have had an attr or had an empty attr. 97 mColSpecs.SetLength(1); 98 mColSpecs[0].mUnit = eFramesetUnit_Relative; 99 mColSpecs[0].mValue = 1; 100 } 101 } 102 103 return Span(mColSpecs); 104 } 105 106 bool HTMLFrameSetElement::ParseAttribute(int32_t aNamespaceID, 107 nsAtom* aAttribute, 108 const nsAString& aValue, 109 nsIPrincipal* aMaybeScriptedPrincipal, 110 nsAttrValue& aResult) { 111 if (aNamespaceID == kNameSpaceID_None) { 112 if (aAttribute == nsGkAtoms::bordercolor) { 113 return aResult.ParseColor(aValue); 114 } 115 if (aAttribute == nsGkAtoms::frameborder) { 116 return nsGenericHTMLElement::ParseFrameborderValue(aValue, aResult); 117 } 118 if (aAttribute == nsGkAtoms::border) { 119 return aResult.ParseIntWithBounds(aValue, 0, 100); 120 } 121 } 122 123 return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue, 124 aMaybeScriptedPrincipal, aResult); 125 } 126 127 nsChangeHint HTMLFrameSetElement::GetAttributeChangeHint( 128 const nsAtom* aAttribute, AttrModType aModType) const { 129 nsChangeHint retval = 130 nsGenericHTMLElement::GetAttributeChangeHint(aAttribute, aModType); 131 if (aAttribute == nsGkAtoms::rows || aAttribute == nsGkAtoms::cols) { 132 retval |= mCurrentRowColHint; 133 } 134 return retval; 135 } 136 137 /** 138 * Translate a "rows" or "cols" spec into an array of nsFramesetSpecs 139 */ 140 nsresult HTMLFrameSetElement::ParseRowCol(const nsAttrValue& aValue, 141 nsTArray<nsFramesetSpec>& aSpecs) { 142 if (aValue.IsEmptyString()) { 143 aSpecs.Clear(); 144 return NS_OK; 145 } 146 147 static const char16_t sAster('*'); 148 static const char16_t sPercent('%'); 149 static const char16_t sComma(','); 150 151 nsAutoString spec(nsAttrValueOrString(&aValue).String()); 152 // remove whitespace (Bug 33699) and quotation marks (bug 224598) 153 // also remove leading/trailing commas (bug 31482) 154 spec.StripChars(u" \n\r\t\"\'"); 155 spec.Trim(","); 156 157 // Count the commas. Don't count more than X commas (bug 576447). 158 static_assert(NS_MAX_FRAMESET_SPEC_COUNT * sizeof(nsFramesetSpec) < (1 << 30), 159 "Too many frameset specs allowed to allocate"); 160 int32_t commaX = spec.FindChar(sComma); 161 size_t count = 1; 162 while (commaX != kNotFound && count < NS_MAX_FRAMESET_SPEC_COUNT) { 163 count++; 164 commaX = spec.FindChar(sComma, commaX + 1); 165 } 166 167 nsTArray<nsFramesetSpec> specs; 168 if (!specs.SetLength(count, fallible)) { 169 aSpecs.Clear(); 170 return NS_ERROR_OUT_OF_MEMORY; 171 } 172 173 // Pre-grab the compat mode; we may need it later in the loop. 174 bool isInQuirks = InNavQuirksMode(OwnerDoc()); 175 176 // Parse each comma separated token 177 178 size_t start = 0; 179 size_t specLen = spec.Length(); 180 181 for (size_t i = 0; i < count; i++) { 182 // Find our comma 183 commaX = spec.FindChar(sComma, start); 184 MOZ_ASSERT(i == count - 1 || commaX != kNotFound, 185 "Failed to find comma, somehow"); 186 size_t end = (commaX == kNotFound) ? specLen : commaX; 187 188 // Note: If end == start then it means that the token has no 189 // data in it other than a terminating comma (or the end of the spec). 190 // So default to a fixed width of 0. 191 specs[i].mUnit = eFramesetUnit_Fixed; 192 specs[i].mValue = 0; 193 if (end > start) { 194 size_t numberEnd = end; 195 char16_t ch = spec.CharAt(numberEnd - 1); 196 if (sAster == ch) { 197 specs[i].mUnit = eFramesetUnit_Relative; 198 numberEnd--; 199 } else if (sPercent == ch) { 200 specs[i].mUnit = eFramesetUnit_Percent; 201 numberEnd--; 202 // check for "*%" 203 if (numberEnd > start) { 204 ch = spec.CharAt(numberEnd - 1); 205 if (sAster == ch) { 206 specs[i].mUnit = eFramesetUnit_Relative; 207 numberEnd--; 208 } 209 } 210 } 211 212 // Translate value to an integer 213 nsAutoString token; 214 spec.Mid(token, start, numberEnd - start); 215 216 // Treat * as 1* 217 if ((eFramesetUnit_Relative == specs[i].mUnit) && (0 == token.Length())) { 218 specs[i].mValue = 1; 219 } else { 220 // Otherwise just convert to integer. 221 nsresult err; 222 specs[i].mValue = token.ToInteger(&err); 223 if (NS_FAILED(err)) { 224 specs[i].mValue = 0; 225 } 226 } 227 228 // Treat 0* as 1* in quirks mode (bug 40383) 229 if (isInQuirks) { 230 if ((eFramesetUnit_Relative == specs[i].mUnit) && 231 (0 == specs[i].mValue)) { 232 specs[i].mValue = 1; 233 } 234 } 235 236 // In standards mode, just set negative sizes to zero 237 if (specs[i].mValue < 0) { 238 specs[i].mValue = 0; 239 } 240 start = end + 1; 241 } 242 } 243 244 aSpecs = std::move(specs); 245 246 return NS_OK; 247 } 248 249 bool HTMLFrameSetElement::IsEventAttributeNameInternal(nsAtom* aName) { 250 return nsContentUtils::IsEventAttributeName( 251 aName, EventNameType_HTML | EventNameType_HTMLBodyOrFramesetOnly); 252 } 253 254 #define EVENT(name_, id_, type_, struct_) /* nothing; handled by the shim */ 255 // nsGenericHTMLElement::GetOnError returns 256 // already_AddRefed<EventHandlerNonNull> while other getters return 257 // EventHandlerNonNull*, so allow passing in the type to use here. 258 #define WINDOW_EVENT_HELPER(name_, type_) \ 259 type_* HTMLFrameSetElement::GetOn##name_() { \ 260 if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) { \ 261 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \ 262 return globalWin->GetOn##name_(); \ 263 } \ 264 return nullptr; \ 265 } \ 266 void HTMLFrameSetElement::SetOn##name_(type_* handler) { \ 267 nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow(); \ 268 if (!win) { \ 269 return; \ 270 } \ 271 \ 272 nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \ 273 return globalWin->SetOn##name_(handler); \ 274 } 275 #define WINDOW_EVENT(name_, id_, type_, struct_) \ 276 WINDOW_EVENT_HELPER(name_, EventHandlerNonNull) 277 #define BEFOREUNLOAD_EVENT(name_, id_, type_, struct_) \ 278 WINDOW_EVENT_HELPER(name_, OnBeforeUnloadEventHandlerNonNull) 279 #include "mozilla/EventNameList.h" // IWYU pragma: keep 280 #undef BEFOREUNLOAD_EVENT 281 #undef WINDOW_EVENT 282 #undef WINDOW_EVENT_HELPER 283 #undef EVENT 284 285 } // namespace mozilla::dom