diff options
Diffstat (limited to 'dom/html/HTMLFrameSetElement.cpp')
-rw-r--r-- | dom/html/HTMLFrameSetElement.cpp | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/dom/html/HTMLFrameSetElement.cpp b/dom/html/HTMLFrameSetElement.cpp new file mode 100644 index 0000000000..d6a794698b --- /dev/null +++ b/dom/html/HTMLFrameSetElement.cpp @@ -0,0 +1,316 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "HTMLFrameSetElement.h" +#include "mozilla/Try.h" +#include "mozilla/dom/HTMLFrameSetElementBinding.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/EventHandlerBinding.h" +#include "nsGlobalWindowInner.h" +#include "mozilla/UniquePtrExtensions.h" + +NS_IMPL_NS_NEW_HTML_ELEMENT(FrameSet) + +namespace mozilla::dom { + +HTMLFrameSetElement::~HTMLFrameSetElement() = default; + +JSObject* HTMLFrameSetElement::WrapNode(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return HTMLFrameSetElement_Binding::Wrap(aCx, this, aGivenProto); +} + +NS_IMPL_ELEMENT_CLONE(HTMLFrameSetElement) + +void HTMLFrameSetElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName, + const nsAttrValue* aValue, + bool aNotify) { + /* The main goal here is to see whether the _number_ of rows or + * columns has changed. If it has, we need to reframe; otherwise + * we want to reflow. + * Ideally, the style hint would be changed back to reflow after the reframe + * has been performed. Unfortunately, however, the reframe will be performed + * by the call to MutationObservers::AttributeChanged, which occurs *after* + * AfterSetAttr is called, leaving us with no convenient way of changing the + * value back to reflow afterwards. However, + * MutationObservers::AttributeChanged is effectively the only consumer of + * this value, so as long as we always set the value correctly here, we should + * be fine. + */ + mCurrentRowColHint = NS_STYLE_HINT_REFLOW; + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::rows) { + if (aValue) { + int32_t oldRows = mNumRows; + ParseRowCol(*aValue, mNumRows, &mRowSpecs); + if (mNumRows != oldRows) { + mCurrentRowColHint = nsChangeHint_ReconstructFrame; + } + } + } else if (aName == nsGkAtoms::cols) { + if (aValue) { + int32_t oldCols = mNumCols; + ParseRowCol(*aValue, mNumCols, &mColSpecs); + if (mNumCols != oldCols) { + mCurrentRowColHint = nsChangeHint_ReconstructFrame; + } + } + } + } + + return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, aValue, + aNotify); +} + +nsresult HTMLFrameSetElement::GetRowSpec(int32_t* aNumValues, + const nsFramesetSpec** aSpecs) { + MOZ_ASSERT(aNumValues, "Must have a pointer to an integer here!"); + MOZ_ASSERT(aSpecs, "Must have a pointer to an array of nsFramesetSpecs"); + *aNumValues = 0; + *aSpecs = nullptr; + + if (!mRowSpecs) { + if (const nsAttrValue* value = GetParsedAttr(nsGkAtoms::rows)) { + MOZ_TRY(ParseRowCol(*value, mNumRows, &mRowSpecs)); + } + + if (!mRowSpecs) { // we may not have had an attr or had an empty attr + mRowSpecs = MakeUnique<nsFramesetSpec[]>(1); + mNumRows = 1; + mRowSpecs[0].mUnit = eFramesetUnit_Relative; + mRowSpecs[0].mValue = 1; + } + } + + *aSpecs = mRowSpecs.get(); + *aNumValues = mNumRows; + return NS_OK; +} + +nsresult HTMLFrameSetElement::GetColSpec(int32_t* aNumValues, + const nsFramesetSpec** aSpecs) { + MOZ_ASSERT(aNumValues, "Must have a pointer to an integer here!"); + MOZ_ASSERT(aSpecs, "Must have a pointer to an array of nsFramesetSpecs"); + *aNumValues = 0; + *aSpecs = nullptr; + + if (!mColSpecs) { + if (const nsAttrValue* value = GetParsedAttr(nsGkAtoms::cols)) { + MOZ_TRY(ParseRowCol(*value, mNumCols, &mColSpecs)); + } + + if (!mColSpecs) { // we may not have had an attr or had an empty attr + mColSpecs = MakeUnique<nsFramesetSpec[]>(1); + mNumCols = 1; + mColSpecs[0].mUnit = eFramesetUnit_Relative; + mColSpecs[0].mValue = 1; + } + } + + *aSpecs = mColSpecs.get(); + *aNumValues = mNumCols; + return NS_OK; +} + +bool HTMLFrameSetElement::ParseAttribute(int32_t aNamespaceID, + nsAtom* aAttribute, + const nsAString& aValue, + nsIPrincipal* aMaybeScriptedPrincipal, + nsAttrValue& aResult) { + if (aNamespaceID == kNameSpaceID_None) { + if (aAttribute == nsGkAtoms::bordercolor) { + return aResult.ParseColor(aValue); + } + if (aAttribute == nsGkAtoms::frameborder) { + return nsGenericHTMLElement::ParseFrameborderValue(aValue, aResult); + } + if (aAttribute == nsGkAtoms::border) { + return aResult.ParseIntWithBounds(aValue, 0, 100); + } + } + + return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue, + aMaybeScriptedPrincipal, aResult); +} + +nsChangeHint HTMLFrameSetElement::GetAttributeChangeHint( + const nsAtom* aAttribute, int32_t aModType) const { + nsChangeHint retval = + nsGenericHTMLElement::GetAttributeChangeHint(aAttribute, aModType); + if (aAttribute == nsGkAtoms::rows || aAttribute == nsGkAtoms::cols) { + retval |= mCurrentRowColHint; + } + return retval; +} + +/** + * Translate a "rows" or "cols" spec into an array of nsFramesetSpecs + */ +nsresult HTMLFrameSetElement::ParseRowCol(const nsAttrValue& aValue, + int32_t& aNumSpecs, + UniquePtr<nsFramesetSpec[]>* aSpecs) { + if (aValue.IsEmptyString()) { + aNumSpecs = 0; + *aSpecs = nullptr; + return NS_OK; + } + + MOZ_ASSERT(aValue.Type() == nsAttrValue::eString); + + static const char16_t sAster('*'); + static const char16_t sPercent('%'); + static const char16_t sComma(','); + + nsAutoString spec(aValue.GetStringValue()); + // remove whitespace (Bug 33699) and quotation marks (bug 224598) + // also remove leading/trailing commas (bug 31482) + spec.StripChars(u" \n\r\t\"\'"); + spec.Trim(","); + + // Count the commas. Don't count more than X commas (bug 576447). + static_assert(NS_MAX_FRAMESET_SPEC_COUNT * sizeof(nsFramesetSpec) < (1 << 30), + "Too many frameset specs allowed to allocate"); + int32_t commaX = spec.FindChar(sComma); + int32_t count = 1; + while (commaX != kNotFound && count < NS_MAX_FRAMESET_SPEC_COUNT) { + count++; + commaX = spec.FindChar(sComma, commaX + 1); + } + + auto specs = MakeUniqueFallible<nsFramesetSpec[]>(count); + if (!specs) { + *aSpecs = nullptr; + aNumSpecs = 0; + return NS_ERROR_OUT_OF_MEMORY; + } + + // Pre-grab the compat mode; we may need it later in the loop. + bool isInQuirks = InNavQuirksMode(OwnerDoc()); + + // Parse each comma separated token + + int32_t start = 0; + int32_t specLen = spec.Length(); + + for (int32_t i = 0; i < count; i++) { + // Find our comma + commaX = spec.FindChar(sComma, start); + NS_ASSERTION(i == count - 1 || commaX != kNotFound, + "Failed to find comma, somehow"); + int32_t end = (commaX == kNotFound) ? specLen : commaX; + + // Note: If end == start then it means that the token has no + // data in it other than a terminating comma (or the end of the spec). + // So default to a fixed width of 0. + specs[i].mUnit = eFramesetUnit_Fixed; + specs[i].mValue = 0; + if (end > start) { + int32_t numberEnd = end; + char16_t ch = spec.CharAt(numberEnd - 1); + if (sAster == ch) { + specs[i].mUnit = eFramesetUnit_Relative; + numberEnd--; + } else if (sPercent == ch) { + specs[i].mUnit = eFramesetUnit_Percent; + numberEnd--; + // check for "*%" + if (numberEnd > start) { + ch = spec.CharAt(numberEnd - 1); + if (sAster == ch) { + specs[i].mUnit = eFramesetUnit_Relative; + numberEnd--; + } + } + } + + // Translate value to an integer + nsAutoString token; + spec.Mid(token, start, numberEnd - start); + + // Treat * as 1* + if ((eFramesetUnit_Relative == specs[i].mUnit) && (0 == token.Length())) { + specs[i].mValue = 1; + } else { + // Otherwise just convert to integer. + nsresult err; + specs[i].mValue = token.ToInteger(&err); + if (NS_FAILED(err)) { + specs[i].mValue = 0; + } + } + + // Treat 0* as 1* in quirks mode (bug 40383) + if (isInQuirks) { + if ((eFramesetUnit_Relative == specs[i].mUnit) && + (0 == specs[i].mValue)) { + specs[i].mValue = 1; + } + } + + // Catch zero and negative frame sizes for Nav compatibility + // Nav resized absolute and relative frames to "1" and + // percent frames to an even percentage of the width + // + // if (isInQuirks && (specs[i].mValue <= 0)) { + // if (eFramesetUnit_Percent == specs[i].mUnit) { + // specs[i].mValue = 100 / count; + // } else { + // specs[i].mValue = 1; + // } + //} else { + + // In standards mode, just set negative sizes to zero + if (specs[i].mValue < 0) { + specs[i].mValue = 0; + } + start = end + 1; + } + } + + aNumSpecs = count; + // Transfer ownership to caller here + *aSpecs = std::move(specs); + + return NS_OK; +} + +bool HTMLFrameSetElement::IsEventAttributeNameInternal(nsAtom* aName) { + return nsContentUtils::IsEventAttributeName( + aName, EventNameType_HTML | EventNameType_HTMLBodyOrFramesetOnly); +} + +#define EVENT(name_, id_, type_, struct_) /* nothing; handled by the shim */ +// nsGenericHTMLElement::GetOnError returns +// already_AddRefed<EventHandlerNonNull> while other getters return +// EventHandlerNonNull*, so allow passing in the type to use here. +#define WINDOW_EVENT_HELPER(name_, type_) \ + type_* HTMLFrameSetElement::GetOn##name_() { \ + if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) { \ + nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \ + return globalWin->GetOn##name_(); \ + } \ + return nullptr; \ + } \ + void HTMLFrameSetElement::SetOn##name_(type_* handler) { \ + nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow(); \ + if (!win) { \ + return; \ + } \ + \ + nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \ + return globalWin->SetOn##name_(handler); \ + } +#define WINDOW_EVENT(name_, id_, type_, struct_) \ + WINDOW_EVENT_HELPER(name_, EventHandlerNonNull) +#define BEFOREUNLOAD_EVENT(name_, id_, type_, struct_) \ + WINDOW_EVENT_HELPER(name_, OnBeforeUnloadEventHandlerNonNull) +#include "mozilla/EventNameList.h" // IWYU pragma: keep +#undef BEFOREUNLOAD_EVENT +#undef WINDOW_EVENT +#undef WINDOW_EVENT_HELPER +#undef EVENT + +} // namespace mozilla::dom |