зеркало из https://github.com/mozilla/gecko-dev.git
Bug 854538 - Add a ::-moz-range-progress pseudo-element to <input type=range> for Firefox OS. r=dholbert
This commit is contained in:
Родитель
7f9fbda32f
Коммит
3ea1b4dc5c
|
@ -2602,7 +2602,7 @@ nsHTMLInputElement::CancelRangeThumbDrag(bool aIsForUserEvent)
|
|||
SetValueInternal(val, true, true);
|
||||
nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
|
||||
if (frame) {
|
||||
frame->UpdateThumbPositionForValueChange();
|
||||
frame->UpdateForValueChange();
|
||||
}
|
||||
}
|
||||
mIsDraggingRange = false;
|
||||
|
@ -2618,7 +2618,7 @@ nsHTMLInputElement::SetValueOfRangeForUserEvent(double aValue)
|
|||
SetValueInternal(val, true, true);
|
||||
nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
|
||||
if (frame) {
|
||||
frame->UpdateThumbPositionForValueChange();
|
||||
frame->UpdateForValueChange();
|
||||
}
|
||||
nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
|
||||
static_cast<nsIDOMHTMLInputElement*>(this),
|
||||
|
|
|
@ -63,59 +63,63 @@ nsRangeFrame::DestroyFrom(nsIFrame* aDestructRoot)
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsRangeFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
|
||||
nsRangeFrame::MakeAnonymousDiv(nsIContent** aResult,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
nsTArray<ContentInfo>& aElements)
|
||||
{
|
||||
// Get the NodeInfoManager and tag necessary to create the anonymous divs.
|
||||
nsCOMPtr<nsIDocument> doc = mContent->GetDocument();
|
||||
|
||||
// Create the track div:
|
||||
nsCOMPtr<nsINodeInfo> nodeInfo;
|
||||
nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::div, nullptr,
|
||||
kNameSpaceID_XHTML,
|
||||
nsIDOMNode::ELEMENT_NODE);
|
||||
NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
|
||||
nsresult rv = NS_NewHTMLElement(getter_AddRefs(mTrackDiv), nodeInfo.forget(),
|
||||
nsresult rv = NS_NewHTMLElement(aResult, nodeInfo.forget(),
|
||||
mozilla::dom::NOT_FROM_PARSER);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// Associate ::-moz-range-track pseudo-element to the anonymous child.
|
||||
nsCSSPseudoElements::Type pseudoType =
|
||||
nsCSSPseudoElements::ePseudo_mozRangeTrack;
|
||||
// Associate the pseudo-element with the anonymous child.
|
||||
nsRefPtr<nsStyleContext> newStyleContext =
|
||||
PresContext()->StyleSet()->ResolvePseudoElementStyle(mContent->AsElement(),
|
||||
pseudoType,
|
||||
aPseudoType,
|
||||
StyleContext());
|
||||
|
||||
if (!aElements.AppendElement(ContentInfo(mTrackDiv, newStyleContext))) {
|
||||
if (!aElements.AppendElement(ContentInfo(*aResult, newStyleContext))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// Create the thumb div:
|
||||
nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::div, nullptr,
|
||||
kNameSpaceID_XHTML,
|
||||
nsIDOMNode::ELEMENT_NODE);
|
||||
NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
|
||||
rv = NS_NewHTMLElement(getter_AddRefs(mThumbDiv), nodeInfo.forget(),
|
||||
mozilla::dom::NOT_FROM_PARSER);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// Associate ::-moz-range-thumb pseudo-element to the anonymous child.
|
||||
pseudoType = nsCSSPseudoElements::ePseudo_mozRangeThumb;
|
||||
newStyleContext =
|
||||
PresContext()->StyleSet()->ResolvePseudoElementStyle(mContent->AsElement(),
|
||||
pseudoType,
|
||||
StyleContext());
|
||||
|
||||
if (!aElements.AppendElement(ContentInfo(mThumbDiv, newStyleContext))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRangeFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Create the ::-moz-range-track pseuto-element (a div):
|
||||
rv = MakeAnonymousDiv(getter_AddRefs(mTrackDiv),
|
||||
nsCSSPseudoElements::ePseudo_mozRangeTrack,
|
||||
aElements);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Create the ::-moz-range-progress pseudo-element (a div):
|
||||
rv = MakeAnonymousDiv(getter_AddRefs(mProgressDiv),
|
||||
nsCSSPseudoElements::ePseudo_mozRangeProgress,
|
||||
aElements);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Create the ::-moz-range-thumb pseudo-element (a div):
|
||||
rv = MakeAnonymousDiv(getter_AddRefs(mThumbDiv),
|
||||
nsCSSPseudoElements::ePseudo_mozRangeThumb,
|
||||
aElements);
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
nsRangeFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
|
||||
uint32_t aFilter)
|
||||
{
|
||||
aElements.MaybeAppendElement(mTrackDiv);
|
||||
aElements.MaybeAppendElement(mProgressDiv);
|
||||
aElements.MaybeAppendElement(mThumbDiv);
|
||||
}
|
||||
|
||||
|
@ -136,8 +140,9 @@ nsRangeFrame::Reflow(nsPresContext* aPresContext,
|
|||
DO_GLOBAL_REFLOW_COUNT("nsRangeFrame");
|
||||
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
||||
|
||||
NS_ASSERTION(mTrackDiv, "Track div must exist!");
|
||||
NS_ASSERTION(mThumbDiv, "Thumb div must exist!");
|
||||
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.");
|
||||
|
@ -166,6 +171,11 @@ nsRangeFrame::Reflow(nsPresContext* aPresContext,
|
|||
ConsiderChildOverflow(aDesiredSize.mOverflowAreas, trackFrame);
|
||||
}
|
||||
|
||||
nsIFrame* rangeProgressFrame = mProgressDiv->GetPrimaryFrame();
|
||||
if (rangeProgressFrame) {
|
||||
ConsiderChildOverflow(aDesiredSize.mOverflowAreas, rangeProgressFrame);
|
||||
}
|
||||
|
||||
nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame();
|
||||
if (thumbFrame) {
|
||||
ConsiderChildOverflow(aDesiredSize.mOverflowAreas, thumbFrame);
|
||||
|
@ -265,6 +275,34 @@ nsRangeFrame::ReflowAnonymousContent(nsPresContext* aPresContext,
|
|||
aDesiredSize.height));
|
||||
}
|
||||
|
||||
nsIFrame* rangeProgressFrame = mProgressDiv->GetPrimaryFrame();
|
||||
|
||||
if (rangeProgressFrame) { // display:none?
|
||||
nsHTMLReflowState progressReflowState(aPresContext, aReflowState,
|
||||
rangeProgressFrame,
|
||||
nsSize(aReflowState.ComputedWidth(),
|
||||
NS_UNCONSTRAINEDSIZE));
|
||||
|
||||
// We first reflow the range-progress frame at {0,0} to obtain its
|
||||
// unadjusted dimensions, then we adjust it to so that the appropriate edge
|
||||
// ends at the thumb.
|
||||
|
||||
nsReflowStatus frameStatus = NS_FRAME_COMPLETE;
|
||||
nsHTMLReflowMetrics progressDesiredSize;
|
||||
nsresult rv = ReflowChild(rangeProgressFrame, aPresContext,
|
||||
progressDesiredSize, progressReflowState, 0, 0,
|
||||
0, frameStatus);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(frameStatus),
|
||||
"We gave our child unconstrained height, so it should be complete");
|
||||
rv = FinishReflowChild(rangeProgressFrame, aPresContext,
|
||||
&progressReflowState, progressDesiredSize, 0, 0, 0);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
DoUpdateRangeProgressFrame(rangeProgressFrame, nsSize(aDesiredSize.width,
|
||||
aDesiredSize.height));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -382,16 +420,22 @@ nsRangeFrame::GetValueAtEventPoint(nsGUIEvent* aEvent)
|
|||
}
|
||||
|
||||
void
|
||||
nsRangeFrame::UpdateThumbPositionForValueChange()
|
||||
nsRangeFrame::UpdateForValueChange()
|
||||
{
|
||||
if (NS_SUBTREE_DIRTY(this)) {
|
||||
return; // we're going to be updated when we reflow
|
||||
}
|
||||
nsIFrame* rangeProgressFrame = mProgressDiv->GetPrimaryFrame();
|
||||
nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame();
|
||||
if (!thumbFrame) {
|
||||
if (!rangeProgressFrame && !thumbFrame) {
|
||||
return; // diplay:none?
|
||||
}
|
||||
DoUpdateThumbPosition(thumbFrame, GetSize());
|
||||
if (rangeProgressFrame) {
|
||||
DoUpdateRangeProgressFrame(rangeProgressFrame, GetSize());
|
||||
}
|
||||
if (thumbFrame) {
|
||||
DoUpdateThumbPosition(thumbFrame, GetSize());
|
||||
}
|
||||
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.
|
||||
|
@ -449,6 +493,51 @@ nsRangeFrame::DoUpdateThumbPosition(nsIFrame* aThumbFrame,
|
|||
aThumbFrame->SetPosition(newPosition);
|
||||
}
|
||||
|
||||
void
|
||||
nsRangeFrame::DoUpdateRangeProgressFrame(nsIFrame* aRangeProgressFrame,
|
||||
const nsSize& aRangeSize)
|
||||
{
|
||||
MOZ_ASSERT(aRangeProgressFrame);
|
||||
|
||||
// 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 = aRangeProgressFrame->GetSize();
|
||||
nsRect progRect(borderAndPadding.left, borderAndPadding.top,
|
||||
progSize.width, progSize.height);
|
||||
|
||||
nsSize rangeContentBoxSize(aRangeSize);
|
||||
rangeContentBoxSize.width -= borderAndPadding.LeftRight();
|
||||
rangeContentBoxSize.height -= borderAndPadding.TopBottom();
|
||||
|
||||
double fraction = GetValueAsFractionOfRange();
|
||||
MOZ_ASSERT(fraction >= 0.0 && fraction <= 1.0);
|
||||
|
||||
// We are called under Reflow, so we need to pass IsHorizontal a valid rect.
|
||||
nsSize frameSizeOverride(aRangeSize.width, aRangeSize.height);
|
||||
if (IsHorizontal(&frameSizeOverride)) {
|
||||
nscoord progLength = NSToCoordRound(fraction * rangeContentBoxSize.width);
|
||||
if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
|
||||
progRect.x += rangeContentBoxSize.width - progLength;
|
||||
}
|
||||
progRect.y += (rangeContentBoxSize.height - progSize.height)/2;
|
||||
progRect.width = progLength;
|
||||
} else {
|
||||
nscoord progLength = NSToCoordRound(fraction * rangeContentBoxSize.height);
|
||||
progRect.x += (rangeContentBoxSize.width - progSize.width)/2;
|
||||
progRect.y += rangeContentBoxSize.height - progLength;
|
||||
progRect.height = progLength;
|
||||
}
|
||||
aRangeProgressFrame->SetRect(progRect);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsRangeFrame::AttributeChanged(int32_t aNameSpaceID,
|
||||
nsIAtom* aAttribute,
|
||||
|
@ -467,17 +556,17 @@ nsRangeFrame::AttributeChanged(int32_t aNameSpaceID,
|
|||
// in the middle of a type change away from type=range, under the
|
||||
// SetAttr(..., nsGkAtoms::value, ...) call in nsHTMLInputElement::
|
||||
// HandleTypeChange. In that case the nsHTMLInputElement's type will
|
||||
// already have changed, and if we call UpdateThumbPositionForValueChange()
|
||||
// already have changed, and if we call UpdateForValueChange()
|
||||
// we'll fail the asserts under that call that check the type of our
|
||||
// nsHTMLInputElement. Given that we're changing away from being a range
|
||||
// and this frame will shortly be destroyed, there's no point in calling
|
||||
// UpdateThumbPositionForValueChange() anyway.
|
||||
// UpdateForValueChange() anyway.
|
||||
MOZ_ASSERT(mContent->IsHTML(nsGkAtoms::input), "bad cast");
|
||||
bool typeIsRange = static_cast<nsHTMLInputElement*>(mContent)->GetType() ==
|
||||
NS_FORM_INPUT_RANGE;
|
||||
MOZ_ASSERT(typeIsRange || aAttribute == nsGkAtoms::value, "why?");
|
||||
if (typeIsRange) {
|
||||
UpdateThumbPositionForValueChange();
|
||||
UpdateForValueChange();
|
||||
}
|
||||
} else if (aAttribute == nsGkAtoms::orient) {
|
||||
PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eResize,
|
||||
|
@ -600,6 +689,8 @@ nsRangeFrame::ShouldUseNativeStyle() const
|
|||
STYLES_DISABLING_NATIVE_THEMING) &&
|
||||
!PresContext()->HasAuthorSpecifiedRules(mTrackDiv->GetPrimaryFrame(),
|
||||
STYLES_DISABLING_NATIVE_THEMING) &&
|
||||
!PresContext()->HasAuthorSpecifiedRules(mProgressDiv->GetPrimaryFrame(),
|
||||
STYLES_DISABLING_NATIVE_THEMING) &&
|
||||
!PresContext()->HasAuthorSpecifiedRules(mThumbDiv->GetPrimaryFrame(),
|
||||
STYLES_DISABLING_NATIVE_THEMING);
|
||||
}
|
||||
|
|
|
@ -97,14 +97,20 @@ public:
|
|||
double GetValueAtEventPoint(nsGUIEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Helper to reposition the thumb and schedule a repaint when the value of
|
||||
* the range changes. (This does not reflow, since the position and size of
|
||||
* the thumb do not affect the position or size of any other frames.)
|
||||
* Helper that's used when the value of the range changes to reposition the
|
||||
* thumb, resize the range-progress element, and schedule a repaint. (This
|
||||
* does not reflow, since the position and size of the thumb and
|
||||
* range-progress element do not affect the position or size of any other
|
||||
* frames.)
|
||||
*/
|
||||
void UpdateThumbPositionForValueChange();
|
||||
void UpdateForValueChange();
|
||||
|
||||
private:
|
||||
|
||||
nsresult MakeAnonymousDiv(nsIContent** aResult,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
nsTArray<ContentInfo>& aElements);
|
||||
|
||||
// Helper function which reflows the anonymous div frames.
|
||||
nsresult ReflowAnonymousContent(nsPresContext* aPresContext,
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
|
@ -113,6 +119,9 @@ private:
|
|||
void DoUpdateThumbPosition(nsIFrame* aThumbFrame,
|
||||
const nsSize& aRangeSize);
|
||||
|
||||
void DoUpdateRangeProgressFrame(nsIFrame* aProgressFrame,
|
||||
const nsSize& aRangeSize);
|
||||
|
||||
/**
|
||||
* Returns the input element's value as a fraction of the difference between
|
||||
* the input's minimum and its maximum (i.e. returns 0.0 when the value is
|
||||
|
@ -122,13 +131,21 @@ private:
|
|||
double GetValueAsFractionOfRange();
|
||||
|
||||
/**
|
||||
* The div used to show the track.
|
||||
* The div used to show the ::-moz-range-track pseudo-element.
|
||||
* @see nsRangeFrame::CreateAnonymousContent
|
||||
*/
|
||||
nsCOMPtr<nsIContent> mTrackDiv;
|
||||
|
||||
/**
|
||||
* The div used to show the thumb.
|
||||
* The div used to show the ::-moz-range-progress pseudo-element, which is
|
||||
* used to (optionally) style the specific chunk of track leading up to the
|
||||
* thumb's current position.
|
||||
* @see nsRangeFrame::CreateAnonymousContent
|
||||
*/
|
||||
nsCOMPtr<nsIContent> mProgressDiv;
|
||||
|
||||
/**
|
||||
* The div used to show the ::-moz-range-thumb pseudo-element.
|
||||
* @see nsRangeFrame::CreateAnonymousContent
|
||||
*/
|
||||
nsCOMPtr<nsIContent> mThumbDiv;
|
||||
|
|
|
@ -770,6 +770,28 @@ input[type=range][orient=vertical]::-moz-range-track {
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout handles positioning of this pseudo-element specially (so that content
|
||||
* authors can concentrate on styling this pseudo-element without worrying
|
||||
* about the logic to position it). Specifically the 'margin', 'top' and 'left'
|
||||
* properties are ignored. Additionally, if the range is horizontal, the width
|
||||
* property is ignored, and if the range range is vertical, the height property
|
||||
* is ignored.
|
||||
*/
|
||||
::-moz-range-progress {
|
||||
/* Prevent styling that would change the type of frame we construct. */
|
||||
display: inline-block !important;
|
||||
float: none !important;
|
||||
position: static !important;
|
||||
/* Since one of width/height will be ignored, this just sets the "other"
|
||||
dimension.
|
||||
*/
|
||||
width: 0.2em;
|
||||
height: 0.2em;
|
||||
/* Prevent nsFrame::HandlePress setting mouse capture to this element. */
|
||||
-moz-user-select: none ! important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout handles positioning of this pseudo-element specially (so that content
|
||||
* authors can concentrate on styling the thumb without worrying about the
|
||||
|
|
|
@ -54,6 +54,7 @@ CSS_PSEUDO_ELEMENT(mozMathAnonymous, ":-moz-math-anonymous", 0)
|
|||
// HTML5 Forms pseudo elements
|
||||
CSS_PSEUDO_ELEMENT(mozProgressBar, ":-moz-progress-bar", 0)
|
||||
CSS_PSEUDO_ELEMENT(mozRangeTrack, ":-moz-range-track", 0)
|
||||
CSS_PSEUDO_ELEMENT(mozRangeProgress, ":-moz-range-progress", 0)
|
||||
CSS_PSEUDO_ELEMENT(mozRangeThumb, ":-moz-range-thumb", 0)
|
||||
CSS_PSEUDO_ELEMENT(mozMeterBar, ":-moz-meter-bar", 0)
|
||||
CSS_PSEUDO_ELEMENT(mozPlaceholder, ":-moz-placeholder", 0)
|
||||
|
|
Загрузка…
Ссылка в новой задаче