/* -*- 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/. */
void nsRangeFrame::Destroy(DestroyContext& aContext) {
NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(), "nsRangeFrame should not have continuations; if it does we " "need to call RegUnregAccessKey only for the first.");
if (mListMutationObserver) {
mListMutationObserver->Detach();
}
aContext.AddAnonymousContent(mTrackDiv.forget());
aContext.AddAnonymousContent(mProgressDiv.forget());
aContext.AddAnonymousContent(mThumbDiv.forget());
nsContainerFrame::Destroy(aContext);
}
// Associate the pseudo-element with the anonymous child. if (StaticPrefs::layout_css_modern_range_pseudos_enabled()) {
result->SetPseudoElementType(aModernPseudoType);
} else {
result->SetPseudoElementType(aOldPseudoType);
}
// XXX(Bug 1631371) Check if this should use a fallible operation as it // pretended earlier, or change the return type to void.
aElements.AppendElement(result);
return result.forget();
}
nsresult nsRangeFrame::CreateAnonymousContent(
nsTArray<ContentInfo>& aElements) {
Document* doc = mContent->OwnerDoc(); // Create the ::-moz-range-track pseudo-element (a div):
mTrackDiv = MakeAnonymousDiv(*doc, PseudoStyleType::mozRangeTrack,
PseudoStyleType::sliderTrack, aElements); // Create the ::-moz-range-progress pseudo-element (a div):
mProgressDiv = MakeAnonymousDiv(*doc, PseudoStyleType::mozRangeProgress,
PseudoStyleType::sliderFill, aElements); // Create the ::-moz-range-thumb pseudo-element (a div):
mThumbDiv = MakeAnonymousDiv(*doc, PseudoStyleType::mozRangeThumb,
PseudoStyleType::sliderThumb, aElements); return NS_OK;
}
if (mProgressDiv) {
aElements.AppendElement(mProgressDiv);
}
if (mThumbDiv) {
aElements.AppendElement(mThumbDiv);
}
}
void nsRangeFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) { const nsStyleDisplay* disp = StyleDisplay(); if (IsThemed(disp)) {
DisplayBorderBackgroundOutline(aBuilder, aLists); // Only create items for the thumb. Specifically, we do not want the track // to paint, since *our* background is used to paint the track, and we don't // want the unthemed track painting over the top of the themed track. // This logic is copied from // nsContainerFrame::BuildDisplayListForNonBlockChildren as // called by BuildDisplayListForInline. if (nsIFrame* thumb = mThumbDiv->GetPrimaryFrame()) {
nsDisplayListSet set(aLists, aLists.Content());
BuildDisplayListForChild(aBuilder, thumb, set, DisplayChildFlag::Inline);
}
} else {
BuildDisplayListForInline(aBuilder, aLists);
}
}
NS_ASSERTION(mTrackDiv, "::-moz-range-track div must exist!");
NS_ASSERTION(mProgressDiv, "::-moz-range-progress div must exist!");
NS_ASSERTION(mThumbDiv, "::-moz-range-thumb div must exist!");
NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(), "nsRangeFrame should not have continuations; if it does we " "need to call RegUnregAccessKey only for the first.");
MOZ_ASSERT(aStatus.IsEmpty(), "This type of frame can't be split.");
}
void nsRangeFrame::ReflowAnonymousContent(nsPresContext* aPresContext,
ReflowOutput& aDesiredSize, const LogicalSize& aContentBoxSize, const ReflowInput& aReflowInput) { constauto parentWM = aReflowInput.GetWritingMode(); // The width/height of our content box, which is the available width/height // for our anonymous content. const nsSize rangeFrameContentBoxSize =
aContentBoxSize.GetPhysicalSize(parentWM); for (auto* div : {mTrackDiv.get(), mThumbDiv.get(), mProgressDiv.get()}) {
nsIFrame* child = div->GetPrimaryFrame(); if (!child) { continue;
} const WritingMode wm = child->GetWritingMode(); const LogicalSize parentSizeInChildWM =
aContentBoxSize.ConvertTo(wm, parentWM);
LogicalSize availSize = parentSizeInChildWM;
availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
ReflowInput childReflowInput(aPresContext, aReflowInput, child, availSize,
Some(parentSizeInChildWM));
const nsPoint pos = [&] { if (div != mTrackDiv) { // Where we position the thumb and range-progress depends on its size, // so we first reflow them at {0,0} to obtain the size, then position // them afterwards. return nsPoint();
} // Find the x/y position of the track. The idea here is that we allow // content authors to style the width, height, border and padding of the // track, but we ignore margin and positioning properties and do the // positioning ourself to keep the center of the track's border box on the // center of the nsRangeFrame's content. These coordinates are with // respect to the nsRangeFrame's border-box.
nscoord trackX = rangeFrameContentBoxSize.Width() / 2;
nscoord trackY = rangeFrameContentBoxSize.Height() / 2;
// Account for the track's border and padding (we ignore its margin): // FIXME(emilio): Assumes the track height is constrained, which might not // be true if authors override it.
trackX -= childReflowInput.ComputedPhysicalBorderPadding().left +
childReflowInput.ComputedWidth() / 2;
trackY -= childReflowInput.ComputedPhysicalBorderPadding().top +
childReflowInput.ComputedHeight() / 2;
// Make relative to our border box instead of our content box:
trackX += aReflowInput.ComputedPhysicalBorderPadding().left;
trackY += aReflowInput.ComputedPhysicalBorderPadding().top; return nsPoint(trackX, trackY);
}();
nsReflowStatus frameStatus;
ReflowOutput childDesiredSize(aReflowInput);
ReflowChild(child, aPresContext, childDesiredSize, childReflowInput, pos.x,
pos.y, ReflowChildFlags::Default, frameStatus);
MOZ_ASSERT(
frameStatus.IsFullyComplete(), "We gave our child unconstrained height, so it should be complete");
FinishReflowChild(child, aPresContext, childDesiredSize, &childReflowInput,
pos.x, pos.y, ReflowChildFlags::Default); if (div == mThumbDiv) {
DoUpdateThumbPosition(child, rangeFrameContentBoxSize);
} elseif (div == mProgressDiv) {
DoUpdateRangeProgressFrame(child, rangeFrameContentBoxSize);
}
ConsiderChildOverflow(aDesiredSize.mOverflowAreas, child);
}
}
double nsRangeFrame::GetValueAsFractionOfRange() { constauto& input = InputElement(); if (MOZ_UNLIKELY(!input.IsDoneCreating())) { // Our element isn't done being created, so its values haven't yet been // sanitized! (It's rare that we'd be reflowed when our element is in this // state, but it can happen if the parser decides to yield while processing // its tasks to build the element.) We can't trust that any of our numeric // values will make sense until they've been sanitized; so for now, just // use 0.0 as a fallback fraction-of-range value here (i.e. behave as if // we're at our minimum, which is how the spec handles some edge cases). return 0.0;
} return GetDoubleAsFractionOfRange(input.GetValueAsDecimal());
}
Decimal minimum = input->GetMinimum();
Decimal maximum = input->GetMaximum();
MOZ_ASSERT(minimum.isFinite() && maximum.isFinite(), "type=range should have a default maximum/minimum"); if (maximum <= minimum) { return minimum;
}
Decimal range = maximum - minimum;
LayoutDeviceIntPoint absPoint; if (aEvent->mClass == eTouchEventClass) {
MOZ_ASSERT(aEvent->AsTouchEvent()->mTouches.Length() == 1, "Unexpected number of mTouches");
absPoint = aEvent->AsTouchEvent()->mTouches[0]->mRefPoint;
} else {
absPoint = aEvent->mRefPoint;
}
nsPoint point = nsLayoutUtils::GetEventCoordinatesRelativeTo(
aEvent, absPoint, RelativeTo{this});
if (point == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) { // We don't want to change the current value for this error state. returnstatic_cast<dom::HTMLInputElement*>(GetContent())
->GetValueAsDecimal();
}
nsRect rangeRect;
nsSize thumbSize; if (IsThemed()) { // Themed ranges draw on the border-box rect.
rangeRect = GetRectRelativeToSelf(); // We need to get the size of the thumb from the theme.
nsPresContext* pc = PresContext();
LayoutDeviceIntSize size = pc->Theme()->GetMinimumWidgetSize(
pc, this, StyleAppearance::RangeThumb);
thumbSize =
LayoutDeviceIntSize::ToAppUnits(size, pc->AppUnitsPerDevPixel()); // For GTK, GetMinimumWidgetSize returns zero for the thumb dimension // perpendicular to the orientation of the slider. That's okay since we // only care about the dimension in the direction of the slider when using // |thumbSize| below, but it means this assertion need to check // IsHorizontal().
MOZ_ASSERT((IsHorizontal() && thumbSize.width > 0) ||
(!IsHorizontal() && thumbSize.height > 0), "The thumb is expected to take up some slider space");
} else {
rangeRect = GetContentRectRelativeToSelf();
nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame(); if (thumbFrame) { // diplay:none?
thumbSize = thumbFrame->GetSize();
}
}
Decimal fraction; if (IsHorizontal()) {
nscoord traversableDistance = rangeRect.width - thumbSize.width; if (traversableDistance <= 0) { return minimum;
}
nscoord posAtStart = rangeRect.x + thumbSize.width / 2;
nscoord posAtEnd = posAtStart + traversableDistance;
nscoord posOfPoint = std::clamp(point.x, posAtStart, posAtEnd);
fraction = Decimal(posOfPoint - posAtStart) / Decimal(traversableDistance); if (IsRightToLeft()) {
fraction = Decimal(1) - fraction;
}
} else {
nscoord traversableDistance = rangeRect.height - thumbSize.height; if (traversableDistance <= 0) { return minimum;
}
nscoord posAtStart = rangeRect.y + thumbSize.height / 2;
nscoord posAtEnd = posAtStart + traversableDistance;
nscoord posOfPoint = std::clamp(point.y, posAtStart, posAtEnd); // For a vertical range, the top (posAtStart) is the highest value, so we // subtract the fraction from 1.0 to get that polarity correct.
fraction = Decimal(posOfPoint - posAtStart) / Decimal(traversableDistance); if (IsUpwards()) {
fraction = Decimal(1) - fraction;
}
}
void nsRangeFrame::UpdateForValueChange() { if (IsSubtreeDirty()) { return; // we're going to be updated when we reflow
}
nsIFrame* rangeProgressFrame = mProgressDiv->GetPrimaryFrame();
nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame(); if (!rangeProgressFrame && !thumbFrame) { return; // diplay:none?
} const nsSize contentBoxSize = GetContentRect().Size(); if (rangeProgressFrame) {
DoUpdateRangeProgressFrame(rangeProgressFrame, contentBoxSize);
} if (thumbFrame) {
DoUpdateThumbPosition(thumbFrame, contentBoxSize);
} if (IsThemed()) { // We don't know the exact dimensions or location of the thumb when native // theming is applied, so we just repaint the entire range.
InvalidateFrame();
}
// The idea here is that we want to position the thumb so that the center // of the thumb is on an imaginary line drawn from the middle of one edge // of the range frame's content box to the middle of the opposite edge of // its content box (the opposite edges being the left/right edge if the // range is horizontal, or else the top/bottom edges if the range is // vertical). How far along this line the center of the thumb is placed // depends on the value of the range.
// The idea here is that we want to position the ::-moz-range-progress // pseudo-element so that the center line running along its length is on the // corresponding center line of the nsRangeFrame's content box. In the other // dimension, we align the "start" edge of the ::-moz-range-progress // pseudo-element's border-box with the corresponding edge of the // nsRangeFrame's content box, and we size the progress element's border-box // to have a length of GetValueAsFractionOfRange() times the nsRangeFrame's // content-box size.
nsMargin borderAndPadding = GetUsedBorderAndPadding();
nsSize progSize = aProgressFrame->GetSize();
nsRect progRect(borderAndPadding.left, borderAndPadding.top, progSize.width,
progSize.height);
nsresult nsRangeFrame::AttributeChanged(int32_t aNameSpaceID,
nsAtom* aAttribute, int32_t aModType) {
NS_ASSERTION(mTrackDiv, "The track div must exist!");
NS_ASSERTION(mThumbDiv, "The thumb div must exist!");
if (aNameSpaceID == kNameSpaceID_None) { if (aAttribute == nsGkAtoms::value || aAttribute == nsGkAtoms::min ||
aAttribute == nsGkAtoms::max || aAttribute == nsGkAtoms::step) { // We want to update the position of the thumb, except in one special // case: If the value attribute is being set, it is possible that we are // in the middle of a type change away from type=range, under the // SetAttr(..., nsGkAtoms::value, ...) call in HTMLInputElement:: // HandleTypeChange. In that case the HTMLInputElement's type will // already have changed, and if we call UpdateForValueChange() // we'll fail the asserts under that call that check the type of our // HTMLInputElement. Given that we're changing away from being a range // and this frame will shortly be destroyed, there's no point in calling // UpdateForValueChange() anyway.
MOZ_ASSERT(mContent->IsHTMLElement(nsGkAtoms::input), "bad cast"); bool typeIsRange = static_cast<dom::HTMLInputElement*>(GetContent())->ControlType() ==
FormControlType::InputRange; // If script changed the <input>'s type before setting these attributes // then we don't need to do anything since we are going to be reframed. if (typeIsRange) {
UpdateForValueChange();
}
} elseif (aAttribute == nsGkAtoms::orient) {
PresShell()->FrameNeedsReflow(this, IntrinsicDirty::None,
NS_FRAME_IS_DIRTY);
} elseif (aAttribute == nsGkAtoms::list_) { constbool isRemoval = aModType == MutationEvent_Binding::REMOVAL; if (mListMutationObserver) {
mListMutationObserver->Detach(); if (isRemoval) {
mListMutationObserver = nullptr;
} else {
mListMutationObserver->Attach();
}
} elseif (!isRemoval) {
mListMutationObserver = new ListMutationObserver(*this, true);
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.