1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Blake Ross <blakeross@telocity.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsCOMPtr.h"
40 : #include "nsTextControlFrame.h"
41 : #include "nsIDocument.h"
42 : #include "nsIFormControl.h"
43 : #include "nsIServiceManager.h"
44 : #include "nsFrameSelection.h"
45 : #include "nsIPlaintextEditor.h"
46 : #include "nsEditorCID.h"
47 : #include "nsLayoutCID.h"
48 : #include "nsIDocumentEncoder.h"
49 : #include "nsCaret.h"
50 : #include "nsISelectionListener.h"
51 : #include "nsISelectionPrivate.h"
52 : #include "nsIController.h"
53 : #include "nsIControllers.h"
54 : #include "nsIControllerContext.h"
55 : #include "nsGenericHTMLElement.h"
56 : #include "nsIEditorIMESupport.h"
57 : #include "nsIPhonetic.h"
58 : #include "nsIEditorObserver.h"
59 : #include "nsEditProperty.h"
60 : #include "nsIDOMHTMLTextAreaElement.h"
61 : #include "nsINameSpaceManager.h"
62 : #include "nsINodeInfo.h"
63 : #include "nsFormControlFrame.h" //for registering accesskeys
64 :
65 : #include "nsIContent.h"
66 : #include "nsIAtom.h"
67 : #include "nsPresContext.h"
68 : #include "nsRenderingContext.h"
69 : #include "nsGkAtoms.h"
70 : #include "nsLayoutUtils.h"
71 : #include "nsIComponentManager.h"
72 : #include "nsIView.h"
73 : #include "nsIViewManager.h"
74 : #include "nsIDOMHTMLInputElement.h"
75 : #include "nsIDOMElement.h"
76 : #include "nsIDOMDocument.h"
77 : #include "nsIDOMHTMLElement.h"
78 : #include "nsIPresShell.h"
79 :
80 : #include "nsBoxLayoutState.h"
81 : //for keylistener for "return" check
82 : #include "nsIPrivateDOMEvent.h"
83 : #include "nsIDOMEventTarget.h"
84 : #include "nsIDocument.h" //observe documents to send onchangenotifications
85 : #include "nsIStyleSheet.h"//observe documents to send onchangenotifications
86 : #include "nsIStyleRule.h"//observe documents to send onchangenotifications
87 : #include "nsIDOMEventListener.h"//observe documents to send onchangenotifications
88 : #include "nsGUIEvent.h"
89 : #include "nsIDOMNSEvent.h"
90 :
91 : #include "nsIDOMCharacterData.h" //for selection setting helper func
92 : #include "nsIDOMNodeList.h" //for selection setting helper func
93 : #include "nsIDOMRange.h" //for selection setting helper func
94 : #include "nsPIDOMWindow.h" //needed for notify selection changed to update the menus ect.
95 : #ifdef ACCESSIBILITY
96 : #include "nsAccessibilityService.h"
97 : #endif
98 : #include "nsIDOMNode.h"
99 :
100 : #include "nsITransactionManager.h"
101 : #include "nsIDOMText.h" //for multiline getselection
102 : #include "nsNodeInfoManager.h"
103 : #include "nsContentCreatorFunctions.h"
104 : #include "nsINativeKeyBindings.h"
105 : #include "nsIJSContextStack.h"
106 : #include "nsFocusManager.h"
107 : #include "nsTextEditRules.h"
108 : #include "nsPresState.h"
109 :
110 : #include "mozilla/FunctionTimer.h"
111 :
112 : #define DEFAULT_COLUMN_WIDTH 20
113 :
114 : nsIFrame*
115 0 : NS_NewTextControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
116 : {
117 0 : return new (aPresShell) nsTextControlFrame(aPresShell, aContext);
118 : }
119 :
120 0 : NS_IMPL_FRAMEARENA_HELPERS(nsTextControlFrame)
121 :
122 0 : NS_QUERYFRAME_HEAD(nsTextControlFrame)
123 0 : NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
124 0 : NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
125 0 : NS_QUERYFRAME_ENTRY(nsITextControlFrame)
126 0 : NS_QUERYFRAME_ENTRY(nsIStatefulFrame)
127 0 : NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
128 :
129 : #ifdef ACCESSIBILITY
130 : already_AddRefed<nsAccessible>
131 0 : nsTextControlFrame::CreateAccessible()
132 : {
133 0 : nsAccessibilityService* accService = nsIPresShell::AccService();
134 0 : if (accService) {
135 : return accService->CreateHTMLTextFieldAccessible(mContent,
136 0 : PresContext()->PresShell());
137 : }
138 :
139 0 : return nsnull;
140 : }
141 : #endif
142 :
143 : #ifdef DEBUG
144 : class EditorInitializerEntryTracker {
145 : public:
146 0 : explicit EditorInitializerEntryTracker(nsTextControlFrame &frame)
147 : : mFrame(frame)
148 0 : , mFirstEntry(false)
149 : {
150 0 : if (!mFrame.mInEditorInitialization) {
151 0 : mFrame.mInEditorInitialization = true;
152 0 : mFirstEntry = true;
153 : }
154 0 : }
155 0 : ~EditorInitializerEntryTracker()
156 : {
157 0 : if (mFirstEntry) {
158 0 : mFrame.mInEditorInitialization = false;
159 : }
160 0 : }
161 0 : bool EnteredMoreThanOnce() const { return !mFirstEntry; }
162 : private:
163 : nsTextControlFrame &mFrame;
164 : bool mFirstEntry;
165 : };
166 : #endif
167 :
168 0 : nsTextControlFrame::nsTextControlFrame(nsIPresShell* aShell, nsStyleContext* aContext)
169 : : nsStackFrame(aShell, aContext)
170 : , mUseEditor(false)
171 : , mIsProcessing(false)
172 : , mNotifyOnInput(true)
173 : , mFireChangeEventState(false)
174 : #ifdef DEBUG
175 0 : , mInEditorInitialization(false)
176 : #endif
177 : {
178 0 : }
179 :
180 0 : nsTextControlFrame::~nsTextControlFrame()
181 : {
182 0 : }
183 :
184 : void
185 0 : nsTextControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
186 : {
187 0 : mScrollEvent.Revoke();
188 :
189 0 : EditorInitializer* initializer = (EditorInitializer*) Properties().Get(TextControlInitializer());
190 0 : if (initializer) {
191 0 : initializer->Revoke();
192 0 : Properties().Delete(TextControlInitializer());
193 : }
194 :
195 : // Unbind the text editor state object from the frame. The editor will live
196 : // on, but things like controllers will be released.
197 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
198 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
199 0 : txtCtrl->UnbindFromFrame(this);
200 :
201 0 : nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
202 :
203 0 : nsBoxFrame::DestroyFrom(aDestructRoot);
204 0 : }
205 :
206 : nsIAtom*
207 0 : nsTextControlFrame::GetType() const
208 : {
209 0 : return nsGkAtoms::textInputFrame;
210 : }
211 :
212 : nsresult
213 0 : nsTextControlFrame::CalcIntrinsicSize(nsRenderingContext* aRenderingContext,
214 : nsSize& aIntrinsicSize,
215 : float aFontSizeInflation)
216 : {
217 : // Get leading and the Average/MaxAdvance char width
218 0 : nscoord lineHeight = 0;
219 0 : nscoord charWidth = 0;
220 0 : nscoord charMaxAdvance = 0;
221 :
222 0 : nsRefPtr<nsFontMetrics> fontMet;
223 : nsresult rv =
224 : nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet),
225 0 : aFontSizeInflation);
226 0 : NS_ENSURE_SUCCESS(rv, rv);
227 0 : aRenderingContext->SetFont(fontMet);
228 :
229 : lineHeight =
230 : nsHTMLReflowState::CalcLineHeight(GetStyleContext(), NS_AUTOHEIGHT,
231 0 : aFontSizeInflation);
232 0 : charWidth = fontMet->AveCharWidth();
233 0 : charMaxAdvance = fontMet->MaxAdvance();
234 :
235 : // Set the width equal to the width in characters
236 0 : PRInt32 cols = GetCols();
237 0 : aIntrinsicSize.width = cols * charWidth;
238 :
239 : // To better match IE, take the maximum character width(in twips) and remove
240 : // 4 pixels add this on as additional padding(internalPadding). But only do
241 : // this if charMaxAdvance != charWidth; if they are equal, this is almost
242 : // certainly a fixed-width font.
243 0 : if (charWidth != charMaxAdvance) {
244 : nscoord internalPadding = NS_MAX(0, charMaxAdvance -
245 0 : nsPresContext::CSSPixelsToAppUnits(4));
246 0 : nscoord t = nsPresContext::CSSPixelsToAppUnits(1);
247 : // Round to a multiple of t
248 0 : nscoord rest = internalPadding % t;
249 0 : if (rest < t - rest) {
250 0 : internalPadding -= rest;
251 : } else {
252 0 : internalPadding += t - rest;
253 : }
254 : // Now add the extra padding on (so that small input sizes work well)
255 0 : aIntrinsicSize.width += internalPadding;
256 : } else {
257 : // This is to account for the anonymous <br> having a 1 twip width
258 : // in Full Standards mode, see BRFrame::Reflow and bug 228752.
259 0 : if (PresContext()->CompatibilityMode() == eCompatibility_FullStandards) {
260 0 : aIntrinsicSize.width += 1;
261 : }
262 :
263 : // Also add in the padding of our value div child. Note that it hasn't
264 : // been reflowed yet, so we can't get its used padding, but it shouldn't be
265 : // using percentage padding anyway.
266 0 : nsMargin childPadding;
267 0 : nsIFrame* firstChild = GetFirstPrincipalChild();
268 0 : if (firstChild && firstChild->GetStylePadding()->GetPadding(childPadding)) {
269 0 : aIntrinsicSize.width += childPadding.LeftRight();
270 : } else {
271 0 : NS_ERROR("Percentage padding on value div?");
272 : }
273 : }
274 :
275 : // Increment width with cols * letter-spacing.
276 : {
277 0 : const nsStyleCoord& lsCoord = GetStyleText()->mLetterSpacing;
278 0 : if (eStyleUnit_Coord == lsCoord.GetUnit()) {
279 0 : nscoord letterSpacing = lsCoord.GetCoordValue();
280 0 : if (letterSpacing != 0) {
281 0 : aIntrinsicSize.width += cols * letterSpacing;
282 : }
283 : }
284 : }
285 :
286 : // Set the height equal to total number of rows (times the height of each
287 : // line, of course)
288 0 : aIntrinsicSize.height = lineHeight * GetRows();
289 :
290 : // Add in the size of the scrollbars for textarea
291 0 : if (IsTextArea()) {
292 0 : nsIFrame* first = GetFirstPrincipalChild();
293 :
294 0 : nsIScrollableFrame *scrollableFrame = do_QueryFrame(first);
295 0 : NS_ASSERTION(scrollableFrame, "Child must be scrollable");
296 :
297 0 : if (scrollableFrame) {
298 : nsMargin scrollbarSizes =
299 0 : scrollableFrame->GetDesiredScrollbarSizes(PresContext(), aRenderingContext);
300 :
301 0 : aIntrinsicSize.width += scrollbarSizes.LeftRight();
302 :
303 0 : aIntrinsicSize.height += scrollbarSizes.TopBottom();;
304 : }
305 : }
306 :
307 0 : return NS_OK;
308 : }
309 :
310 : nsresult
311 0 : nsTextControlFrame::EnsureEditorInitialized()
312 : {
313 : // This method initializes our editor, if needed.
314 :
315 : // This code used to be called from CreateAnonymousContent(), but
316 : // when the editor set the initial string, it would trigger a
317 : // PresShell listener which called FlushPendingNotifications()
318 : // during frame construction. This was causing other form controls
319 : // to display wrong values. Additionally, calling this every time
320 : // a text frame control is instantiated means that we're effectively
321 : // instantiating the editor for all text fields, even if they
322 : // never get used. So, now this method is being called lazily only
323 : // when we actually need an editor.
324 :
325 : // Check if this method has been called already.
326 : // If so, just return early.
327 0 : if (mUseEditor)
328 0 : return NS_OK;
329 :
330 : NS_TIME_FUNCTION;
331 :
332 0 : nsIDocument* doc = mContent->GetCurrentDoc();
333 0 : NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
334 :
335 0 : nsWeakFrame weakFrame(this);
336 :
337 : // Flush out content on our document. Have to do this, because script
338 : // blockers don't prevent the sink flushing out content and notifying in the
339 : // process, which can destroy frames.
340 0 : doc->FlushPendingNotifications(Flush_ContentAndNotify);
341 0 : NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_ERROR_FAILURE);
342 :
343 : // Make sure that editor init doesn't do things that would kill us off
344 : // (especially off the script blockers it'll create for its DOM mutations).
345 0 : nsAutoScriptBlocker scriptBlocker;
346 :
347 : // Time to mess with our security context... See comments in GetValue()
348 : // for why this is needed.
349 0 : nsCxPusher pusher;
350 0 : pusher.PushNull();
351 :
352 : // Make sure that we try to focus the content even if the method fails
353 : class EnsureSetFocus {
354 : public:
355 0 : explicit EnsureSetFocus(nsTextControlFrame* aFrame)
356 0 : : mFrame(aFrame) {}
357 0 : ~EnsureSetFocus() {
358 0 : if (nsContentUtils::IsFocusedContent(mFrame->GetContent()))
359 0 : mFrame->SetFocus(true, false);
360 0 : }
361 : private:
362 : nsTextControlFrame *mFrame;
363 : };
364 0 : EnsureSetFocus makeSureSetFocusHappens(this);
365 :
366 : #ifdef DEBUG
367 : // Make sure we are not being called again until we're finished.
368 : // If reentrancy happens, just pretend that we don't have an editor.
369 0 : const EditorInitializerEntryTracker tracker(*this);
370 0 : NS_ASSERTION(!tracker.EnteredMoreThanOnce(),
371 : "EnsureEditorInitialized has been called while a previous call was in progress");
372 : #endif
373 :
374 : // Create an editor for the frame, if one doesn't already exist
375 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
376 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
377 0 : nsresult rv = txtCtrl->CreateEditor();
378 0 : NS_ENSURE_SUCCESS(rv, rv);
379 :
380 : // Turn on mUseEditor so that subsequent calls will use the
381 : // editor.
382 0 : mUseEditor = true;
383 :
384 : // Set the selection to the beginning of the text field.
385 0 : if (weakFrame.IsAlive()) {
386 0 : SetSelectionEndPoints(0, 0);
387 : }
388 :
389 0 : return NS_OK;
390 : }
391 :
392 : nsresult
393 0 : nsTextControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
394 : {
395 0 : NS_ASSERTION(mContent, "We should have a content!");
396 :
397 0 : mState |= NS_FRAME_INDEPENDENT_SELECTION;
398 :
399 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
400 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
401 :
402 : // Bind the frame to its text control
403 0 : nsresult rv = txtCtrl->BindToFrame(this);
404 0 : NS_ENSURE_SUCCESS(rv, rv);
405 :
406 0 : nsIContent* rootNode = txtCtrl->GetRootEditorNode();
407 0 : NS_ENSURE_TRUE(rootNode, NS_ERROR_OUT_OF_MEMORY);
408 :
409 0 : if (!aElements.AppendElement(rootNode))
410 0 : return NS_ERROR_OUT_OF_MEMORY;
411 :
412 : // Do we need a placeholder node?
413 0 : nsAutoString placeholderTxt;
414 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder,
415 0 : placeholderTxt);
416 0 : nsContentUtils::RemoveNewlines(placeholderTxt);
417 0 : mUsePlaceholder = !placeholderTxt.IsEmpty();
418 :
419 : // Create the placeholder anonymous content if needed.
420 0 : if (mUsePlaceholder) {
421 0 : nsIContent* placeholderNode = txtCtrl->CreatePlaceholderNode();
422 0 : NS_ENSURE_TRUE(placeholderNode, NS_ERROR_OUT_OF_MEMORY);
423 :
424 0 : if (!aElements.AppendElement(placeholderNode))
425 0 : return NS_ERROR_OUT_OF_MEMORY;
426 : }
427 :
428 0 : rv = UpdateValueDisplay(false);
429 0 : NS_ENSURE_SUCCESS(rv, rv);
430 :
431 : // textareas are eagerly initialized
432 0 : bool initEagerly = !IsSingleLineTextControl();
433 0 : if (!initEagerly) {
434 : // Also, input elements which have a cached selection should get eager
435 : // editor initialization.
436 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
437 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
438 0 : initEagerly = txtCtrl->HasCachedSelection();
439 : }
440 0 : if (!initEagerly) {
441 0 : nsCOMPtr<nsIDOMHTMLElement> element = do_QueryInterface(txtCtrl);
442 0 : if (element) {
443 : // so are input text controls with spellcheck=true
444 0 : element->GetSpellcheck(&initEagerly);
445 : }
446 : }
447 :
448 0 : if (initEagerly) {
449 0 : NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
450 : "Someone forgot a script blocker?");
451 0 : EditorInitializer* initializer = (EditorInitializer*) Properties().Get(TextControlInitializer());
452 0 : if (initializer) {
453 0 : initializer->Revoke();
454 : }
455 0 : initializer = new EditorInitializer(this);
456 0 : Properties().Set(TextControlInitializer(),initializer);
457 0 : if (!nsContentUtils::AddScriptRunner(initializer)) {
458 0 : initializer->Revoke(); // paranoia
459 0 : Properties().Delete(TextControlInitializer());
460 0 : delete initializer;
461 0 : return NS_ERROR_OUT_OF_MEMORY;
462 : }
463 : }
464 :
465 0 : return NS_OK;
466 : }
467 :
468 : void
469 0 : nsTextControlFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
470 : PRUint32 aFilter)
471 : {
472 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
473 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
474 :
475 0 : aElements.MaybeAppendElement(txtCtrl->GetRootEditorNode());
476 0 : if (!(aFilter & nsIContent::eSkipPlaceholderContent))
477 0 : aElements.MaybeAppendElement(txtCtrl->GetPlaceholderNode());
478 0 : }
479 :
480 : nscoord
481 0 : nsTextControlFrame::GetMinWidth(nsRenderingContext* aRenderingContext)
482 : {
483 : // Our min width is just our preferred width if we have auto width.
484 : nscoord result;
485 0 : DISPLAY_MIN_WIDTH(this, result);
486 :
487 0 : result = GetPrefWidth(aRenderingContext);
488 :
489 0 : return result;
490 : }
491 :
492 : nsSize
493 0 : nsTextControlFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
494 : nsSize aCBSize, nscoord aAvailableWidth,
495 : nsSize aMargin, nsSize aBorder,
496 : nsSize aPadding, bool aShrinkWrap)
497 : {
498 : float inflation =
499 0 : nsLayoutUtils::FontSizeInflationFor(this, nsLayoutUtils::eInReflow);
500 0 : nsSize autoSize;
501 0 : nsresult rv = CalcIntrinsicSize(aRenderingContext, autoSize, inflation);
502 0 : if (NS_FAILED(rv)) {
503 : // What now?
504 0 : autoSize.SizeTo(0, 0);
505 : }
506 : #ifdef DEBUG
507 : // Note: Ancestor ComputeAutoSize only computes a width if we're auto-width
508 0 : else if (GetStylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) {
509 : nsSize ancestorAutoSize =
510 : nsStackFrame::ComputeAutoSize(aRenderingContext,
511 : aCBSize, aAvailableWidth,
512 : aMargin, aBorder,
513 0 : aPadding, aShrinkWrap);
514 : // Disabled when there's inflation; see comment in GetPrefSize.
515 0 : NS_ASSERTION(inflation != 1.0f || ancestorAutoSize.width == autoSize.width,
516 : "Incorrect size computed by ComputeAutoSize?");
517 : }
518 : #endif
519 :
520 : return autoSize;
521 : }
522 :
523 :
524 : // We inherit our GetPrefWidth from nsBoxFrame
525 :
526 : NS_IMETHODIMP
527 0 : nsTextControlFrame::Reflow(nsPresContext* aPresContext,
528 : nsHTMLReflowMetrics& aDesiredSize,
529 : const nsHTMLReflowState& aReflowState,
530 : nsReflowStatus& aStatus)
531 : {
532 0 : DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame");
533 0 : DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
534 :
535 : // make sure the the form registers itself on the initial/first reflow
536 0 : if (mState & NS_FRAME_FIRST_REFLOW) {
537 0 : nsFormControlFrame::RegUnRegAccessKey(this, true);
538 : }
539 :
540 : return nsStackFrame::Reflow(aPresContext, aDesiredSize, aReflowState,
541 0 : aStatus);
542 : }
543 :
544 : nsSize
545 0 : nsTextControlFrame::GetPrefSize(nsBoxLayoutState& aState)
546 : {
547 0 : if (!DoesNeedRecalc(mPrefSize))
548 0 : return mPrefSize;
549 :
550 : #ifdef DEBUG_LAYOUT
551 : PropagateDebug(aState);
552 : #endif
553 :
554 0 : nsSize pref(0,0);
555 :
556 : // FIXME: This inflation parameter isn't correct; we should fix it if
557 : // we want font size inflation to work well in XUL. If we do, we can
558 : // also re-enable the assertion in ComputeAutoSize when inflation is
559 : // enabled.
560 0 : nsresult rv = CalcIntrinsicSize(aState.GetRenderingContext(), pref, 1.0f);
561 0 : NS_ENSURE_SUCCESS(rv, pref);
562 0 : AddBorderAndPadding(pref);
563 :
564 : bool widthSet, heightSet;
565 0 : nsIBox::AddCSSPrefSize(this, pref, widthSet, heightSet);
566 :
567 0 : nsSize minSize = GetMinSize(aState);
568 0 : nsSize maxSize = GetMaxSize(aState);
569 0 : mPrefSize = BoundsCheck(minSize, pref, maxSize);
570 :
571 : #ifdef DEBUG_rods
572 : {
573 : nsMargin borderPadding(0,0,0,0);
574 : GetBorderAndPadding(borderPadding);
575 : nsSize size(169, 24);
576 : nsSize actual(pref.width/15,
577 : pref.height/15);
578 : printf("nsGfxText(field) %d,%d %d,%d %d,%d\n",
579 : size.width, size.height, actual.width, actual.height, actual.width-size.width, actual.height-size.height); // text field
580 : }
581 : #endif
582 :
583 0 : return mPrefSize;
584 : }
585 :
586 : nsSize
587 0 : nsTextControlFrame::GetMinSize(nsBoxLayoutState& aState)
588 : {
589 : // XXXbz why? Why not the nsBoxFrame sizes?
590 0 : return nsBox::GetMinSize(aState);
591 : }
592 :
593 : nsSize
594 0 : nsTextControlFrame::GetMaxSize(nsBoxLayoutState& aState)
595 : {
596 : // XXXbz why? Why not the nsBoxFrame sizes?
597 0 : return nsBox::GetMaxSize(aState);
598 : }
599 :
600 : nscoord
601 0 : nsTextControlFrame::GetBoxAscent(nsBoxLayoutState& aState)
602 : {
603 : // Return the baseline of the first (nominal) row, with centering for
604 : // single-line controls.
605 :
606 : float inflation =
607 0 : nsLayoutUtils::FontSizeInflationFor(this, nsLayoutUtils::eInReflow);
608 :
609 : // First calculate the ascent wrt the client rect
610 0 : nsRect clientRect;
611 0 : GetClientRect(clientRect);
612 : nscoord lineHeight =
613 0 : IsSingleLineTextControl() ? clientRect.height :
614 : nsHTMLReflowState::CalcLineHeight(GetStyleContext(), NS_AUTOHEIGHT,
615 0 : inflation);
616 :
617 0 : nsRefPtr<nsFontMetrics> fontMet;
618 : nsresult rv =
619 : nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet),
620 0 : inflation);
621 0 : NS_ENSURE_SUCCESS(rv, 0);
622 :
623 0 : nscoord ascent = nsLayoutUtils::GetCenteredFontBaseline(fontMet, lineHeight);
624 :
625 : // Now adjust for our borders and padding
626 0 : ascent += clientRect.y;
627 :
628 0 : return ascent;
629 : }
630 :
631 : bool
632 0 : nsTextControlFrame::IsCollapsed()
633 : {
634 : // We're never collapsed in the box sense.
635 0 : return false;
636 : }
637 :
638 : bool
639 0 : nsTextControlFrame::IsLeaf() const
640 : {
641 0 : return true;
642 : }
643 :
644 : NS_IMETHODIMP
645 0 : nsTextControlFrame::ScrollOnFocusEvent::Run()
646 : {
647 0 : if (mFrame) {
648 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(mFrame->GetContent());
649 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
650 0 : nsISelectionController* selCon = txtCtrl->GetSelectionController();
651 0 : if (selCon) {
652 0 : mFrame->mScrollEvent.Forget();
653 : selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
654 : nsISelectionController::SELECTION_FOCUS_REGION,
655 0 : nsISelectionController::SCROLL_SYNCHRONOUS);
656 : }
657 : }
658 0 : return NS_OK;
659 : }
660 :
661 : //IMPLEMENTING NS_IFORMCONTROLFRAME
662 0 : void nsTextControlFrame::SetFocus(bool aOn, bool aRepaint)
663 : {
664 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
665 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
666 :
667 : // Revoke the previous scroll event if one exists
668 0 : mScrollEvent.Revoke();
669 :
670 0 : if (!aOn) {
671 0 : if (mUsePlaceholder) {
672 : PRInt32 textLength;
673 0 : GetTextLength(&textLength);
674 :
675 0 : if (!textLength) {
676 0 : nsWeakFrame weakFrame(this);
677 :
678 0 : txtCtrl->SetPlaceholderClass(true, true);
679 :
680 0 : if (!weakFrame.IsAlive()) {
681 : return;
682 : }
683 : }
684 : }
685 :
686 : return;
687 : }
688 :
689 0 : nsISelectionController* selCon = txtCtrl->GetSelectionController();
690 0 : if (!selCon)
691 : return;
692 :
693 0 : if (mUsePlaceholder) {
694 0 : nsWeakFrame weakFrame(this);
695 :
696 0 : txtCtrl->SetPlaceholderClass(false, true);
697 :
698 0 : if (!weakFrame.IsAlive()) {
699 : return;
700 : }
701 : }
702 :
703 0 : InitFocusedValue();
704 :
705 0 : nsCOMPtr<nsISelection> ourSel;
706 : selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
707 0 : getter_AddRefs(ourSel));
708 0 : if (!ourSel) return;
709 :
710 0 : nsIPresShell* presShell = PresContext()->GetPresShell();
711 0 : nsRefPtr<nsCaret> caret = presShell->GetCaret();
712 0 : if (!caret) return;
713 :
714 : // Scroll the current selection into view
715 0 : nsISelection *caretSelection = caret->GetCaretDOMSelection();
716 0 : const bool isFocusedRightNow = ourSel == caretSelection;
717 0 : if (!isFocusedRightNow) {
718 : // Don't scroll the current selection if we've been focused using the mouse.
719 0 : PRUint32 lastFocusMethod = 0;
720 0 : nsIDocument* doc = GetContent()->GetCurrentDoc();
721 0 : if (doc) {
722 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
723 0 : if (fm) {
724 0 : fm->GetLastFocusMethod(doc->GetWindow(), &lastFocusMethod);
725 : }
726 : }
727 0 : if (!(lastFocusMethod & nsIFocusManager::FLAG_BYMOUSE)) {
728 0 : nsRefPtr<ScrollOnFocusEvent> event = new ScrollOnFocusEvent(this);
729 0 : nsresult rv = NS_DispatchToCurrentThread(event);
730 0 : if (NS_SUCCEEDED(rv)) {
731 0 : mScrollEvent = event;
732 : }
733 : }
734 : }
735 :
736 : // tell the caret to use our selection
737 0 : caret->SetCaretDOMSelection(ourSel);
738 :
739 : // mutual-exclusion: the selection is either controlled by the
740 : // document or by the text input/area. Clear any selection in the
741 : // document since the focus is now on our independent selection.
742 :
743 0 : nsCOMPtr<nsISelectionController> selcon = do_QueryInterface(presShell);
744 0 : nsCOMPtr<nsISelection> docSel;
745 0 : selcon->GetSelection(nsISelectionController::SELECTION_NORMAL,
746 0 : getter_AddRefs(docSel));
747 0 : if (!docSel) return;
748 :
749 0 : bool isCollapsed = false;
750 0 : docSel->GetIsCollapsed(&isCollapsed);
751 0 : if (!isCollapsed)
752 0 : docSel->RemoveAllRanges();
753 : }
754 :
755 0 : nsresult nsTextControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue)
756 : {
757 0 : if (!mIsProcessing)//some kind of lock.
758 : {
759 0 : mIsProcessing = true;
760 0 : if (nsGkAtoms::select == aName)
761 : {
762 : // Select all the text.
763 : //
764 : // XXX: This is lame, we can't call editor's SelectAll method
765 : // because that triggers AutoCopies in unix builds.
766 : // Instead, we have to call our own homegrown version
767 : // of select all which merely builds a range that selects
768 : // all of the content and adds that to the selection.
769 :
770 0 : nsWeakFrame weakThis = this;
771 0 : SelectAllOrCollapseToEndOfText(true); // NOTE: can destroy the world
772 0 : if (!weakThis.IsAlive()) {
773 0 : return NS_OK;
774 : }
775 : }
776 0 : mIsProcessing = false;
777 : }
778 0 : return NS_OK;
779 : }
780 :
781 : nsresult
782 0 : nsTextControlFrame::GetFormProperty(nsIAtom* aName, nsAString& aValue) const
783 : {
784 0 : NS_ASSERTION(nsGkAtoms::value != aName,
785 : "Should get the value from the content node instead");
786 0 : return NS_OK;
787 : }
788 :
789 :
790 :
791 : NS_IMETHODIMP
792 0 : nsTextControlFrame::GetEditor(nsIEditor **aEditor)
793 : {
794 0 : NS_ENSURE_ARG_POINTER(aEditor);
795 :
796 0 : nsresult rv = EnsureEditorInitialized();
797 0 : NS_ENSURE_SUCCESS(rv, rv);
798 :
799 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
800 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
801 0 : *aEditor = txtCtrl->GetTextEditor();
802 0 : NS_IF_ADDREF(*aEditor);
803 0 : return NS_OK;
804 : }
805 :
806 : NS_IMETHODIMP
807 0 : nsTextControlFrame::GetTextLength(PRInt32* aTextLength)
808 : {
809 0 : NS_ENSURE_ARG_POINTER(aTextLength);
810 :
811 0 : nsAutoString textContents;
812 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
813 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
814 0 : txtCtrl->GetTextEditorValue(textContents, false); // this is expensive!
815 0 : *aTextLength = textContents.Length();
816 0 : return NS_OK;
817 : }
818 :
819 : nsresult
820 0 : nsTextControlFrame::SetSelectionInternal(nsIDOMNode *aStartNode,
821 : PRInt32 aStartOffset,
822 : nsIDOMNode *aEndNode,
823 : PRInt32 aEndOffset,
824 : nsITextControlFrame::SelectionDirection aDirection)
825 : {
826 : // Create a new range to represent the new selection.
827 : // Note that we use a new range to avoid having to do
828 : // isIncreasing checks to avoid possible errors.
829 :
830 0 : nsRefPtr<nsRange> range = new nsRange();
831 0 : nsresult rv = range->SetStart(aStartNode, aStartOffset);
832 0 : NS_ENSURE_SUCCESS(rv, rv);
833 :
834 0 : rv = range->SetEnd(aEndNode, aEndOffset);
835 0 : NS_ENSURE_SUCCESS(rv, rv);
836 :
837 : // Get the selection, clear it and add the new range to it!
838 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
839 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
840 0 : nsISelectionController* selCon = txtCtrl->GetSelectionController();
841 0 : NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
842 :
843 0 : nsCOMPtr<nsISelection> selection;
844 0 : selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
845 0 : NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
846 :
847 0 : nsCOMPtr<nsISelectionPrivate> selPriv = do_QueryInterface(selection, &rv);
848 0 : NS_ENSURE_SUCCESS(rv, rv);
849 :
850 : nsDirection direction;
851 0 : if (aDirection == eNone) {
852 : // Preserve the direction
853 0 : direction = selPriv->GetSelectionDirection();
854 : } else {
855 0 : direction = (aDirection == eBackward) ? eDirPrevious : eDirNext;
856 : }
857 :
858 0 : rv = selection->RemoveAllRanges();
859 0 : NS_ENSURE_SUCCESS(rv, rv);
860 :
861 0 : rv = selection->AddRange(range); // NOTE: can destroy the world
862 0 : NS_ENSURE_SUCCESS(rv, rv);
863 :
864 0 : selPriv->SetSelectionDirection(direction);
865 0 : return rv;
866 : }
867 :
868 : nsresult
869 0 : nsTextControlFrame::ScrollSelectionIntoView()
870 : {
871 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
872 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
873 0 : nsISelectionController* selCon = txtCtrl->GetSelectionController();
874 0 : if (selCon) {
875 : // Scroll the selection into view (see bug 231389).
876 : return selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
877 : nsISelectionController::SELECTION_FOCUS_REGION,
878 0 : nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY);
879 : }
880 :
881 0 : return NS_ERROR_FAILURE;
882 : }
883 :
884 : nsresult
885 0 : nsTextControlFrame::GetRootNodeAndInitializeEditor(nsIDOMElement **aRootElement)
886 : {
887 0 : NS_ENSURE_ARG_POINTER(aRootElement);
888 :
889 0 : nsCOMPtr<nsIEditor> editor;
890 0 : GetEditor(getter_AddRefs(editor));
891 0 : if (!editor)
892 0 : return NS_OK;
893 :
894 0 : return editor->GetRootElement(aRootElement);
895 : }
896 :
897 : nsresult
898 0 : nsTextControlFrame::SelectAllOrCollapseToEndOfText(bool aSelect)
899 : {
900 0 : nsCOMPtr<nsIDOMElement> rootElement;
901 0 : nsresult rv = GetRootNodeAndInitializeEditor(getter_AddRefs(rootElement));
902 0 : NS_ENSURE_SUCCESS(rv, rv);
903 :
904 0 : nsCOMPtr<nsIContent> rootContent = do_QueryInterface(rootElement);
905 0 : nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
906 :
907 0 : NS_ENSURE_TRUE(rootNode && rootContent, NS_ERROR_FAILURE);
908 :
909 0 : PRInt32 numChildren = rootContent->GetChildCount();
910 :
911 0 : if (numChildren > 0) {
912 : // We never want to place the selection after the last
913 : // br under the root node!
914 0 : nsIContent *child = rootContent->GetChildAt(numChildren - 1);
915 0 : if (child) {
916 0 : if (child->Tag() == nsGkAtoms::br)
917 0 : --numChildren;
918 : }
919 0 : if (!aSelect && numChildren) {
920 0 : child = rootContent->GetChildAt(numChildren - 1);
921 0 : if (child && child->IsNodeOfType(nsINode::eTEXT)) {
922 0 : rootNode = do_QueryInterface(child);
923 0 : const nsTextFragment* fragment = child->GetText();
924 0 : numChildren = fragment ? fragment->GetLength() : 0;
925 : }
926 : }
927 : }
928 :
929 : rv = SetSelectionInternal(rootNode, aSelect ? 0 : numChildren,
930 0 : rootNode, numChildren);
931 0 : NS_ENSURE_SUCCESS(rv, rv);
932 :
933 0 : return ScrollSelectionIntoView();
934 : }
935 :
936 : nsresult
937 0 : nsTextControlFrame::SetSelectionEndPoints(PRInt32 aSelStart, PRInt32 aSelEnd,
938 : nsITextControlFrame::SelectionDirection aDirection)
939 : {
940 0 : NS_ASSERTION(aSelStart <= aSelEnd, "Invalid selection offsets!");
941 :
942 0 : if (aSelStart > aSelEnd)
943 0 : return NS_ERROR_FAILURE;
944 :
945 0 : nsCOMPtr<nsIDOMNode> startNode, endNode;
946 : PRInt32 startOffset, endOffset;
947 :
948 : // Calculate the selection start point.
949 :
950 0 : nsresult rv = OffsetToDOMPoint(aSelStart, getter_AddRefs(startNode), &startOffset);
951 :
952 0 : NS_ENSURE_SUCCESS(rv, rv);
953 :
954 0 : if (aSelStart == aSelEnd) {
955 : // Collapsed selection, so start and end are the same!
956 0 : endNode = startNode;
957 0 : endOffset = startOffset;
958 : }
959 : else {
960 : // Selection isn't collapsed so we have to calculate
961 : // the end point too.
962 :
963 0 : rv = OffsetToDOMPoint(aSelEnd, getter_AddRefs(endNode), &endOffset);
964 :
965 0 : NS_ENSURE_SUCCESS(rv, rv);
966 : }
967 :
968 0 : return SetSelectionInternal(startNode, startOffset, endNode, endOffset, aDirection);
969 : }
970 :
971 : NS_IMETHODIMP
972 0 : nsTextControlFrame::SetSelectionRange(PRInt32 aSelStart, PRInt32 aSelEnd,
973 : nsITextControlFrame::SelectionDirection aDirection)
974 : {
975 0 : nsresult rv = EnsureEditorInitialized();
976 0 : NS_ENSURE_SUCCESS(rv, rv);
977 :
978 0 : if (aSelStart > aSelEnd) {
979 : // Simulate what we'd see SetSelectionStart() was called, followed
980 : // by a SetSelectionEnd().
981 :
982 0 : aSelStart = aSelEnd;
983 : }
984 :
985 0 : return SetSelectionEndPoints(aSelStart, aSelEnd, aDirection);
986 : }
987 :
988 :
989 : NS_IMETHODIMP
990 0 : nsTextControlFrame::SetSelectionStart(PRInt32 aSelectionStart)
991 : {
992 0 : nsresult rv = EnsureEditorInitialized();
993 0 : NS_ENSURE_SUCCESS(rv, rv);
994 :
995 0 : PRInt32 selStart = 0, selEnd = 0;
996 :
997 0 : rv = GetSelectionRange(&selStart, &selEnd);
998 0 : NS_ENSURE_SUCCESS(rv, rv);
999 :
1000 0 : if (aSelectionStart > selEnd) {
1001 : // Collapse to the new start point.
1002 0 : selEnd = aSelectionStart;
1003 : }
1004 :
1005 0 : selStart = aSelectionStart;
1006 :
1007 0 : return SetSelectionEndPoints(selStart, selEnd);
1008 : }
1009 :
1010 : NS_IMETHODIMP
1011 0 : nsTextControlFrame::SetSelectionEnd(PRInt32 aSelectionEnd)
1012 : {
1013 0 : nsresult rv = EnsureEditorInitialized();
1014 0 : NS_ENSURE_SUCCESS(rv, rv);
1015 :
1016 0 : PRInt32 selStart = 0, selEnd = 0;
1017 :
1018 0 : rv = GetSelectionRange(&selStart, &selEnd);
1019 0 : NS_ENSURE_SUCCESS(rv, rv);
1020 :
1021 0 : if (aSelectionEnd < selStart) {
1022 : // Collapse to the new end point.
1023 0 : selStart = aSelectionEnd;
1024 : }
1025 :
1026 0 : selEnd = aSelectionEnd;
1027 :
1028 0 : return SetSelectionEndPoints(selStart, selEnd);
1029 : }
1030 :
1031 : nsresult
1032 0 : nsTextControlFrame::DOMPointToOffset(nsIDOMNode* aNode,
1033 : PRInt32 aNodeOffset,
1034 : PRInt32* aResult)
1035 : {
1036 0 : NS_ENSURE_ARG_POINTER(aNode && aResult);
1037 :
1038 0 : *aResult = 0;
1039 :
1040 0 : nsCOMPtr<nsIDOMElement> rootElement;
1041 0 : nsresult rv = GetRootNodeAndInitializeEditor(getter_AddRefs(rootElement));
1042 0 : nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
1043 :
1044 0 : NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
1045 :
1046 0 : nsCOMPtr<nsIDOMNodeList> nodeList;
1047 :
1048 0 : rv = rootNode->GetChildNodes(getter_AddRefs(nodeList));
1049 0 : NS_ENSURE_SUCCESS(rv, rv);
1050 0 : NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
1051 :
1052 0 : PRUint32 length = 0;
1053 0 : rv = nodeList->GetLength(&length);
1054 0 : NS_ENSURE_SUCCESS(rv, rv);
1055 :
1056 0 : if (!length || aNodeOffset < 0)
1057 0 : return NS_OK;
1058 :
1059 0 : NS_ASSERTION(length <= 2, "We should have one text node and one mozBR at most");
1060 :
1061 0 : nsCOMPtr<nsIDOMNode> firstNode;
1062 0 : rv = nodeList->Item(0, getter_AddRefs(firstNode));
1063 0 : NS_ENSURE_SUCCESS(rv, rv);
1064 0 : nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(firstNode);
1065 :
1066 0 : nsCOMPtr<nsIDOMText> nodeAsText = do_QueryInterface(aNode);
1067 0 : if (nodeAsText || (aNode == rootNode && aNodeOffset == 0)) {
1068 : // Selection is somewhere inside the text node; the offset is aNodeOffset
1069 0 : *aResult = aNodeOffset;
1070 : } else {
1071 : // Selection is on the mozBR node, so offset should be set to the length
1072 : // of the text node.
1073 0 : if (textNode) {
1074 0 : rv = textNode->GetLength(&length);
1075 0 : NS_ENSURE_SUCCESS(rv, rv);
1076 0 : *aResult = PRInt32(length);
1077 : }
1078 : }
1079 :
1080 0 : return NS_OK;
1081 : }
1082 :
1083 : nsresult
1084 0 : nsTextControlFrame::OffsetToDOMPoint(PRInt32 aOffset,
1085 : nsIDOMNode** aResult,
1086 : PRInt32* aPosition)
1087 : {
1088 0 : NS_ENSURE_ARG_POINTER(aResult && aPosition);
1089 :
1090 0 : *aResult = nsnull;
1091 0 : *aPosition = 0;
1092 :
1093 0 : nsCOMPtr<nsIDOMElement> rootElement;
1094 0 : nsresult rv = GetRootNodeAndInitializeEditor(getter_AddRefs(rootElement));
1095 0 : NS_ENSURE_SUCCESS(rv, rv);
1096 0 : nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
1097 :
1098 0 : NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
1099 :
1100 0 : nsCOMPtr<nsIDOMNodeList> nodeList;
1101 :
1102 0 : rv = rootNode->GetChildNodes(getter_AddRefs(nodeList));
1103 0 : NS_ENSURE_SUCCESS(rv, rv);
1104 0 : NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
1105 :
1106 0 : PRUint32 length = 0;
1107 :
1108 0 : rv = nodeList->GetLength(&length);
1109 0 : NS_ENSURE_SUCCESS(rv, rv);
1110 :
1111 0 : NS_ASSERTION(length <= 2, "We should have one text node and one mozBR at most");
1112 :
1113 0 : nsCOMPtr<nsIDOMNode> firstNode;
1114 0 : rv = nodeList->Item(0, getter_AddRefs(firstNode));
1115 0 : NS_ENSURE_SUCCESS(rv, rv);
1116 0 : nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(firstNode);
1117 :
1118 0 : if (length == 0 || aOffset < 0) {
1119 0 : NS_IF_ADDREF(*aResult = rootNode);
1120 0 : *aPosition = 0;
1121 0 : } else if (textNode) {
1122 0 : PRUint32 textLength = 0;
1123 0 : textNode->GetLength(&textLength);
1124 0 : if (length == 2 && PRUint32(aOffset) == textLength) {
1125 : // If we're at the end of the text node and we have a trailing BR node,
1126 : // set the selection on the BR node.
1127 0 : NS_IF_ADDREF(*aResult = rootNode);
1128 0 : *aPosition = 1;
1129 : } else {
1130 : // Otherwise, set the selection on the textnode itself.
1131 0 : NS_IF_ADDREF(*aResult = firstNode);
1132 0 : *aPosition = NS_MIN(aOffset, PRInt32(textLength));
1133 : }
1134 : } else {
1135 0 : NS_IF_ADDREF(*aResult = rootNode);
1136 0 : *aPosition = 0;
1137 : }
1138 :
1139 0 : return NS_OK;
1140 : }
1141 :
1142 : NS_IMETHODIMP
1143 0 : nsTextControlFrame::GetSelectionRange(PRInt32* aSelectionStart,
1144 : PRInt32* aSelectionEnd,
1145 : SelectionDirection* aDirection)
1146 : {
1147 : // make sure we have an editor
1148 0 : nsresult rv = EnsureEditorInitialized();
1149 0 : NS_ENSURE_SUCCESS(rv, rv);
1150 :
1151 0 : if (aSelectionStart) {
1152 0 : *aSelectionStart = 0;
1153 : }
1154 0 : if (aSelectionEnd) {
1155 0 : *aSelectionEnd = 0;
1156 : }
1157 0 : if (aDirection) {
1158 0 : *aDirection = eNone;
1159 : }
1160 :
1161 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1162 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
1163 0 : nsISelectionController* selCon = txtCtrl->GetSelectionController();
1164 0 : NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
1165 0 : nsCOMPtr<nsISelection> selection;
1166 0 : rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
1167 0 : NS_ENSURE_SUCCESS(rv, rv);
1168 0 : NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
1169 :
1170 0 : PRInt32 numRanges = 0;
1171 0 : selection->GetRangeCount(&numRanges);
1172 :
1173 0 : if (numRanges < 1)
1174 0 : return NS_OK;
1175 :
1176 : // We only operate on the first range in the selection!
1177 :
1178 0 : if (aDirection) {
1179 0 : nsCOMPtr<nsISelectionPrivate> selPriv = do_QueryInterface(selection);
1180 0 : if (selPriv) {
1181 0 : nsDirection direction = selPriv->GetSelectionDirection();
1182 0 : if (direction == eDirNext) {
1183 0 : *aDirection = eForward;
1184 0 : } else if (direction == eDirPrevious) {
1185 0 : *aDirection = eBackward;
1186 : } else {
1187 0 : NS_NOTREACHED("Invalid nsDirection enum value");
1188 : }
1189 : }
1190 : }
1191 :
1192 0 : if (!aSelectionStart || !aSelectionEnd) {
1193 0 : return NS_OK;
1194 : }
1195 :
1196 0 : nsCOMPtr<nsIDOMRange> firstRange;
1197 0 : rv = selection->GetRangeAt(0, getter_AddRefs(firstRange));
1198 0 : NS_ENSURE_SUCCESS(rv, rv);
1199 0 : NS_ENSURE_TRUE(firstRange, NS_ERROR_FAILURE);
1200 :
1201 0 : nsCOMPtr<nsIDOMNode> startNode, endNode;
1202 0 : PRInt32 startOffset = 0, endOffset = 0;
1203 :
1204 : // Get the start point of the range.
1205 :
1206 0 : rv = firstRange->GetStartContainer(getter_AddRefs(startNode));
1207 0 : NS_ENSURE_SUCCESS(rv, rv);
1208 0 : NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
1209 :
1210 0 : rv = firstRange->GetStartOffset(&startOffset);
1211 0 : NS_ENSURE_SUCCESS(rv, rv);
1212 :
1213 : // Get the end point of the range.
1214 :
1215 0 : rv = firstRange->GetEndContainer(getter_AddRefs(endNode));
1216 0 : NS_ENSURE_SUCCESS(rv, rv);
1217 0 : NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE);
1218 :
1219 0 : rv = firstRange->GetEndOffset(&endOffset);
1220 0 : NS_ENSURE_SUCCESS(rv, rv);
1221 :
1222 : // Convert the start point to a selection offset.
1223 :
1224 0 : rv = DOMPointToOffset(startNode, startOffset, aSelectionStart);
1225 0 : NS_ENSURE_SUCCESS(rv, rv);
1226 :
1227 : // Convert the end point to a selection offset.
1228 :
1229 0 : return DOMPointToOffset(endNode, endOffset, aSelectionEnd);
1230 : }
1231 :
1232 : /////END INTERFACE IMPLEMENTATIONS
1233 :
1234 : ////NSIFRAME
1235 : NS_IMETHODIMP
1236 0 : nsTextControlFrame::AttributeChanged(PRInt32 aNameSpaceID,
1237 : nsIAtom* aAttribute,
1238 : PRInt32 aModType)
1239 : {
1240 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1241 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
1242 0 : nsISelectionController* selCon = txtCtrl->GetSelectionController();
1243 : const bool needEditor = nsGkAtoms::maxlength == aAttribute ||
1244 : nsGkAtoms::readonly == aAttribute ||
1245 : nsGkAtoms::disabled == aAttribute ||
1246 0 : nsGkAtoms::spellcheck == aAttribute;
1247 0 : nsCOMPtr<nsIEditor> editor;
1248 0 : if (needEditor) {
1249 0 : GetEditor(getter_AddRefs(editor));
1250 : }
1251 0 : if ((needEditor && !editor) || !selCon)
1252 0 : return nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);;
1253 :
1254 0 : nsresult rv = NS_OK;
1255 :
1256 0 : if (nsGkAtoms::maxlength == aAttribute)
1257 : {
1258 : PRInt32 maxLength;
1259 0 : bool maxDefined = GetMaxLength(&maxLength);
1260 :
1261 0 : nsCOMPtr<nsIPlaintextEditor> textEditor = do_QueryInterface(editor);
1262 0 : if (textEditor)
1263 : {
1264 0 : if (maxDefined)
1265 : { // set the maxLength attribute
1266 0 : textEditor->SetMaxTextLength(maxLength);
1267 : // if maxLength>docLength, we need to truncate the doc content
1268 : }
1269 : else { // unset the maxLength attribute
1270 0 : textEditor->SetMaxTextLength(-1);
1271 : }
1272 : }
1273 0 : rv = NS_OK; // don't propagate the error
1274 : }
1275 0 : else if (nsGkAtoms::readonly == aAttribute)
1276 : {
1277 : PRUint32 flags;
1278 0 : editor->GetFlags(&flags);
1279 0 : if (AttributeExists(nsGkAtoms::readonly))
1280 : { // set readonly
1281 0 : flags |= nsIPlaintextEditor::eEditorReadonlyMask;
1282 0 : if (nsContentUtils::IsFocusedContent(mContent))
1283 0 : selCon->SetCaretEnabled(false);
1284 : }
1285 : else
1286 : { // unset readonly
1287 0 : flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
1288 0 : if (!(flags & nsIPlaintextEditor::eEditorDisabledMask) &&
1289 0 : nsContentUtils::IsFocusedContent(mContent))
1290 0 : selCon->SetCaretEnabled(true);
1291 : }
1292 0 : editor->SetFlags(flags);
1293 : }
1294 0 : else if (nsGkAtoms::disabled == aAttribute)
1295 : {
1296 : PRUint32 flags;
1297 0 : editor->GetFlags(&flags);
1298 0 : if (AttributeExists(nsGkAtoms::disabled))
1299 : { // set disabled
1300 0 : flags |= nsIPlaintextEditor::eEditorDisabledMask;
1301 0 : selCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
1302 0 : if (nsContentUtils::IsFocusedContent(mContent))
1303 0 : selCon->SetCaretEnabled(false);
1304 : }
1305 : else
1306 : { // unset disabled
1307 0 : flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
1308 0 : selCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
1309 0 : if (nsContentUtils::IsFocusedContent(mContent)) {
1310 0 : selCon->SetCaretEnabled(true);
1311 : }
1312 : }
1313 0 : editor->SetFlags(flags);
1314 : }
1315 0 : else if (!mUseEditor && nsGkAtoms::value == aAttribute) {
1316 0 : UpdateValueDisplay(true);
1317 : }
1318 : // Allow the base class to handle common attributes supported
1319 : // by all form elements...
1320 : else {
1321 0 : rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
1322 : }
1323 :
1324 0 : return rv;
1325 : }
1326 :
1327 :
1328 : nsresult
1329 0 : nsTextControlFrame::GetText(nsString& aText)
1330 : {
1331 0 : nsresult rv = NS_OK;
1332 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1333 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
1334 0 : if (IsSingleLineTextControl()) {
1335 : // There will be no line breaks so we can ignore the wrap property.
1336 0 : txtCtrl->GetTextEditorValue(aText, true);
1337 : } else {
1338 0 : nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(mContent);
1339 0 : if (textArea) {
1340 0 : rv = textArea->GetValue(aText);
1341 : }
1342 : }
1343 0 : return rv;
1344 : }
1345 :
1346 :
1347 : nsresult
1348 0 : nsTextControlFrame::GetPhonetic(nsAString& aPhonetic)
1349 : {
1350 0 : aPhonetic.Truncate(0);
1351 :
1352 0 : nsCOMPtr<nsIEditor> editor;
1353 0 : nsresult rv = GetEditor(getter_AddRefs(editor));
1354 0 : NS_ENSURE_SUCCESS(rv, rv);
1355 :
1356 0 : nsCOMPtr<nsIEditorIMESupport> imeSupport = do_QueryInterface(editor);
1357 0 : if (imeSupport) {
1358 0 : nsCOMPtr<nsIPhonetic> phonetic = do_QueryInterface(imeSupport);
1359 0 : if (phonetic)
1360 0 : phonetic->GetPhonetic(aPhonetic);
1361 : }
1362 0 : return NS_OK;
1363 : }
1364 :
1365 : ///END NSIFRAME OVERLOADS
1366 : /////BEGIN PROTECTED METHODS
1367 :
1368 : bool
1369 0 : nsTextControlFrame::GetMaxLength(PRInt32* aSize)
1370 : {
1371 0 : *aSize = -1;
1372 :
1373 0 : nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
1374 0 : if (content) {
1375 0 : const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::maxlength);
1376 0 : if (attr && attr->Type() == nsAttrValue::eInteger) {
1377 0 : *aSize = attr->GetIntegerValue();
1378 :
1379 0 : return true;
1380 : }
1381 : }
1382 0 : return false;
1383 : }
1384 :
1385 : // this is where we propagate a content changed event
1386 : void
1387 0 : nsTextControlFrame::FireOnInput(bool aTrusted)
1388 : {
1389 0 : if (!mNotifyOnInput)
1390 0 : return; // if notification is turned off, do nothing
1391 :
1392 : // Dispatch the "input" event
1393 0 : nsEventStatus status = nsEventStatus_eIgnore;
1394 0 : nsUIEvent event(aTrusted, NS_FORM_INPUT, 0);
1395 :
1396 : // Have the content handle the event, propagating it according to normal
1397 : // DOM rules.
1398 0 : nsCOMPtr<nsIPresShell> shell = PresContext()->PresShell();
1399 0 : shell->HandleEventWithTarget(&event, nsnull, mContent, &status);
1400 : }
1401 :
1402 : nsresult
1403 0 : nsTextControlFrame::InitFocusedValue()
1404 : {
1405 0 : return GetText(mFocusedValue);
1406 : }
1407 :
1408 : NS_IMETHODIMP
1409 0 : nsTextControlFrame::CheckFireOnChange()
1410 : {
1411 0 : nsString value;
1412 0 : GetText(value);
1413 0 : if (!mFocusedValue.Equals(value))
1414 : {
1415 0 : mFocusedValue = value;
1416 : // Dispatch the change event.
1417 : nsContentUtils::DispatchTrustedEvent(mContent->OwnerDoc(), mContent,
1418 0 : NS_LITERAL_STRING("change"), true,
1419 0 : false);
1420 : }
1421 0 : return NS_OK;
1422 : }
1423 :
1424 : // END IMPLEMENTING NS_IFORMCONTROLFRAME
1425 :
1426 : NS_IMETHODIMP
1427 0 : nsTextControlFrame::SetInitialChildList(ChildListID aListID,
1428 : nsFrameList& aChildList)
1429 : {
1430 0 : nsresult rv = nsBoxFrame::SetInitialChildList(aListID, aChildList);
1431 :
1432 0 : nsIFrame* first = GetFirstPrincipalChild();
1433 :
1434 : // Mark the scroll frame as being a reflow root. This will allow
1435 : // incremental reflows to be initiated at the scroll frame, rather
1436 : // than descending from the root frame of the frame hierarchy.
1437 0 : if (first) {
1438 0 : first->AddStateBits(NS_FRAME_REFLOW_ROOT);
1439 :
1440 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1441 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
1442 0 : txtCtrl->InitializeKeyboardEventListeners();
1443 :
1444 : nsPoint* contentScrollPos = static_cast<nsPoint*>
1445 0 : (Properties().Get(ContentScrollPos()));
1446 0 : if (contentScrollPos) {
1447 : // If we have a scroll pos stored to be passed to our anonymous
1448 : // div, do it here!
1449 0 : nsIStatefulFrame* statefulFrame = do_QueryFrame(first);
1450 0 : NS_ASSERTION(statefulFrame, "unexpected type of frame for the anonymous div");
1451 0 : nsPresState fakePresState;
1452 0 : fakePresState.SetScrollState(*contentScrollPos);
1453 0 : statefulFrame->RestoreState(&fakePresState);
1454 0 : Properties().Remove(ContentScrollPos());
1455 : delete contentScrollPos;
1456 : }
1457 : }
1458 0 : return rv;
1459 : }
1460 :
1461 : bool
1462 0 : nsTextControlFrame::IsScrollable() const
1463 : {
1464 0 : return !IsSingleLineTextControl();
1465 : }
1466 :
1467 : void
1468 0 : nsTextControlFrame::SetValueChanged(bool aValueChanged)
1469 : {
1470 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1471 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
1472 :
1473 0 : if (mUsePlaceholder && !nsContentUtils::IsFocusedContent(mContent)) {
1474 : // If the content is focused, we don't care about the changes because
1475 : // the placeholder is going to be hidden/shown on blur.
1476 : PRInt32 textLength;
1477 0 : GetTextLength(&textLength);
1478 :
1479 0 : nsWeakFrame weakFrame(this);
1480 0 : txtCtrl->SetPlaceholderClass(!textLength, true);
1481 0 : if (!weakFrame.IsAlive()) {
1482 : return;
1483 : }
1484 : }
1485 :
1486 0 : txtCtrl->SetValueChanged(aValueChanged);
1487 : }
1488 :
1489 :
1490 : nsresult
1491 0 : nsTextControlFrame::UpdateValueDisplay(bool aNotify,
1492 : bool aBeforeEditorInit,
1493 : const nsAString *aValue)
1494 : {
1495 0 : if (!IsSingleLineTextControl()) // textareas don't use this
1496 0 : return NS_OK;
1497 :
1498 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1499 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
1500 0 : nsIContent* rootNode = txtCtrl->GetRootEditorNode();
1501 :
1502 0 : NS_PRECONDITION(rootNode, "Must have a div content\n");
1503 0 : NS_PRECONDITION(!mUseEditor,
1504 : "Do not call this after editor has been initialized");
1505 0 : NS_ASSERTION(!mUsePlaceholder || txtCtrl->GetPlaceholderNode(),
1506 : "A placeholder div must exist");
1507 :
1508 0 : nsIContent *textContent = rootNode->GetChildAt(0);
1509 0 : if (!textContent) {
1510 : // Set up a textnode with our value
1511 0 : nsCOMPtr<nsIContent> textNode;
1512 0 : nsresult rv = NS_NewTextNode(getter_AddRefs(textNode),
1513 0 : mContent->NodeInfo()->NodeInfoManager());
1514 0 : NS_ENSURE_SUCCESS(rv, rv);
1515 :
1516 0 : NS_ASSERTION(textNode, "Must have textcontent!\n");
1517 :
1518 0 : rootNode->AppendChildTo(textNode, aNotify);
1519 0 : textContent = textNode;
1520 : }
1521 :
1522 0 : NS_ENSURE_TRUE(textContent, NS_ERROR_UNEXPECTED);
1523 :
1524 : // Get the current value of the textfield from the content.
1525 0 : nsAutoString value;
1526 0 : if (aValue) {
1527 0 : value = *aValue;
1528 : } else {
1529 0 : txtCtrl->GetTextEditorValue(value, true);
1530 : }
1531 :
1532 : // Update the display of the placeholder value if needed.
1533 : // We don't need to do this if we're about to initialize the
1534 : // editor, since EnsureEditorInitialized takes care of this.
1535 0 : if (mUsePlaceholder && !aBeforeEditorInit)
1536 : {
1537 0 : nsWeakFrame weakFrame(this);
1538 0 : txtCtrl->SetPlaceholderClass(value.IsEmpty(), aNotify);
1539 0 : NS_ENSURE_STATE(weakFrame.IsAlive());
1540 : }
1541 :
1542 0 : if (aBeforeEditorInit && value.IsEmpty()) {
1543 0 : rootNode->RemoveChildAt(0, true);
1544 0 : return NS_OK;
1545 : }
1546 :
1547 0 : if (!value.IsEmpty() && IsPasswordTextControl()) {
1548 0 : nsTextEditRules::FillBufWithPWChars(&value, value.Length());
1549 : }
1550 0 : return textContent->SetText(value, aNotify);
1551 : }
1552 :
1553 : NS_IMETHODIMP
1554 0 : nsTextControlFrame::GetOwnedSelectionController(nsISelectionController** aSelCon)
1555 : {
1556 0 : NS_ENSURE_ARG_POINTER(aSelCon);
1557 :
1558 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1559 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
1560 :
1561 0 : *aSelCon = txtCtrl->GetSelectionController();
1562 0 : NS_IF_ADDREF(*aSelCon);
1563 :
1564 0 : return NS_OK;
1565 : }
1566 :
1567 : nsFrameSelection*
1568 0 : nsTextControlFrame::GetOwnedFrameSelection()
1569 : {
1570 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1571 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
1572 :
1573 0 : return txtCtrl->GetConstFrameSelection();
1574 : }
1575 :
1576 : NS_IMETHODIMP
1577 0 : nsTextControlFrame::SaveState(nsIStatefulFrame::SpecialStateID aStateID, nsPresState** aState)
1578 : {
1579 0 : NS_ENSURE_ARG_POINTER(aState);
1580 :
1581 0 : *aState = nsnull;
1582 :
1583 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1584 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
1585 :
1586 0 : nsIContent* rootNode = txtCtrl->GetRootEditorNode();
1587 0 : if (rootNode) {
1588 : // Query the nsIStatefulFrame from the HTMLScrollFrame
1589 0 : nsIStatefulFrame* scrollStateFrame = do_QueryFrame(rootNode->GetPrimaryFrame());
1590 0 : if (scrollStateFrame) {
1591 0 : return scrollStateFrame->SaveState(aStateID, aState);
1592 : }
1593 : }
1594 :
1595 0 : return NS_OK;
1596 : }
1597 :
1598 : NS_IMETHODIMP
1599 0 : nsTextControlFrame::RestoreState(nsPresState* aState)
1600 : {
1601 0 : NS_ENSURE_ARG_POINTER(aState);
1602 :
1603 0 : nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1604 0 : NS_ASSERTION(txtCtrl, "Content not a text control element");
1605 :
1606 0 : nsIContent* rootNode = txtCtrl->GetRootEditorNode();
1607 0 : if (rootNode) {
1608 : // Query the nsIStatefulFrame from the HTMLScrollFrame
1609 0 : nsIStatefulFrame* scrollStateFrame = do_QueryFrame(rootNode->GetPrimaryFrame());
1610 0 : if (scrollStateFrame) {
1611 0 : return scrollStateFrame->RestoreState(aState);
1612 : }
1613 : }
1614 :
1615 : // Most likely, we don't have our anonymous content constructed yet, which
1616 : // would cause us to end up here. In this case, we'll just store the scroll
1617 : // pos ourselves, and forward it to the scroll frame later when it's created.
1618 0 : Properties().Set(ContentScrollPos(), new nsPoint(aState->GetScrollState()));
1619 0 : return NS_OK;
1620 : }
1621 :
|