layout/forms/nsRangeFrame.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial