/src/mozilla-central/layout/forms/nsRangeFrame.cpp
Line | Count | Source (jump to first uncovered line) |
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 "nsRangeFrame.h" |
8 | | |
9 | | #include "mozilla/EventStates.h" |
10 | | #include "mozilla/TouchEvents.h" |
11 | | |
12 | | #include "gfxContext.h" |
13 | | #include "nsContentCreatorFunctions.h" |
14 | | #include "nsCSSPseudoElements.h" |
15 | | #include "nsCSSRendering.h" |
16 | | #include "nsCheckboxRadioFrame.h" |
17 | | #include "nsIContent.h" |
18 | | #include "nsIDocument.h" |
19 | | #include "nsNameSpaceManager.h" |
20 | | #include "nsIPresShell.h" |
21 | | #include "nsGkAtoms.h" |
22 | | #include "mozilla/dom/HTMLInputElement.h" |
23 | | #include "nsPresContext.h" |
24 | | #include "nsNodeInfoManager.h" |
25 | | #include "mozilla/dom/Element.h" |
26 | | #include "mozilla/ServoStyleSet.h" |
27 | | #include "nsStyleConsts.h" |
28 | | |
29 | | #ifdef ACCESSIBILITY |
30 | | #include "nsAccessibilityService.h" |
31 | | #endif |
32 | | |
33 | 0 | #define LONG_SIDE_TO_SHORT_SIDE_RATIO 10 |
34 | | |
35 | | using namespace mozilla; |
36 | | using namespace mozilla::dom; |
37 | | using namespace mozilla::image; |
38 | | |
39 | | NS_IMPL_ISUPPORTS(nsRangeFrame::DummyTouchListener, nsIDOMEventListener) |
40 | | |
41 | | nsIFrame* |
42 | | NS_NewRangeFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle) |
43 | 0 | { |
44 | 0 | return new (aPresShell) nsRangeFrame(aStyle); |
45 | 0 | } |
46 | | |
47 | | nsRangeFrame::nsRangeFrame(ComputedStyle* aStyle) |
48 | | : nsContainerFrame(aStyle, kClassID) |
49 | 0 | { |
50 | 0 | } |
51 | | |
52 | | nsRangeFrame::~nsRangeFrame() |
53 | 0 | { |
54 | 0 | } |
55 | | |
56 | | NS_IMPL_FRAMEARENA_HELPERS(nsRangeFrame) |
57 | | |
58 | 0 | NS_QUERYFRAME_HEAD(nsRangeFrame) |
59 | 0 | NS_QUERYFRAME_ENTRY(nsRangeFrame) |
60 | 0 | NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) |
61 | 0 | NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) |
62 | | |
63 | | void |
64 | | nsRangeFrame::Init(nsIContent* aContent, |
65 | | nsContainerFrame* aParent, |
66 | | nsIFrame* aPrevInFlow) |
67 | 0 | { |
68 | 0 | // With APZ enabled, touch events may be handled directly by the APZC code |
69 | 0 | // if the APZ knows that there is no content interested in the touch event. |
70 | 0 | // The range input element *is* interested in touch events, but doesn't use |
71 | 0 | // the usual mechanism (i.e. registering an event listener) to handle touch |
72 | 0 | // input. Instead, we do it here so that the APZ finds out about it, and |
73 | 0 | // makes sure to wait for content to run handlers before handling the touch |
74 | 0 | // input itself. |
75 | 0 | if (!mDummyTouchListener) { |
76 | 0 | mDummyTouchListener = new DummyTouchListener(); |
77 | 0 | } |
78 | 0 | aContent->AddEventListener(NS_LITERAL_STRING("touchstart"), mDummyTouchListener, false); |
79 | 0 |
|
80 | 0 | ServoStyleSet* styleSet = PresContext()->StyleSet(); |
81 | 0 |
|
82 | 0 | mOuterFocusStyle = |
83 | 0 | styleSet->ProbePseudoElementStyle(*aContent->AsElement(), |
84 | 0 | CSSPseudoElementType::mozFocusOuter, |
85 | 0 | Style()); |
86 | 0 |
|
87 | 0 | return nsContainerFrame::Init(aContent, aParent, aPrevInFlow); |
88 | 0 | } |
89 | | |
90 | | void |
91 | | nsRangeFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) |
92 | 0 | { |
93 | 0 | NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(), |
94 | 0 | "nsRangeFrame should not have continuations; if it does we " |
95 | 0 | "need to call RegUnregAccessKey only for the first."); |
96 | 0 |
|
97 | 0 | mContent->RemoveEventListener(NS_LITERAL_STRING("touchstart"), mDummyTouchListener, false); |
98 | 0 |
|
99 | 0 | nsCheckboxRadioFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false); |
100 | 0 | aPostDestroyData.AddAnonymousContent(mTrackDiv.forget()); |
101 | 0 | aPostDestroyData.AddAnonymousContent(mProgressDiv.forget()); |
102 | 0 | aPostDestroyData.AddAnonymousContent(mThumbDiv.forget()); |
103 | 0 | nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData); |
104 | 0 | } |
105 | | |
106 | | nsresult |
107 | | nsRangeFrame::MakeAnonymousDiv(Element** aResult, |
108 | | CSSPseudoElementType aPseudoType, |
109 | | nsTArray<ContentInfo>& aElements) |
110 | 0 | { |
111 | 0 | nsCOMPtr<nsIDocument> doc = mContent->GetComposedDoc(); |
112 | 0 | RefPtr<Element> resultElement = doc->CreateHTMLElement(nsGkAtoms::div); |
113 | 0 |
|
114 | 0 | // Associate the pseudo-element with the anonymous child. |
115 | 0 | resultElement->SetPseudoElementType(aPseudoType); |
116 | 0 |
|
117 | 0 | if (!aElements.AppendElement(resultElement)) { |
118 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
119 | 0 | } |
120 | 0 | |
121 | 0 | resultElement.forget(aResult); |
122 | 0 | return NS_OK; |
123 | 0 | } |
124 | | |
125 | | nsresult |
126 | | nsRangeFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) |
127 | 0 | { |
128 | 0 | nsresult rv; |
129 | 0 |
|
130 | 0 | // Create the ::-moz-range-track pseuto-element (a div): |
131 | 0 | rv = MakeAnonymousDiv(getter_AddRefs(mTrackDiv), |
132 | 0 | CSSPseudoElementType::mozRangeTrack, |
133 | 0 | aElements); |
134 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
135 | 0 |
|
136 | 0 | // Create the ::-moz-range-progress pseudo-element (a div): |
137 | 0 | rv = MakeAnonymousDiv(getter_AddRefs(mProgressDiv), |
138 | 0 | CSSPseudoElementType::mozRangeProgress, |
139 | 0 | aElements); |
140 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
141 | 0 |
|
142 | 0 | // Create the ::-moz-range-thumb pseudo-element (a div): |
143 | 0 | rv = MakeAnonymousDiv(getter_AddRefs(mThumbDiv), |
144 | 0 | CSSPseudoElementType::mozRangeThumb, |
145 | 0 | aElements); |
146 | 0 | return rv; |
147 | 0 | } |
148 | | |
149 | | void |
150 | | nsRangeFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, |
151 | | uint32_t aFilter) |
152 | 0 | { |
153 | 0 | if (mTrackDiv) { |
154 | 0 | aElements.AppendElement(mTrackDiv); |
155 | 0 | } |
156 | 0 |
|
157 | 0 | if (mProgressDiv) { |
158 | 0 | aElements.AppendElement(mProgressDiv); |
159 | 0 | } |
160 | 0 |
|
161 | 0 | if (mThumbDiv) { |
162 | 0 | aElements.AppendElement(mThumbDiv); |
163 | 0 | } |
164 | 0 | } |
165 | | |
166 | | class nsDisplayRangeFocusRing final : public nsDisplayItem |
167 | | { |
168 | | public: |
169 | | nsDisplayRangeFocusRing(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) |
170 | 0 | : nsDisplayItem(aBuilder, aFrame) { |
171 | 0 | MOZ_COUNT_CTOR(nsDisplayRangeFocusRing); |
172 | 0 | } |
173 | | #ifdef NS_BUILD_REFCNT_LOGGING |
174 | | virtual ~nsDisplayRangeFocusRing() { |
175 | | MOZ_COUNT_DTOR(nsDisplayRangeFocusRing); |
176 | | } |
177 | | #endif |
178 | | |
179 | | nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override; |
180 | | void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
181 | | const nsDisplayItemGeometry* aGeometry, |
182 | | nsRegion *aInvalidRegion) const override; |
183 | | virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, |
184 | | bool* aSnap) const override; |
185 | | virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; |
186 | | NS_DISPLAY_DECL_NAME("RangeFocusRing", TYPE_RANGE_FOCUS_RING) |
187 | | }; |
188 | | |
189 | | nsDisplayItemGeometry* |
190 | | nsDisplayRangeFocusRing::AllocateGeometry(nsDisplayListBuilder* aBuilder) |
191 | 0 | { |
192 | 0 | return new nsDisplayItemGenericImageGeometry(this, aBuilder); |
193 | 0 | } |
194 | | |
195 | | void |
196 | | nsDisplayRangeFocusRing::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
197 | | const nsDisplayItemGeometry* aGeometry, |
198 | | nsRegion* aInvalidRegion) const |
199 | 0 | { |
200 | 0 | auto geometry = |
201 | 0 | static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry); |
202 | 0 |
|
203 | 0 | if (aBuilder->ShouldSyncDecodeImages() && |
204 | 0 | geometry->ShouldInvalidateToSyncDecodeImages()) { |
205 | 0 | bool snap; |
206 | 0 | aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); |
207 | 0 | } |
208 | 0 |
|
209 | 0 | nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); |
210 | 0 | } |
211 | | |
212 | | nsRect |
213 | | nsDisplayRangeFocusRing::GetBounds(nsDisplayListBuilder* aBuilder, |
214 | | bool* aSnap) const |
215 | 0 | { |
216 | 0 | *aSnap = false; |
217 | 0 | nsRect rect(ToReferenceFrame(), Frame()->GetSize()); |
218 | 0 |
|
219 | 0 | // We want to paint as if specifying a border for ::-moz-focus-outer |
220 | 0 | // specifies an outline for our frame, so inflate by the border widths: |
221 | 0 | ComputedStyle* computedStyle = |
222 | 0 | static_cast<nsRangeFrame*>(mFrame)->mOuterFocusStyle; |
223 | 0 | MOZ_ASSERT(computedStyle, "We only exist if mOuterFocusStyle is non-null"); |
224 | 0 | rect.Inflate(computedStyle->StyleBorder()->GetComputedBorder()); |
225 | 0 |
|
226 | 0 | return rect; |
227 | 0 | } |
228 | | |
229 | | void |
230 | | nsDisplayRangeFocusRing::Paint(nsDisplayListBuilder* aBuilder, |
231 | | gfxContext* aCtx) |
232 | 0 | { |
233 | 0 | bool unused; |
234 | 0 | ComputedStyle* computedStyle = |
235 | 0 | static_cast<nsRangeFrame*>(mFrame)->mOuterFocusStyle; |
236 | 0 | MOZ_ASSERT(computedStyle, "We only exist if mOuterFocusStyle is non-null"); |
237 | 0 |
|
238 | 0 | PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages() |
239 | 0 | ? PaintBorderFlags::SYNC_DECODE_IMAGES |
240 | 0 | : PaintBorderFlags(); |
241 | 0 |
|
242 | 0 | ImgDrawResult result = |
243 | 0 | nsCSSRendering::PaintBorder(mFrame->PresContext(), *aCtx, mFrame, |
244 | 0 | GetPaintRect(), GetBounds(aBuilder, &unused), |
245 | 0 | computedStyle, flags); |
246 | 0 |
|
247 | 0 | nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result); |
248 | 0 | } |
249 | | |
250 | | void |
251 | | nsRangeFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
252 | | const nsDisplayListSet& aLists) |
253 | 0 | { |
254 | 0 | const nsStyleDisplay* disp = StyleDisplay(); |
255 | 0 | if (IsThemed(disp)) { |
256 | 0 | DisplayBorderBackgroundOutline(aBuilder, aLists); |
257 | 0 | // Only create items for the thumb. Specifically, we do not want |
258 | 0 | // the track to paint, since *our* background is used to paint |
259 | 0 | // the track, and we don't want the unthemed track painting over |
260 | 0 | // the top of the themed track. |
261 | 0 | // This logic is copied from |
262 | 0 | // nsContainerFrame::BuildDisplayListForNonBlockChildren as |
263 | 0 | // called by BuildDisplayListForInline. |
264 | 0 | nsIFrame* thumb = mThumbDiv->GetPrimaryFrame(); |
265 | 0 | if (thumb) { |
266 | 0 | nsDisplayListSet set(aLists, aLists.Content()); |
267 | 0 | BuildDisplayListForChild(aBuilder, thumb, set, DISPLAY_CHILD_INLINE); |
268 | 0 | } |
269 | 0 | } else { |
270 | 0 | BuildDisplayListForInline(aBuilder, aLists); |
271 | 0 | } |
272 | 0 |
|
273 | 0 | // Draw a focus outline if appropriate: |
274 | 0 |
|
275 | 0 | if (!aBuilder->IsForPainting() || |
276 | 0 | !IsVisibleForPainting(aBuilder)) { |
277 | 0 | // we don't want the focus ring item for hit-testing or if the item isn't |
278 | 0 | // in the area being [re]painted |
279 | 0 | return; |
280 | 0 | } |
281 | 0 | |
282 | 0 | EventStates eventStates = mContent->AsElement()->State(); |
283 | 0 | if (eventStates.HasState(NS_EVENT_STATE_DISABLED) || |
284 | 0 | !eventStates.HasState(NS_EVENT_STATE_FOCUSRING)) { |
285 | 0 | return; // can't have focus or doesn't match :-moz-focusring |
286 | 0 | } |
287 | 0 | |
288 | 0 | if (!mOuterFocusStyle || |
289 | 0 | !mOuterFocusStyle->StyleBorder()->HasBorder()) { |
290 | 0 | // no ::-moz-focus-outer specified border (how style specifies a focus ring |
291 | 0 | // for range) |
292 | 0 | return; |
293 | 0 | } |
294 | 0 | |
295 | 0 | if (IsThemed(disp) && |
296 | 0 | PresContext()->GetTheme()->ThemeDrawsFocusForWidget(disp->mAppearance)) { |
297 | 0 | return; // the native theme displays its own visual indication of focus |
298 | 0 | } |
299 | 0 | |
300 | 0 | aLists.Content()->AppendToTop( |
301 | 0 | MakeDisplayItem<nsDisplayRangeFocusRing>(aBuilder, this)); |
302 | 0 | } |
303 | | |
304 | | void |
305 | | nsRangeFrame::Reflow(nsPresContext* aPresContext, |
306 | | ReflowOutput& aDesiredSize, |
307 | | const ReflowInput& aReflowInput, |
308 | | nsReflowStatus& aStatus) |
309 | 0 | { |
310 | 0 | MarkInReflow(); |
311 | 0 | DO_GLOBAL_REFLOW_COUNT("nsRangeFrame"); |
312 | 0 | DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); |
313 | 0 | MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); |
314 | 0 |
|
315 | 0 | NS_ASSERTION(mTrackDiv, "::-moz-range-track div must exist!"); |
316 | 0 | NS_ASSERTION(mProgressDiv, "::-moz-range-progress div must exist!"); |
317 | 0 | NS_ASSERTION(mThumbDiv, "::-moz-range-thumb div must exist!"); |
318 | 0 | NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(), |
319 | 0 | "nsRangeFrame should not have continuations; if it does we " |
320 | 0 | "need to call RegUnregAccessKey only for the first."); |
321 | 0 |
|
322 | 0 | if (mState & NS_FRAME_FIRST_REFLOW) { |
323 | 0 | nsCheckboxRadioFrame::RegUnRegAccessKey(this, true); |
324 | 0 | } |
325 | 0 |
|
326 | 0 | WritingMode wm = aReflowInput.GetWritingMode(); |
327 | 0 | nscoord computedBSize = aReflowInput.ComputedBSize(); |
328 | 0 | if (computedBSize == NS_AUTOHEIGHT) { |
329 | 0 | computedBSize = 0; |
330 | 0 | } |
331 | 0 | LogicalSize |
332 | 0 | finalSize(wm, |
333 | 0 | aReflowInput.ComputedISize() + |
334 | 0 | aReflowInput.ComputedLogicalBorderPadding().IStartEnd(wm), |
335 | 0 | computedBSize + |
336 | 0 | aReflowInput.ComputedLogicalBorderPadding().BStartEnd(wm)); |
337 | 0 | aDesiredSize.SetSize(wm, finalSize); |
338 | 0 |
|
339 | 0 | ReflowAnonymousContent(aPresContext, aDesiredSize, aReflowInput); |
340 | 0 |
|
341 | 0 | aDesiredSize.SetOverflowAreasToDesiredBounds(); |
342 | 0 |
|
343 | 0 | nsIFrame* trackFrame = mTrackDiv->GetPrimaryFrame(); |
344 | 0 | if (trackFrame) { |
345 | 0 | ConsiderChildOverflow(aDesiredSize.mOverflowAreas, trackFrame); |
346 | 0 | } |
347 | 0 |
|
348 | 0 | nsIFrame* rangeProgressFrame = mProgressDiv->GetPrimaryFrame(); |
349 | 0 | if (rangeProgressFrame) { |
350 | 0 | ConsiderChildOverflow(aDesiredSize.mOverflowAreas, rangeProgressFrame); |
351 | 0 | } |
352 | 0 |
|
353 | 0 | nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame(); |
354 | 0 | if (thumbFrame) { |
355 | 0 | ConsiderChildOverflow(aDesiredSize.mOverflowAreas, thumbFrame); |
356 | 0 | } |
357 | 0 |
|
358 | 0 | FinishAndStoreOverflow(&aDesiredSize); |
359 | 0 |
|
360 | 0 | MOZ_ASSERT(aStatus.IsEmpty(), "This type of frame can't be split."); |
361 | 0 |
|
362 | 0 | NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); |
363 | 0 | } |
364 | | |
365 | | void |
366 | | nsRangeFrame::ReflowAnonymousContent(nsPresContext* aPresContext, |
367 | | ReflowOutput& aDesiredSize, |
368 | | const ReflowInput& aReflowInput) |
369 | 0 | { |
370 | 0 | // The width/height of our content box, which is the available width/height |
371 | 0 | // for our anonymous content: |
372 | 0 | nscoord rangeFrameContentBoxWidth = aReflowInput.ComputedWidth(); |
373 | 0 | nscoord rangeFrameContentBoxHeight = aReflowInput.ComputedHeight(); |
374 | 0 | if (rangeFrameContentBoxHeight == NS_AUTOHEIGHT) { |
375 | 0 | rangeFrameContentBoxHeight = 0; |
376 | 0 | } |
377 | 0 |
|
378 | 0 | nsIFrame* trackFrame = mTrackDiv->GetPrimaryFrame(); |
379 | 0 |
|
380 | 0 | if (trackFrame) { // display:none? |
381 | 0 |
|
382 | 0 | // Position the track: |
383 | 0 | // The idea here is that we allow content authors to style the width, |
384 | 0 | // height, border and padding of the track, but we ignore margin and |
385 | 0 | // positioning properties and do the positioning ourself to keep the center |
386 | 0 | // of the track's border box on the center of the nsRangeFrame's content |
387 | 0 | // box. |
388 | 0 |
|
389 | 0 | WritingMode wm = trackFrame->GetWritingMode(); |
390 | 0 | LogicalSize availSize = aReflowInput.ComputedSize(wm); |
391 | 0 | availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; |
392 | 0 | ReflowInput trackReflowInput(aPresContext, aReflowInput, |
393 | 0 | trackFrame, availSize); |
394 | 0 |
|
395 | 0 | // Find the x/y position of the track frame such that it will be positioned |
396 | 0 | // as described above. These coordinates are with respect to the |
397 | 0 | // nsRangeFrame's border-box. |
398 | 0 | nscoord trackX = rangeFrameContentBoxWidth / 2; |
399 | 0 | nscoord trackY = rangeFrameContentBoxHeight / 2; |
400 | 0 |
|
401 | 0 | // Account for the track's border and padding (we ignore its margin): |
402 | 0 | trackX -= trackReflowInput.ComputedPhysicalBorderPadding().left + |
403 | 0 | trackReflowInput.ComputedWidth() / 2; |
404 | 0 | trackY -= trackReflowInput.ComputedPhysicalBorderPadding().top + |
405 | 0 | trackReflowInput.ComputedHeight() / 2; |
406 | 0 |
|
407 | 0 | // Make relative to our border box instead of our content box: |
408 | 0 | trackX += aReflowInput.ComputedPhysicalBorderPadding().left; |
409 | 0 | trackY += aReflowInput.ComputedPhysicalBorderPadding().top; |
410 | 0 |
|
411 | 0 | nsReflowStatus frameStatus; |
412 | 0 | ReflowOutput trackDesiredSize(aReflowInput); |
413 | 0 | ReflowChild(trackFrame, aPresContext, trackDesiredSize, |
414 | 0 | trackReflowInput, trackX, trackY, 0, frameStatus); |
415 | 0 | MOZ_ASSERT(frameStatus.IsFullyComplete(), |
416 | 0 | "We gave our child unconstrained height, so it should be complete"); |
417 | 0 | FinishReflowChild(trackFrame, aPresContext, trackDesiredSize, |
418 | 0 | &trackReflowInput, trackX, trackY, 0); |
419 | 0 | } |
420 | 0 |
|
421 | 0 | nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame(); |
422 | 0 |
|
423 | 0 | if (thumbFrame) { // display:none? |
424 | 0 | WritingMode wm = thumbFrame->GetWritingMode(); |
425 | 0 | LogicalSize availSize = aReflowInput.ComputedSize(wm); |
426 | 0 | availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; |
427 | 0 | ReflowInput thumbReflowInput(aPresContext, aReflowInput, |
428 | 0 | thumbFrame, availSize); |
429 | 0 |
|
430 | 0 | // Where we position the thumb depends on its size, so we first reflow |
431 | 0 | // the thumb at {0,0} to obtain its size, then position it afterwards. |
432 | 0 |
|
433 | 0 | nsReflowStatus frameStatus; |
434 | 0 | ReflowOutput thumbDesiredSize(aReflowInput); |
435 | 0 | ReflowChild(thumbFrame, aPresContext, thumbDesiredSize, |
436 | 0 | thumbReflowInput, 0, 0, 0, frameStatus); |
437 | 0 | MOZ_ASSERT(frameStatus.IsFullyComplete(), |
438 | 0 | "We gave our child unconstrained height, so it should be complete"); |
439 | 0 | FinishReflowChild(thumbFrame, aPresContext, thumbDesiredSize, |
440 | 0 | &thumbReflowInput, 0, 0, 0); |
441 | 0 | DoUpdateThumbPosition(thumbFrame, nsSize(aDesiredSize.Width(), |
442 | 0 | aDesiredSize.Height())); |
443 | 0 | } |
444 | 0 |
|
445 | 0 | nsIFrame* rangeProgressFrame = mProgressDiv->GetPrimaryFrame(); |
446 | 0 |
|
447 | 0 | if (rangeProgressFrame) { // display:none? |
448 | 0 | WritingMode wm = rangeProgressFrame->GetWritingMode(); |
449 | 0 | LogicalSize availSize = aReflowInput.ComputedSize(wm); |
450 | 0 | availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; |
451 | 0 | ReflowInput progressReflowInput(aPresContext, aReflowInput, |
452 | 0 | rangeProgressFrame, availSize); |
453 | 0 |
|
454 | 0 | // We first reflow the range-progress frame at {0,0} to obtain its |
455 | 0 | // unadjusted dimensions, then we adjust it to so that the appropriate edge |
456 | 0 | // ends at the thumb. |
457 | 0 |
|
458 | 0 | nsReflowStatus frameStatus; |
459 | 0 | ReflowOutput progressDesiredSize(aReflowInput); |
460 | 0 | ReflowChild(rangeProgressFrame, aPresContext, |
461 | 0 | progressDesiredSize, progressReflowInput, 0, 0, |
462 | 0 | 0, frameStatus); |
463 | 0 | MOZ_ASSERT(frameStatus.IsFullyComplete(), |
464 | 0 | "We gave our child unconstrained height, so it should be complete"); |
465 | 0 | FinishReflowChild(rangeProgressFrame, aPresContext, |
466 | 0 | progressDesiredSize, &progressReflowInput, 0, 0, 0); |
467 | 0 | DoUpdateRangeProgressFrame(rangeProgressFrame, nsSize(aDesiredSize.Width(), |
468 | 0 | aDesiredSize.Height())); |
469 | 0 | } |
470 | 0 | } |
471 | | |
472 | | #ifdef ACCESSIBILITY |
473 | | a11y::AccType |
474 | | nsRangeFrame::AccessibleType() |
475 | 0 | { |
476 | 0 | return a11y::eHTMLRangeType; |
477 | 0 | } |
478 | | #endif |
479 | | |
480 | | double |
481 | | nsRangeFrame::GetValueAsFractionOfRange() |
482 | 0 | { |
483 | 0 | MOZ_ASSERT(mContent->IsHTMLElement(nsGkAtoms::input), "bad cast"); |
484 | 0 | dom::HTMLInputElement* input = static_cast<dom::HTMLInputElement*>(GetContent()); |
485 | 0 |
|
486 | 0 | MOZ_ASSERT(input->ControlType() == NS_FORM_INPUT_RANGE); |
487 | 0 |
|
488 | 0 | Decimal value = input->GetValueAsDecimal(); |
489 | 0 | Decimal minimum = input->GetMinimum(); |
490 | 0 | Decimal maximum = input->GetMaximum(); |
491 | 0 |
|
492 | 0 | MOZ_ASSERT(value.isFinite() && minimum.isFinite() && maximum.isFinite(), |
493 | 0 | "type=range should have a default maximum/minimum"); |
494 | 0 |
|
495 | 0 | if (maximum <= minimum) { |
496 | 0 | MOZ_ASSERT(value == minimum, "Unsanitized value"); |
497 | 0 | return 0.0; |
498 | 0 | } |
499 | 0 |
|
500 | 0 | MOZ_ASSERT(value >= minimum && value <= maximum, "Unsanitized value"); |
501 | 0 |
|
502 | 0 | return ((value - minimum) / (maximum - minimum)).toDouble(); |
503 | 0 | } |
504 | | |
505 | | Decimal |
506 | | nsRangeFrame::GetValueAtEventPoint(WidgetGUIEvent* aEvent) |
507 | 0 | { |
508 | 0 | MOZ_ASSERT(aEvent->mClass == eMouseEventClass || |
509 | 0 | aEvent->mClass == eTouchEventClass, |
510 | 0 | "Unexpected event type - aEvent->mRefPoint may be meaningless"); |
511 | 0 |
|
512 | 0 | MOZ_ASSERT(mContent->IsHTMLElement(nsGkAtoms::input), "bad cast"); |
513 | 0 | dom::HTMLInputElement* input = static_cast<dom::HTMLInputElement*>(GetContent()); |
514 | 0 |
|
515 | 0 | MOZ_ASSERT(input->ControlType() == NS_FORM_INPUT_RANGE); |
516 | 0 |
|
517 | 0 | Decimal minimum = input->GetMinimum(); |
518 | 0 | Decimal maximum = input->GetMaximum(); |
519 | 0 | MOZ_ASSERT(minimum.isFinite() && maximum.isFinite(), |
520 | 0 | "type=range should have a default maximum/minimum"); |
521 | 0 | if (maximum <= minimum) { |
522 | 0 | return minimum; |
523 | 0 | } |
524 | 0 | Decimal range = maximum - minimum; |
525 | 0 |
|
526 | 0 | LayoutDeviceIntPoint absPoint; |
527 | 0 | if (aEvent->mClass == eTouchEventClass) { |
528 | 0 | MOZ_ASSERT(aEvent->AsTouchEvent()->mTouches.Length() == 1, |
529 | 0 | "Unexpected number of mTouches"); |
530 | 0 | absPoint = aEvent->AsTouchEvent()->mTouches[0]->mRefPoint; |
531 | 0 | } else { |
532 | 0 | absPoint = aEvent->mRefPoint; |
533 | 0 | } |
534 | 0 | nsPoint point = |
535 | 0 | nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, absPoint, this); |
536 | 0 |
|
537 | 0 | if (point == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) { |
538 | 0 | // We don't want to change the current value for this error state. |
539 | 0 | return static_cast<dom::HTMLInputElement*>(GetContent())->GetValueAsDecimal(); |
540 | 0 | } |
541 | 0 | |
542 | 0 | nsRect rangeContentRect = GetContentRectRelativeToSelf(); |
543 | 0 | nsSize thumbSize; |
544 | 0 |
|
545 | 0 | if (IsThemed()) { |
546 | 0 | // We need to get the size of the thumb from the theme. |
547 | 0 | nsPresContext *presContext = PresContext(); |
548 | 0 | bool notUsedCanOverride; |
549 | 0 | LayoutDeviceIntSize size; |
550 | 0 | presContext->GetTheme()-> |
551 | 0 | GetMinimumWidgetSize(presContext, this, StyleAppearance::RangeThumb, &size, |
552 | 0 | ¬UsedCanOverride); |
553 | 0 | thumbSize.width = presContext->DevPixelsToAppUnits(size.width); |
554 | 0 | thumbSize.height = presContext->DevPixelsToAppUnits(size.height); |
555 | 0 | // For GTK, GetMinimumWidgetSize returns zero for the thumb dimension |
556 | 0 | // perpendicular to the orientation of the slider. That's okay since we |
557 | 0 | // only care about the dimension in the direction of the slider when using |
558 | 0 | // |thumbSize| below, but it means this assertion need to check |
559 | 0 | // IsHorizontal(). |
560 | 0 | MOZ_ASSERT((IsHorizontal() && thumbSize.width > 0) || |
561 | 0 | (!IsHorizontal() && thumbSize.height > 0), |
562 | 0 | "The thumb is expected to take up some slider space"); |
563 | 0 | } else { |
564 | 0 | nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame(); |
565 | 0 | if (thumbFrame) { // diplay:none? |
566 | 0 | thumbSize = thumbFrame->GetSize(); |
567 | 0 | } |
568 | 0 | } |
569 | 0 |
|
570 | 0 | Decimal fraction; |
571 | 0 | if (IsHorizontal()) { |
572 | 0 | nscoord traversableDistance = rangeContentRect.width - thumbSize.width; |
573 | 0 | if (traversableDistance <= 0) { |
574 | 0 | return minimum; |
575 | 0 | } |
576 | 0 | nscoord posAtStart = rangeContentRect.x + thumbSize.width/2; |
577 | 0 | nscoord posAtEnd = posAtStart + traversableDistance; |
578 | 0 | nscoord posOfPoint = mozilla::clamped(point.x, posAtStart, posAtEnd); |
579 | 0 | fraction = Decimal(posOfPoint - posAtStart) / Decimal(traversableDistance); |
580 | 0 | if (IsRightToLeft()) { |
581 | 0 | fraction = Decimal(1) - fraction; |
582 | 0 | } |
583 | 0 | } else { |
584 | 0 | nscoord traversableDistance = rangeContentRect.height - thumbSize.height; |
585 | 0 | if (traversableDistance <= 0) { |
586 | 0 | return minimum; |
587 | 0 | } |
588 | 0 | nscoord posAtStart = rangeContentRect.y + thumbSize.height/2; |
589 | 0 | nscoord posAtEnd = posAtStart + traversableDistance; |
590 | 0 | nscoord posOfPoint = mozilla::clamped(point.y, posAtStart, posAtEnd); |
591 | 0 | // For a vertical range, the top (posAtStart) is the highest value, so we |
592 | 0 | // subtract the fraction from 1.0 to get that polarity correct. |
593 | 0 | fraction = Decimal(1) - Decimal(posOfPoint - posAtStart) / Decimal(traversableDistance); |
594 | 0 | } |
595 | 0 |
|
596 | 0 | MOZ_ASSERT(fraction >= Decimal(0) && fraction <= Decimal(1)); |
597 | 0 | return minimum + fraction * range; |
598 | 0 | } |
599 | | |
600 | | void |
601 | | nsRangeFrame::UpdateForValueChange() |
602 | 0 | { |
603 | 0 | if (NS_SUBTREE_DIRTY(this)) { |
604 | 0 | return; // we're going to be updated when we reflow |
605 | 0 | } |
606 | 0 | nsIFrame* rangeProgressFrame = mProgressDiv->GetPrimaryFrame(); |
607 | 0 | nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame(); |
608 | 0 | if (!rangeProgressFrame && !thumbFrame) { |
609 | 0 | return; // diplay:none? |
610 | 0 | } |
611 | 0 | if (rangeProgressFrame) { |
612 | 0 | DoUpdateRangeProgressFrame(rangeProgressFrame, GetSize()); |
613 | 0 | } |
614 | 0 | if (thumbFrame) { |
615 | 0 | DoUpdateThumbPosition(thumbFrame, GetSize()); |
616 | 0 | } |
617 | 0 | if (IsThemed()) { |
618 | 0 | // We don't know the exact dimensions or location of the thumb when native |
619 | 0 | // theming is applied, so we just repaint the entire range. |
620 | 0 | InvalidateFrame(); |
621 | 0 | } |
622 | 0 |
|
623 | 0 | #ifdef ACCESSIBILITY |
624 | 0 | nsAccessibilityService* accService = nsIPresShell::AccService(); |
625 | 0 | if (accService) { |
626 | 0 | accService->RangeValueChanged(PresShell(), mContent); |
627 | 0 | } |
628 | 0 | #endif |
629 | 0 |
|
630 | 0 | SchedulePaint(); |
631 | 0 | } |
632 | | |
633 | | void |
634 | | nsRangeFrame::DoUpdateThumbPosition(nsIFrame* aThumbFrame, |
635 | | const nsSize& aRangeSize) |
636 | 0 | { |
637 | 0 | MOZ_ASSERT(aThumbFrame); |
638 | 0 |
|
639 | 0 | // The idea here is that we want to position the thumb so that the center |
640 | 0 | // of the thumb is on an imaginary line drawn from the middle of one edge |
641 | 0 | // of the range frame's content box to the middle of the opposite edge of |
642 | 0 | // its content box (the opposite edges being the left/right edge if the |
643 | 0 | // range is horizontal, or else the top/bottom edges if the range is |
644 | 0 | // vertical). How far along this line the center of the thumb is placed |
645 | 0 | // depends on the value of the range. |
646 | 0 |
|
647 | 0 | nsMargin borderAndPadding = GetUsedBorderAndPadding(); |
648 | 0 | nsPoint newPosition(borderAndPadding.left, borderAndPadding.top); |
649 | 0 |
|
650 | 0 | nsSize rangeContentBoxSize(aRangeSize); |
651 | 0 | rangeContentBoxSize.width -= borderAndPadding.LeftRight(); |
652 | 0 | rangeContentBoxSize.height -= borderAndPadding.TopBottom(); |
653 | 0 |
|
654 | 0 | nsSize thumbSize = aThumbFrame->GetSize(); |
655 | 0 | double fraction = GetValueAsFractionOfRange(); |
656 | 0 | MOZ_ASSERT(fraction >= 0.0 && fraction <= 1.0); |
657 | 0 |
|
658 | 0 | if (IsHorizontal()) { |
659 | 0 | if (thumbSize.width < rangeContentBoxSize.width) { |
660 | 0 | nscoord traversableDistance = |
661 | 0 | rangeContentBoxSize.width - thumbSize.width; |
662 | 0 | if (IsRightToLeft()) { |
663 | 0 | newPosition.x += NSToCoordRound((1.0 - fraction) * traversableDistance); |
664 | 0 | } else { |
665 | 0 | newPosition.x += NSToCoordRound(fraction * traversableDistance); |
666 | 0 | } |
667 | 0 | newPosition.y += (rangeContentBoxSize.height - thumbSize.height)/2; |
668 | 0 | } |
669 | 0 | } else { |
670 | 0 | if (thumbSize.height < rangeContentBoxSize.height) { |
671 | 0 | nscoord traversableDistance = |
672 | 0 | rangeContentBoxSize.height - thumbSize.height; |
673 | 0 | newPosition.x += (rangeContentBoxSize.width - thumbSize.width)/2; |
674 | 0 | newPosition.y += NSToCoordRound((1.0 - fraction) * traversableDistance); |
675 | 0 | } |
676 | 0 | } |
677 | 0 | aThumbFrame->SetPosition(newPosition); |
678 | 0 | } |
679 | | |
680 | | void |
681 | | nsRangeFrame::DoUpdateRangeProgressFrame(nsIFrame* aRangeProgressFrame, |
682 | | const nsSize& aRangeSize) |
683 | 0 | { |
684 | 0 | MOZ_ASSERT(aRangeProgressFrame); |
685 | 0 |
|
686 | 0 | // The idea here is that we want to position the ::-moz-range-progress |
687 | 0 | // pseudo-element so that the center line running along its length is on the |
688 | 0 | // corresponding center line of the nsRangeFrame's content box. In the other |
689 | 0 | // dimension, we align the "start" edge of the ::-moz-range-progress |
690 | 0 | // pseudo-element's border-box with the corresponding edge of the |
691 | 0 | // nsRangeFrame's content box, and we size the progress element's border-box |
692 | 0 | // to have a length of GetValueAsFractionOfRange() times the nsRangeFrame's |
693 | 0 | // content-box size. |
694 | 0 |
|
695 | 0 | nsMargin borderAndPadding = GetUsedBorderAndPadding(); |
696 | 0 | nsSize progSize = aRangeProgressFrame->GetSize(); |
697 | 0 | nsRect progRect(borderAndPadding.left, borderAndPadding.top, |
698 | 0 | progSize.width, progSize.height); |
699 | 0 |
|
700 | 0 | nsSize rangeContentBoxSize(aRangeSize); |
701 | 0 | rangeContentBoxSize.width -= borderAndPadding.LeftRight(); |
702 | 0 | rangeContentBoxSize.height -= borderAndPadding.TopBottom(); |
703 | 0 |
|
704 | 0 | double fraction = GetValueAsFractionOfRange(); |
705 | 0 | MOZ_ASSERT(fraction >= 0.0 && fraction <= 1.0); |
706 | 0 |
|
707 | 0 | if (IsHorizontal()) { |
708 | 0 | nscoord progLength = NSToCoordRound(fraction * rangeContentBoxSize.width); |
709 | 0 | if (IsRightToLeft()) { |
710 | 0 | progRect.x += rangeContentBoxSize.width - progLength; |
711 | 0 | } |
712 | 0 | progRect.y += (rangeContentBoxSize.height - progSize.height)/2; |
713 | 0 | progRect.width = progLength; |
714 | 0 | } else { |
715 | 0 | nscoord progLength = NSToCoordRound(fraction * rangeContentBoxSize.height); |
716 | 0 | progRect.x += (rangeContentBoxSize.width - progSize.width)/2; |
717 | 0 | progRect.y += rangeContentBoxSize.height - progLength; |
718 | 0 | progRect.height = progLength; |
719 | 0 | } |
720 | 0 | aRangeProgressFrame->SetRect(progRect); |
721 | 0 | } |
722 | | |
723 | | nsresult |
724 | | nsRangeFrame::AttributeChanged(int32_t aNameSpaceID, |
725 | | nsAtom* aAttribute, |
726 | | int32_t aModType) |
727 | 0 | { |
728 | 0 | NS_ASSERTION(mTrackDiv, "The track div must exist!"); |
729 | 0 | NS_ASSERTION(mThumbDiv, "The thumb div must exist!"); |
730 | 0 |
|
731 | 0 | if (aNameSpaceID == kNameSpaceID_None) { |
732 | 0 | if (aAttribute == nsGkAtoms::value || |
733 | 0 | aAttribute == nsGkAtoms::min || |
734 | 0 | aAttribute == nsGkAtoms::max || |
735 | 0 | aAttribute == nsGkAtoms::step) { |
736 | 0 | // We want to update the position of the thumb, except in one special |
737 | 0 | // case: If the value attribute is being set, it is possible that we are |
738 | 0 | // in the middle of a type change away from type=range, under the |
739 | 0 | // SetAttr(..., nsGkAtoms::value, ...) call in HTMLInputElement:: |
740 | 0 | // HandleTypeChange. In that case the HTMLInputElement's type will |
741 | 0 | // already have changed, and if we call UpdateForValueChange() |
742 | 0 | // we'll fail the asserts under that call that check the type of our |
743 | 0 | // HTMLInputElement. Given that we're changing away from being a range |
744 | 0 | // and this frame will shortly be destroyed, there's no point in calling |
745 | 0 | // UpdateForValueChange() anyway. |
746 | 0 | MOZ_ASSERT(mContent->IsHTMLElement(nsGkAtoms::input), "bad cast"); |
747 | 0 | bool typeIsRange = |
748 | 0 | static_cast<dom::HTMLInputElement*>(GetContent())->ControlType() == |
749 | 0 | NS_FORM_INPUT_RANGE; |
750 | 0 | // If script changed the <input>'s type before setting these attributes |
751 | 0 | // then we don't need to do anything since we are going to be reframed. |
752 | 0 | if (typeIsRange) { |
753 | 0 | UpdateForValueChange(); |
754 | 0 | } |
755 | 0 | } else if (aAttribute == nsGkAtoms::orient) { |
756 | 0 | PresShell()->FrameNeedsReflow(this, nsIPresShell::eResize, |
757 | 0 | NS_FRAME_IS_DIRTY); |
758 | 0 | } |
759 | 0 | } |
760 | 0 |
|
761 | 0 | return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); |
762 | 0 | } |
763 | | |
764 | | LogicalSize |
765 | | nsRangeFrame::ComputeAutoSize(gfxContext* aRenderingContext, |
766 | | WritingMode aWM, |
767 | | const LogicalSize& aCBSize, |
768 | | nscoord aAvailableISize, |
769 | | const LogicalSize& aMargin, |
770 | | const LogicalSize& aBorder, |
771 | | const LogicalSize& aPadding, |
772 | | ComputeSizeFlags aFlags) |
773 | 0 | { |
774 | 0 | nscoord oneEm = NSToCoordRound(StyleFont()->mFont.size * |
775 | 0 | nsLayoutUtils::FontSizeInflationFor(this)); // 1em |
776 | 0 |
|
777 | 0 | bool isInlineOriented = IsInlineOriented(); |
778 | 0 |
|
779 | 0 | const WritingMode wm = GetWritingMode(); |
780 | 0 | LogicalSize autoSize(wm); |
781 | 0 |
|
782 | 0 | // nsFrame::ComputeSize calls GetMinimumWidgetSize to prevent us from being |
783 | 0 | // given too small a size when we're natively themed. If we're themed, we set |
784 | 0 | // our "thickness" dimension to zero below and rely on that |
785 | 0 | // GetMinimumWidgetSize check to correct that dimension to the natural |
786 | 0 | // thickness of a slider in the current theme. |
787 | 0 |
|
788 | 0 | if (isInlineOriented) { |
789 | 0 | autoSize.ISize(wm) = LONG_SIDE_TO_SHORT_SIDE_RATIO * oneEm; |
790 | 0 | autoSize.BSize(wm) = IsThemed() ? 0 : oneEm; |
791 | 0 | } else { |
792 | 0 | autoSize.ISize(wm) = IsThemed() ? 0 : oneEm; |
793 | 0 | autoSize.BSize(wm) = LONG_SIDE_TO_SHORT_SIDE_RATIO * oneEm; |
794 | 0 | } |
795 | 0 |
|
796 | 0 | return autoSize.ConvertTo(aWM, wm); |
797 | 0 | } |
798 | | |
799 | | nscoord |
800 | | nsRangeFrame::GetMinISize(gfxContext *aRenderingContext) |
801 | 0 | { |
802 | 0 | // nsFrame::ComputeSize calls GetMinimumWidgetSize to prevent us from being |
803 | 0 | // given too small a size when we're natively themed. If we aren't native |
804 | 0 | // themed, we don't mind how small we're sized. |
805 | 0 | return nscoord(0); |
806 | 0 | } |
807 | | |
808 | | nscoord |
809 | | nsRangeFrame::GetPrefISize(gfxContext *aRenderingContext) |
810 | 0 | { |
811 | 0 | bool isInline = IsInlineOriented(); |
812 | 0 |
|
813 | 0 | if (!isInline && IsThemed()) { |
814 | 0 | // nsFrame::ComputeSize calls GetMinimumWidgetSize to prevent us from being |
815 | 0 | // given too small a size when we're natively themed. We return zero and |
816 | 0 | // depend on that correction to get our "natural" width when we're a |
817 | 0 | // vertical slider. |
818 | 0 | return 0; |
819 | 0 | } |
820 | 0 | |
821 | 0 | nscoord prefISize = NSToCoordRound(StyleFont()->mFont.size * |
822 | 0 | nsLayoutUtils::FontSizeInflationFor(this)); // 1em |
823 | 0 |
|
824 | 0 | if (isInline) { |
825 | 0 | prefISize *= LONG_SIDE_TO_SHORT_SIDE_RATIO; |
826 | 0 | } |
827 | 0 |
|
828 | 0 | return prefISize; |
829 | 0 | } |
830 | | |
831 | | bool |
832 | | nsRangeFrame::IsHorizontal() const |
833 | 0 | { |
834 | 0 | dom::HTMLInputElement* element = |
835 | 0 | static_cast<dom::HTMLInputElement*>(GetContent()); |
836 | 0 | return element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::orient, |
837 | 0 | nsGkAtoms::horizontal, eCaseMatters) || |
838 | 0 | (!element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::orient, |
839 | 0 | nsGkAtoms::vertical, eCaseMatters) && |
840 | 0 | GetWritingMode().IsVertical() == |
841 | 0 | element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::orient, |
842 | 0 | nsGkAtoms::block, eCaseMatters)); |
843 | 0 | } |
844 | | |
845 | | double |
846 | | nsRangeFrame::GetMin() const |
847 | 0 | { |
848 | 0 | return static_cast<dom::HTMLInputElement*>(GetContent())->GetMinimum().toDouble(); |
849 | 0 | } |
850 | | |
851 | | double |
852 | | nsRangeFrame::GetMax() const |
853 | 0 | { |
854 | 0 | return static_cast<dom::HTMLInputElement*>(GetContent())->GetMaximum().toDouble(); |
855 | 0 | } |
856 | | |
857 | | double |
858 | | nsRangeFrame::GetValue() const |
859 | 0 | { |
860 | 0 | return static_cast<dom::HTMLInputElement*>(GetContent())->GetValueAsDecimal().toDouble(); |
861 | 0 | } |
862 | | |
863 | | #define STYLES_DISABLING_NATIVE_THEMING \ |
864 | 0 | NS_AUTHOR_SPECIFIED_BACKGROUND | \ |
865 | 0 | NS_AUTHOR_SPECIFIED_PADDING | \ |
866 | 0 | NS_AUTHOR_SPECIFIED_BORDER |
867 | | |
868 | | bool |
869 | | nsRangeFrame::ShouldUseNativeStyle() const |
870 | 0 | { |
871 | 0 | nsIFrame* trackFrame = mTrackDiv->GetPrimaryFrame(); |
872 | 0 | nsIFrame* progressFrame = mProgressDiv->GetPrimaryFrame(); |
873 | 0 | nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame(); |
874 | 0 |
|
875 | 0 | return (StyleDisplay()->mAppearance == StyleAppearance::Range) && |
876 | 0 | !PresContext()->HasAuthorSpecifiedRules(this, |
877 | 0 | (NS_AUTHOR_SPECIFIED_BORDER | |
878 | 0 | NS_AUTHOR_SPECIFIED_BACKGROUND)) && |
879 | 0 | trackFrame && |
880 | 0 | !PresContext()->HasAuthorSpecifiedRules(trackFrame, |
881 | 0 | STYLES_DISABLING_NATIVE_THEMING) && |
882 | 0 | progressFrame && |
883 | 0 | !PresContext()->HasAuthorSpecifiedRules(progressFrame, |
884 | 0 | STYLES_DISABLING_NATIVE_THEMING) && |
885 | 0 | thumbFrame && |
886 | 0 | !PresContext()->HasAuthorSpecifiedRules(thumbFrame, |
887 | 0 | STYLES_DISABLING_NATIVE_THEMING); |
888 | 0 | } |
889 | | |
890 | | ComputedStyle* |
891 | | nsRangeFrame::GetAdditionalComputedStyle(int32_t aIndex) const |
892 | 0 | { |
893 | 0 | // We only implement this so that SetAdditionalComputedStyle will be |
894 | 0 | // called if style changes that would change the -moz-focus-outer |
895 | 0 | // pseudo-element have occurred. |
896 | 0 | if (aIndex != 0) { |
897 | 0 | return nullptr; |
898 | 0 | } |
899 | 0 | return mOuterFocusStyle; |
900 | 0 | } |
901 | | |
902 | | void |
903 | | nsRangeFrame::SetAdditionalComputedStyle(int32_t aIndex, |
904 | | ComputedStyle* aComputedStyle) |
905 | 0 | { |
906 | 0 | MOZ_ASSERT(aIndex == 0, |
907 | 0 | "GetAdditionalComputedStyle is handling other indexes?"); |
908 | 0 |
|
909 | 0 | // The -moz-focus-outer pseudo-element's style has changed. |
910 | 0 | mOuterFocusStyle = aComputedStyle; |
911 | 0 | } |