LCOV - code coverage report
Current view: top level - layout/forms - nsNumberControlFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 5 307 1.6 %
Date: 2017-07-14 16:53:18 Functions: 1 37 2.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "nsNumberControlFrame.h"
       7             : 
       8             : #include "HTMLInputElement.h"
       9             : #include "ICUUtils.h"
      10             : #include "nsIFocusManager.h"
      11             : #include "nsIPresShell.h"
      12             : #include "nsFocusManager.h"
      13             : #include "nsFontMetrics.h"
      14             : #include "nsFormControlFrame.h"
      15             : #include "nsGkAtoms.h"
      16             : #include "nsNameSpaceManager.h"
      17             : #include "nsThemeConstants.h"
      18             : #include "mozilla/BasicEvents.h"
      19             : #include "mozilla/EventStates.h"
      20             : #include "nsContentUtils.h"
      21             : #include "nsContentCreatorFunctions.h"
      22             : #include "nsContentList.h"
      23             : #include "nsCSSPseudoElements.h"
      24             : #include "nsStyleSet.h"
      25             : #include "mozilla/StyleSetHandle.h"
      26             : #include "mozilla/StyleSetHandleInlines.h"
      27             : #include "nsIDOMMutationEvent.h"
      28             : #include "nsThreadUtils.h"
      29             : #include "mozilla/FloatingPoint.h"
      30             : 
      31             : #ifdef ACCESSIBILITY
      32             : #include "mozilla/a11y/AccTypes.h"
      33             : #endif
      34             : 
      35             : using namespace mozilla;
      36             : using namespace mozilla::dom;
      37             : 
      38             : nsIFrame*
      39           0 : NS_NewNumberControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
      40             : {
      41           0 :   return new (aPresShell) nsNumberControlFrame(aContext);
      42             : }
      43             : 
      44           0 : NS_IMPL_FRAMEARENA_HELPERS(nsNumberControlFrame)
      45             : 
      46           0 : NS_QUERYFRAME_HEAD(nsNumberControlFrame)
      47           0 :   NS_QUERYFRAME_ENTRY(nsNumberControlFrame)
      48           0 :   NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
      49           0 :   NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
      50           0 : NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
      51             : 
      52           0 : nsNumberControlFrame::nsNumberControlFrame(nsStyleContext* aContext)
      53             :   : nsContainerFrame(aContext, kClassID)
      54           0 :   , mHandlingInputEvent(false)
      55             : {
      56           0 : }
      57             : 
      58             : void
      59           0 : nsNumberControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
      60             : {
      61           0 :   NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(),
      62             :                "nsNumberControlFrame should not have continuations; if it does we "
      63             :                "need to call RegUnregAccessKey only for the first");
      64           0 :   nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
      65           0 :   nsContentUtils::DestroyAnonymousContent(&mOuterWrapper);
      66           0 :   nsContainerFrame::DestroyFrom(aDestructRoot);
      67           0 : }
      68             : 
      69             : nscoord
      70           0 : nsNumberControlFrame::GetMinISize(gfxContext* aRenderingContext)
      71             : {
      72             :   nscoord result;
      73           0 :   DISPLAY_MIN_WIDTH(this, result);
      74             : 
      75           0 :   nsIFrame* kid = mFrames.FirstChild();
      76           0 :   if (kid) { // display:none?
      77           0 :     result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
      78             :                                                   kid,
      79             :                                                   nsLayoutUtils::MIN_ISIZE);
      80             :   } else {
      81           0 :     result = 0;
      82             :   }
      83             : 
      84           0 :   return result;
      85             : }
      86             : 
      87             : nscoord
      88           0 : nsNumberControlFrame::GetPrefISize(gfxContext* aRenderingContext)
      89             : {
      90             :   nscoord result;
      91           0 :   DISPLAY_PREF_WIDTH(this, result);
      92             : 
      93           0 :   nsIFrame* kid = mFrames.FirstChild();
      94           0 :   if (kid) { // display:none?
      95           0 :     result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
      96             :                                                   kid,
      97             :                                                   nsLayoutUtils::PREF_ISIZE);
      98             :   } else {
      99           0 :     result = 0;
     100             :   }
     101             : 
     102           0 :   return result;
     103             : }
     104             : 
     105             : void
     106           0 : nsNumberControlFrame::Reflow(nsPresContext* aPresContext,
     107             :                              ReflowOutput& aDesiredSize,
     108             :                              const ReflowInput& aReflowInput,
     109             :                              nsReflowStatus& aStatus)
     110             : {
     111           0 :   MarkInReflow();
     112           0 :   DO_GLOBAL_REFLOW_COUNT("nsNumberControlFrame");
     113           0 :   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
     114             : 
     115           0 :   NS_ASSERTION(mOuterWrapper, "Outer wrapper div must exist!");
     116             : 
     117           0 :   NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(),
     118             :                "nsNumberControlFrame should not have continuations; if it does we "
     119             :                "need to call RegUnregAccessKey only for the first");
     120             : 
     121           0 :   NS_ASSERTION(!mFrames.FirstChild() ||
     122             :                !mFrames.FirstChild()->GetNextSibling(),
     123             :                "We expect at most one direct child frame");
     124             : 
     125           0 :   if (mState & NS_FRAME_FIRST_REFLOW) {
     126           0 :     nsFormControlFrame::RegUnRegAccessKey(this, true);
     127             :   }
     128             : 
     129           0 :   const WritingMode myWM = aReflowInput.GetWritingMode();
     130             : 
     131             :   // The ISize of our content box, which is the available ISize
     132             :   // for our anonymous content:
     133           0 :   const nscoord contentBoxISize = aReflowInput.ComputedISize();
     134           0 :   nscoord contentBoxBSize = aReflowInput.ComputedBSize();
     135             : 
     136             :   // Figure out our border-box sizes as well (by adding borderPadding to
     137             :   // content-box sizes):
     138             :   const nscoord borderBoxISize = contentBoxISize +
     139           0 :     aReflowInput.ComputedLogicalBorderPadding().IStartEnd(myWM);
     140             : 
     141             :   nscoord borderBoxBSize;
     142           0 :   if (contentBoxBSize != NS_INTRINSICSIZE) {
     143           0 :     borderBoxBSize = contentBoxBSize +
     144           0 :       aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
     145             :   } // else, we'll figure out borderBoxBSize after we resolve contentBoxBSize.
     146             : 
     147           0 :   nsIFrame* outerWrapperFrame = mOuterWrapper->GetPrimaryFrame();
     148             : 
     149           0 :   if (!outerWrapperFrame) { // display:none?
     150           0 :     if (contentBoxBSize == NS_INTRINSICSIZE) {
     151           0 :       contentBoxBSize = 0;
     152             :       borderBoxBSize =
     153           0 :         aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
     154             :     }
     155             :   } else {
     156           0 :     NS_ASSERTION(outerWrapperFrame == mFrames.FirstChild(), "huh?");
     157             : 
     158           0 :     ReflowOutput wrappersDesiredSize(aReflowInput);
     159             : 
     160           0 :     WritingMode wrapperWM = outerWrapperFrame->GetWritingMode();
     161           0 :     LogicalSize availSize = aReflowInput.ComputedSize(wrapperWM);
     162           0 :     availSize.BSize(wrapperWM) = NS_UNCONSTRAINEDSIZE;
     163             : 
     164             :     ReflowInput wrapperReflowInput(aPresContext, aReflowInput,
     165           0 :                                          outerWrapperFrame, availSize);
     166             : 
     167             :     // Convert wrapper margin into my own writing-mode (in case it differs):
     168             :     LogicalMargin wrapperMargin =
     169           0 :       wrapperReflowInput.ComputedLogicalMargin().ConvertTo(myWM, wrapperWM);
     170             : 
     171             :     // offsets of wrapper frame within this frame:
     172             :     LogicalPoint
     173             :       wrapperOffset(myWM,
     174           0 :                     aReflowInput.ComputedLogicalBorderPadding().IStart(myWM) +
     175           0 :                     wrapperMargin.IStart(myWM),
     176           0 :                     aReflowInput.ComputedLogicalBorderPadding().BStart(myWM) +
     177           0 :                     wrapperMargin.BStart(myWM));
     178             : 
     179           0 :     nsReflowStatus childStatus;
     180             :     // We initially reflow the child with a dummy containerSize; positioning
     181             :     // will be fixed later.
     182           0 :     const nsSize dummyContainerSize;
     183           0 :     ReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize,
     184             :                 wrapperReflowInput, myWM, wrapperOffset, dummyContainerSize, 0,
     185           0 :                 childStatus);
     186           0 :     MOZ_ASSERT(childStatus.IsFullyComplete(),
     187             :                "We gave our child unconstrained available block-size, "
     188             :                "so it should be complete");
     189             : 
     190             :     nscoord wrappersMarginBoxBSize =
     191           0 :       wrappersDesiredSize.BSize(myWM) + wrapperMargin.BStartEnd(myWM);
     192             : 
     193           0 :     if (contentBoxBSize == NS_INTRINSICSIZE) {
     194             :       // We are intrinsically sized -- we should shrinkwrap the outer wrapper's
     195             :       // block-size:
     196           0 :       contentBoxBSize = wrappersMarginBoxBSize;
     197             : 
     198             :       // Make sure we obey min/max-bsize in the case when we're doing intrinsic
     199             :       // sizing (we get it for free when we have a non-intrinsic
     200             :       // aReflowInput.ComputedBSize()).  Note that we do this before
     201             :       // adjusting for borderpadding, since ComputedMaxBSize and
     202             :       // ComputedMinBSize are content heights.
     203             :       contentBoxBSize =
     204           0 :         NS_CSS_MINMAX(contentBoxBSize,
     205             :                       aReflowInput.ComputedMinBSize(),
     206           0 :                       aReflowInput.ComputedMaxBSize());
     207             : 
     208           0 :       borderBoxBSize = contentBoxBSize +
     209           0 :         aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
     210             :     }
     211             : 
     212             :     // Center child in block axis
     213           0 :     nscoord extraSpace = contentBoxBSize - wrappersMarginBoxBSize;
     214           0 :     wrapperOffset.B(myWM) += std::max(0, extraSpace / 2);
     215             : 
     216             :     // Needed in FinishReflowChild, for logical-to-physical conversion:
     217           0 :     nsSize borderBoxSize = LogicalSize(myWM, borderBoxISize, borderBoxBSize).
     218           0 :                            GetPhysicalSize(myWM);
     219             : 
     220             :     // Place the child
     221             :     FinishReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize,
     222             :                       &wrapperReflowInput, myWM, wrapperOffset,
     223           0 :                       borderBoxSize, 0);
     224             : 
     225             :     nsSize contentBoxSize =
     226           0 :       LogicalSize(myWM, contentBoxISize, contentBoxBSize).
     227           0 :         GetPhysicalSize(myWM);
     228           0 :     aDesiredSize.SetBlockStartAscent(
     229           0 :        wrappersDesiredSize.BlockStartAscent() +
     230           0 :        outerWrapperFrame->BStart(aReflowInput.GetWritingMode(),
     231           0 :                                  contentBoxSize));
     232             :   }
     233             : 
     234           0 :   LogicalSize logicalDesiredSize(myWM, borderBoxISize, borderBoxBSize);
     235           0 :   aDesiredSize.SetSize(myWM, logicalDesiredSize);
     236             : 
     237           0 :   aDesiredSize.SetOverflowAreasToDesiredBounds();
     238             : 
     239           0 :   if (outerWrapperFrame) {
     240           0 :     ConsiderChildOverflow(aDesiredSize.mOverflowAreas, outerWrapperFrame);
     241             :   }
     242             : 
     243           0 :   FinishAndStoreOverflow(&aDesiredSize);
     244             : 
     245           0 :   aStatus.Reset();
     246             : 
     247           0 :   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
     248           0 : }
     249             : 
     250             : void
     251           0 : nsNumberControlFrame::SyncDisabledState()
     252             : {
     253           0 :   EventStates eventStates = mContent->AsElement()->State();
     254           0 :   if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
     255           0 :     mTextField->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
     256           0 :                         true);
     257             :   } else {
     258           0 :     mTextField->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
     259             :   }
     260           0 : }
     261             : 
     262             : nsresult
     263           0 : nsNumberControlFrame::AttributeChanged(int32_t  aNameSpaceID,
     264             :                                        nsIAtom* aAttribute,
     265             :                                        int32_t  aModType)
     266             : {
     267             :   // nsGkAtoms::disabled is handled by SyncDisabledState
     268           0 :   if (aNameSpaceID == kNameSpaceID_None) {
     269           0 :     if (aAttribute == nsGkAtoms::placeholder ||
     270           0 :         aAttribute == nsGkAtoms::readonly ||
     271           0 :         aAttribute == nsGkAtoms::tabindex) {
     272           0 :       if (aModType == nsIDOMMutationEvent::REMOVAL) {
     273           0 :         mTextField->UnsetAttr(aNameSpaceID, aAttribute, true);
     274             :       } else {
     275           0 :         MOZ_ASSERT(aModType == nsIDOMMutationEvent::ADDITION ||
     276             :                    aModType == nsIDOMMutationEvent::MODIFICATION);
     277           0 :         nsAutoString value;
     278           0 :         mContent->GetAttr(aNameSpaceID, aAttribute, value);
     279           0 :         mTextField->SetAttr(aNameSpaceID, aAttribute, value, true);
     280             :       }
     281             :     }
     282             :   }
     283             : 
     284           0 :   return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
     285           0 :                                             aModType);
     286             : }
     287             : 
     288             : void
     289           0 : nsNumberControlFrame::ContentStatesChanged(EventStates aStates)
     290             : {
     291           0 :   if (aStates.HasState(NS_EVENT_STATE_DISABLED)) {
     292           0 :     nsContentUtils::AddScriptRunner(new SyncDisabledStateEvent(this));
     293             :   }
     294           0 : }
     295             : 
     296             : nsITextControlFrame*
     297           0 : nsNumberControlFrame::GetTextFieldFrame()
     298             : {
     299           0 :   return do_QueryFrame(GetAnonTextControl()->GetPrimaryFrame());
     300             : }
     301             : 
     302           0 : class FocusTextField : public Runnable
     303             : {
     304             : public:
     305           0 :   FocusTextField(nsIContent* aNumber, nsIContent* aTextField)
     306           0 :     : mozilla::Runnable("FocusTextField")
     307             :     , mNumber(aNumber)
     308           0 :     , mTextField(aTextField)
     309           0 :   {}
     310             : 
     311           0 :   NS_IMETHOD Run() override
     312             :   {
     313           0 :     if (mNumber->AsElement()->State().HasState(NS_EVENT_STATE_FOCUS)) {
     314           0 :       HTMLInputElement::FromContent(mTextField)->Focus();
     315             :     }
     316             : 
     317           0 :     return NS_OK;
     318             :   }
     319             : 
     320             : private:
     321             :   nsCOMPtr<nsIContent> mNumber;
     322             :   nsCOMPtr<nsIContent> mTextField;
     323             : };
     324             : 
     325             : nsresult
     326           0 : nsNumberControlFrame::MakeAnonymousElement(Element** aResult,
     327             :                                            nsTArray<ContentInfo>& aElements,
     328             :                                            nsIAtom* aTagName,
     329             :                                            CSSPseudoElementType aPseudoType)
     330             : {
     331             :   // Get the NodeInfoManager and tag necessary to create the anonymous divs.
     332           0 :   nsCOMPtr<nsIDocument> doc = mContent->GetComposedDoc();
     333           0 :   RefPtr<Element> resultElement = doc->CreateHTMLElement(aTagName);
     334           0 :   resultElement->SetPseudoElementType(aPseudoType);
     335             : 
     336             :   // Associate the pseudo-element with the anonymous child
     337           0 :   if (!aElements.AppendElement(resultElement)) {
     338           0 :     return NS_ERROR_OUT_OF_MEMORY;
     339             :   }
     340             : 
     341           0 :   if (aPseudoType == CSSPseudoElementType::mozNumberSpinDown ||
     342             :       aPseudoType == CSSPseudoElementType::mozNumberSpinUp) {
     343           0 :     resultElement->SetAttr(kNameSpaceID_None, nsGkAtoms::role,
     344           0 :                            NS_LITERAL_STRING("button"), false);
     345             :   }
     346             : 
     347           0 :   resultElement.forget(aResult);
     348           0 :   return NS_OK;
     349             : }
     350             : 
     351             : nsresult
     352           0 : nsNumberControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
     353             : {
     354             :   nsresult rv;
     355             : 
     356             :   // We create an anonymous tree for our input element that is structured as
     357             :   // follows:
     358             :   //
     359             :   // input
     360             :   //   div      - outer wrapper with "display:flex" by default
     361             :   //     input  - text input field
     362             :   //     div    - spin box wrapping up/down arrow buttons
     363             :   //       div  - spin up (up arrow button)
     364             :   //       div  - spin down (down arrow button)
     365             :   //
     366             :   // If you change this, be careful to change the destruction order in
     367             :   // nsNumberControlFrame::DestroyFrom.
     368             : 
     369             : 
     370             :   // Create the anonymous outer wrapper:
     371           0 :   rv = MakeAnonymousElement(getter_AddRefs(mOuterWrapper),
     372             :                             aElements,
     373             :                             nsGkAtoms::div,
     374           0 :                             CSSPseudoElementType::mozNumberWrapper);
     375           0 :   NS_ENSURE_SUCCESS(rv, rv);
     376             : 
     377           0 :   ContentInfo& outerWrapperCI = aElements.LastElement();
     378             : 
     379             :   // Create the ::-moz-number-text pseudo-element:
     380           0 :   rv = MakeAnonymousElement(getter_AddRefs(mTextField),
     381             :                             outerWrapperCI.mChildren,
     382             :                             nsGkAtoms::input,
     383           0 :                             CSSPseudoElementType::mozNumberText);
     384           0 :   NS_ENSURE_SUCCESS(rv, rv);
     385             : 
     386           0 :   mTextField->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
     387           0 :                       NS_LITERAL_STRING("text"), PR_FALSE);
     388             : 
     389           0 :   HTMLInputElement* content = HTMLInputElement::FromContent(mContent);
     390           0 :   HTMLInputElement* textField = HTMLInputElement::FromContent(mTextField);
     391             : 
     392             :   // Initialize the text field value:
     393           0 :   nsAutoString value;
     394           0 :   content->GetValue(value, CallerType::System);
     395           0 :   SetValueOfAnonTextControl(value);
     396             : 
     397             :   // If we're readonly, make sure our anonymous text control is too:
     398           0 :   nsAutoString readonly;
     399           0 :   if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::readonly, readonly)) {
     400           0 :     mTextField->SetAttr(kNameSpaceID_None, nsGkAtoms::readonly, readonly, false);
     401             :   }
     402             : 
     403             :   // Propogate our tabindex:
     404             :   int32_t tabIndex;
     405           0 :   content->GetTabIndex(&tabIndex);
     406           0 :   textField->SetTabIndex(tabIndex);
     407             : 
     408             :   // Initialize the text field's placeholder, if ours is set:
     409           0 :   nsAutoString placeholder;
     410           0 :   if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, placeholder)) {
     411           0 :     mTextField->SetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, placeholder, false);
     412             :   }
     413             : 
     414           0 :   if (mContent->AsElement()->State().HasState(NS_EVENT_STATE_FOCUS)) {
     415             :     // We don't want to focus the frame but the text field.
     416           0 :     RefPtr<FocusTextField> focusJob = new FocusTextField(mContent, mTextField);
     417           0 :     nsContentUtils::AddScriptRunner(focusJob);
     418             :   }
     419             : 
     420           0 :   if (StyleDisplay()->mAppearance == NS_THEME_TEXTFIELD) {
     421             :     // The author has elected to hide the spinner by setting this
     422             :     // -moz-appearance. We will reframe if it changes.
     423           0 :     return rv;
     424             :   }
     425             : 
     426             :   // Create the ::-moz-number-spin-box pseudo-element:
     427           0 :   rv = MakeAnonymousElement(getter_AddRefs(mSpinBox),
     428             :                             outerWrapperCI.mChildren,
     429             :                             nsGkAtoms::div,
     430           0 :                             CSSPseudoElementType::mozNumberSpinBox);
     431           0 :   NS_ENSURE_SUCCESS(rv, rv);
     432             : 
     433           0 :   ContentInfo& spinBoxCI = outerWrapperCI.mChildren.LastElement();
     434             : 
     435             :   // Create the ::-moz-number-spin-up pseudo-element:
     436           0 :   rv = MakeAnonymousElement(getter_AddRefs(mSpinUp),
     437             :                             spinBoxCI.mChildren,
     438             :                             nsGkAtoms::div,
     439           0 :                             CSSPseudoElementType::mozNumberSpinUp);
     440           0 :   NS_ENSURE_SUCCESS(rv, rv);
     441             : 
     442             :   // Create the ::-moz-number-spin-down pseudo-element:
     443           0 :   rv = MakeAnonymousElement(getter_AddRefs(mSpinDown),
     444             :                             spinBoxCI.mChildren,
     445             :                             nsGkAtoms::div,
     446           0 :                             CSSPseudoElementType::mozNumberSpinDown);
     447             : 
     448           0 :   SyncDisabledState();
     449             : 
     450           0 :   return rv;
     451             : }
     452             : 
     453             : void
     454           0 : nsNumberControlFrame::SetFocus(bool aOn, bool aRepaint)
     455             : {
     456           0 :   GetTextFieldFrame()->SetFocus(aOn, aRepaint);
     457           0 : }
     458             : 
     459             : nsresult
     460           0 : nsNumberControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue)
     461             : {
     462           0 :   return GetTextFieldFrame()->SetFormProperty(aName, aValue);
     463             : }
     464             : 
     465             : HTMLInputElement*
     466           0 : nsNumberControlFrame::GetAnonTextControl()
     467             : {
     468           0 :   return mTextField ? HTMLInputElement::FromContent(mTextField) : nullptr;
     469             : }
     470             : 
     471             : /* static */ nsNumberControlFrame*
     472           0 : nsNumberControlFrame::GetNumberControlFrameForTextField(nsIFrame* aFrame)
     473             : {
     474             :   // If aFrame is the anon text field for an <input type=number> then we expect
     475             :   // the frame of its mContent's grandparent to be that input's frame. We
     476             :   // have to check for this via the content tree because we don't know whether
     477             :   // extra frames will be wrapped around any of the elements between aFrame and
     478             :   // the nsNumberControlFrame that we're looking for (e.g. flex wrappers).
     479           0 :   nsIContent* content = aFrame->GetContent();
     480           0 :   if (content->IsInNativeAnonymousSubtree() &&
     481           0 :       content->GetParent() && content->GetParent()->GetParent()) {
     482           0 :     nsIContent* grandparent = content->GetParent()->GetParent();
     483           0 :     if (grandparent->IsHTMLElement(nsGkAtoms::input) &&
     484           0 :         grandparent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
     485             :                                  nsGkAtoms::number, eCaseMatters)) {
     486           0 :       return do_QueryFrame(grandparent->GetPrimaryFrame());
     487             :     }
     488             :   }
     489           0 :   return nullptr;
     490             : }
     491             : 
     492             : /* static */ nsNumberControlFrame*
     493          18 : nsNumberControlFrame::GetNumberControlFrameForSpinButton(nsIFrame* aFrame)
     494             : {
     495             :   // If aFrame is a spin button for an <input type=number> then we expect the
     496             :   // frame of its mContent's great-grandparent to be that input's frame. We
     497             :   // have to check for this via the content tree because we don't know whether
     498             :   // extra frames will be wrapped around any of the elements between aFrame and
     499             :   // the nsNumberControlFrame that we're looking for (e.g. flex wrappers).
     500          18 :   nsIContent* content = aFrame->GetContent();
     501          36 :   if (content->IsInNativeAnonymousSubtree() &&
     502          18 :       content->GetParent() && content->GetParent()->GetParent() &&
     503           0 :       content->GetParent()->GetParent()->GetParent()) {
     504           0 :     nsIContent* greatgrandparent = content->GetParent()->GetParent()->GetParent();
     505           0 :     if (greatgrandparent->IsHTMLElement(nsGkAtoms::input) &&
     506           0 :         greatgrandparent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
     507             :                                       nsGkAtoms::number, eCaseMatters)) {
     508           0 :       return do_QueryFrame(greatgrandparent->GetPrimaryFrame());
     509             :     }
     510             :   }
     511          18 :   return nullptr;
     512             : }
     513             : 
     514             : int32_t
     515           0 : nsNumberControlFrame::GetSpinButtonForPointerEvent(WidgetGUIEvent* aEvent) const
     516             : {
     517           0 :   MOZ_ASSERT(aEvent->mClass == eMouseEventClass, "Unexpected event type");
     518             : 
     519           0 :   if (!mSpinBox) {
     520             :     // we don't have a spinner
     521           0 :     return eSpinButtonNone;
     522             :   }
     523           0 :   if (aEvent->mOriginalTarget == mSpinUp) {
     524           0 :     return eSpinButtonUp;
     525             :   }
     526           0 :   if (aEvent->mOriginalTarget == mSpinDown) {
     527           0 :     return eSpinButtonDown;
     528             :   }
     529           0 :   if (aEvent->mOriginalTarget == mSpinBox) {
     530             :     // In the case that the up/down buttons are hidden (display:none) we use
     531             :     // just the spin box element, spinning up if the pointer is over the top
     532             :     // half of the element, or down if it's over the bottom half. This is
     533             :     // important to handle since this is the state things are in for the
     534             :     // default UA style sheet. See the comment in forms.css for why.
     535           0 :     LayoutDeviceIntPoint absPoint = aEvent->mRefPoint;
     536             :     nsPoint point =
     537             :       nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
     538           0 :                        absPoint, mSpinBox->GetPrimaryFrame());
     539           0 :     if (point != nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) {
     540           0 :       if (point.y < mSpinBox->GetPrimaryFrame()->GetSize().height / 2) {
     541           0 :         return eSpinButtonUp;
     542             :       }
     543           0 :       return eSpinButtonDown;
     544             :     }
     545             :   }
     546           0 :   return eSpinButtonNone;
     547             : }
     548             : 
     549             : void
     550           0 : nsNumberControlFrame::SpinnerStateChanged() const
     551             : {
     552           0 :   MOZ_ASSERT(mSpinUp && mSpinDown,
     553             :              "We should not be called when we have no spinner");
     554             : 
     555           0 :   nsIFrame* spinUpFrame = mSpinUp->GetPrimaryFrame();
     556           0 :   if (spinUpFrame && spinUpFrame->IsThemed()) {
     557           0 :     spinUpFrame->InvalidateFrame();
     558             :   }
     559           0 :   nsIFrame* spinDownFrame = mSpinDown->GetPrimaryFrame();
     560           0 :   if (spinDownFrame && spinDownFrame->IsThemed()) {
     561           0 :     spinDownFrame->InvalidateFrame();
     562             :   }
     563           0 : }
     564             : 
     565             : bool
     566           0 : nsNumberControlFrame::SpinnerUpButtonIsDepressed() const
     567             : {
     568           0 :   return HTMLInputElement::FromContent(mContent)->
     569           0 :            NumberSpinnerUpButtonIsDepressed();
     570             : }
     571             : 
     572             : bool
     573           0 : nsNumberControlFrame::SpinnerDownButtonIsDepressed() const
     574             : {
     575           0 :   return HTMLInputElement::FromContent(mContent)->
     576           0 :            NumberSpinnerDownButtonIsDepressed();
     577             : }
     578             : 
     579             : bool
     580           0 : nsNumberControlFrame::IsFocused() const
     581             : {
     582             :   // Normally this depends on the state of our anonymous text control (which
     583             :   // takes focus for us), but in the case that it does not have a frame we will
     584             :   // have focus ourself.
     585           0 :   return mTextField->AsElement()->State().HasState(NS_EVENT_STATE_FOCUS) ||
     586           0 :          mContent->AsElement()->State().HasState(NS_EVENT_STATE_FOCUS);
     587             : }
     588             : 
     589             : void
     590           0 : nsNumberControlFrame::HandleFocusEvent(WidgetEvent* aEvent)
     591             : {
     592           0 :   if (aEvent->mOriginalTarget != mTextField) {
     593             :     // Move focus to our text field
     594           0 :     HTMLInputElement::FromContent(mTextField)->Focus();
     595             :   }
     596           0 : }
     597             : 
     598             : nsresult
     599           0 : nsNumberControlFrame::HandleSelectCall()
     600             : {
     601           0 :   return HTMLInputElement::FromContent(mTextField)->Select();
     602             : }
     603             : 
     604             : #define STYLES_DISABLING_NATIVE_THEMING \
     605             :   NS_AUTHOR_SPECIFIED_BACKGROUND | \
     606             :   NS_AUTHOR_SPECIFIED_PADDING | \
     607             :   NS_AUTHOR_SPECIFIED_BORDER
     608             : 
     609             : bool
     610           0 : nsNumberControlFrame::ShouldUseNativeStyleForSpinner() const
     611             : {
     612           0 :   MOZ_ASSERT(mSpinUp && mSpinDown,
     613             :              "We should not be called when we have no spinner");
     614             : 
     615           0 :   nsIFrame* spinUpFrame = mSpinUp->GetPrimaryFrame();
     616           0 :   nsIFrame* spinDownFrame = mSpinDown->GetPrimaryFrame();
     617             : 
     618           0 :   return spinUpFrame &&
     619           0 :     spinUpFrame->StyleDisplay()->mAppearance == NS_THEME_SPINNER_UPBUTTON &&
     620           0 :     !PresContext()->HasAuthorSpecifiedRules(spinUpFrame,
     621           0 :                                             STYLES_DISABLING_NATIVE_THEMING) &&
     622           0 :     spinDownFrame &&
     623           0 :     spinDownFrame->StyleDisplay()->mAppearance == NS_THEME_SPINNER_DOWNBUTTON &&
     624           0 :     !PresContext()->HasAuthorSpecifiedRules(spinDownFrame,
     625           0 :                                             STYLES_DISABLING_NATIVE_THEMING);
     626             : }
     627             : 
     628             : void
     629           0 : nsNumberControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
     630             :                                                uint32_t aFilter)
     631             : {
     632             :   // Only one direct anonymous child:
     633           0 :   if (mOuterWrapper) {
     634           0 :     aElements.AppendElement(mOuterWrapper);
     635             :   }
     636           0 : }
     637             : 
     638             : void
     639           0 : nsNumberControlFrame::SetValueOfAnonTextControl(const nsAString& aValue)
     640             : {
     641           0 :   if (mHandlingInputEvent) {
     642             :     // We have been called while our HTMLInputElement is processing a DOM
     643             :     // 'input' event targeted at our anonymous text control. Our
     644             :     // HTMLInputElement has taken the value of our anon text control and
     645             :     // called SetValueInternal on itself to keep its own value in sync. As a
     646             :     // result SetValueInternal has called us. In this one case we do not want
     647             :     // to update our anon text control, especially since aValue will be the
     648             :     // sanitized value, and only the internal value should be sanitized (not
     649             :     // the value shown to the user, and certainly we shouldn't change it as
     650             :     // they type).
     651           0 :     return;
     652             :   }
     653             : 
     654             :   // Init to aValue so that we set aValue as the value of our text control if
     655             :   // aValue isn't a valid number (in which case the HTMLInputElement's validity
     656             :   // state will be set to invalid) or if aValue can't be localized:
     657           0 :   nsAutoString localizedValue(aValue);
     658             : 
     659             : #ifdef ENABLE_INTL_API
     660             :   // Try and localize the value we will set:
     661           0 :   Decimal val = HTMLInputElement::StringToDecimal(aValue);
     662           0 :   if (val.isFinite()) {
     663           0 :     ICUUtils::LanguageTagIterForContent langTagIter(mContent);
     664           0 :     ICUUtils::LocalizeNumber(val.toDouble(), langTagIter, localizedValue);
     665             :   }
     666             : #endif
     667             : 
     668             :   // We need to update the value of our anonymous text control here. Note that
     669             :   // this must be its value, and not its 'value' attribute (the default value),
     670             :   // since the default value is ignored once a user types into the text
     671             :   // control.
     672             :   //
     673             :   // Pass NonSystem as the caller type; this should work fine for actual number
     674             :   // inputs, and be safe in case our input has a type we don't expect for some
     675             :   // reason.
     676           0 :   IgnoredErrorResult rv;
     677           0 :   HTMLInputElement::FromContent(mTextField)->SetValue(localizedValue,
     678             :                                                       CallerType::NonSystem,
     679           0 :                                                       rv);
     680             : }
     681             : 
     682             : void
     683           0 : nsNumberControlFrame::GetValueOfAnonTextControl(nsAString& aValue)
     684             : {
     685           0 :   if (!mTextField) {
     686           0 :     aValue.Truncate();
     687           0 :     return;
     688             :   }
     689             : 
     690           0 :   HTMLInputElement::FromContent(mTextField)->GetValue(aValue, CallerType::System);
     691             : 
     692             : #ifdef ENABLE_INTL_API
     693             :   // Here we need to de-localize any number typed in by the user. That is, we
     694             :   // need to convert it from the number format of the user's language, region,
     695             :   // etc. to the format that the HTML 5 spec defines to be a "valid
     696             :   // floating-point number":
     697             :   //
     698             :   //   http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#floating-point-numbers
     699             :   //
     700             :   // This is necessary to allow the number that we return to be parsed by
     701             :   // functions like HTMLInputElement::StringToDecimal (the HTML-5-conforming
     702             :   // parsing function) which don't know how to handle numbers that are
     703             :   // formatted differently (for example, with non-ASCII digits, with grouping
     704             :   // separator characters or with a decimal separator character other than
     705             :   // '.').
     706             : 
     707           0 :   ICUUtils::LanguageTagIterForContent langTagIter(mContent);
     708           0 :   double value = ICUUtils::ParseNumber(aValue, langTagIter);
     709           0 :   if (!IsFinite(value)) {
     710           0 :     aValue.Truncate();
     711           0 :     return;
     712             :   }
     713           0 :   if (value == HTMLInputElement::StringToDecimal(aValue).toDouble()) {
     714             :     // We want to preserve the formatting of the number as typed in by the user
     715             :     // whenever possible. Since the localized serialization parses to the same
     716             :     // number as the de-localized serialization, we can do that. This helps
     717             :     // prevent normalization of input such as "2e2" (which would otherwise be
     718             :     // converted to "200"). Content relies on this.
     719             :     //
     720             :     // Typically we will only get here for locales in which numbers are
     721             :     // formatted in the same way as they are for HTML5's "valid floating-point
     722             :     // number" format.
     723           0 :     return;
     724             :   }
     725             :   // We can't preserve the formatting, otherwise functions such as
     726             :   // HTMLInputElement::StringToDecimal would incorrectly process the number
     727             :   // input by the user. For example, "12.345" with lang=de de-localizes as
     728             :   // 12345, but HTMLInputElement::StringToDecimal would mistakenly parse it as
     729             :   // 12.345. Another example would be "12,345" with lang=de which de-localizes
     730             :   // as 12.345, but HTMLInputElement::StringToDecimal would parse it to NaN.
     731           0 :   aValue.Truncate();
     732           0 :   aValue.AppendFloat(value);
     733             : #endif
     734             : }
     735             : 
     736             : bool
     737           0 : nsNumberControlFrame::AnonTextControlIsEmpty()
     738             : {
     739           0 :   if (!mTextField) {
     740           0 :     return true;
     741             :   }
     742           0 :   nsAutoString value;
     743           0 :   HTMLInputElement::FromContent(mTextField)->GetValue(value, CallerType::System);
     744           0 :   return value.IsEmpty();
     745             : }
     746             : 
     747             : Element*
     748           0 : nsNumberControlFrame::GetPseudoElement(CSSPseudoElementType aType)
     749             : {
     750           0 :   if (aType == CSSPseudoElementType::mozNumberWrapper) {
     751           0 :     return mOuterWrapper;
     752             :   }
     753             : 
     754           0 :   if (aType == CSSPseudoElementType::mozNumberText) {
     755           0 :     return mTextField;
     756             :   }
     757             : 
     758           0 :   if (aType == CSSPseudoElementType::mozNumberSpinBox) {
     759             :     // Might be null.
     760           0 :     return mSpinBox;
     761             :   }
     762             : 
     763           0 :   if (aType == CSSPseudoElementType::mozNumberSpinUp) {
     764             :     // Might be null.
     765           0 :     return mSpinUp;
     766             :   }
     767             : 
     768           0 :   if (aType == CSSPseudoElementType::mozNumberSpinDown) {
     769             :     // Might be null.
     770           0 :     return mSpinDown;
     771             :   }
     772             : 
     773           0 :   return nsContainerFrame::GetPseudoElement(aType);
     774             : }
     775             : 
     776             : #ifdef ACCESSIBILITY
     777             : a11y::AccType
     778           0 : nsNumberControlFrame::AccessibleType()
     779             : {
     780           0 :   return a11y::eHTMLSpinnerType;
     781             : }
     782             : #endif

Generated by: LCOV version 1.13