layout/forms/nsNumberControlFrame.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.

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

mercurial