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 "nsFormControlFrame.h"
7 
8 #include "nsGkAtoms.h"
9 #include "nsLayoutUtils.h"
10 #include "nsIDOMHTMLInputElement.h"
11 #include "mozilla/EventStateManager.h"
12 #include "mozilla/LookAndFeel.h"
13 #include "nsDeviceContext.h"
14 #include "nsIContent.h"
15 
16 using namespace mozilla;
17 
18 //#define FCF_NOISY
19 
nsFormControlFrame(nsStyleContext * aContext)20 nsFormControlFrame::nsFormControlFrame(nsStyleContext* aContext)
21   : nsAtomicContainerFrame(aContext)
22 {
23 }
24 
~nsFormControlFrame()25 nsFormControlFrame::~nsFormControlFrame()
26 {
27 }
28 
29 nsIAtom*
GetType() const30 nsFormControlFrame::GetType() const
31 {
32   return nsGkAtoms::formControlFrame;
33 }
34 
35 void
DestroyFrom(nsIFrame * aDestructRoot)36 nsFormControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
37 {
38   // Unregister the access key registered in reflow
39   nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
40   nsAtomicContainerFrame::DestroyFrom(aDestructRoot);
41 }
42 
43 NS_QUERYFRAME_HEAD(nsFormControlFrame)
NS_QUERYFRAME_ENTRY(nsIFormControlFrame)44   NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
45 NS_QUERYFRAME_TAIL_INHERITING(nsAtomicContainerFrame)
46 
47 /* virtual */ nscoord
48 nsFormControlFrame::GetMinISize(nsRenderingContext *aRenderingContext)
49 {
50   nscoord result;
51   DISPLAY_MIN_WIDTH(this, result);
52   result = GetIntrinsicISize();
53   return result;
54 }
55 
56 /* virtual */ nscoord
GetPrefISize(nsRenderingContext * aRenderingContext)57 nsFormControlFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
58 {
59   nscoord result;
60   DISPLAY_PREF_WIDTH(this, result);
61   result = GetIntrinsicISize();
62   return result;
63 }
64 
65 /* virtual */
66 LogicalSize
ComputeAutoSize(nsRenderingContext * aRenderingContext,WritingMode aWM,const LogicalSize & aCBSize,nscoord aAvailableISize,const LogicalSize & aMargin,const LogicalSize & aBorder,const LogicalSize & aPadding,ComputeSizeFlags aFlags)67 nsFormControlFrame::ComputeAutoSize(nsRenderingContext* aRenderingContext,
68                                     WritingMode         aWM,
69                                     const LogicalSize&  aCBSize,
70                                     nscoord             aAvailableISize,
71                                     const LogicalSize&  aMargin,
72                                     const LogicalSize&  aBorder,
73                                     const LogicalSize&  aPadding,
74                                     ComputeSizeFlags    aFlags)
75 {
76   const WritingMode wm = GetWritingMode();
77   LogicalSize result(wm, GetIntrinsicISize(), GetIntrinsicBSize());
78   return result.ConvertTo(aWM, wm);
79 }
80 
81 nscoord
GetIntrinsicISize()82 nsFormControlFrame::GetIntrinsicISize()
83 {
84   // Provide a reasonable default for sites that use an "auto" height.
85   // Note that if you change this, you should change the values in forms.css
86   // as well.  This is the 13px default width minus the 2px default border.
87   return nsPresContext::CSSPixelsToAppUnits(13 - 2 * 2);
88 }
89 
90 nscoord
GetIntrinsicBSize()91 nsFormControlFrame::GetIntrinsicBSize()
92 {
93   // Provide a reasonable default for sites that use an "auto" height.
94   // Note that if you change this, you should change the values in forms.css
95   // as well. This is the 13px default width minus the 2px default border.
96   return nsPresContext::CSSPixelsToAppUnits(13 - 2 * 2);
97 }
98 
99 nscoord
GetLogicalBaseline(WritingMode aWritingMode) const100 nsFormControlFrame::GetLogicalBaseline(WritingMode aWritingMode) const
101 {
102   NS_ASSERTION(!NS_SUBTREE_DIRTY(this),
103                "frame must not be dirty");
104   // Treat radio buttons and checkboxes as having an intrinsic baseline
105   // at the block-end of the control (use the block-end content edge rather
106   // than the margin edge).
107   // For "inverted" lines (typically in writing-mode:vertical-lr), use the
108   // block-start end instead.
109   return aWritingMode.IsLineInverted()
110     ? GetLogicalUsedBorderAndPadding(aWritingMode).BStart(aWritingMode)
111     : BSize(aWritingMode) -
112          GetLogicalUsedBorderAndPadding(aWritingMode).BEnd(aWritingMode);
113 }
114 
115 void
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)116 nsFormControlFrame::Reflow(nsPresContext*          aPresContext,
117                            ReflowOutput&     aDesiredSize,
118                            const ReflowInput& aReflowInput,
119                            nsReflowStatus&          aStatus)
120 {
121   MarkInReflow();
122   DO_GLOBAL_REFLOW_COUNT("nsFormControlFrame");
123   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
124   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
125                  ("enter nsFormControlFrame::Reflow: aMaxSize=%d,%d",
126                   aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
127 
128   if (mState & NS_FRAME_FIRST_REFLOW) {
129     RegUnRegAccessKey(static_cast<nsIFrame*>(this), true);
130   }
131 
132   aStatus = NS_FRAME_COMPLETE;
133   aDesiredSize.SetSize(aReflowInput.GetWritingMode(),
134                        aReflowInput.ComputedSizeWithBorderPadding());
135 
136   if (nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) {
137     float inflation = nsLayoutUtils::FontSizeInflationFor(this);
138     aDesiredSize.Width() *= inflation;
139     aDesiredSize.Height() *= inflation;
140   }
141 
142   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
143                  ("exit nsFormControlFrame::Reflow: size=%d,%d",
144                   aDesiredSize.Width(), aDesiredSize.Height()));
145   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
146 
147   aDesiredSize.SetOverflowAreasToDesiredBounds();
148   FinishAndStoreOverflow(&aDesiredSize);
149 }
150 
151 nsresult
RegUnRegAccessKey(nsIFrame * aFrame,bool aDoReg)152 nsFormControlFrame::RegUnRegAccessKey(nsIFrame * aFrame, bool aDoReg)
153 {
154   NS_ENSURE_ARG_POINTER(aFrame);
155 
156   nsPresContext* presContext = aFrame->PresContext();
157 
158   NS_ASSERTION(presContext, "aPresContext is NULL in RegUnRegAccessKey!");
159 
160   nsAutoString accessKey;
161 
162   nsIContent* content = aFrame->GetContent();
163   content->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey);
164   if (!accessKey.IsEmpty()) {
165     EventStateManager* stateManager = presContext->EventStateManager();
166     if (aDoReg) {
167       stateManager->RegisterAccessKey(content, (uint32_t)accessKey.First());
168     } else {
169       stateManager->UnregisterAccessKey(content, (uint32_t)accessKey.First());
170     }
171     return NS_OK;
172   }
173   return NS_ERROR_FAILURE;
174 }
175 
176 void
SetFocus(bool aOn,bool aRepaint)177 nsFormControlFrame::SetFocus(bool aOn, bool aRepaint)
178 {
179 }
180 
181 nsresult
HandleEvent(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)182 nsFormControlFrame::HandleEvent(nsPresContext* aPresContext,
183                                 WidgetGUIEvent* aEvent,
184                                 nsEventStatus* aEventStatus)
185 {
186   // Check for user-input:none style
187   const nsStyleUserInterface* uiStyle = StyleUserInterface();
188   if (uiStyle->mUserInput == StyleUserInput::None ||
189       uiStyle->mUserInput == StyleUserInput::Disabled) {
190     return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
191   }
192   return NS_OK;
193 }
194 
195 void
GetCurrentCheckState(bool * aState)196 nsFormControlFrame::GetCurrentCheckState(bool *aState)
197 {
198   nsCOMPtr<nsIDOMHTMLInputElement> inputElement = do_QueryInterface(mContent);
199   if (inputElement) {
200     inputElement->GetChecked(aState);
201   }
202 }
203 
204 nsresult
SetFormProperty(nsIAtom * aName,const nsAString & aValue)205 nsFormControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue)
206 {
207   return NS_OK;
208 }
209 
210 // static
211 nsRect
GetUsableScreenRect(nsPresContext * aPresContext)212 nsFormControlFrame::GetUsableScreenRect(nsPresContext* aPresContext)
213 {
214   nsRect screen;
215 
216   nsDeviceContext *context = aPresContext->DeviceContext();
217   int32_t dropdownCanOverlapOSBar =
218     LookAndFeel::GetInt(LookAndFeel::eIntID_MenusCanOverlapOSBar, 0);
219   if ( dropdownCanOverlapOSBar )
220     context->GetRect(screen);
221   else
222     context->GetClientRect(screen);
223 
224   return screen;
225 }
226