layout/forms/nsNumberControlFrame.cpp

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

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 "nsNumberControlFrame.h"
michael@0 7
michael@0 8 #include "HTMLInputElement.h"
michael@0 9 #include "ICUUtils.h"
michael@0 10 #include "nsIFocusManager.h"
michael@0 11 #include "nsIPresShell.h"
michael@0 12 #include "nsFocusManager.h"
michael@0 13 #include "nsFontMetrics.h"
michael@0 14 #include "nsFormControlFrame.h"
michael@0 15 #include "nsGkAtoms.h"
michael@0 16 #include "nsINodeInfo.h"
michael@0 17 #include "nsNameSpaceManager.h"
michael@0 18 #include "nsThemeConstants.h"
michael@0 19 #include "mozilla/BasicEvents.h"
michael@0 20 #include "mozilla/EventStates.h"
michael@0 21 #include "nsContentUtils.h"
michael@0 22 #include "nsContentCreatorFunctions.h"
michael@0 23 #include "nsContentList.h"
michael@0 24 #include "nsStyleSet.h"
michael@0 25 #include "nsIDOMMutationEvent.h"
michael@0 26
michael@0 27 #ifdef ACCESSIBILITY
michael@0 28 #include "mozilla/a11y/AccTypes.h"
michael@0 29 #endif
michael@0 30
michael@0 31 using namespace mozilla;
michael@0 32 using namespace mozilla::dom;
michael@0 33
michael@0 34 nsIFrame*
michael@0 35 NS_NewNumberControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 36 {
michael@0 37 return new (aPresShell) nsNumberControlFrame(aContext);
michael@0 38 }
michael@0 39
michael@0 40 NS_IMPL_FRAMEARENA_HELPERS(nsNumberControlFrame)
michael@0 41
michael@0 42 NS_QUERYFRAME_HEAD(nsNumberControlFrame)
michael@0 43 NS_QUERYFRAME_ENTRY(nsNumberControlFrame)
michael@0 44 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
michael@0 45 NS_QUERYFRAME_ENTRY(nsITextControlFrame)
michael@0 46 NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
michael@0 47 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
michael@0 48
michael@0 49 nsNumberControlFrame::nsNumberControlFrame(nsStyleContext* aContext)
michael@0 50 : nsContainerFrame(aContext)
michael@0 51 , mHandlingInputEvent(false)
michael@0 52 {
michael@0 53 }
michael@0 54
michael@0 55 void
michael@0 56 nsNumberControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
michael@0 57 {
michael@0 58 NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(),
michael@0 59 "nsNumberControlFrame should not have continuations; if it does we "
michael@0 60 "need to call RegUnregAccessKey only for the first");
michael@0 61 nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
michael@0 62 nsContentUtils::DestroyAnonymousContent(&mOuterWrapper);
michael@0 63 nsContainerFrame::DestroyFrom(aDestructRoot);
michael@0 64 }
michael@0 65
michael@0 66 nscoord
michael@0 67 nsNumberControlFrame::GetMinWidth(nsRenderingContext* aRenderingContext)
michael@0 68 {
michael@0 69 nscoord result;
michael@0 70 DISPLAY_MIN_WIDTH(this, result);
michael@0 71
michael@0 72 nsIFrame* kid = mFrames.FirstChild();
michael@0 73 if (kid) { // display:none?
michael@0 74 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
michael@0 75 kid,
michael@0 76 nsLayoutUtils::MIN_WIDTH);
michael@0 77 } else {
michael@0 78 result = 0;
michael@0 79 }
michael@0 80
michael@0 81 return result;
michael@0 82 }
michael@0 83
michael@0 84 nscoord
michael@0 85 nsNumberControlFrame::GetPrefWidth(nsRenderingContext* aRenderingContext)
michael@0 86 {
michael@0 87 nscoord result;
michael@0 88 DISPLAY_PREF_WIDTH(this, result);
michael@0 89
michael@0 90 nsIFrame* kid = mFrames.FirstChild();
michael@0 91 if (kid) { // display:none?
michael@0 92 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
michael@0 93 kid,
michael@0 94 nsLayoutUtils::PREF_WIDTH);
michael@0 95 } else {
michael@0 96 result = 0;
michael@0 97 }
michael@0 98
michael@0 99 return result;
michael@0 100 }
michael@0 101
michael@0 102 nsresult
michael@0 103 nsNumberControlFrame::Reflow(nsPresContext* aPresContext,
michael@0 104 nsHTMLReflowMetrics& aDesiredSize,
michael@0 105 const nsHTMLReflowState& aReflowState,
michael@0 106 nsReflowStatus& aStatus)
michael@0 107 {
michael@0 108 DO_GLOBAL_REFLOW_COUNT("nsNumberControlFrame");
michael@0 109 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
michael@0 110
michael@0 111 NS_ASSERTION(mOuterWrapper, "Outer wrapper div must exist!");
michael@0 112
michael@0 113 NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(),
michael@0 114 "nsNumberControlFrame should not have continuations; if it does we "
michael@0 115 "need to call RegUnregAccessKey only for the first");
michael@0 116
michael@0 117 NS_ASSERTION(!mFrames.FirstChild() ||
michael@0 118 !mFrames.FirstChild()->GetNextSibling(),
michael@0 119 "We expect at most one direct child frame");
michael@0 120
michael@0 121 if (mState & NS_FRAME_FIRST_REFLOW) {
michael@0 122 nsFormControlFrame::RegUnRegAccessKey(this, true);
michael@0 123 }
michael@0 124
michael@0 125 // The width of our content box, which is the available width
michael@0 126 // for our anonymous content:
michael@0 127 const nscoord contentBoxWidth = aReflowState.ComputedWidth();
michael@0 128 nscoord contentBoxHeight = aReflowState.ComputedHeight();
michael@0 129
michael@0 130 nsIFrame* outerWrapperFrame = mOuterWrapper->GetPrimaryFrame();
michael@0 131
michael@0 132 if (!outerWrapperFrame) { // display:none?
michael@0 133 if (contentBoxHeight == NS_INTRINSICSIZE) {
michael@0 134 contentBoxHeight = 0;
michael@0 135 }
michael@0 136 } else {
michael@0 137 NS_ASSERTION(outerWrapperFrame == mFrames.FirstChild(), "huh?");
michael@0 138
michael@0 139 nsHTMLReflowMetrics wrappersDesiredSize(aReflowState);
michael@0 140
michael@0 141 nsHTMLReflowState wrapperReflowState(aPresContext, aReflowState,
michael@0 142 outerWrapperFrame,
michael@0 143 nsSize(contentBoxWidth,
michael@0 144 NS_UNCONSTRAINEDSIZE));
michael@0 145
michael@0 146 // offsets of wrapper frame
michael@0 147 nscoord xoffset = aReflowState.ComputedPhysicalBorderPadding().left +
michael@0 148 wrapperReflowState.ComputedPhysicalMargin().left;
michael@0 149 nscoord yoffset = aReflowState.ComputedPhysicalBorderPadding().top +
michael@0 150 wrapperReflowState.ComputedPhysicalMargin().top;
michael@0 151
michael@0 152 nsReflowStatus childStatus;
michael@0 153 nsresult rv = ReflowChild(outerWrapperFrame, aPresContext,
michael@0 154 wrappersDesiredSize, wrapperReflowState,
michael@0 155 xoffset, yoffset, 0, childStatus);
michael@0 156 NS_ENSURE_SUCCESS(rv, rv);
michael@0 157 MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus),
michael@0 158 "We gave our child unconstrained height, so it should be complete");
michael@0 159
michael@0 160 nscoord wrappersMarginBoxHeight = wrappersDesiredSize.Height() +
michael@0 161 wrapperReflowState.ComputedPhysicalMargin().TopBottom();
michael@0 162
michael@0 163 if (contentBoxHeight == NS_INTRINSICSIZE) {
michael@0 164 // We are intrinsically sized -- we should shrinkwrap the outer wrapper's
michael@0 165 // height:
michael@0 166 contentBoxHeight = wrappersMarginBoxHeight;
michael@0 167
michael@0 168 // Make sure we obey min/max-height in the case when we're doing intrinsic
michael@0 169 // sizing (we get it for free when we have a non-intrinsic
michael@0 170 // aReflowState.ComputedHeight()). Note that we do this before
michael@0 171 // adjusting for borderpadding, since mComputedMaxHeight and
michael@0 172 // mComputedMinHeight are content heights.
michael@0 173 contentBoxHeight =
michael@0 174 NS_CSS_MINMAX(contentBoxHeight,
michael@0 175 aReflowState.ComputedMinHeight(),
michael@0 176 aReflowState.ComputedMaxHeight());
michael@0 177 }
michael@0 178
michael@0 179 // Center child vertically
michael@0 180 nscoord extraSpace = contentBoxHeight - wrappersMarginBoxHeight;
michael@0 181 yoffset += std::max(0, extraSpace / 2);
michael@0 182
michael@0 183 // Place the child
michael@0 184 rv = FinishReflowChild(outerWrapperFrame, aPresContext,
michael@0 185 wrappersDesiredSize, &wrapperReflowState,
michael@0 186 xoffset, yoffset, 0);
michael@0 187 NS_ENSURE_SUCCESS(rv, rv);
michael@0 188
michael@0 189 aDesiredSize.SetTopAscent(wrappersDesiredSize.TopAscent() +
michael@0 190 outerWrapperFrame->GetPosition().y);
michael@0 191 }
michael@0 192
michael@0 193 aDesiredSize.Width() = contentBoxWidth +
michael@0 194 aReflowState.ComputedPhysicalBorderPadding().LeftRight();
michael@0 195 aDesiredSize.Height() = contentBoxHeight +
michael@0 196 aReflowState.ComputedPhysicalBorderPadding().TopBottom();
michael@0 197
michael@0 198 aDesiredSize.SetOverflowAreasToDesiredBounds();
michael@0 199
michael@0 200 if (outerWrapperFrame) {
michael@0 201 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, outerWrapperFrame);
michael@0 202 }
michael@0 203
michael@0 204 FinishAndStoreOverflow(&aDesiredSize);
michael@0 205
michael@0 206 aStatus = NS_FRAME_COMPLETE;
michael@0 207
michael@0 208 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
michael@0 209
michael@0 210 return NS_OK;
michael@0 211 }
michael@0 212
michael@0 213 void
michael@0 214 nsNumberControlFrame::SyncDisabledState()
michael@0 215 {
michael@0 216 EventStates eventStates = mContent->AsElement()->State();
michael@0 217 if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
michael@0 218 mTextField->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
michael@0 219 true);
michael@0 220 } else {
michael@0 221 mTextField->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
michael@0 222 }
michael@0 223 }
michael@0 224
michael@0 225 nsresult
michael@0 226 nsNumberControlFrame::AttributeChanged(int32_t aNameSpaceID,
michael@0 227 nsIAtom* aAttribute,
michael@0 228 int32_t aModType)
michael@0 229 {
michael@0 230 // nsGkAtoms::disabled is handled by SyncDisabledState
michael@0 231 if (aNameSpaceID == kNameSpaceID_None) {
michael@0 232 if (aAttribute == nsGkAtoms::placeholder ||
michael@0 233 aAttribute == nsGkAtoms::readonly ||
michael@0 234 aAttribute == nsGkAtoms::tabindex) {
michael@0 235 if (aModType == nsIDOMMutationEvent::REMOVAL) {
michael@0 236 mTextField->UnsetAttr(aNameSpaceID, aAttribute, true);
michael@0 237 } else {
michael@0 238 MOZ_ASSERT(aModType == nsIDOMMutationEvent::ADDITION ||
michael@0 239 aModType == nsIDOMMutationEvent::MODIFICATION);
michael@0 240 nsAutoString value;
michael@0 241 mContent->GetAttr(aNameSpaceID, aAttribute, value);
michael@0 242 mTextField->SetAttr(aNameSpaceID, aAttribute, value, true);
michael@0 243 }
michael@0 244 }
michael@0 245 }
michael@0 246
michael@0 247 return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
michael@0 248 aModType);
michael@0 249 }
michael@0 250
michael@0 251 void
michael@0 252 nsNumberControlFrame::ContentStatesChanged(EventStates aStates)
michael@0 253 {
michael@0 254 if (aStates.HasState(NS_EVENT_STATE_DISABLED)) {
michael@0 255 nsContentUtils::AddScriptRunner(new SyncDisabledStateEvent(this));
michael@0 256 }
michael@0 257 }
michael@0 258
michael@0 259 nsITextControlFrame*
michael@0 260 nsNumberControlFrame::GetTextFieldFrame()
michael@0 261 {
michael@0 262 return do_QueryFrame(GetAnonTextControl()->GetPrimaryFrame());
michael@0 263 }
michael@0 264
michael@0 265 nsresult
michael@0 266 nsNumberControlFrame::MakeAnonymousElement(Element** aResult,
michael@0 267 nsTArray<ContentInfo>& aElements,
michael@0 268 nsIAtom* aTagName,
michael@0 269 nsCSSPseudoElements::Type aPseudoType,
michael@0 270 nsStyleContext* aParentContext)
michael@0 271 {
michael@0 272 // Get the NodeInfoManager and tag necessary to create the anonymous divs.
michael@0 273 nsCOMPtr<nsIDocument> doc = mContent->GetDocument();
michael@0 274 nsRefPtr<Element> resultElement = doc->CreateHTMLElement(aTagName);
michael@0 275
michael@0 276 // If we legitimately fail this assertion and need to allow
michael@0 277 // non-pseudo-element anonymous children, then we'll need to add a branch
michael@0 278 // that calls ResolveStyleFor((*aResult)->AsElement(), aParentContext)") to
michael@0 279 // set newStyleContext.
michael@0 280 NS_ASSERTION(aPseudoType != nsCSSPseudoElements::ePseudo_NotPseudoElement,
michael@0 281 "Expecting anonymous children to all be pseudo-elements");
michael@0 282 // Associate the pseudo-element with the anonymous child
michael@0 283 nsRefPtr<nsStyleContext> newStyleContext =
michael@0 284 PresContext()->StyleSet()->ResolvePseudoElementStyle(mContent->AsElement(),
michael@0 285 aPseudoType,
michael@0 286 aParentContext,
michael@0 287 resultElement);
michael@0 288
michael@0 289 if (!aElements.AppendElement(ContentInfo(resultElement, newStyleContext))) {
michael@0 290 return NS_ERROR_OUT_OF_MEMORY;
michael@0 291 }
michael@0 292
michael@0 293 if (aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberSpinDown ||
michael@0 294 aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberSpinUp) {
michael@0 295 resultElement->SetAttr(kNameSpaceID_None, nsGkAtoms::role,
michael@0 296 NS_LITERAL_STRING("button"), false);
michael@0 297 }
michael@0 298
michael@0 299 resultElement.forget(aResult);
michael@0 300 return NS_OK;
michael@0 301 }
michael@0 302
michael@0 303 nsresult
michael@0 304 nsNumberControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
michael@0 305 {
michael@0 306 nsresult rv;
michael@0 307
michael@0 308 // We create an anonymous tree for our input element that is structured as
michael@0 309 // follows:
michael@0 310 //
michael@0 311 // input
michael@0 312 // div - outer wrapper with "display:flex" by default
michael@0 313 // input - text input field
michael@0 314 // div - spin box wrapping up/down arrow buttons
michael@0 315 // div - spin up (up arrow button)
michael@0 316 // div - spin down (down arrow button)
michael@0 317 //
michael@0 318 // If you change this, be careful to change the destruction order in
michael@0 319 // nsNumberControlFrame::DestroyFrom.
michael@0 320
michael@0 321
michael@0 322 // Create the anonymous outer wrapper:
michael@0 323 rv = MakeAnonymousElement(getter_AddRefs(mOuterWrapper),
michael@0 324 aElements,
michael@0 325 nsGkAtoms::div,
michael@0 326 nsCSSPseudoElements::ePseudo_mozNumberWrapper,
michael@0 327 mStyleContext);
michael@0 328 NS_ENSURE_SUCCESS(rv, rv);
michael@0 329
michael@0 330 ContentInfo& outerWrapperCI = aElements.LastElement();
michael@0 331
michael@0 332 // Create the ::-moz-number-text pseudo-element:
michael@0 333 rv = MakeAnonymousElement(getter_AddRefs(mTextField),
michael@0 334 outerWrapperCI.mChildren,
michael@0 335 nsGkAtoms::input,
michael@0 336 nsCSSPseudoElements::ePseudo_mozNumberText,
michael@0 337 outerWrapperCI.mStyleContext);
michael@0 338 NS_ENSURE_SUCCESS(rv, rv);
michael@0 339
michael@0 340 mTextField->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
michael@0 341 NS_LITERAL_STRING("text"), PR_FALSE);
michael@0 342
michael@0 343 HTMLInputElement* content = HTMLInputElement::FromContent(mContent);
michael@0 344 HTMLInputElement* textField = HTMLInputElement::FromContent(mTextField);
michael@0 345
michael@0 346 // Initialize the text field value:
michael@0 347 nsAutoString value;
michael@0 348 content->GetValue(value);
michael@0 349 SetValueOfAnonTextControl(value);
michael@0 350
michael@0 351 // If we're readonly, make sure our anonymous text control is too:
michael@0 352 nsAutoString readonly;
michael@0 353 if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::readonly, readonly)) {
michael@0 354 mTextField->SetAttr(kNameSpaceID_None, nsGkAtoms::readonly, readonly, false);
michael@0 355 }
michael@0 356
michael@0 357 // Propogate our tabindex:
michael@0 358 int32_t tabIndex;
michael@0 359 content->GetTabIndex(&tabIndex);
michael@0 360 textField->SetTabIndex(tabIndex);
michael@0 361
michael@0 362 // Initialize the text field's placeholder, if ours is set:
michael@0 363 nsAutoString placeholder;
michael@0 364 if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, placeholder)) {
michael@0 365 mTextField->SetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, placeholder, false);
michael@0 366 }
michael@0 367
michael@0 368 if (mContent->AsElement()->State().HasState(NS_EVENT_STATE_FOCUS)) {
michael@0 369 // We don't want to focus the frame but the text field.
michael@0 370 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
michael@0 371 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mTextField);
michael@0 372 NS_ASSERTION(element, "Really, this should be a nsIDOMElement!");
michael@0 373 fm->SetFocus(element, 0);
michael@0 374 }
michael@0 375
michael@0 376 if (StyleDisplay()->mAppearance == NS_THEME_TEXTFIELD) {
michael@0 377 // The author has elected to hide the spinner by setting this
michael@0 378 // -moz-appearance. We will reframe if it changes.
michael@0 379 return rv;
michael@0 380 }
michael@0 381
michael@0 382 // Create the ::-moz-number-spin-box pseudo-element:
michael@0 383 rv = MakeAnonymousElement(getter_AddRefs(mSpinBox),
michael@0 384 outerWrapperCI.mChildren,
michael@0 385 nsGkAtoms::div,
michael@0 386 nsCSSPseudoElements::ePseudo_mozNumberSpinBox,
michael@0 387 outerWrapperCI.mStyleContext);
michael@0 388 NS_ENSURE_SUCCESS(rv, rv);
michael@0 389
michael@0 390 ContentInfo& spinBoxCI = outerWrapperCI.mChildren.LastElement();
michael@0 391
michael@0 392 // Create the ::-moz-number-spin-up pseudo-element:
michael@0 393 rv = MakeAnonymousElement(getter_AddRefs(mSpinUp),
michael@0 394 spinBoxCI.mChildren,
michael@0 395 nsGkAtoms::div,
michael@0 396 nsCSSPseudoElements::ePseudo_mozNumberSpinUp,
michael@0 397 spinBoxCI.mStyleContext);
michael@0 398 NS_ENSURE_SUCCESS(rv, rv);
michael@0 399
michael@0 400 // Create the ::-moz-number-spin-down pseudo-element:
michael@0 401 rv = MakeAnonymousElement(getter_AddRefs(mSpinDown),
michael@0 402 spinBoxCI.mChildren,
michael@0 403 nsGkAtoms::div,
michael@0 404 nsCSSPseudoElements::ePseudo_mozNumberSpinDown,
michael@0 405 spinBoxCI.mStyleContext);
michael@0 406
michael@0 407 SyncDisabledState();
michael@0 408
michael@0 409 return rv;
michael@0 410 }
michael@0 411
michael@0 412 nsIAtom*
michael@0 413 nsNumberControlFrame::GetType() const
michael@0 414 {
michael@0 415 return nsGkAtoms::numberControlFrame;
michael@0 416 }
michael@0 417
michael@0 418 NS_IMETHODIMP
michael@0 419 nsNumberControlFrame::GetEditor(nsIEditor **aEditor)
michael@0 420 {
michael@0 421 return GetTextFieldFrame()->GetEditor(aEditor);
michael@0 422 }
michael@0 423
michael@0 424 NS_IMETHODIMP
michael@0 425 nsNumberControlFrame::SetSelectionStart(int32_t aSelectionStart)
michael@0 426 {
michael@0 427 return GetTextFieldFrame()->SetSelectionStart(aSelectionStart);
michael@0 428 }
michael@0 429
michael@0 430 NS_IMETHODIMP
michael@0 431 nsNumberControlFrame::SetSelectionEnd(int32_t aSelectionEnd)
michael@0 432 {
michael@0 433 return GetTextFieldFrame()->SetSelectionEnd(aSelectionEnd);
michael@0 434 }
michael@0 435
michael@0 436 NS_IMETHODIMP
michael@0 437 nsNumberControlFrame::SetSelectionRange(int32_t aSelectionStart,
michael@0 438 int32_t aSelectionEnd,
michael@0 439 SelectionDirection aDirection)
michael@0 440 {
michael@0 441 return GetTextFieldFrame()->SetSelectionRange(aSelectionStart, aSelectionEnd,
michael@0 442 aDirection);
michael@0 443 }
michael@0 444
michael@0 445 NS_IMETHODIMP
michael@0 446 nsNumberControlFrame::GetSelectionRange(int32_t* aSelectionStart,
michael@0 447 int32_t* aSelectionEnd,
michael@0 448 SelectionDirection* aDirection)
michael@0 449 {
michael@0 450 return GetTextFieldFrame()->GetSelectionRange(aSelectionStart, aSelectionEnd,
michael@0 451 aDirection);
michael@0 452 }
michael@0 453
michael@0 454 NS_IMETHODIMP
michael@0 455 nsNumberControlFrame::GetOwnedSelectionController(nsISelectionController** aSelCon)
michael@0 456 {
michael@0 457 return GetTextFieldFrame()->GetOwnedSelectionController(aSelCon);
michael@0 458 }
michael@0 459
michael@0 460 nsFrameSelection*
michael@0 461 nsNumberControlFrame::GetOwnedFrameSelection()
michael@0 462 {
michael@0 463 return GetTextFieldFrame()->GetOwnedFrameSelection();
michael@0 464 }
michael@0 465
michael@0 466 nsresult
michael@0 467 nsNumberControlFrame::GetPhonetic(nsAString& aPhonetic)
michael@0 468 {
michael@0 469 return GetTextFieldFrame()->GetPhonetic(aPhonetic);
michael@0 470 }
michael@0 471
michael@0 472 nsresult
michael@0 473 nsNumberControlFrame::EnsureEditorInitialized()
michael@0 474 {
michael@0 475 return GetTextFieldFrame()->EnsureEditorInitialized();
michael@0 476 }
michael@0 477
michael@0 478 nsresult
michael@0 479 nsNumberControlFrame::ScrollSelectionIntoView()
michael@0 480 {
michael@0 481 return GetTextFieldFrame()->ScrollSelectionIntoView();
michael@0 482 }
michael@0 483
michael@0 484 void
michael@0 485 nsNumberControlFrame::SetFocus(bool aOn, bool aRepaint)
michael@0 486 {
michael@0 487 GetTextFieldFrame()->SetFocus(aOn, aRepaint);
michael@0 488 }
michael@0 489
michael@0 490 nsresult
michael@0 491 nsNumberControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue)
michael@0 492 {
michael@0 493 return GetTextFieldFrame()->SetFormProperty(aName, aValue);
michael@0 494 }
michael@0 495
michael@0 496 HTMLInputElement*
michael@0 497 nsNumberControlFrame::GetAnonTextControl()
michael@0 498 {
michael@0 499 return mTextField ? HTMLInputElement::FromContent(mTextField) : nullptr;
michael@0 500 }
michael@0 501
michael@0 502 /* static */ nsNumberControlFrame*
michael@0 503 nsNumberControlFrame::GetNumberControlFrameForTextField(nsIFrame* aFrame)
michael@0 504 {
michael@0 505 // If aFrame is the anon text field for an <input type=number> then we expect
michael@0 506 // the frame of its mContent's grandparent to be that input's frame. We
michael@0 507 // have to check for this via the content tree because we don't know whether
michael@0 508 // extra frames will be wrapped around any of the elements between aFrame and
michael@0 509 // the nsNumberControlFrame that we're looking for (e.g. flex wrappers).
michael@0 510 nsIContent* content = aFrame->GetContent();
michael@0 511 if (content->IsInNativeAnonymousSubtree() &&
michael@0 512 content->GetParent() && content->GetParent()->GetParent()) {
michael@0 513 nsIContent* grandparent = content->GetParent()->GetParent();
michael@0 514 if (grandparent->IsHTML(nsGkAtoms::input) &&
michael@0 515 grandparent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
michael@0 516 nsGkAtoms::number, eCaseMatters)) {
michael@0 517 return do_QueryFrame(grandparent->GetPrimaryFrame());
michael@0 518 }
michael@0 519 }
michael@0 520 return nullptr;
michael@0 521 }
michael@0 522
michael@0 523 /* static */ nsNumberControlFrame*
michael@0 524 nsNumberControlFrame::GetNumberControlFrameForSpinButton(nsIFrame* aFrame)
michael@0 525 {
michael@0 526 // If aFrame is a spin button for an <input type=number> then we expect the
michael@0 527 // frame of its mContent's great-grandparent to be that input's frame. We
michael@0 528 // have to check for this via the content tree because we don't know whether
michael@0 529 // extra frames will be wrapped around any of the elements between aFrame and
michael@0 530 // the nsNumberControlFrame that we're looking for (e.g. flex wrappers).
michael@0 531 nsIContent* content = aFrame->GetContent();
michael@0 532 if (content->IsInNativeAnonymousSubtree() &&
michael@0 533 content->GetParent() && content->GetParent()->GetParent() &&
michael@0 534 content->GetParent()->GetParent()->GetParent()) {
michael@0 535 nsIContent* greatgrandparent = content->GetParent()->GetParent()->GetParent();
michael@0 536 if (greatgrandparent->IsHTML(nsGkAtoms::input) &&
michael@0 537 greatgrandparent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
michael@0 538 nsGkAtoms::number, eCaseMatters)) {
michael@0 539 return do_QueryFrame(greatgrandparent->GetPrimaryFrame());
michael@0 540 }
michael@0 541 }
michael@0 542 return nullptr;
michael@0 543 }
michael@0 544
michael@0 545 int32_t
michael@0 546 nsNumberControlFrame::GetSpinButtonForPointerEvent(WidgetGUIEvent* aEvent) const
michael@0 547 {
michael@0 548 MOZ_ASSERT(aEvent->eventStructType == NS_MOUSE_EVENT,
michael@0 549 "Unexpected event type");
michael@0 550
michael@0 551 if (!mSpinBox) {
michael@0 552 // we don't have a spinner
michael@0 553 return eSpinButtonNone;
michael@0 554 }
michael@0 555 if (aEvent->originalTarget == mSpinUp) {
michael@0 556 return eSpinButtonUp;
michael@0 557 }
michael@0 558 if (aEvent->originalTarget == mSpinDown) {
michael@0 559 return eSpinButtonDown;
michael@0 560 }
michael@0 561 if (aEvent->originalTarget == mSpinBox) {
michael@0 562 // In the case that the up/down buttons are hidden (display:none) we use
michael@0 563 // just the spin box element, spinning up if the pointer is over the top
michael@0 564 // half of the element, or down if it's over the bottom half. This is
michael@0 565 // important to handle since this is the state things are in for the
michael@0 566 // default UA style sheet. See the comment in forms.css for why.
michael@0 567 LayoutDeviceIntPoint absPoint = aEvent->refPoint;
michael@0 568 nsPoint point =
michael@0 569 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
michael@0 570 LayoutDeviceIntPoint::ToUntyped(absPoint),
michael@0 571 mSpinBox->GetPrimaryFrame());
michael@0 572 if (point != nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) {
michael@0 573 if (point.y < mSpinBox->GetPrimaryFrame()->GetSize().height / 2) {
michael@0 574 return eSpinButtonUp;
michael@0 575 }
michael@0 576 return eSpinButtonDown;
michael@0 577 }
michael@0 578 }
michael@0 579 return eSpinButtonNone;
michael@0 580 }
michael@0 581
michael@0 582 void
michael@0 583 nsNumberControlFrame::SpinnerStateChanged() const
michael@0 584 {
michael@0 585 MOZ_ASSERT(mSpinUp && mSpinDown,
michael@0 586 "We should not be called when we have no spinner");
michael@0 587
michael@0 588 nsIFrame* spinUpFrame = mSpinUp->GetPrimaryFrame();
michael@0 589 if (spinUpFrame && spinUpFrame->IsThemed()) {
michael@0 590 spinUpFrame->InvalidateFrame();
michael@0 591 }
michael@0 592 nsIFrame* spinDownFrame = mSpinDown->GetPrimaryFrame();
michael@0 593 if (spinDownFrame && spinDownFrame->IsThemed()) {
michael@0 594 spinDownFrame->InvalidateFrame();
michael@0 595 }
michael@0 596 }
michael@0 597
michael@0 598 bool
michael@0 599 nsNumberControlFrame::SpinnerUpButtonIsDepressed() const
michael@0 600 {
michael@0 601 return HTMLInputElement::FromContent(mContent)->
michael@0 602 NumberSpinnerUpButtonIsDepressed();
michael@0 603 }
michael@0 604
michael@0 605 bool
michael@0 606 nsNumberControlFrame::SpinnerDownButtonIsDepressed() const
michael@0 607 {
michael@0 608 return HTMLInputElement::FromContent(mContent)->
michael@0 609 NumberSpinnerDownButtonIsDepressed();
michael@0 610 }
michael@0 611
michael@0 612 bool
michael@0 613 nsNumberControlFrame::IsFocused() const
michael@0 614 {
michael@0 615 // Normally this depends on the state of our anonymous text control (which
michael@0 616 // takes focus for us), but in the case that it does not have a frame we will
michael@0 617 // have focus ourself.
michael@0 618 return mTextField->AsElement()->State().HasState(NS_EVENT_STATE_FOCUS) ||
michael@0 619 mContent->AsElement()->State().HasState(NS_EVENT_STATE_FOCUS);
michael@0 620 }
michael@0 621
michael@0 622 void
michael@0 623 nsNumberControlFrame::HandleFocusEvent(WidgetEvent* aEvent)
michael@0 624 {
michael@0 625 if (aEvent->originalTarget != mTextField) {
michael@0 626 // Move focus to our text field
michael@0 627 HTMLInputElement::FromContent(mTextField)->Focus();
michael@0 628 }
michael@0 629 }
michael@0 630
michael@0 631 nsresult
michael@0 632 nsNumberControlFrame::HandleSelectCall()
michael@0 633 {
michael@0 634 return HTMLInputElement::FromContent(mTextField)->Select();
michael@0 635 }
michael@0 636
michael@0 637 #define STYLES_DISABLING_NATIVE_THEMING \
michael@0 638 NS_AUTHOR_SPECIFIED_BACKGROUND | \
michael@0 639 NS_AUTHOR_SPECIFIED_PADDING | \
michael@0 640 NS_AUTHOR_SPECIFIED_BORDER
michael@0 641
michael@0 642 bool
michael@0 643 nsNumberControlFrame::ShouldUseNativeStyleForSpinner() const
michael@0 644 {
michael@0 645 MOZ_ASSERT(mSpinUp && mSpinDown,
michael@0 646 "We should not be called when we have no spinner");
michael@0 647
michael@0 648 nsIFrame* spinUpFrame = mSpinUp->GetPrimaryFrame();
michael@0 649 nsIFrame* spinDownFrame = mSpinDown->GetPrimaryFrame();
michael@0 650
michael@0 651 return spinUpFrame &&
michael@0 652 spinUpFrame->StyleDisplay()->mAppearance == NS_THEME_SPINNER_UP_BUTTON &&
michael@0 653 !PresContext()->HasAuthorSpecifiedRules(spinUpFrame,
michael@0 654 STYLES_DISABLING_NATIVE_THEMING) &&
michael@0 655 spinDownFrame &&
michael@0 656 spinDownFrame->StyleDisplay()->mAppearance == NS_THEME_SPINNER_DOWN_BUTTON &&
michael@0 657 !PresContext()->HasAuthorSpecifiedRules(spinDownFrame,
michael@0 658 STYLES_DISABLING_NATIVE_THEMING);
michael@0 659 }
michael@0 660
michael@0 661 void
michael@0 662 nsNumberControlFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
michael@0 663 uint32_t aFilter)
michael@0 664 {
michael@0 665 // Only one direct anonymous child:
michael@0 666 aElements.MaybeAppendElement(mOuterWrapper);
michael@0 667 }
michael@0 668
michael@0 669 void
michael@0 670 nsNumberControlFrame::SetValueOfAnonTextControl(const nsAString& aValue)
michael@0 671 {
michael@0 672 if (mHandlingInputEvent) {
michael@0 673 // We have been called while our HTMLInputElement is processing a DOM
michael@0 674 // 'input' event targeted at our anonymous text control. Our
michael@0 675 // HTMLInputElement has taken the value of our anon text control and
michael@0 676 // called SetValueInternal on itself to keep its own value in sync. As a
michael@0 677 // result SetValueInternal has called us. In this one case we do not want
michael@0 678 // to update our anon text control, especially since aValue will be the
michael@0 679 // sanitized value, and only the internal value should be sanitized (not
michael@0 680 // the value shown to the user, and certainly we shouldn't change it as
michael@0 681 // they type).
michael@0 682 return;
michael@0 683 }
michael@0 684
michael@0 685 // Init to aValue so that we set aValue as the value of our text control if
michael@0 686 // aValue isn't a valid number (in which case the HTMLInputElement's validity
michael@0 687 // state will be set to invalid) or if aValue can't be localized:
michael@0 688 nsAutoString localizedValue(aValue);
michael@0 689
michael@0 690 #ifdef ENABLE_INTL_API
michael@0 691 // Try and localize the value we will set:
michael@0 692 Decimal val = HTMLInputElement::StringToDecimal(aValue);
michael@0 693 if (val.isFinite()) {
michael@0 694 ICUUtils::LanguageTagIterForContent langTagIter(mContent);
michael@0 695 ICUUtils::LocalizeNumber(val.toDouble(), langTagIter, localizedValue);
michael@0 696 }
michael@0 697 #endif
michael@0 698
michael@0 699 // We need to update the value of our anonymous text control here. Note that
michael@0 700 // this must be its value, and not its 'value' attribute (the default value),
michael@0 701 // since the default value is ignored once a user types into the text
michael@0 702 // control.
michael@0 703 HTMLInputElement::FromContent(mTextField)->SetValue(localizedValue);
michael@0 704 }
michael@0 705
michael@0 706 void
michael@0 707 nsNumberControlFrame::GetValueOfAnonTextControl(nsAString& aValue)
michael@0 708 {
michael@0 709 if (!mTextField) {
michael@0 710 aValue.Truncate();
michael@0 711 return;
michael@0 712 }
michael@0 713
michael@0 714 HTMLInputElement::FromContent(mTextField)->GetValue(aValue);
michael@0 715
michael@0 716 #ifdef ENABLE_INTL_API
michael@0 717 // Here we need to de-localize any number typed in by the user. That is, we
michael@0 718 // need to convert it from the number format of the user's language, region,
michael@0 719 // etc. to the format that the HTML 5 spec defines to be a "valid
michael@0 720 // floating-point number":
michael@0 721 //
michael@0 722 // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#floating-point-numbers
michael@0 723 //
michael@0 724 // so that it can be parsed by functions like HTMLInputElement::
michael@0 725 // StringToDecimal (the HTML-5-conforming parsing function) which don't know
michael@0 726 // how to handle numbers that are formatted differently (for example, with
michael@0 727 // non-ASCII digits, with grouping separator characters or with a decimal
michael@0 728 // separator character other than '.').
michael@0 729 //
michael@0 730 // We need to be careful to avoid normalizing numbers that are already
michael@0 731 // formatted for a locale that matches the format of HTML 5's "valid
michael@0 732 // floating-point number" and have no grouping separator characters. (In
michael@0 733 // other words we want to return the number as specified by the user, not the
michael@0 734 // de-localized serialization, since the latter will normalize the value.)
michael@0 735 // For example, if the user's locale is English and the user types in "2e2"
michael@0 736 // then inputElement.value should be "2e2" and not "100". This is because
michael@0 737 // content (and tests) expect us to avoid "normalizing" the number that the
michael@0 738 // user types in if it's not necessary in order to make sure it conforms to
michael@0 739 // HTML 5's "valid floating-point number" format.
michael@0 740 //
michael@0 741 // Note that we also need to be careful when trying to avoid normalization.
michael@0 742 // For example, just because "1.234" _looks_ like a valid floating-point
michael@0 743 // number according to the spec does not mean that it should be returned
michael@0 744 // as-is. If the user's locale is German, then this represents the value
michael@0 745 // 1234, not 1.234, so it still needs to be de-localized. Alternatively, if
michael@0 746 // the user's locale is English and they type in "1,234" we _do_ need to
michael@0 747 // normalize the number to "1234" because HTML 5's valid floating-point
michael@0 748 // number format does not allow the ',' grouping separator. We can detect all
michael@0 749 // the cases where we need to convert by seeing if the locale-specific
michael@0 750 // parsing function understands the user input to mean the same thing as the
michael@0 751 // HTML-5-conforming parsing function. If so, then we should return the value
michael@0 752 // as-is to avoid normalization. Otherwise, we return the de-localized
michael@0 753 // serialization.
michael@0 754 ICUUtils::LanguageTagIterForContent langTagIter(mContent);
michael@0 755 double value = ICUUtils::ParseNumber(aValue, langTagIter);
michael@0 756 if (NS_finite(value) &&
michael@0 757 value != HTMLInputElement::StringToDecimal(aValue).toDouble()) {
michael@0 758 aValue.Truncate();
michael@0 759 aValue.AppendFloat(value);
michael@0 760 }
michael@0 761 #endif
michael@0 762 // else, we return whatever FromContent put into aValue (the number as typed
michael@0 763 // in by the user)
michael@0 764 }
michael@0 765
michael@0 766 bool
michael@0 767 nsNumberControlFrame::AnonTextControlIsEmpty()
michael@0 768 {
michael@0 769 if (!mTextField) {
michael@0 770 return true;
michael@0 771 }
michael@0 772 nsAutoString value;
michael@0 773 HTMLInputElement::FromContent(mTextField)->GetValue(value);
michael@0 774 return value.IsEmpty();
michael@0 775 }
michael@0 776
michael@0 777 Element*
michael@0 778 nsNumberControlFrame::GetPseudoElement(nsCSSPseudoElements::Type aType)
michael@0 779 {
michael@0 780 if (aType == nsCSSPseudoElements::ePseudo_mozNumberWrapper) {
michael@0 781 return mOuterWrapper;
michael@0 782 }
michael@0 783
michael@0 784 if (aType == nsCSSPseudoElements::ePseudo_mozNumberText) {
michael@0 785 return mTextField;
michael@0 786 }
michael@0 787
michael@0 788 if (aType == nsCSSPseudoElements::ePseudo_mozNumberSpinBox) {
michael@0 789 MOZ_ASSERT(mSpinBox);
michael@0 790 return mSpinBox;
michael@0 791 }
michael@0 792
michael@0 793 if (aType == nsCSSPseudoElements::ePseudo_mozNumberSpinUp) {
michael@0 794 MOZ_ASSERT(mSpinUp);
michael@0 795 return mSpinUp;
michael@0 796 }
michael@0 797
michael@0 798 if (aType == nsCSSPseudoElements::ePseudo_mozNumberSpinDown) {
michael@0 799 MOZ_ASSERT(mSpinDown);
michael@0 800 return mSpinDown;
michael@0 801 }
michael@0 802
michael@0 803 return nsContainerFrame::GetPseudoElement(aType);
michael@0 804 }
michael@0 805
michael@0 806 #ifdef ACCESSIBILITY
michael@0 807 a11y::AccType
michael@0 808 nsNumberControlFrame::AccessibleType()
michael@0 809 {
michael@0 810 return a11y::eHTMLSpinnerType;
michael@0 811 }
michael@0 812 #endif

mercurial