1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/forms/nsNumberControlFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,812 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsNumberControlFrame.h" 1.10 + 1.11 +#include "HTMLInputElement.h" 1.12 +#include "ICUUtils.h" 1.13 +#include "nsIFocusManager.h" 1.14 +#include "nsIPresShell.h" 1.15 +#include "nsFocusManager.h" 1.16 +#include "nsFontMetrics.h" 1.17 +#include "nsFormControlFrame.h" 1.18 +#include "nsGkAtoms.h" 1.19 +#include "nsINodeInfo.h" 1.20 +#include "nsNameSpaceManager.h" 1.21 +#include "nsThemeConstants.h" 1.22 +#include "mozilla/BasicEvents.h" 1.23 +#include "mozilla/EventStates.h" 1.24 +#include "nsContentUtils.h" 1.25 +#include "nsContentCreatorFunctions.h" 1.26 +#include "nsContentList.h" 1.27 +#include "nsStyleSet.h" 1.28 +#include "nsIDOMMutationEvent.h" 1.29 + 1.30 +#ifdef ACCESSIBILITY 1.31 +#include "mozilla/a11y/AccTypes.h" 1.32 +#endif 1.33 + 1.34 +using namespace mozilla; 1.35 +using namespace mozilla::dom; 1.36 + 1.37 +nsIFrame* 1.38 +NS_NewNumberControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.39 +{ 1.40 + return new (aPresShell) nsNumberControlFrame(aContext); 1.41 +} 1.42 + 1.43 +NS_IMPL_FRAMEARENA_HELPERS(nsNumberControlFrame) 1.44 + 1.45 +NS_QUERYFRAME_HEAD(nsNumberControlFrame) 1.46 + NS_QUERYFRAME_ENTRY(nsNumberControlFrame) 1.47 + NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) 1.48 + NS_QUERYFRAME_ENTRY(nsITextControlFrame) 1.49 + NS_QUERYFRAME_ENTRY(nsIFormControlFrame) 1.50 +NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) 1.51 + 1.52 +nsNumberControlFrame::nsNumberControlFrame(nsStyleContext* aContext) 1.53 + : nsContainerFrame(aContext) 1.54 + , mHandlingInputEvent(false) 1.55 +{ 1.56 +} 1.57 + 1.58 +void 1.59 +nsNumberControlFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.60 +{ 1.61 + NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(), 1.62 + "nsNumberControlFrame should not have continuations; if it does we " 1.63 + "need to call RegUnregAccessKey only for the first"); 1.64 + nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false); 1.65 + nsContentUtils::DestroyAnonymousContent(&mOuterWrapper); 1.66 + nsContainerFrame::DestroyFrom(aDestructRoot); 1.67 +} 1.68 + 1.69 +nscoord 1.70 +nsNumberControlFrame::GetMinWidth(nsRenderingContext* aRenderingContext) 1.71 +{ 1.72 + nscoord result; 1.73 + DISPLAY_MIN_WIDTH(this, result); 1.74 + 1.75 + nsIFrame* kid = mFrames.FirstChild(); 1.76 + if (kid) { // display:none? 1.77 + result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, 1.78 + kid, 1.79 + nsLayoutUtils::MIN_WIDTH); 1.80 + } else { 1.81 + result = 0; 1.82 + } 1.83 + 1.84 + return result; 1.85 +} 1.86 + 1.87 +nscoord 1.88 +nsNumberControlFrame::GetPrefWidth(nsRenderingContext* aRenderingContext) 1.89 +{ 1.90 + nscoord result; 1.91 + DISPLAY_PREF_WIDTH(this, result); 1.92 + 1.93 + nsIFrame* kid = mFrames.FirstChild(); 1.94 + if (kid) { // display:none? 1.95 + result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, 1.96 + kid, 1.97 + nsLayoutUtils::PREF_WIDTH); 1.98 + } else { 1.99 + result = 0; 1.100 + } 1.101 + 1.102 + return result; 1.103 +} 1.104 + 1.105 +nsresult 1.106 +nsNumberControlFrame::Reflow(nsPresContext* aPresContext, 1.107 + nsHTMLReflowMetrics& aDesiredSize, 1.108 + const nsHTMLReflowState& aReflowState, 1.109 + nsReflowStatus& aStatus) 1.110 +{ 1.111 + DO_GLOBAL_REFLOW_COUNT("nsNumberControlFrame"); 1.112 + DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); 1.113 + 1.114 + NS_ASSERTION(mOuterWrapper, "Outer wrapper div must exist!"); 1.115 + 1.116 + NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(), 1.117 + "nsNumberControlFrame should not have continuations; if it does we " 1.118 + "need to call RegUnregAccessKey only for the first"); 1.119 + 1.120 + NS_ASSERTION(!mFrames.FirstChild() || 1.121 + !mFrames.FirstChild()->GetNextSibling(), 1.122 + "We expect at most one direct child frame"); 1.123 + 1.124 + if (mState & NS_FRAME_FIRST_REFLOW) { 1.125 + nsFormControlFrame::RegUnRegAccessKey(this, true); 1.126 + } 1.127 + 1.128 + // The width of our content box, which is the available width 1.129 + // for our anonymous content: 1.130 + const nscoord contentBoxWidth = aReflowState.ComputedWidth(); 1.131 + nscoord contentBoxHeight = aReflowState.ComputedHeight(); 1.132 + 1.133 + nsIFrame* outerWrapperFrame = mOuterWrapper->GetPrimaryFrame(); 1.134 + 1.135 + if (!outerWrapperFrame) { // display:none? 1.136 + if (contentBoxHeight == NS_INTRINSICSIZE) { 1.137 + contentBoxHeight = 0; 1.138 + } 1.139 + } else { 1.140 + NS_ASSERTION(outerWrapperFrame == mFrames.FirstChild(), "huh?"); 1.141 + 1.142 + nsHTMLReflowMetrics wrappersDesiredSize(aReflowState); 1.143 + 1.144 + nsHTMLReflowState wrapperReflowState(aPresContext, aReflowState, 1.145 + outerWrapperFrame, 1.146 + nsSize(contentBoxWidth, 1.147 + NS_UNCONSTRAINEDSIZE)); 1.148 + 1.149 + // offsets of wrapper frame 1.150 + nscoord xoffset = aReflowState.ComputedPhysicalBorderPadding().left + 1.151 + wrapperReflowState.ComputedPhysicalMargin().left; 1.152 + nscoord yoffset = aReflowState.ComputedPhysicalBorderPadding().top + 1.153 + wrapperReflowState.ComputedPhysicalMargin().top; 1.154 + 1.155 + nsReflowStatus childStatus; 1.156 + nsresult rv = ReflowChild(outerWrapperFrame, aPresContext, 1.157 + wrappersDesiredSize, wrapperReflowState, 1.158 + xoffset, yoffset, 0, childStatus); 1.159 + NS_ENSURE_SUCCESS(rv, rv); 1.160 + MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus), 1.161 + "We gave our child unconstrained height, so it should be complete"); 1.162 + 1.163 + nscoord wrappersMarginBoxHeight = wrappersDesiredSize.Height() + 1.164 + wrapperReflowState.ComputedPhysicalMargin().TopBottom(); 1.165 + 1.166 + if (contentBoxHeight == NS_INTRINSICSIZE) { 1.167 + // We are intrinsically sized -- we should shrinkwrap the outer wrapper's 1.168 + // height: 1.169 + contentBoxHeight = wrappersMarginBoxHeight; 1.170 + 1.171 + // Make sure we obey min/max-height in the case when we're doing intrinsic 1.172 + // sizing (we get it for free when we have a non-intrinsic 1.173 + // aReflowState.ComputedHeight()). Note that we do this before 1.174 + // adjusting for borderpadding, since mComputedMaxHeight and 1.175 + // mComputedMinHeight are content heights. 1.176 + contentBoxHeight = 1.177 + NS_CSS_MINMAX(contentBoxHeight, 1.178 + aReflowState.ComputedMinHeight(), 1.179 + aReflowState.ComputedMaxHeight()); 1.180 + } 1.181 + 1.182 + // Center child vertically 1.183 + nscoord extraSpace = contentBoxHeight - wrappersMarginBoxHeight; 1.184 + yoffset += std::max(0, extraSpace / 2); 1.185 + 1.186 + // Place the child 1.187 + rv = FinishReflowChild(outerWrapperFrame, aPresContext, 1.188 + wrappersDesiredSize, &wrapperReflowState, 1.189 + xoffset, yoffset, 0); 1.190 + NS_ENSURE_SUCCESS(rv, rv); 1.191 + 1.192 + aDesiredSize.SetTopAscent(wrappersDesiredSize.TopAscent() + 1.193 + outerWrapperFrame->GetPosition().y); 1.194 + } 1.195 + 1.196 + aDesiredSize.Width() = contentBoxWidth + 1.197 + aReflowState.ComputedPhysicalBorderPadding().LeftRight(); 1.198 + aDesiredSize.Height() = contentBoxHeight + 1.199 + aReflowState.ComputedPhysicalBorderPadding().TopBottom(); 1.200 + 1.201 + aDesiredSize.SetOverflowAreasToDesiredBounds(); 1.202 + 1.203 + if (outerWrapperFrame) { 1.204 + ConsiderChildOverflow(aDesiredSize.mOverflowAreas, outerWrapperFrame); 1.205 + } 1.206 + 1.207 + FinishAndStoreOverflow(&aDesiredSize); 1.208 + 1.209 + aStatus = NS_FRAME_COMPLETE; 1.210 + 1.211 + NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); 1.212 + 1.213 + return NS_OK; 1.214 +} 1.215 + 1.216 +void 1.217 +nsNumberControlFrame::SyncDisabledState() 1.218 +{ 1.219 + EventStates eventStates = mContent->AsElement()->State(); 1.220 + if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) { 1.221 + mTextField->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(), 1.222 + true); 1.223 + } else { 1.224 + mTextField->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true); 1.225 + } 1.226 +} 1.227 + 1.228 +nsresult 1.229 +nsNumberControlFrame::AttributeChanged(int32_t aNameSpaceID, 1.230 + nsIAtom* aAttribute, 1.231 + int32_t aModType) 1.232 +{ 1.233 + // nsGkAtoms::disabled is handled by SyncDisabledState 1.234 + if (aNameSpaceID == kNameSpaceID_None) { 1.235 + if (aAttribute == nsGkAtoms::placeholder || 1.236 + aAttribute == nsGkAtoms::readonly || 1.237 + aAttribute == nsGkAtoms::tabindex) { 1.238 + if (aModType == nsIDOMMutationEvent::REMOVAL) { 1.239 + mTextField->UnsetAttr(aNameSpaceID, aAttribute, true); 1.240 + } else { 1.241 + MOZ_ASSERT(aModType == nsIDOMMutationEvent::ADDITION || 1.242 + aModType == nsIDOMMutationEvent::MODIFICATION); 1.243 + nsAutoString value; 1.244 + mContent->GetAttr(aNameSpaceID, aAttribute, value); 1.245 + mTextField->SetAttr(aNameSpaceID, aAttribute, value, true); 1.246 + } 1.247 + } 1.248 + } 1.249 + 1.250 + return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, 1.251 + aModType); 1.252 +} 1.253 + 1.254 +void 1.255 +nsNumberControlFrame::ContentStatesChanged(EventStates aStates) 1.256 +{ 1.257 + if (aStates.HasState(NS_EVENT_STATE_DISABLED)) { 1.258 + nsContentUtils::AddScriptRunner(new SyncDisabledStateEvent(this)); 1.259 + } 1.260 +} 1.261 + 1.262 +nsITextControlFrame* 1.263 +nsNumberControlFrame::GetTextFieldFrame() 1.264 +{ 1.265 + return do_QueryFrame(GetAnonTextControl()->GetPrimaryFrame()); 1.266 +} 1.267 + 1.268 +nsresult 1.269 +nsNumberControlFrame::MakeAnonymousElement(Element** aResult, 1.270 + nsTArray<ContentInfo>& aElements, 1.271 + nsIAtom* aTagName, 1.272 + nsCSSPseudoElements::Type aPseudoType, 1.273 + nsStyleContext* aParentContext) 1.274 +{ 1.275 + // Get the NodeInfoManager and tag necessary to create the anonymous divs. 1.276 + nsCOMPtr<nsIDocument> doc = mContent->GetDocument(); 1.277 + nsRefPtr<Element> resultElement = doc->CreateHTMLElement(aTagName); 1.278 + 1.279 + // If we legitimately fail this assertion and need to allow 1.280 + // non-pseudo-element anonymous children, then we'll need to add a branch 1.281 + // that calls ResolveStyleFor((*aResult)->AsElement(), aParentContext)") to 1.282 + // set newStyleContext. 1.283 + NS_ASSERTION(aPseudoType != nsCSSPseudoElements::ePseudo_NotPseudoElement, 1.284 + "Expecting anonymous children to all be pseudo-elements"); 1.285 + // Associate the pseudo-element with the anonymous child 1.286 + nsRefPtr<nsStyleContext> newStyleContext = 1.287 + PresContext()->StyleSet()->ResolvePseudoElementStyle(mContent->AsElement(), 1.288 + aPseudoType, 1.289 + aParentContext, 1.290 + resultElement); 1.291 + 1.292 + if (!aElements.AppendElement(ContentInfo(resultElement, newStyleContext))) { 1.293 + return NS_ERROR_OUT_OF_MEMORY; 1.294 + } 1.295 + 1.296 + if (aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberSpinDown || 1.297 + aPseudoType == nsCSSPseudoElements::ePseudo_mozNumberSpinUp) { 1.298 + resultElement->SetAttr(kNameSpaceID_None, nsGkAtoms::role, 1.299 + NS_LITERAL_STRING("button"), false); 1.300 + } 1.301 + 1.302 + resultElement.forget(aResult); 1.303 + return NS_OK; 1.304 +} 1.305 + 1.306 +nsresult 1.307 +nsNumberControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) 1.308 +{ 1.309 + nsresult rv; 1.310 + 1.311 + // We create an anonymous tree for our input element that is structured as 1.312 + // follows: 1.313 + // 1.314 + // input 1.315 + // div - outer wrapper with "display:flex" by default 1.316 + // input - text input field 1.317 + // div - spin box wrapping up/down arrow buttons 1.318 + // div - spin up (up arrow button) 1.319 + // div - spin down (down arrow button) 1.320 + // 1.321 + // If you change this, be careful to change the destruction order in 1.322 + // nsNumberControlFrame::DestroyFrom. 1.323 + 1.324 + 1.325 + // Create the anonymous outer wrapper: 1.326 + rv = MakeAnonymousElement(getter_AddRefs(mOuterWrapper), 1.327 + aElements, 1.328 + nsGkAtoms::div, 1.329 + nsCSSPseudoElements::ePseudo_mozNumberWrapper, 1.330 + mStyleContext); 1.331 + NS_ENSURE_SUCCESS(rv, rv); 1.332 + 1.333 + ContentInfo& outerWrapperCI = aElements.LastElement(); 1.334 + 1.335 + // Create the ::-moz-number-text pseudo-element: 1.336 + rv = MakeAnonymousElement(getter_AddRefs(mTextField), 1.337 + outerWrapperCI.mChildren, 1.338 + nsGkAtoms::input, 1.339 + nsCSSPseudoElements::ePseudo_mozNumberText, 1.340 + outerWrapperCI.mStyleContext); 1.341 + NS_ENSURE_SUCCESS(rv, rv); 1.342 + 1.343 + mTextField->SetAttr(kNameSpaceID_None, nsGkAtoms::type, 1.344 + NS_LITERAL_STRING("text"), PR_FALSE); 1.345 + 1.346 + HTMLInputElement* content = HTMLInputElement::FromContent(mContent); 1.347 + HTMLInputElement* textField = HTMLInputElement::FromContent(mTextField); 1.348 + 1.349 + // Initialize the text field value: 1.350 + nsAutoString value; 1.351 + content->GetValue(value); 1.352 + SetValueOfAnonTextControl(value); 1.353 + 1.354 + // If we're readonly, make sure our anonymous text control is too: 1.355 + nsAutoString readonly; 1.356 + if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::readonly, readonly)) { 1.357 + mTextField->SetAttr(kNameSpaceID_None, nsGkAtoms::readonly, readonly, false); 1.358 + } 1.359 + 1.360 + // Propogate our tabindex: 1.361 + int32_t tabIndex; 1.362 + content->GetTabIndex(&tabIndex); 1.363 + textField->SetTabIndex(tabIndex); 1.364 + 1.365 + // Initialize the text field's placeholder, if ours is set: 1.366 + nsAutoString placeholder; 1.367 + if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, placeholder)) { 1.368 + mTextField->SetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, placeholder, false); 1.369 + } 1.370 + 1.371 + if (mContent->AsElement()->State().HasState(NS_EVENT_STATE_FOCUS)) { 1.372 + // We don't want to focus the frame but the text field. 1.373 + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); 1.374 + nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mTextField); 1.375 + NS_ASSERTION(element, "Really, this should be a nsIDOMElement!"); 1.376 + fm->SetFocus(element, 0); 1.377 + } 1.378 + 1.379 + if (StyleDisplay()->mAppearance == NS_THEME_TEXTFIELD) { 1.380 + // The author has elected to hide the spinner by setting this 1.381 + // -moz-appearance. We will reframe if it changes. 1.382 + return rv; 1.383 + } 1.384 + 1.385 + // Create the ::-moz-number-spin-box pseudo-element: 1.386 + rv = MakeAnonymousElement(getter_AddRefs(mSpinBox), 1.387 + outerWrapperCI.mChildren, 1.388 + nsGkAtoms::div, 1.389 + nsCSSPseudoElements::ePseudo_mozNumberSpinBox, 1.390 + outerWrapperCI.mStyleContext); 1.391 + NS_ENSURE_SUCCESS(rv, rv); 1.392 + 1.393 + ContentInfo& spinBoxCI = outerWrapperCI.mChildren.LastElement(); 1.394 + 1.395 + // Create the ::-moz-number-spin-up pseudo-element: 1.396 + rv = MakeAnonymousElement(getter_AddRefs(mSpinUp), 1.397 + spinBoxCI.mChildren, 1.398 + nsGkAtoms::div, 1.399 + nsCSSPseudoElements::ePseudo_mozNumberSpinUp, 1.400 + spinBoxCI.mStyleContext); 1.401 + NS_ENSURE_SUCCESS(rv, rv); 1.402 + 1.403 + // Create the ::-moz-number-spin-down pseudo-element: 1.404 + rv = MakeAnonymousElement(getter_AddRefs(mSpinDown), 1.405 + spinBoxCI.mChildren, 1.406 + nsGkAtoms::div, 1.407 + nsCSSPseudoElements::ePseudo_mozNumberSpinDown, 1.408 + spinBoxCI.mStyleContext); 1.409 + 1.410 + SyncDisabledState(); 1.411 + 1.412 + return rv; 1.413 +} 1.414 + 1.415 +nsIAtom* 1.416 +nsNumberControlFrame::GetType() const 1.417 +{ 1.418 + return nsGkAtoms::numberControlFrame; 1.419 +} 1.420 + 1.421 +NS_IMETHODIMP 1.422 +nsNumberControlFrame::GetEditor(nsIEditor **aEditor) 1.423 +{ 1.424 + return GetTextFieldFrame()->GetEditor(aEditor); 1.425 +} 1.426 + 1.427 +NS_IMETHODIMP 1.428 +nsNumberControlFrame::SetSelectionStart(int32_t aSelectionStart) 1.429 +{ 1.430 + return GetTextFieldFrame()->SetSelectionStart(aSelectionStart); 1.431 +} 1.432 + 1.433 +NS_IMETHODIMP 1.434 +nsNumberControlFrame::SetSelectionEnd(int32_t aSelectionEnd) 1.435 +{ 1.436 + return GetTextFieldFrame()->SetSelectionEnd(aSelectionEnd); 1.437 +} 1.438 + 1.439 +NS_IMETHODIMP 1.440 +nsNumberControlFrame::SetSelectionRange(int32_t aSelectionStart, 1.441 + int32_t aSelectionEnd, 1.442 + SelectionDirection aDirection) 1.443 +{ 1.444 + return GetTextFieldFrame()->SetSelectionRange(aSelectionStart, aSelectionEnd, 1.445 + aDirection); 1.446 +} 1.447 + 1.448 +NS_IMETHODIMP 1.449 +nsNumberControlFrame::GetSelectionRange(int32_t* aSelectionStart, 1.450 + int32_t* aSelectionEnd, 1.451 + SelectionDirection* aDirection) 1.452 +{ 1.453 + return GetTextFieldFrame()->GetSelectionRange(aSelectionStart, aSelectionEnd, 1.454 + aDirection); 1.455 +} 1.456 + 1.457 +NS_IMETHODIMP 1.458 +nsNumberControlFrame::GetOwnedSelectionController(nsISelectionController** aSelCon) 1.459 +{ 1.460 + return GetTextFieldFrame()->GetOwnedSelectionController(aSelCon); 1.461 +} 1.462 + 1.463 +nsFrameSelection* 1.464 +nsNumberControlFrame::GetOwnedFrameSelection() 1.465 +{ 1.466 + return GetTextFieldFrame()->GetOwnedFrameSelection(); 1.467 +} 1.468 + 1.469 +nsresult 1.470 +nsNumberControlFrame::GetPhonetic(nsAString& aPhonetic) 1.471 +{ 1.472 + return GetTextFieldFrame()->GetPhonetic(aPhonetic); 1.473 +} 1.474 + 1.475 +nsresult 1.476 +nsNumberControlFrame::EnsureEditorInitialized() 1.477 +{ 1.478 + return GetTextFieldFrame()->EnsureEditorInitialized(); 1.479 +} 1.480 + 1.481 +nsresult 1.482 +nsNumberControlFrame::ScrollSelectionIntoView() 1.483 +{ 1.484 + return GetTextFieldFrame()->ScrollSelectionIntoView(); 1.485 +} 1.486 + 1.487 +void 1.488 +nsNumberControlFrame::SetFocus(bool aOn, bool aRepaint) 1.489 +{ 1.490 + GetTextFieldFrame()->SetFocus(aOn, aRepaint); 1.491 +} 1.492 + 1.493 +nsresult 1.494 +nsNumberControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue) 1.495 +{ 1.496 + return GetTextFieldFrame()->SetFormProperty(aName, aValue); 1.497 +} 1.498 + 1.499 +HTMLInputElement* 1.500 +nsNumberControlFrame::GetAnonTextControl() 1.501 +{ 1.502 + return mTextField ? HTMLInputElement::FromContent(mTextField) : nullptr; 1.503 +} 1.504 + 1.505 +/* static */ nsNumberControlFrame* 1.506 +nsNumberControlFrame::GetNumberControlFrameForTextField(nsIFrame* aFrame) 1.507 +{ 1.508 + // If aFrame is the anon text field for an <input type=number> then we expect 1.509 + // the frame of its mContent's grandparent to be that input's frame. We 1.510 + // have to check for this via the content tree because we don't know whether 1.511 + // extra frames will be wrapped around any of the elements between aFrame and 1.512 + // the nsNumberControlFrame that we're looking for (e.g. flex wrappers). 1.513 + nsIContent* content = aFrame->GetContent(); 1.514 + if (content->IsInNativeAnonymousSubtree() && 1.515 + content->GetParent() && content->GetParent()->GetParent()) { 1.516 + nsIContent* grandparent = content->GetParent()->GetParent(); 1.517 + if (grandparent->IsHTML(nsGkAtoms::input) && 1.518 + grandparent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, 1.519 + nsGkAtoms::number, eCaseMatters)) { 1.520 + return do_QueryFrame(grandparent->GetPrimaryFrame()); 1.521 + } 1.522 + } 1.523 + return nullptr; 1.524 +} 1.525 + 1.526 +/* static */ nsNumberControlFrame* 1.527 +nsNumberControlFrame::GetNumberControlFrameForSpinButton(nsIFrame* aFrame) 1.528 +{ 1.529 + // If aFrame is a spin button for an <input type=number> then we expect the 1.530 + // frame of its mContent's great-grandparent to be that input's frame. We 1.531 + // have to check for this via the content tree because we don't know whether 1.532 + // extra frames will be wrapped around any of the elements between aFrame and 1.533 + // the nsNumberControlFrame that we're looking for (e.g. flex wrappers). 1.534 + nsIContent* content = aFrame->GetContent(); 1.535 + if (content->IsInNativeAnonymousSubtree() && 1.536 + content->GetParent() && content->GetParent()->GetParent() && 1.537 + content->GetParent()->GetParent()->GetParent()) { 1.538 + nsIContent* greatgrandparent = content->GetParent()->GetParent()->GetParent(); 1.539 + if (greatgrandparent->IsHTML(nsGkAtoms::input) && 1.540 + greatgrandparent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, 1.541 + nsGkAtoms::number, eCaseMatters)) { 1.542 + return do_QueryFrame(greatgrandparent->GetPrimaryFrame()); 1.543 + } 1.544 + } 1.545 + return nullptr; 1.546 +} 1.547 + 1.548 +int32_t 1.549 +nsNumberControlFrame::GetSpinButtonForPointerEvent(WidgetGUIEvent* aEvent) const 1.550 +{ 1.551 + MOZ_ASSERT(aEvent->eventStructType == NS_MOUSE_EVENT, 1.552 + "Unexpected event type"); 1.553 + 1.554 + if (!mSpinBox) { 1.555 + // we don't have a spinner 1.556 + return eSpinButtonNone; 1.557 + } 1.558 + if (aEvent->originalTarget == mSpinUp) { 1.559 + return eSpinButtonUp; 1.560 + } 1.561 + if (aEvent->originalTarget == mSpinDown) { 1.562 + return eSpinButtonDown; 1.563 + } 1.564 + if (aEvent->originalTarget == mSpinBox) { 1.565 + // In the case that the up/down buttons are hidden (display:none) we use 1.566 + // just the spin box element, spinning up if the pointer is over the top 1.567 + // half of the element, or down if it's over the bottom half. This is 1.568 + // important to handle since this is the state things are in for the 1.569 + // default UA style sheet. See the comment in forms.css for why. 1.570 + LayoutDeviceIntPoint absPoint = aEvent->refPoint; 1.571 + nsPoint point = 1.572 + nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, 1.573 + LayoutDeviceIntPoint::ToUntyped(absPoint), 1.574 + mSpinBox->GetPrimaryFrame()); 1.575 + if (point != nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) { 1.576 + if (point.y < mSpinBox->GetPrimaryFrame()->GetSize().height / 2) { 1.577 + return eSpinButtonUp; 1.578 + } 1.579 + return eSpinButtonDown; 1.580 + } 1.581 + } 1.582 + return eSpinButtonNone; 1.583 +} 1.584 + 1.585 +void 1.586 +nsNumberControlFrame::SpinnerStateChanged() const 1.587 +{ 1.588 + MOZ_ASSERT(mSpinUp && mSpinDown, 1.589 + "We should not be called when we have no spinner"); 1.590 + 1.591 + nsIFrame* spinUpFrame = mSpinUp->GetPrimaryFrame(); 1.592 + if (spinUpFrame && spinUpFrame->IsThemed()) { 1.593 + spinUpFrame->InvalidateFrame(); 1.594 + } 1.595 + nsIFrame* spinDownFrame = mSpinDown->GetPrimaryFrame(); 1.596 + if (spinDownFrame && spinDownFrame->IsThemed()) { 1.597 + spinDownFrame->InvalidateFrame(); 1.598 + } 1.599 +} 1.600 + 1.601 +bool 1.602 +nsNumberControlFrame::SpinnerUpButtonIsDepressed() const 1.603 +{ 1.604 + return HTMLInputElement::FromContent(mContent)-> 1.605 + NumberSpinnerUpButtonIsDepressed(); 1.606 +} 1.607 + 1.608 +bool 1.609 +nsNumberControlFrame::SpinnerDownButtonIsDepressed() const 1.610 +{ 1.611 + return HTMLInputElement::FromContent(mContent)-> 1.612 + NumberSpinnerDownButtonIsDepressed(); 1.613 +} 1.614 + 1.615 +bool 1.616 +nsNumberControlFrame::IsFocused() const 1.617 +{ 1.618 + // Normally this depends on the state of our anonymous text control (which 1.619 + // takes focus for us), but in the case that it does not have a frame we will 1.620 + // have focus ourself. 1.621 + return mTextField->AsElement()->State().HasState(NS_EVENT_STATE_FOCUS) || 1.622 + mContent->AsElement()->State().HasState(NS_EVENT_STATE_FOCUS); 1.623 +} 1.624 + 1.625 +void 1.626 +nsNumberControlFrame::HandleFocusEvent(WidgetEvent* aEvent) 1.627 +{ 1.628 + if (aEvent->originalTarget != mTextField) { 1.629 + // Move focus to our text field 1.630 + HTMLInputElement::FromContent(mTextField)->Focus(); 1.631 + } 1.632 +} 1.633 + 1.634 +nsresult 1.635 +nsNumberControlFrame::HandleSelectCall() 1.636 +{ 1.637 + return HTMLInputElement::FromContent(mTextField)->Select(); 1.638 +} 1.639 + 1.640 +#define STYLES_DISABLING_NATIVE_THEMING \ 1.641 + NS_AUTHOR_SPECIFIED_BACKGROUND | \ 1.642 + NS_AUTHOR_SPECIFIED_PADDING | \ 1.643 + NS_AUTHOR_SPECIFIED_BORDER 1.644 + 1.645 +bool 1.646 +nsNumberControlFrame::ShouldUseNativeStyleForSpinner() const 1.647 +{ 1.648 + MOZ_ASSERT(mSpinUp && mSpinDown, 1.649 + "We should not be called when we have no spinner"); 1.650 + 1.651 + nsIFrame* spinUpFrame = mSpinUp->GetPrimaryFrame(); 1.652 + nsIFrame* spinDownFrame = mSpinDown->GetPrimaryFrame(); 1.653 + 1.654 + return spinUpFrame && 1.655 + spinUpFrame->StyleDisplay()->mAppearance == NS_THEME_SPINNER_UP_BUTTON && 1.656 + !PresContext()->HasAuthorSpecifiedRules(spinUpFrame, 1.657 + STYLES_DISABLING_NATIVE_THEMING) && 1.658 + spinDownFrame && 1.659 + spinDownFrame->StyleDisplay()->mAppearance == NS_THEME_SPINNER_DOWN_BUTTON && 1.660 + !PresContext()->HasAuthorSpecifiedRules(spinDownFrame, 1.661 + STYLES_DISABLING_NATIVE_THEMING); 1.662 +} 1.663 + 1.664 +void 1.665 +nsNumberControlFrame::AppendAnonymousContentTo(nsBaseContentList& aElements, 1.666 + uint32_t aFilter) 1.667 +{ 1.668 + // Only one direct anonymous child: 1.669 + aElements.MaybeAppendElement(mOuterWrapper); 1.670 +} 1.671 + 1.672 +void 1.673 +nsNumberControlFrame::SetValueOfAnonTextControl(const nsAString& aValue) 1.674 +{ 1.675 + if (mHandlingInputEvent) { 1.676 + // We have been called while our HTMLInputElement is processing a DOM 1.677 + // 'input' event targeted at our anonymous text control. Our 1.678 + // HTMLInputElement has taken the value of our anon text control and 1.679 + // called SetValueInternal on itself to keep its own value in sync. As a 1.680 + // result SetValueInternal has called us. In this one case we do not want 1.681 + // to update our anon text control, especially since aValue will be the 1.682 + // sanitized value, and only the internal value should be sanitized (not 1.683 + // the value shown to the user, and certainly we shouldn't change it as 1.684 + // they type). 1.685 + return; 1.686 + } 1.687 + 1.688 + // Init to aValue so that we set aValue as the value of our text control if 1.689 + // aValue isn't a valid number (in which case the HTMLInputElement's validity 1.690 + // state will be set to invalid) or if aValue can't be localized: 1.691 + nsAutoString localizedValue(aValue); 1.692 + 1.693 +#ifdef ENABLE_INTL_API 1.694 + // Try and localize the value we will set: 1.695 + Decimal val = HTMLInputElement::StringToDecimal(aValue); 1.696 + if (val.isFinite()) { 1.697 + ICUUtils::LanguageTagIterForContent langTagIter(mContent); 1.698 + ICUUtils::LocalizeNumber(val.toDouble(), langTagIter, localizedValue); 1.699 + } 1.700 +#endif 1.701 + 1.702 + // We need to update the value of our anonymous text control here. Note that 1.703 + // this must be its value, and not its 'value' attribute (the default value), 1.704 + // since the default value is ignored once a user types into the text 1.705 + // control. 1.706 + HTMLInputElement::FromContent(mTextField)->SetValue(localizedValue); 1.707 +} 1.708 + 1.709 +void 1.710 +nsNumberControlFrame::GetValueOfAnonTextControl(nsAString& aValue) 1.711 +{ 1.712 + if (!mTextField) { 1.713 + aValue.Truncate(); 1.714 + return; 1.715 + } 1.716 + 1.717 + HTMLInputElement::FromContent(mTextField)->GetValue(aValue); 1.718 + 1.719 +#ifdef ENABLE_INTL_API 1.720 + // Here we need to de-localize any number typed in by the user. That is, we 1.721 + // need to convert it from the number format of the user's language, region, 1.722 + // etc. to the format that the HTML 5 spec defines to be a "valid 1.723 + // floating-point number": 1.724 + // 1.725 + // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#floating-point-numbers 1.726 + // 1.727 + // so that it can be parsed by functions like HTMLInputElement:: 1.728 + // StringToDecimal (the HTML-5-conforming parsing function) which don't know 1.729 + // how to handle numbers that are formatted differently (for example, with 1.730 + // non-ASCII digits, with grouping separator characters or with a decimal 1.731 + // separator character other than '.'). 1.732 + // 1.733 + // We need to be careful to avoid normalizing numbers that are already 1.734 + // formatted for a locale that matches the format of HTML 5's "valid 1.735 + // floating-point number" and have no grouping separator characters. (In 1.736 + // other words we want to return the number as specified by the user, not the 1.737 + // de-localized serialization, since the latter will normalize the value.) 1.738 + // For example, if the user's locale is English and the user types in "2e2" 1.739 + // then inputElement.value should be "2e2" and not "100". This is because 1.740 + // content (and tests) expect us to avoid "normalizing" the number that the 1.741 + // user types in if it's not necessary in order to make sure it conforms to 1.742 + // HTML 5's "valid floating-point number" format. 1.743 + // 1.744 + // Note that we also need to be careful when trying to avoid normalization. 1.745 + // For example, just because "1.234" _looks_ like a valid floating-point 1.746 + // number according to the spec does not mean that it should be returned 1.747 + // as-is. If the user's locale is German, then this represents the value 1.748 + // 1234, not 1.234, so it still needs to be de-localized. Alternatively, if 1.749 + // the user's locale is English and they type in "1,234" we _do_ need to 1.750 + // normalize the number to "1234" because HTML 5's valid floating-point 1.751 + // number format does not allow the ',' grouping separator. We can detect all 1.752 + // the cases where we need to convert by seeing if the locale-specific 1.753 + // parsing function understands the user input to mean the same thing as the 1.754 + // HTML-5-conforming parsing function. If so, then we should return the value 1.755 + // as-is to avoid normalization. Otherwise, we return the de-localized 1.756 + // serialization. 1.757 + ICUUtils::LanguageTagIterForContent langTagIter(mContent); 1.758 + double value = ICUUtils::ParseNumber(aValue, langTagIter); 1.759 + if (NS_finite(value) && 1.760 + value != HTMLInputElement::StringToDecimal(aValue).toDouble()) { 1.761 + aValue.Truncate(); 1.762 + aValue.AppendFloat(value); 1.763 + } 1.764 +#endif 1.765 + // else, we return whatever FromContent put into aValue (the number as typed 1.766 + // in by the user) 1.767 +} 1.768 + 1.769 +bool 1.770 +nsNumberControlFrame::AnonTextControlIsEmpty() 1.771 +{ 1.772 + if (!mTextField) { 1.773 + return true; 1.774 + } 1.775 + nsAutoString value; 1.776 + HTMLInputElement::FromContent(mTextField)->GetValue(value); 1.777 + return value.IsEmpty(); 1.778 +} 1.779 + 1.780 +Element* 1.781 +nsNumberControlFrame::GetPseudoElement(nsCSSPseudoElements::Type aType) 1.782 +{ 1.783 + if (aType == nsCSSPseudoElements::ePseudo_mozNumberWrapper) { 1.784 + return mOuterWrapper; 1.785 + } 1.786 + 1.787 + if (aType == nsCSSPseudoElements::ePseudo_mozNumberText) { 1.788 + return mTextField; 1.789 + } 1.790 + 1.791 + if (aType == nsCSSPseudoElements::ePseudo_mozNumberSpinBox) { 1.792 + MOZ_ASSERT(mSpinBox); 1.793 + return mSpinBox; 1.794 + } 1.795 + 1.796 + if (aType == nsCSSPseudoElements::ePseudo_mozNumberSpinUp) { 1.797 + MOZ_ASSERT(mSpinUp); 1.798 + return mSpinUp; 1.799 + } 1.800 + 1.801 + if (aType == nsCSSPseudoElements::ePseudo_mozNumberSpinDown) { 1.802 + MOZ_ASSERT(mSpinDown); 1.803 + return mSpinDown; 1.804 + } 1.805 + 1.806 + return nsContainerFrame::GetPseudoElement(aType); 1.807 +} 1.808 + 1.809 +#ifdef ACCESSIBILITY 1.810 +a11y::AccType 1.811 +nsNumberControlFrame::AccessibleType() 1.812 +{ 1.813 + return a11y::eHTMLSpinnerType; 1.814 +} 1.815 +#endif