1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 ts=2 et tw=80: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla.org client code.
17 : *
18 : * The Initial Developer of the Original Code is Mozilla Foundation.
19 : * Portions created by the Initial Developer are Copyright (C) 2009
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Ehsan Akhgari <ehsan@mozilla.com> (Original Author)
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 "nsTextEditorState.h"
40 :
41 : #include "nsCOMPtr.h"
42 : #include "nsIPresShell.h"
43 : #include "nsIView.h"
44 : #include "nsCaret.h"
45 : #include "nsEditorCID.h"
46 : #include "nsLayoutCID.h"
47 : #include "nsITextControlFrame.h"
48 : #include "nsIPlaintextEditor.h"
49 : #include "nsIDOMDocument.h"
50 : #include "nsContentCreatorFunctions.h"
51 : #include "nsTextControlFrame.h"
52 : #include "nsIControllers.h"
53 : #include "nsIDOMHTMLInputElement.h"
54 : #include "nsIDOMHTMLTextAreaElement.h"
55 : #include "nsITransactionManager.h"
56 : #include "nsIControllerContext.h"
57 : #include "nsAttrValue.h"
58 : #include "nsGenericHTMLElement.h"
59 : #include "nsIDOMEventListener.h"
60 : #include "nsIEditorObserver.h"
61 : #include "nsINativeKeyBindings.h"
62 : #include "nsIDocumentEncoder.h"
63 : #include "nsISelectionPrivate.h"
64 : #include "nsPIDOMWindow.h"
65 : #include "nsServiceManagerUtils.h"
66 : #include "nsIEditor.h"
67 : #include "nsTextEditRules.h"
68 : #include "nsEventListenerManager.h"
69 : #include "nsContentUtils.h"
70 :
71 : using namespace mozilla::dom;
72 :
73 : static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID);
74 :
75 : static nsINativeKeyBindings *sNativeInputBindings = nsnull;
76 : static nsINativeKeyBindings *sNativeTextAreaBindings = nsnull;
77 :
78 0 : class RestoreSelectionState : public nsRunnable {
79 : public:
80 0 : RestoreSelectionState(nsTextEditorState *aState, nsTextControlFrame *aFrame)
81 : : mFrame(aFrame),
82 0 : mTextEditorState(aState)
83 : {
84 0 : }
85 :
86 0 : NS_IMETHOD Run() {
87 0 : if (mFrame) {
88 : // SetSelectionRange leads to Selection::AddRange which flushes Layout -
89 : // need to block script to avoid nested PrepareEditor calls (bug 642800).
90 0 : nsAutoScriptBlocker scriptBlocker;
91 : nsTextEditorState::SelectionProperties& properties =
92 0 : mTextEditorState->GetSelectionProperties();
93 : mFrame->SetSelectionRange(properties.mStart,
94 : properties.mEnd,
95 0 : properties.mDirection);
96 0 : if (!mTextEditorState->mSelectionRestoreEagerInit) {
97 0 : mTextEditorState->HideSelectionIfBlurred();
98 : }
99 0 : mTextEditorState->mSelectionRestoreEagerInit = false;
100 : }
101 0 : mTextEditorState->FinishedRestoringSelection();
102 0 : return NS_OK;
103 : }
104 :
105 : // Let the text editor tell us we're no longer relevant - avoids use of nsWeakFrame
106 0 : void Revoke() {
107 0 : mFrame = nsnull;
108 0 : }
109 :
110 : private:
111 : nsTextControlFrame* mFrame;
112 : nsTextEditorState* mTextEditorState;
113 : };
114 :
115 : /*static*/
116 : bool
117 0 : nsITextControlElement::GetWrapPropertyEnum(nsIContent* aContent,
118 : nsITextControlElement::nsHTMLTextWrap& aWrapProp)
119 : {
120 : // soft is the default; "physical" defaults to soft as well because all other
121 : // browsers treat it that way and there is no real reason to maintain physical
122 : // and virtual as separate entities if no one else does. Only hard and off
123 : // do anything different.
124 0 : aWrapProp = eHTMLTextWrap_Soft; // the default
125 :
126 0 : nsAutoString wrap;
127 0 : if (aContent->IsHTML()) {
128 : static nsIContent::AttrValuesArray strings[] =
129 : {&nsGkAtoms::HARD, &nsGkAtoms::OFF, nsnull};
130 :
131 0 : switch (aContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::wrap,
132 0 : strings, eIgnoreCase)) {
133 0 : case 0: aWrapProp = eHTMLTextWrap_Hard; break;
134 0 : case 1: aWrapProp = eHTMLTextWrap_Off; break;
135 : }
136 :
137 0 : return true;
138 : }
139 :
140 0 : return false;
141 : }
142 :
143 : static bool
144 0 : SuppressEventHandlers(nsPresContext* aPresContext)
145 : {
146 0 : bool suppressHandlers = false;
147 :
148 0 : if (aPresContext)
149 : {
150 : // Right now we only suppress event handlers and controller manipulation
151 : // when in a print preview or print context!
152 :
153 : // In the current implementation, we only paginate when
154 : // printing or in print preview.
155 :
156 0 : suppressHandlers = aPresContext->IsPaginated();
157 : }
158 :
159 0 : return suppressHandlers;
160 : }
161 :
162 : class nsAnonDivObserver MOZ_FINAL : public nsStubMutationObserver
163 : {
164 : public:
165 0 : nsAnonDivObserver(nsTextEditorState* aTextEditorState)
166 0 : : mTextEditorState(aTextEditorState) {}
167 : NS_DECL_ISUPPORTS
168 : NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
169 : NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
170 : NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
171 : NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
172 :
173 : private:
174 : nsTextEditorState* mTextEditorState;
175 : };
176 :
177 : class nsTextInputSelectionImpl MOZ_FINAL : public nsSupportsWeakReference
178 : , public nsISelectionController
179 : {
180 : public:
181 0 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
182 1464 : NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsTextInputSelectionImpl, nsISelectionController)
183 :
184 : nsTextInputSelectionImpl(nsFrameSelection *aSel, nsIPresShell *aShell, nsIContent *aLimiter);
185 0 : ~nsTextInputSelectionImpl(){}
186 :
187 : void SetScrollableFrame(nsIScrollableFrame *aScrollableFrame);
188 0 : nsFrameSelection* GetConstFrameSelection()
189 0 : { return mFrameSelection; }
190 :
191 : //NSISELECTIONCONTROLLER INTERFACES
192 : NS_IMETHOD SetDisplaySelection(PRInt16 toggle);
193 : NS_IMETHOD GetDisplaySelection(PRInt16 *_retval);
194 : NS_IMETHOD SetSelectionFlags(PRInt16 aInEnable);
195 : NS_IMETHOD GetSelectionFlags(PRInt16 *aOutEnable);
196 : NS_IMETHOD GetSelection(PRInt16 type, nsISelection **_retval);
197 : NS_IMETHOD ScrollSelectionIntoView(PRInt16 aType, PRInt16 aRegion, PRInt16 aFlags);
198 : NS_IMETHOD RepaintSelection(PRInt16 type);
199 : NS_IMETHOD RepaintSelection(nsPresContext* aPresContext, SelectionType aSelectionType);
200 : NS_IMETHOD SetCaretEnabled(bool enabled);
201 : NS_IMETHOD SetCaretReadOnly(bool aReadOnly);
202 : NS_IMETHOD GetCaretEnabled(bool *_retval);
203 : NS_IMETHOD GetCaretVisible(bool *_retval);
204 : NS_IMETHOD SetCaretVisibilityDuringSelection(bool aVisibility);
205 : NS_IMETHOD CharacterMove(bool aForward, bool aExtend);
206 : NS_IMETHOD CharacterExtendForDelete();
207 : NS_IMETHOD CharacterExtendForBackspace();
208 : NS_IMETHOD WordMove(bool aForward, bool aExtend);
209 : NS_IMETHOD WordExtendForDelete(bool aForward);
210 : NS_IMETHOD LineMove(bool aForward, bool aExtend);
211 : NS_IMETHOD IntraLineMove(bool aForward, bool aExtend);
212 : NS_IMETHOD PageMove(bool aForward, bool aExtend);
213 : NS_IMETHOD CompleteScroll(bool aForward);
214 : NS_IMETHOD CompleteMove(bool aForward, bool aExtend);
215 : NS_IMETHOD ScrollPage(bool aForward);
216 : NS_IMETHOD ScrollLine(bool aForward);
217 : NS_IMETHOD ScrollCharacter(bool aRight);
218 : NS_IMETHOD SelectAll(void);
219 : NS_IMETHOD CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, bool *_retval);
220 : virtual nsresult CheckVisibilityContent(nsIContent* aNode, PRInt16 aStartOffset, PRInt16 aEndOffset, bool* aRetval);
221 :
222 : private:
223 : nsRefPtr<nsFrameSelection> mFrameSelection;
224 : nsCOMPtr<nsIContent> mLimiter;
225 : nsIScrollableFrame *mScrollFrame;
226 : nsWeakPtr mPresShellWeak;
227 : };
228 :
229 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTextInputSelectionImpl)
230 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTextInputSelectionImpl)
231 0 : NS_INTERFACE_TABLE_HEAD(nsTextInputSelectionImpl)
232 0 : NS_INTERFACE_TABLE3(nsTextInputSelectionImpl,
233 : nsISelectionController,
234 : nsISelectionDisplay,
235 : nsISupportsWeakReference)
236 0 : NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsTextInputSelectionImpl)
237 0 : NS_INTERFACE_MAP_END
238 :
239 1464 : NS_IMPL_CYCLE_COLLECTION_2(nsTextInputSelectionImpl, mFrameSelection, mLimiter)
240 :
241 :
242 : // BEGIN nsTextInputSelectionImpl
243 :
244 0 : nsTextInputSelectionImpl::nsTextInputSelectionImpl(nsFrameSelection *aSel,
245 : nsIPresShell *aShell,
246 : nsIContent *aLimiter)
247 0 : : mScrollFrame(nsnull)
248 : {
249 0 : if (aSel && aShell)
250 : {
251 0 : mFrameSelection = aSel;//we are the owner now!
252 0 : mLimiter = aLimiter;
253 0 : mFrameSelection->Init(aShell, mLimiter);
254 0 : mPresShellWeak = do_GetWeakReference(aShell);
255 : }
256 0 : }
257 :
258 : void
259 0 : nsTextInputSelectionImpl::SetScrollableFrame(nsIScrollableFrame *aScrollableFrame)
260 : {
261 0 : mScrollFrame = aScrollableFrame;
262 0 : if (!mScrollFrame && mFrameSelection) {
263 0 : mFrameSelection->DisconnectFromPresShell();
264 0 : mFrameSelection = nsnull;
265 : }
266 0 : }
267 :
268 : NS_IMETHODIMP
269 0 : nsTextInputSelectionImpl::SetDisplaySelection(PRInt16 aToggle)
270 : {
271 0 : if (!mFrameSelection)
272 0 : return NS_ERROR_NULL_POINTER;
273 :
274 0 : mFrameSelection->SetDisplaySelection(aToggle);
275 0 : return NS_OK;
276 : }
277 :
278 : NS_IMETHODIMP
279 0 : nsTextInputSelectionImpl::GetDisplaySelection(PRInt16 *aToggle)
280 : {
281 0 : if (!mFrameSelection)
282 0 : return NS_ERROR_NULL_POINTER;
283 :
284 0 : *aToggle = mFrameSelection->GetDisplaySelection();
285 0 : return NS_OK;
286 : }
287 :
288 : NS_IMETHODIMP
289 0 : nsTextInputSelectionImpl::SetSelectionFlags(PRInt16 aToggle)
290 : {
291 0 : return NS_OK;//stub this out. not used in input
292 : }
293 :
294 : NS_IMETHODIMP
295 0 : nsTextInputSelectionImpl::GetSelectionFlags(PRInt16 *aOutEnable)
296 : {
297 0 : *aOutEnable = nsISelectionDisplay::DISPLAY_TEXT;
298 0 : return NS_OK;
299 : }
300 :
301 : NS_IMETHODIMP
302 0 : nsTextInputSelectionImpl::GetSelection(PRInt16 type, nsISelection **_retval)
303 : {
304 0 : if (!mFrameSelection)
305 0 : return NS_ERROR_NULL_POINTER;
306 :
307 0 : *_retval = mFrameSelection->GetSelection(type);
308 :
309 0 : if (!(*_retval))
310 0 : return NS_ERROR_FAILURE;
311 :
312 0 : NS_ADDREF(*_retval);
313 0 : return NS_OK;
314 : }
315 :
316 : NS_IMETHODIMP
317 0 : nsTextInputSelectionImpl::ScrollSelectionIntoView(PRInt16 aType, PRInt16 aRegion, PRInt16 aFlags)
318 : {
319 0 : if (!mFrameSelection)
320 0 : return NS_ERROR_FAILURE;
321 :
322 0 : return mFrameSelection->ScrollSelectionIntoView(aType, aRegion, aFlags);
323 : }
324 :
325 : NS_IMETHODIMP
326 0 : nsTextInputSelectionImpl::RepaintSelection(PRInt16 type)
327 : {
328 0 : if (!mFrameSelection)
329 0 : return NS_ERROR_FAILURE;
330 :
331 0 : return mFrameSelection->RepaintSelection(type);
332 : }
333 :
334 : NS_IMETHODIMP
335 0 : nsTextInputSelectionImpl::RepaintSelection(nsPresContext* aPresContext, SelectionType aSelectionType)
336 : {
337 0 : if (!mFrameSelection)
338 0 : return NS_ERROR_FAILURE;
339 :
340 0 : return mFrameSelection->RepaintSelection(aSelectionType);
341 : }
342 :
343 : NS_IMETHODIMP
344 0 : nsTextInputSelectionImpl::SetCaretEnabled(bool enabled)
345 : {
346 0 : if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
347 :
348 0 : nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak);
349 0 : if (!shell) return NS_ERROR_FAILURE;
350 :
351 : // tell the pres shell to enable the caret, rather than settings its visibility directly.
352 : // this way the presShell's idea of caret visibility is maintained.
353 0 : nsCOMPtr<nsISelectionController> selCon = do_QueryInterface(shell);
354 0 : if (!selCon) return NS_ERROR_NO_INTERFACE;
355 0 : selCon->SetCaretEnabled(enabled);
356 :
357 0 : return NS_OK;
358 : }
359 :
360 : NS_IMETHODIMP
361 0 : nsTextInputSelectionImpl::SetCaretReadOnly(bool aReadOnly)
362 : {
363 0 : if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
364 : nsresult result;
365 0 : nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
366 0 : if (shell)
367 : {
368 0 : nsRefPtr<nsCaret> caret = shell->GetCaret();
369 0 : if (caret) {
370 : nsISelection* domSel = mFrameSelection->
371 0 : GetSelection(nsISelectionController::SELECTION_NORMAL);
372 0 : if (domSel)
373 0 : caret->SetCaretReadOnly(aReadOnly);
374 0 : return NS_OK;
375 : }
376 : }
377 0 : return NS_ERROR_FAILURE;
378 : }
379 :
380 : NS_IMETHODIMP
381 0 : nsTextInputSelectionImpl::GetCaretEnabled(bool *_retval)
382 : {
383 0 : return GetCaretVisible(_retval);
384 : }
385 :
386 : NS_IMETHODIMP
387 0 : nsTextInputSelectionImpl::GetCaretVisible(bool *_retval)
388 : {
389 0 : if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
390 : nsresult result;
391 0 : nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
392 0 : if (shell)
393 : {
394 0 : nsRefPtr<nsCaret> caret = shell->GetCaret();
395 0 : if (caret) {
396 : nsISelection* domSel = mFrameSelection->
397 0 : GetSelection(nsISelectionController::SELECTION_NORMAL);
398 0 : if (domSel)
399 0 : return caret->GetCaretVisible(_retval);
400 : }
401 : }
402 0 : return NS_ERROR_FAILURE;
403 : }
404 :
405 : NS_IMETHODIMP
406 0 : nsTextInputSelectionImpl::SetCaretVisibilityDuringSelection(bool aVisibility)
407 : {
408 0 : if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
409 : nsresult result;
410 0 : nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
411 0 : if (shell)
412 : {
413 0 : nsRefPtr<nsCaret> caret = shell->GetCaret();
414 0 : if (caret) {
415 : nsISelection* domSel = mFrameSelection->
416 0 : GetSelection(nsISelectionController::SELECTION_NORMAL);
417 0 : if (domSel)
418 0 : caret->SetVisibilityDuringSelection(aVisibility);
419 0 : return NS_OK;
420 : }
421 : }
422 0 : return NS_ERROR_FAILURE;
423 : }
424 :
425 : NS_IMETHODIMP
426 0 : nsTextInputSelectionImpl::CharacterMove(bool aForward, bool aExtend)
427 : {
428 0 : if (mFrameSelection)
429 0 : return mFrameSelection->CharacterMove(aForward, aExtend);
430 0 : return NS_ERROR_NULL_POINTER;
431 : }
432 :
433 : NS_IMETHODIMP
434 0 : nsTextInputSelectionImpl::CharacterExtendForDelete()
435 : {
436 0 : if (mFrameSelection)
437 0 : return mFrameSelection->CharacterExtendForDelete();
438 0 : return NS_ERROR_NULL_POINTER;
439 : }
440 :
441 : NS_IMETHODIMP
442 0 : nsTextInputSelectionImpl::CharacterExtendForBackspace()
443 : {
444 0 : if (mFrameSelection)
445 0 : return mFrameSelection->CharacterExtendForBackspace();
446 0 : return NS_ERROR_NULL_POINTER;
447 : }
448 :
449 : NS_IMETHODIMP
450 0 : nsTextInputSelectionImpl::WordMove(bool aForward, bool aExtend)
451 : {
452 0 : if (mFrameSelection)
453 0 : return mFrameSelection->WordMove(aForward, aExtend);
454 0 : return NS_ERROR_NULL_POINTER;
455 : }
456 :
457 : NS_IMETHODIMP
458 0 : nsTextInputSelectionImpl::WordExtendForDelete(bool aForward)
459 : {
460 0 : if (mFrameSelection)
461 0 : return mFrameSelection->WordExtendForDelete(aForward);
462 0 : return NS_ERROR_NULL_POINTER;
463 : }
464 :
465 : NS_IMETHODIMP
466 0 : nsTextInputSelectionImpl::LineMove(bool aForward, bool aExtend)
467 : {
468 0 : if (mFrameSelection)
469 : {
470 0 : nsresult result = mFrameSelection->LineMove(aForward, aExtend);
471 0 : if (NS_FAILED(result))
472 0 : result = CompleteMove(aForward,aExtend);
473 0 : return result;
474 : }
475 0 : return NS_ERROR_NULL_POINTER;
476 : }
477 :
478 :
479 : NS_IMETHODIMP
480 0 : nsTextInputSelectionImpl::IntraLineMove(bool aForward, bool aExtend)
481 : {
482 0 : if (mFrameSelection)
483 0 : return mFrameSelection->IntraLineMove(aForward, aExtend);
484 0 : return NS_ERROR_NULL_POINTER;
485 : }
486 :
487 :
488 : NS_IMETHODIMP
489 0 : nsTextInputSelectionImpl::PageMove(bool aForward, bool aExtend)
490 : {
491 : // expected behavior for PageMove is to scroll AND move the caret
492 : // and to remain relative position of the caret in view. see Bug 4302.
493 0 : if (mScrollFrame)
494 : {
495 0 : mFrameSelection->CommonPageMove(aForward, aExtend, mScrollFrame);
496 : }
497 : // After ScrollSelectionIntoView(), the pending notifications might be
498 : // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
499 : return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION,
500 0 : nsISelectionController::SCROLL_SYNCHRONOUS);
501 : }
502 :
503 : NS_IMETHODIMP
504 0 : nsTextInputSelectionImpl::CompleteScroll(bool aForward)
505 : {
506 0 : if (!mScrollFrame)
507 0 : return NS_ERROR_NOT_INITIALIZED;
508 :
509 : mScrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
510 : nsIScrollableFrame::WHOLE,
511 0 : nsIScrollableFrame::INSTANT);
512 0 : return NS_OK;
513 : }
514 :
515 : NS_IMETHODIMP
516 0 : nsTextInputSelectionImpl::CompleteMove(bool aForward, bool aExtend)
517 : {
518 : // grab the parent / root DIV for this text widget
519 0 : nsIContent* parentDIV = mFrameSelection->GetLimiter();
520 0 : if (!parentDIV)
521 0 : return NS_ERROR_UNEXPECTED;
522 :
523 : // make the caret be either at the very beginning (0) or the very end
524 0 : PRInt32 offset = 0;
525 0 : nsFrameSelection::HINT hint = nsFrameSelection::HINTLEFT;
526 0 : if (aForward)
527 : {
528 0 : offset = parentDIV->GetChildCount();
529 :
530 : // Prevent the caret from being placed after the last
531 : // BR node in the content tree!
532 :
533 0 : if (offset > 0)
534 : {
535 0 : nsIContent *child = parentDIV->GetLastChild();
536 :
537 0 : if (child->Tag() == nsGkAtoms::br)
538 : {
539 0 : --offset;
540 0 : hint = nsFrameSelection::HINTRIGHT; // for Bug 106855
541 : }
542 : }
543 : }
544 :
545 : mFrameSelection->HandleClick(parentDIV, offset, offset, aExtend,
546 0 : false, hint);
547 :
548 : // if we got this far, attempt to scroll no matter what the above result is
549 0 : return CompleteScroll(aForward);
550 : }
551 :
552 : NS_IMETHODIMP
553 0 : nsTextInputSelectionImpl::ScrollPage(bool aForward)
554 : {
555 0 : if (!mScrollFrame)
556 0 : return NS_ERROR_NOT_INITIALIZED;
557 :
558 : mScrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
559 : nsIScrollableFrame::PAGES,
560 0 : nsIScrollableFrame::SMOOTH);
561 0 : return NS_OK;
562 : }
563 :
564 : NS_IMETHODIMP
565 0 : nsTextInputSelectionImpl::ScrollLine(bool aForward)
566 : {
567 0 : if (!mScrollFrame)
568 0 : return NS_ERROR_NOT_INITIALIZED;
569 :
570 : mScrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
571 : nsIScrollableFrame::LINES,
572 0 : nsIScrollableFrame::SMOOTH);
573 0 : return NS_OK;
574 : }
575 :
576 : NS_IMETHODIMP
577 0 : nsTextInputSelectionImpl::ScrollCharacter(bool aRight)
578 : {
579 0 : if (!mScrollFrame)
580 0 : return NS_ERROR_NOT_INITIALIZED;
581 :
582 : mScrollFrame->ScrollBy(nsIntPoint(aRight ? 1 : -1, 0),
583 : nsIScrollableFrame::LINES,
584 0 : nsIScrollableFrame::SMOOTH);
585 0 : return NS_OK;
586 : }
587 :
588 : NS_IMETHODIMP
589 0 : nsTextInputSelectionImpl::SelectAll()
590 : {
591 0 : if (mFrameSelection)
592 0 : return mFrameSelection->SelectAll();
593 0 : return NS_ERROR_NULL_POINTER;
594 : }
595 :
596 : NS_IMETHODIMP
597 0 : nsTextInputSelectionImpl::CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, bool *_retval)
598 : {
599 0 : if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
600 : nsresult result;
601 0 : nsCOMPtr<nsISelectionController> shell = do_QueryReferent(mPresShellWeak, &result);
602 0 : if (shell)
603 : {
604 0 : return shell->CheckVisibility(node,startOffset,EndOffset, _retval);
605 : }
606 0 : return NS_ERROR_FAILURE;
607 :
608 : }
609 :
610 : nsresult
611 0 : nsTextInputSelectionImpl::CheckVisibilityContent(nsIContent* aNode,
612 : PRInt16 aStartOffset,
613 : PRInt16 aEndOffset,
614 : bool* aRetval)
615 : {
616 0 : if (!mPresShellWeak) {
617 0 : return NS_ERROR_NOT_INITIALIZED;
618 : }
619 :
620 0 : nsCOMPtr<nsISelectionController> shell = do_QueryReferent(mPresShellWeak);
621 0 : NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
622 :
623 0 : return shell->CheckVisibilityContent(aNode, aStartOffset, aEndOffset, aRetval);
624 : }
625 :
626 : class nsTextInputListener : public nsISelectionListener,
627 : public nsIDOMEventListener,
628 : public nsIEditorObserver,
629 : public nsSupportsWeakReference
630 : {
631 : public:
632 : /** the default constructor
633 : */
634 : explicit nsTextInputListener(nsITextControlElement* aTxtCtrlElement);
635 : /** the default destructor. virtual due to the possibility of derivation.
636 : */
637 : virtual ~nsTextInputListener();
638 :
639 : /** SetEditor gives an address to the editor that will be accessed
640 : * @param aEditor the editor this listener calls for editing operations
641 : */
642 0 : void SetFrame(nsTextControlFrame *aFrame){mFrame = aFrame;}
643 :
644 0 : void SettingValue(bool aValue) { mSettingValue = aValue; }
645 :
646 : NS_DECL_ISUPPORTS
647 :
648 : NS_DECL_NSISELECTIONLISTENER
649 :
650 : NS_DECL_NSIDOMEVENTLISTENER
651 :
652 : NS_DECL_NSIEDITOROBSERVER
653 :
654 : protected:
655 :
656 : nsresult UpdateTextInputCommands(const nsAString& commandsToUpdate);
657 :
658 : NS_HIDDEN_(nsINativeKeyBindings*) GetKeyBindings();
659 :
660 : protected:
661 :
662 : nsIFrame* mFrame;
663 :
664 : nsITextControlElement* const mTxtCtrlElement;
665 :
666 : bool mSelectionWasCollapsed;
667 : /**
668 : * Whether we had undo items or not the last time we got EditAction()
669 : * notification (when this state changes we update undo and redo menus)
670 : */
671 : bool mHadUndoItems;
672 : /**
673 : * Whether we had redo items or not the last time we got EditAction()
674 : * notification (when this state changes we update undo and redo menus)
675 : */
676 : bool mHadRedoItems;
677 : /**
678 : * Whether we're in the process of a SetValue call, and should therefore
679 : * refrain from calling OnValueChanged.
680 : */
681 : bool mSettingValue;
682 : };
683 :
684 :
685 : /*
686 : * nsTextInputListener implementation
687 : */
688 :
689 0 : nsTextInputListener::nsTextInputListener(nsITextControlElement* aTxtCtrlElement)
690 : : mFrame(nsnull)
691 : , mTxtCtrlElement(aTxtCtrlElement)
692 : , mSelectionWasCollapsed(true)
693 : , mHadUndoItems(false)
694 : , mHadRedoItems(false)
695 0 : , mSettingValue(false)
696 : {
697 0 : }
698 :
699 0 : nsTextInputListener::~nsTextInputListener()
700 : {
701 0 : }
702 :
703 0 : NS_IMPL_ISUPPORTS4(nsTextInputListener,
704 : nsISelectionListener,
705 : nsIEditorObserver,
706 : nsISupportsWeakReference,
707 : nsIDOMEventListener)
708 :
709 : // BEGIN nsIDOMSelectionListener
710 :
711 : NS_IMETHODIMP
712 0 : nsTextInputListener::NotifySelectionChanged(nsIDOMDocument* aDoc, nsISelection* aSel, PRInt16 aReason)
713 : {
714 : bool collapsed;
715 0 : nsWeakFrame weakFrame = mFrame;
716 :
717 0 : if (!aDoc || !aSel || NS_FAILED(aSel->GetIsCollapsed(&collapsed)))
718 0 : return NS_OK;
719 :
720 : // Fire the select event
721 : // The specs don't exactly say when we should fire the select event.
722 : // IE: Whenever you add/remove a character to/from the selection. Also
723 : // each time for select all. Also if you get to the end of the text
724 : // field you will get new event for each keypress or a continuous
725 : // stream of events if you use the mouse. IE will fire select event
726 : // when the selection collapses to nothing if you are holding down
727 : // the shift or mouse button.
728 : // Mozilla: If we have non-empty selection we will fire a new event for each
729 : // keypress (or mouseup) if the selection changed. Mozilla will also
730 : // create the event each time select all is called, even if everything
731 : // was previously selected, becase technically select all will first collapse
732 : // and then extend. Mozilla will never create an event if the selection
733 : // collapses to nothing.
734 0 : if (!collapsed && (aReason & (nsISelectionListener::MOUSEUP_REASON |
735 : nsISelectionListener::KEYPRESS_REASON |
736 : nsISelectionListener::SELECTALL_REASON)))
737 : {
738 0 : nsIContent* content = mFrame->GetContent();
739 0 : if (content)
740 : {
741 0 : nsCOMPtr<nsIDocument> doc = content->GetDocument();
742 0 : if (doc)
743 : {
744 0 : nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
745 0 : if (presShell)
746 : {
747 0 : nsEventStatus status = nsEventStatus_eIgnore;
748 0 : nsEvent event(true, NS_FORM_SELECTED);
749 :
750 0 : presShell->HandleEventWithTarget(&event, mFrame, content, &status);
751 : }
752 : }
753 : }
754 : }
755 :
756 : // if the collapsed state did not change, don't fire notifications
757 0 : if (collapsed == mSelectionWasCollapsed)
758 0 : return NS_OK;
759 :
760 0 : mSelectionWasCollapsed = collapsed;
761 :
762 0 : if (!weakFrame.IsAlive() || !nsContentUtils::IsFocusedContent(mFrame->GetContent()))
763 0 : return NS_OK;
764 :
765 0 : return UpdateTextInputCommands(NS_LITERAL_STRING("select"));
766 : }
767 :
768 : // END nsIDOMSelectionListener
769 :
770 : static void
771 0 : DoCommandCallback(const char *aCommand, void *aData)
772 : {
773 0 : nsTextControlFrame *frame = static_cast<nsTextControlFrame*>(aData);
774 0 : nsIContent *content = frame->GetContent();
775 :
776 0 : nsCOMPtr<nsIControllers> controllers;
777 0 : nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(content);
778 0 : if (input) {
779 0 : input->GetControllers(getter_AddRefs(controllers));
780 : } else {
781 : nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea =
782 0 : do_QueryInterface(content);
783 :
784 0 : if (textArea) {
785 0 : textArea->GetControllers(getter_AddRefs(controllers));
786 : }
787 : }
788 :
789 0 : if (!controllers) {
790 0 : NS_WARNING("Could not get controllers");
791 : return;
792 : }
793 :
794 0 : nsCOMPtr<nsIController> controller;
795 0 : controllers->GetControllerForCommand(aCommand, getter_AddRefs(controller));
796 0 : if (controller) {
797 0 : controller->DoCommand(aCommand);
798 : }
799 : }
800 :
801 : NS_IMETHODIMP
802 0 : nsTextInputListener::HandleEvent(nsIDOMEvent* aEvent)
803 : {
804 0 : nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aEvent));
805 0 : NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
806 :
807 0 : nsAutoString eventType;
808 0 : aEvent->GetType(eventType);
809 :
810 : nsNativeKeyEvent nativeEvent;
811 0 : nsINativeKeyBindings *bindings = GetKeyBindings();
812 0 : if (bindings &&
813 0 : nsContentUtils::DOMEventToNativeKeyEvent(keyEvent, &nativeEvent, false)) {
814 :
815 0 : bool handled = false;
816 0 : if (eventType.EqualsLiteral("keydown")) {
817 0 : handled = bindings->KeyDown(nativeEvent, DoCommandCallback, mFrame);
818 : }
819 0 : else if (eventType.EqualsLiteral("keyup")) {
820 0 : handled = bindings->KeyUp(nativeEvent, DoCommandCallback, mFrame);
821 : }
822 0 : else if (eventType.EqualsLiteral("keypress")) {
823 0 : handled = bindings->KeyPress(nativeEvent, DoCommandCallback, mFrame);
824 : }
825 : else {
826 0 : NS_ABORT();
827 : }
828 0 : if (handled) {
829 0 : aEvent->PreventDefault();
830 : }
831 : }
832 :
833 0 : return NS_OK;
834 : }
835 :
836 : // BEGIN nsIEditorObserver
837 :
838 : NS_IMETHODIMP
839 0 : nsTextInputListener::EditAction()
840 : {
841 0 : nsWeakFrame weakFrame = mFrame;
842 :
843 0 : nsITextControlFrame* frameBase = do_QueryFrame(mFrame);
844 0 : nsTextControlFrame* frame = static_cast<nsTextControlFrame*> (frameBase);
845 0 : NS_ASSERTION(frame, "Where is our frame?");
846 : //
847 : // Update the undo / redo menus
848 : //
849 0 : nsCOMPtr<nsIEditor> editor;
850 0 : frame->GetEditor(getter_AddRefs(editor));
851 :
852 : // Get the number of undo / redo items
853 0 : PRInt32 numUndoItems = 0;
854 0 : PRInt32 numRedoItems = 0;
855 0 : editor->GetNumberOfUndoItems(&numUndoItems);
856 0 : editor->GetNumberOfRedoItems(&numRedoItems);
857 0 : if ((numUndoItems && !mHadUndoItems) || (!numUndoItems && mHadUndoItems) ||
858 0 : (numRedoItems && !mHadRedoItems) || (!numRedoItems && mHadRedoItems)) {
859 : // Modify the menu if undo or redo items are different
860 0 : UpdateTextInputCommands(NS_LITERAL_STRING("undo"));
861 :
862 0 : mHadUndoItems = numUndoItems != 0;
863 0 : mHadRedoItems = numRedoItems != 0;
864 : }
865 :
866 0 : if (!weakFrame.IsAlive()) {
867 0 : return NS_OK;
868 : }
869 :
870 : // Make sure we know we were changed (do NOT set this to false if there are
871 : // no undo items; JS could change the value and we'd still need to save it)
872 0 : frame->SetValueChanged(true);
873 :
874 0 : if (!mSettingValue) {
875 0 : mTxtCtrlElement->OnValueChanged(true);
876 : }
877 :
878 : // Fire input event
879 0 : bool trusted = false;
880 0 : editor->GetLastKeypressEventTrusted(&trusted);
881 0 : frame->FireOnInput(trusted);
882 :
883 : // mFrame may be dead after this, but we don't need to check for it, because
884 : // we are not uisng it in this function any more.
885 :
886 0 : return NS_OK;
887 : }
888 :
889 : // END nsIEditorObserver
890 :
891 :
892 : nsresult
893 0 : nsTextInputListener::UpdateTextInputCommands(const nsAString& commandsToUpdate)
894 : {
895 0 : nsIContent* content = mFrame->GetContent();
896 0 : NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
897 :
898 0 : nsCOMPtr<nsIDocument> doc = content->GetDocument();
899 0 : NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
900 :
901 0 : nsPIDOMWindow *domWindow = doc->GetWindow();
902 0 : NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
903 :
904 0 : return domWindow->UpdateCommands(commandsToUpdate);
905 : }
906 :
907 : nsINativeKeyBindings*
908 0 : nsTextInputListener::GetKeyBindings()
909 : {
910 0 : if (mTxtCtrlElement->IsTextArea()) {
911 : static bool sNoTextAreaBindings = false;
912 :
913 0 : if (!sNativeTextAreaBindings && !sNoTextAreaBindings) {
914 : CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "textarea",
915 0 : &sNativeTextAreaBindings);
916 :
917 0 : if (!sNativeTextAreaBindings) {
918 0 : sNoTextAreaBindings = true;
919 : }
920 : }
921 :
922 0 : return sNativeTextAreaBindings;
923 : }
924 :
925 : static bool sNoInputBindings = false;
926 0 : if (!sNativeInputBindings && !sNoInputBindings) {
927 : CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "input",
928 0 : &sNativeInputBindings);
929 :
930 0 : if (!sNativeInputBindings) {
931 0 : sNoInputBindings = true;
932 : }
933 : }
934 :
935 0 : return sNativeInputBindings;
936 : }
937 :
938 : // END nsTextInputListener
939 :
940 : // nsTextEditorState
941 :
942 1 : nsTextEditorState::nsTextEditorState(nsITextControlElement* aOwningElement)
943 : : mTextCtrlElement(aOwningElement),
944 : mRestoringSelection(nsnull),
945 : mBoundFrame(nsnull),
946 : mTextListener(nsnull),
947 : mEverInited(false),
948 : mEditorInitialized(false),
949 : mInitializing(false),
950 : mValueTransferInProgress(false),
951 : mSelectionCached(true),
952 1 : mSelectionRestoreEagerInit(false)
953 : {
954 1 : MOZ_COUNT_CTOR(nsTextEditorState);
955 1 : }
956 :
957 2 : nsTextEditorState::~nsTextEditorState()
958 : {
959 1 : MOZ_COUNT_DTOR(nsTextEditorState);
960 1 : Clear();
961 1 : }
962 :
963 : void
964 2 : nsTextEditorState::Clear()
965 : {
966 2 : if (mBoundFrame) {
967 : // Oops, we still have a frame!
968 : // This should happen when the type of a text input control is being changed
969 : // to something which is not a text control. In this case, we should pretend
970 : // that a frame is being destroyed, and clean up after ourselves properly.
971 0 : UnbindFromFrame(mBoundFrame);
972 0 : mEditor = nsnull;
973 : } else {
974 : // If we have a bound frame around, UnbindFromFrame will call DestroyEditor
975 : // for us.
976 2 : DestroyEditor();
977 : }
978 2 : NS_IF_RELEASE(mTextListener);
979 2 : }
980 :
981 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsTextEditorState)
982 1 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsTextEditorState, AddRef)
983 1 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsTextEditorState, Release)
984 1 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(nsTextEditorState)
985 1 : tmp->Clear();
986 1 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSelCon)
987 1 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEditor)
988 1 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRootNode)
989 1 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPlaceholderDiv)
990 1 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
991 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsTextEditorState)
992 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mSelCon, nsISelectionController)
993 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEditor)
994 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRootNode)
995 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPlaceholderDiv)
996 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
997 :
998 : nsFrameSelection*
999 0 : nsTextEditorState::GetConstFrameSelection() {
1000 0 : if (mSelCon)
1001 0 : return mSelCon->GetConstFrameSelection();
1002 0 : return nsnull;
1003 : }
1004 :
1005 : nsIEditor*
1006 0 : nsTextEditorState::GetEditor()
1007 : {
1008 0 : if (!mEditor) {
1009 0 : nsresult rv = PrepareEditor();
1010 0 : NS_ENSURE_SUCCESS(rv, nsnull);
1011 : }
1012 0 : return mEditor;
1013 : }
1014 :
1015 : nsISelectionController*
1016 0 : nsTextEditorState::GetSelectionController() const
1017 : {
1018 0 : return mSelCon;
1019 : }
1020 :
1021 : // Helper class, used below in BindToFrame().
1022 0 : class PrepareEditorEvent : public nsRunnable {
1023 : public:
1024 0 : PrepareEditorEvent(nsTextEditorState &aState,
1025 : nsIContent *aOwnerContent,
1026 : const nsAString &aCurrentValue)
1027 : : mState(aState)
1028 : , mOwnerContent(aOwnerContent)
1029 0 : , mCurrentValue(aCurrentValue)
1030 : {
1031 0 : mState.mValueTransferInProgress = true;
1032 0 : }
1033 :
1034 0 : NS_IMETHOD Run() {
1035 : // Transfer the saved value to the editor if we have one
1036 0 : const nsAString *value = nsnull;
1037 0 : if (!mCurrentValue.IsEmpty()) {
1038 0 : value = &mCurrentValue;
1039 : }
1040 :
1041 0 : mState.PrepareEditor(value);
1042 :
1043 0 : mState.mValueTransferInProgress = false;
1044 :
1045 0 : return NS_OK;
1046 : }
1047 :
1048 : private:
1049 : nsTextEditorState &mState;
1050 : nsCOMPtr<nsIContent> mOwnerContent; // strong reference
1051 : nsAutoString mCurrentValue;
1052 : };
1053 :
1054 : nsresult
1055 0 : nsTextEditorState::BindToFrame(nsTextControlFrame* aFrame)
1056 : {
1057 0 : NS_ASSERTION(aFrame, "The frame to bind to should be valid");
1058 0 : NS_ENSURE_ARG_POINTER(aFrame);
1059 :
1060 0 : NS_ASSERTION(!mBoundFrame, "Cannot bind twice, need to unbind first");
1061 0 : NS_ENSURE_TRUE(!mBoundFrame, NS_ERROR_FAILURE);
1062 :
1063 : // If we'll need to transfer our current value to the editor, save it before
1064 : // binding to the frame.
1065 0 : nsAutoString currentValue;
1066 0 : if (mEditor) {
1067 0 : GetValue(currentValue, true);
1068 : }
1069 :
1070 0 : mBoundFrame = aFrame;
1071 :
1072 0 : nsIContent *rootNode = GetRootNode();
1073 :
1074 0 : nsresult rv = InitializeRootNode();
1075 0 : NS_ENSURE_SUCCESS(rv, rv);
1076 :
1077 0 : nsIPresShell *shell = mBoundFrame->PresContext()->GetPresShell();
1078 0 : NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
1079 :
1080 : // Create selection
1081 0 : nsRefPtr<nsFrameSelection> frameSel = new nsFrameSelection();
1082 :
1083 : // Create a SelectionController
1084 0 : mSelCon = new nsTextInputSelectionImpl(frameSel, shell, rootNode);
1085 0 : NS_ENSURE_TRUE(mSelCon, NS_ERROR_OUT_OF_MEMORY);
1086 0 : mTextListener = new nsTextInputListener(mTextCtrlElement);
1087 0 : NS_ENSURE_TRUE(mTextListener, NS_ERROR_OUT_OF_MEMORY);
1088 0 : NS_ADDREF(mTextListener);
1089 :
1090 0 : mTextListener->SetFrame(mBoundFrame);
1091 0 : mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
1092 :
1093 : // Get the caret and make it a selection listener.
1094 0 : nsRefPtr<nsISelection> domSelection;
1095 0 : if (NS_SUCCEEDED(mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
1096 : getter_AddRefs(domSelection))) &&
1097 0 : domSelection) {
1098 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(domSelection));
1099 0 : nsRefPtr<nsCaret> caret = shell->GetCaret();
1100 0 : nsCOMPtr<nsISelectionListener> listener;
1101 0 : if (caret) {
1102 0 : listener = do_QueryInterface(caret);
1103 0 : if (listener) {
1104 0 : selPriv->AddSelectionListener(listener);
1105 : }
1106 : }
1107 :
1108 0 : selPriv->AddSelectionListener(static_cast<nsISelectionListener*>
1109 0 : (mTextListener));
1110 : }
1111 :
1112 : // If an editor exists from before, prepare it for usage
1113 0 : if (mEditor) {
1114 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
1115 0 : NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
1116 :
1117 : // Set the correct direction on the newly created root node
1118 : PRUint32 flags;
1119 0 : nsresult rv = mEditor->GetFlags(&flags);
1120 0 : NS_ENSURE_SUCCESS(rv, rv);
1121 0 : if (flags & nsIPlaintextEditor::eEditorRightToLeft) {
1122 0 : rootNode->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("rtl"), false);
1123 0 : } else if (flags & nsIPlaintextEditor::eEditorLeftToRight) {
1124 0 : rootNode->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("ltr"), false);
1125 : } else {
1126 : // otherwise, inherit the content node's direction
1127 : }
1128 :
1129 0 : if (!nsContentUtils::AddScriptRunner(
1130 0 : new PrepareEditorEvent(*this, content, currentValue)))
1131 0 : return NS_ERROR_OUT_OF_MEMORY;
1132 : }
1133 :
1134 0 : return NS_OK;
1135 : }
1136 :
1137 : nsresult
1138 0 : nsTextEditorState::PrepareEditor(const nsAString *aValue)
1139 : {
1140 0 : if (!mBoundFrame) {
1141 : // Cannot create an editor without a bound frame.
1142 : // Don't return a failure code, because js callers can't handle that.
1143 0 : return NS_OK;
1144 : }
1145 :
1146 0 : if (mEditorInitialized) {
1147 : // Do not initialize the editor multiple times.
1148 0 : return NS_OK;
1149 : }
1150 :
1151 : // Don't attempt to initialize recursively!
1152 0 : InitializationGuard guard(*this);
1153 0 : if (guard.IsInitializingRecursively()) {
1154 0 : return NS_ERROR_NOT_INITIALIZED;
1155 : }
1156 :
1157 : // Note that we don't check mEditor here, because we might already have one
1158 : // around, in which case we don't create a new one, and we'll just tie the
1159 : // required machinery to it.
1160 :
1161 0 : nsPresContext *presContext = mBoundFrame->PresContext();
1162 0 : nsIPresShell *shell = presContext->GetPresShell();
1163 :
1164 : // Setup the editor flags
1165 0 : PRUint32 editorFlags = 0;
1166 0 : if (IsPlainTextControl())
1167 0 : editorFlags |= nsIPlaintextEditor::eEditorPlaintextMask;
1168 0 : if (IsSingleLineTextControl())
1169 0 : editorFlags |= nsIPlaintextEditor::eEditorSingleLineMask;
1170 0 : if (IsPasswordTextControl())
1171 0 : editorFlags |= nsIPlaintextEditor::eEditorPasswordMask;
1172 :
1173 : // All nsTextControlFrames are widgets
1174 0 : editorFlags |= nsIPlaintextEditor::eEditorWidgetMask;
1175 :
1176 : // Spell check is diabled at creation time. It is enabled once
1177 : // the editor comes into focus.
1178 0 : editorFlags |= nsIPlaintextEditor::eEditorSkipSpellCheck;
1179 :
1180 0 : bool shouldInitializeEditor = false;
1181 0 : nsCOMPtr<nsIEditor> newEditor; // the editor that we might create
1182 0 : nsresult rv = NS_OK;
1183 0 : if (!mEditor) {
1184 0 : shouldInitializeEditor = true;
1185 :
1186 : // Create an editor
1187 0 : newEditor = do_CreateInstance(kTextEditorCID, &rv);
1188 0 : NS_ENSURE_SUCCESS(rv, rv);
1189 :
1190 : // Make sure we clear out the non-breaking space before we initialize the editor
1191 0 : rv = mBoundFrame->UpdateValueDisplay(false, true);
1192 0 : NS_ENSURE_SUCCESS(rv, rv);
1193 : } else {
1194 0 : if (aValue || !mEditorInitialized) {
1195 : // Set the correct value in the root node
1196 0 : rv = mBoundFrame->UpdateValueDisplay(true, !mEditorInitialized, aValue);
1197 0 : NS_ENSURE_SUCCESS(rv, rv);
1198 : }
1199 :
1200 0 : newEditor = mEditor; // just pretend that we have a new editor!
1201 : }
1202 :
1203 0 : if (!mEditorInitialized) {
1204 : // Now initialize the editor.
1205 : //
1206 : // NOTE: Conversion of '\n' to <BR> happens inside the
1207 : // editor's Init() call.
1208 :
1209 : // Get the DOM document
1210 0 : nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(shell->GetDocument());
1211 0 : if (!domdoc)
1212 0 : return NS_ERROR_FAILURE;
1213 :
1214 : // What follows is a bit of a hack. The editor uses the public DOM APIs
1215 : // for its content manipulations, and it causes it to fail some security
1216 : // checks deep inside when initializing. So we push a null JSContext
1217 : // on the JS stack here to make it clear that we're native code.
1218 : // Note that any script that's directly trying to access our value
1219 : // has to be going through some scriptable object to do that and that
1220 : // already does the relevant security checks.
1221 0 : nsCxPusher pusher;
1222 0 : pusher.PushNull();
1223 :
1224 0 : rv = newEditor->Init(domdoc, GetRootNode(), mSelCon, editorFlags);
1225 0 : NS_ENSURE_SUCCESS(rv, rv);
1226 : }
1227 :
1228 : // Initialize the controller for the editor
1229 :
1230 0 : if (!SuppressEventHandlers(presContext)) {
1231 0 : nsCOMPtr<nsIControllers> controllers;
1232 : nsCOMPtr<nsIDOMHTMLInputElement> inputElement =
1233 0 : do_QueryInterface(mTextCtrlElement);
1234 0 : if (inputElement) {
1235 0 : rv = inputElement->GetControllers(getter_AddRefs(controllers));
1236 : } else {
1237 : nsCOMPtr<nsIDOMHTMLTextAreaElement> textAreaElement =
1238 0 : do_QueryInterface(mTextCtrlElement);
1239 :
1240 0 : if (!textAreaElement)
1241 0 : return NS_ERROR_FAILURE;
1242 :
1243 0 : rv = textAreaElement->GetControllers(getter_AddRefs(controllers));
1244 : }
1245 :
1246 0 : NS_ENSURE_SUCCESS(rv, rv);
1247 :
1248 0 : if (controllers) {
1249 : PRUint32 numControllers;
1250 0 : bool found = false;
1251 0 : rv = controllers->GetControllerCount(&numControllers);
1252 0 : for (PRUint32 i = 0; i < numControllers; i ++) {
1253 0 : nsCOMPtr<nsIController> controller;
1254 0 : rv = controllers->GetControllerAt(i, getter_AddRefs(controller));
1255 0 : if (NS_SUCCEEDED(rv) && controller) {
1256 : nsCOMPtr<nsIControllerContext> editController =
1257 0 : do_QueryInterface(controller);
1258 0 : if (editController) {
1259 0 : editController->SetCommandContext(newEditor);
1260 0 : found = true;
1261 : }
1262 : }
1263 : }
1264 0 : if (!found)
1265 0 : rv = NS_ERROR_FAILURE;
1266 : }
1267 : }
1268 :
1269 0 : if (shouldInitializeEditor) {
1270 : // Initialize the plaintext editor
1271 0 : nsCOMPtr<nsIPlaintextEditor> textEditor(do_QueryInterface(newEditor));
1272 0 : if (textEditor) {
1273 : // Set up wrapping
1274 0 : textEditor->SetWrapColumn(GetWrapCols());
1275 :
1276 : // Set max text field length
1277 : PRInt32 maxLength;
1278 0 : if (GetMaxLength(&maxLength)) {
1279 0 : textEditor->SetMaxTextLength(maxLength);
1280 : }
1281 : }
1282 : }
1283 :
1284 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
1285 0 : if (content) {
1286 0 : rv = newEditor->GetFlags(&editorFlags);
1287 0 : NS_ENSURE_SUCCESS(rv, rv);
1288 :
1289 : // Check if the readonly attribute is set.
1290 0 : if (content->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly))
1291 0 : editorFlags |= nsIPlaintextEditor::eEditorReadonlyMask;
1292 :
1293 : // Check if the disabled attribute is set.
1294 : // TODO: call IsDisabled() here!
1295 0 : if (content->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled))
1296 0 : editorFlags |= nsIPlaintextEditor::eEditorDisabledMask;
1297 :
1298 : // Disable the selection if necessary.
1299 0 : if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
1300 0 : mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
1301 :
1302 0 : newEditor->SetFlags(editorFlags);
1303 : }
1304 :
1305 : // Get the current value of the textfield from the content.
1306 : // Note that if we've created a new editor, mEditor is null at this stage,
1307 : // so we will get the real value from the content.
1308 0 : nsAutoString defaultValue;
1309 0 : if (aValue) {
1310 0 : defaultValue = *aValue;
1311 : } else {
1312 0 : GetValue(defaultValue, true);
1313 : }
1314 :
1315 0 : if (shouldInitializeEditor) {
1316 : // Hold on to the newly created editor
1317 0 : mEditor = newEditor;
1318 : }
1319 :
1320 : // If we have a default value, insert it under the div we created
1321 : // above, but be sure to use the editor so that '*' characters get
1322 : // displayed for password fields, etc. SetValue() will call the
1323 : // editor for us.
1324 :
1325 0 : if (!defaultValue.IsEmpty()) {
1326 0 : rv = newEditor->SetFlags(editorFlags);
1327 0 : NS_ENSURE_SUCCESS(rv, rv);
1328 :
1329 : // Now call SetValue() which will make the necessary editor calls to set
1330 : // the default value. Make sure to turn off undo before setting the default
1331 : // value, and turn it back on afterwards. This will make sure we can't undo
1332 : // past the default value.
1333 :
1334 0 : rv = newEditor->EnableUndo(false);
1335 0 : NS_ENSURE_SUCCESS(rv, rv);
1336 :
1337 0 : SetValue(defaultValue, false);
1338 :
1339 0 : rv = newEditor->EnableUndo(true);
1340 0 : NS_ASSERTION(NS_SUCCEEDED(rv),"Transaction Manager must have failed");
1341 :
1342 : // Now restore the original editor flags.
1343 0 : rv = newEditor->SetFlags(editorFlags);
1344 0 : NS_ENSURE_SUCCESS(rv, rv);
1345 : }
1346 :
1347 0 : nsCOMPtr<nsITransactionManager> transMgr;
1348 0 : newEditor->GetTransactionManager(getter_AddRefs(transMgr));
1349 0 : NS_ENSURE_TRUE(transMgr, NS_ERROR_FAILURE);
1350 :
1351 0 : transMgr->SetMaxTransactionCount(nsITextControlElement::DEFAULT_UNDO_CAP);
1352 :
1353 0 : if (IsPasswordTextControl()) {
1354 : // Disable undo for password textfields. Note that we want to do this at
1355 : // the very end of InitEditor, so the calls to EnableUndo when setting the
1356 : // default value don't screw us up.
1357 : // Since changing the control type does a reframe, we don't have to worry
1358 : // about dynamic type changes here.
1359 0 : newEditor->EnableUndo(false);
1360 : }
1361 :
1362 0 : if (!mEditorInitialized) {
1363 0 : newEditor->PostCreate();
1364 0 : mEverInited = true;
1365 0 : mEditorInitialized = true;
1366 : }
1367 :
1368 0 : if (mTextListener)
1369 0 : newEditor->AddEditorObserver(mTextListener);
1370 :
1371 : // Restore our selection after being bound to a new frame
1372 0 : if (mSelectionCached) {
1373 0 : if (mRestoringSelection) // paranoia
1374 0 : mRestoringSelection->Revoke();
1375 0 : mRestoringSelection = new RestoreSelectionState(this, mBoundFrame);
1376 0 : if (mRestoringSelection) {
1377 0 : nsContentUtils::AddScriptRunner(mRestoringSelection);
1378 : }
1379 : }
1380 :
1381 : // The selection cache is no longer going to be valid
1382 0 : mSelectionCached = false;
1383 :
1384 0 : return rv;
1385 : }
1386 :
1387 : void
1388 2 : nsTextEditorState::DestroyEditor()
1389 : {
1390 : // notify the editor that we are going away
1391 2 : if (mEditorInitialized) {
1392 0 : if (mTextListener)
1393 0 : mEditor->RemoveEditorObserver(mTextListener);
1394 :
1395 0 : mEditor->PreDestroy(true);
1396 0 : mEditorInitialized = false;
1397 : }
1398 2 : }
1399 :
1400 : void
1401 0 : nsTextEditorState::UnbindFromFrame(nsTextControlFrame* aFrame)
1402 : {
1403 0 : NS_ENSURE_TRUE(mBoundFrame, );
1404 :
1405 : // If it was, however, it should be unbounded from the same frame.
1406 0 : NS_ASSERTION(!aFrame || aFrame == mBoundFrame, "Unbinding from the wrong frame");
1407 0 : NS_ENSURE_TRUE(!aFrame || aFrame == mBoundFrame, );
1408 :
1409 : // We need to start storing the value outside of the editor if we're not
1410 : // going to use it anymore, so retrieve it for now.
1411 0 : nsAutoString value;
1412 0 : GetValue(value, true);
1413 :
1414 0 : if (mRestoringSelection) {
1415 0 : mRestoringSelection->Revoke();
1416 0 : mRestoringSelection = nsnull;
1417 : }
1418 :
1419 : // Save our selection state if needed.
1420 : // Note that nsTextControlFrame::GetSelectionRange attempts to initialize the
1421 : // editor before grabbing the range, and because this is not an acceptable
1422 : // side effect for unbinding from a text control frame, we need to call
1423 : // GetSelectionRange before calling DestroyEditor, and only if
1424 : // mEditorInitialized indicates that we actually have an editor available.
1425 0 : if (mEditorInitialized) {
1426 : mBoundFrame->GetSelectionRange(&mSelectionProperties.mStart,
1427 : &mSelectionProperties.mEnd,
1428 0 : &mSelectionProperties.mDirection);
1429 0 : mSelectionCached = true;
1430 : }
1431 :
1432 : // Destroy our editor
1433 0 : DestroyEditor();
1434 :
1435 : // Clean up the controller
1436 0 : if (!SuppressEventHandlers(mBoundFrame->PresContext()))
1437 : {
1438 0 : nsCOMPtr<nsIControllers> controllers;
1439 : nsCOMPtr<nsIDOMHTMLInputElement> inputElement =
1440 0 : do_QueryInterface(mTextCtrlElement);
1441 0 : if (inputElement)
1442 0 : inputElement->GetControllers(getter_AddRefs(controllers));
1443 : else
1444 : {
1445 : nsCOMPtr<nsIDOMHTMLTextAreaElement> textAreaElement =
1446 0 : do_QueryInterface(mTextCtrlElement);
1447 0 : if (textAreaElement) {
1448 0 : textAreaElement->GetControllers(getter_AddRefs(controllers));
1449 : }
1450 : }
1451 :
1452 0 : if (controllers)
1453 : {
1454 : PRUint32 numControllers;
1455 0 : nsresult rv = controllers->GetControllerCount(&numControllers);
1456 0 : NS_ASSERTION((NS_SUCCEEDED(rv)), "bad result in gfx text control destructor");
1457 0 : for (PRUint32 i = 0; i < numControllers; i ++)
1458 : {
1459 0 : nsCOMPtr<nsIController> controller;
1460 0 : rv = controllers->GetControllerAt(i, getter_AddRefs(controller));
1461 0 : if (NS_SUCCEEDED(rv) && controller)
1462 : {
1463 0 : nsCOMPtr<nsIControllerContext> editController = do_QueryInterface(controller);
1464 0 : if (editController)
1465 : {
1466 0 : editController->SetCommandContext(nsnull);
1467 : }
1468 : }
1469 : }
1470 : }
1471 : }
1472 :
1473 0 : if (mSelCon) {
1474 0 : if (mTextListener) {
1475 0 : nsRefPtr<nsISelection> domSelection;
1476 0 : if (NS_SUCCEEDED(mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
1477 : getter_AddRefs(domSelection))) &&
1478 0 : domSelection) {
1479 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(domSelection));
1480 :
1481 0 : selPriv->RemoveSelectionListener(static_cast<nsISelectionListener*>
1482 0 : (mTextListener));
1483 : }
1484 : }
1485 :
1486 0 : mSelCon->SetScrollableFrame(nsnull);
1487 0 : mSelCon = nsnull;
1488 : }
1489 :
1490 0 : if (mTextListener)
1491 : {
1492 0 : mTextListener->SetFrame(nsnull);
1493 :
1494 0 : nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mTextCtrlElement);
1495 : nsEventListenerManager* manager =
1496 0 : target->GetListenerManager(false);
1497 0 : if (manager) {
1498 : manager->RemoveEventListenerByType(mTextListener,
1499 0 : NS_LITERAL_STRING("keydown"),
1500 : NS_EVENT_FLAG_BUBBLE |
1501 0 : NS_EVENT_FLAG_SYSTEM_EVENT);
1502 : manager->RemoveEventListenerByType(mTextListener,
1503 0 : NS_LITERAL_STRING("keypress"),
1504 : NS_EVENT_FLAG_BUBBLE |
1505 0 : NS_EVENT_FLAG_SYSTEM_EVENT);
1506 : manager->RemoveEventListenerByType(mTextListener,
1507 0 : NS_LITERAL_STRING("keyup"),
1508 : NS_EVENT_FLAG_BUBBLE |
1509 0 : NS_EVENT_FLAG_SYSTEM_EVENT);
1510 : }
1511 :
1512 0 : NS_RELEASE(mTextListener);
1513 0 : mTextListener = nsnull;
1514 : }
1515 :
1516 0 : mBoundFrame = nsnull;
1517 :
1518 : // Now that we don't have a frame any more, store the value in the text buffer.
1519 : // The only case where we don't do this is if a value transfer is in progress.
1520 0 : if (!mValueTransferInProgress) {
1521 0 : SetValue(value, false);
1522 : }
1523 :
1524 0 : if (mRootNode && mMutationObserver) {
1525 0 : mRootNode->RemoveMutationObserver(mMutationObserver);
1526 0 : mMutationObserver = nsnull;
1527 : }
1528 :
1529 : // Unbind the anonymous content from the tree.
1530 : // We actually hold a reference to the content nodes so that
1531 : // they're not actually destroyed.
1532 0 : nsContentUtils::DestroyAnonymousContent(&mRootNode);
1533 0 : nsContentUtils::DestroyAnonymousContent(&mPlaceholderDiv);
1534 : }
1535 :
1536 : nsresult
1537 0 : nsTextEditorState::CreateRootNode()
1538 : {
1539 0 : NS_ENSURE_TRUE(!mRootNode, NS_ERROR_UNEXPECTED);
1540 0 : NS_ENSURE_ARG_POINTER(mBoundFrame);
1541 :
1542 0 : nsIPresShell *shell = mBoundFrame->PresContext()->GetPresShell();
1543 0 : NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
1544 :
1545 0 : nsIDocument *doc = shell->GetDocument();
1546 0 : NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1547 :
1548 : // Now create a DIV and add it to the anonymous content child list.
1549 0 : nsCOMPtr<nsINodeInfo> nodeInfo;
1550 : nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::div, nsnull,
1551 : kNameSpaceID_XHTML,
1552 0 : nsIDOMNode::ELEMENT_NODE);
1553 0 : NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
1554 :
1555 0 : nsresult rv = NS_NewHTMLElement(getter_AddRefs(mRootNode), nodeInfo.forget(),
1556 0 : NOT_FROM_PARSER);
1557 0 : NS_ENSURE_SUCCESS(rv, rv);
1558 :
1559 0 : if (!IsSingleLineTextControl()) {
1560 0 : mMutationObserver = new nsAnonDivObserver(this);
1561 0 : mRootNode->AddMutationObserver(mMutationObserver);
1562 : }
1563 :
1564 0 : return rv;
1565 : }
1566 :
1567 : nsresult
1568 0 : nsTextEditorState::InitializeRootNode()
1569 : {
1570 : // Set the necessary classes on the text control. We use class values
1571 : // instead of a 'style' attribute so that the style comes from a user-agent
1572 : // style sheet and is still applied even if author styles are disabled.
1573 0 : nsAutoString classValue;
1574 0 : classValue.AppendLiteral("anonymous-div");
1575 0 : PRInt32 wrapCols = GetWrapCols();
1576 0 : if (wrapCols >= 0) {
1577 0 : classValue.AppendLiteral(" wrap");
1578 : }
1579 0 : if (!IsSingleLineTextControl()) {
1580 : // We can't just inherit the overflow because setting visible overflow will
1581 : // crash when the number of lines exceeds the height of the textarea and
1582 : // setting -moz-hidden-unscrollable overflow (NS_STYLE_OVERFLOW_CLIP)
1583 : // doesn't paint the caret for some reason.
1584 0 : const nsStyleDisplay* disp = mBoundFrame->GetStyleDisplay();
1585 0 : if (disp->mOverflowX != NS_STYLE_OVERFLOW_VISIBLE &&
1586 : disp->mOverflowX != NS_STYLE_OVERFLOW_CLIP) {
1587 0 : classValue.AppendLiteral(" inherit-overflow");
1588 : }
1589 : }
1590 : nsresult rv = mRootNode->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
1591 0 : classValue, false);
1592 0 : NS_ENSURE_SUCCESS(rv, rv);
1593 :
1594 0 : return mBoundFrame->UpdateValueDisplay(false);
1595 : }
1596 :
1597 : nsresult
1598 0 : nsTextEditorState::CreatePlaceholderNode()
1599 : {
1600 : #ifdef DEBUG
1601 : {
1602 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
1603 0 : if (content) {
1604 0 : nsAutoString placeholderTxt;
1605 0 : content->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder,
1606 0 : placeholderTxt);
1607 0 : nsContentUtils::RemoveNewlines(placeholderTxt);
1608 0 : NS_ASSERTION(!placeholderTxt.IsEmpty(), "CreatePlaceholderNode() shouldn't \
1609 : be called if @placeholder is the empty string when trimmed from line breaks");
1610 : }
1611 : }
1612 : #endif // DEBUG
1613 :
1614 0 : NS_ENSURE_TRUE(!mPlaceholderDiv, NS_ERROR_UNEXPECTED);
1615 0 : NS_ENSURE_ARG_POINTER(mBoundFrame);
1616 :
1617 0 : nsIPresShell *shell = mBoundFrame->PresContext()->GetPresShell();
1618 0 : NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
1619 :
1620 0 : nsIDocument *doc = shell->GetDocument();
1621 0 : NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1622 :
1623 0 : nsNodeInfoManager* pNodeInfoManager = doc->NodeInfoManager();
1624 0 : NS_ENSURE_TRUE(pNodeInfoManager, NS_ERROR_OUT_OF_MEMORY);
1625 :
1626 : nsresult rv;
1627 0 : nsCOMPtr<nsIContent> placeholderText;
1628 :
1629 : // Create a DIV for the placeholder
1630 : // and add it to the anonymous content child list
1631 0 : nsCOMPtr<nsINodeInfo> nodeInfo;
1632 : nodeInfo = pNodeInfoManager->GetNodeInfo(nsGkAtoms::div, nsnull,
1633 : kNameSpaceID_XHTML,
1634 0 : nsIDOMNode::ELEMENT_NODE);
1635 0 : NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
1636 :
1637 0 : rv = NS_NewHTMLElement(getter_AddRefs(mPlaceholderDiv), nodeInfo.forget(),
1638 0 : NOT_FROM_PARSER);
1639 0 : NS_ENSURE_SUCCESS(rv, rv);
1640 :
1641 : // Create the text node for the placeholder text before doing anything else
1642 0 : rv = NS_NewTextNode(getter_AddRefs(placeholderText), pNodeInfoManager);
1643 0 : NS_ENSURE_SUCCESS(rv, rv);
1644 :
1645 0 : rv = mPlaceholderDiv->AppendChildTo(placeholderText, false);
1646 0 : NS_ENSURE_SUCCESS(rv, rv);
1647 :
1648 : // initialize the text
1649 0 : UpdatePlaceholderText(false);
1650 :
1651 0 : return NS_OK;
1652 : }
1653 :
1654 : bool
1655 0 : nsTextEditorState::GetMaxLength(PRInt32* aMaxLength)
1656 : {
1657 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
1658 0 : NS_ENSURE_TRUE(content, false);
1659 0 : nsGenericHTMLElement* element = nsGenericHTMLElement::FromContent(content);
1660 0 : NS_ENSURE_TRUE(element, false);
1661 :
1662 0 : const nsAttrValue* attr = element->GetParsedAttr(nsGkAtoms::maxlength);
1663 0 : if (attr && attr->Type() == nsAttrValue::eInteger) {
1664 0 : *aMaxLength = attr->GetIntegerValue();
1665 :
1666 0 : return true;
1667 : }
1668 :
1669 0 : return false;
1670 : }
1671 :
1672 : void
1673 1 : nsTextEditorState::GetValue(nsAString& aValue, bool aIgnoreWrap) const
1674 : {
1675 1 : if (mEditor && mBoundFrame && (mEditorInitialized || !IsSingleLineTextControl())) {
1676 0 : bool canCache = aIgnoreWrap && !IsSingleLineTextControl();
1677 0 : if (canCache && !mCachedValue.IsEmpty()) {
1678 0 : aValue = mCachedValue;
1679 0 : return;
1680 : }
1681 :
1682 0 : aValue.Truncate(); // initialize out param
1683 :
1684 : PRUint32 flags = (nsIDocumentEncoder::OutputLFLineBreak |
1685 : nsIDocumentEncoder::OutputPreformatted |
1686 0 : nsIDocumentEncoder::OutputPersistNBSP);
1687 :
1688 0 : if (IsPlainTextControl())
1689 : {
1690 0 : flags |= nsIDocumentEncoder::OutputBodyOnly;
1691 : }
1692 :
1693 0 : if (!aIgnoreWrap) {
1694 : nsITextControlElement::nsHTMLTextWrap wrapProp;
1695 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
1696 0 : if (content &&
1697 0 : nsITextControlElement::GetWrapPropertyEnum(content, wrapProp) &&
1698 : wrapProp == nsITextControlElement::eHTMLTextWrap_Hard) {
1699 0 : flags |= nsIDocumentEncoder::OutputWrap;
1700 : }
1701 : }
1702 :
1703 : // What follows is a bit of a hack. The problem is that we could be in
1704 : // this method because we're being destroyed for whatever reason while
1705 : // script is executing. If that happens, editor will run with the
1706 : // privileges of the executing script, which means it may not be able to
1707 : // access its own DOM nodes! Let's try to deal with that by pushing a null
1708 : // JSContext on the JSContext stack to make it clear that we're native
1709 : // code. Note that any script that's directly trying to access our value
1710 : // has to be going through some scriptable object to do that and that
1711 : // already does the relevant security checks.
1712 : // XXXbz if we could just get the textContent of our anonymous content (eg
1713 : // if plaintext editor didn't create <br> nodes all over), we wouldn't need
1714 : // this.
1715 : { /* Scope for context pusher */
1716 0 : nsCxPusher pusher;
1717 0 : pusher.PushNull();
1718 :
1719 0 : mEditor->OutputToString(NS_LITERAL_STRING("text/plain"), flags,
1720 0 : aValue);
1721 : }
1722 0 : if (canCache) {
1723 0 : mCachedValue = aValue;
1724 : } else {
1725 0 : mCachedValue.Truncate();
1726 : }
1727 : } else {
1728 1 : if (!mTextCtrlElement->ValueChanged() || !mValue) {
1729 1 : mTextCtrlElement->GetDefaultValueFromContent(aValue);
1730 : } else {
1731 0 : aValue = NS_ConvertUTF8toUTF16(*mValue);
1732 : }
1733 : }
1734 : }
1735 :
1736 : void
1737 1 : nsTextEditorState::SetValue(const nsAString& aValue, bool aUserInput)
1738 : {
1739 1 : if (mEditor && mBoundFrame) {
1740 : // The InsertText call below might flush pending notifications, which
1741 : // could lead into a scheduled PrepareEditor to be called. That will
1742 : // lead to crashes (or worse) because we'd be initializing the editor
1743 : // before InsertText returns. This script blocker makes sure that
1744 : // PrepareEditor cannot be called prematurely.
1745 0 : nsAutoScriptBlocker scriptBlocker;
1746 :
1747 0 : bool fireChangeEvent = mBoundFrame->GetFireChangeEventState();
1748 0 : if (aUserInput) {
1749 0 : mBoundFrame->SetFireChangeEventState(true);
1750 : }
1751 :
1752 : #ifdef DEBUG
1753 0 : if (IsSingleLineTextControl()) {
1754 0 : NS_ASSERTION(mEditorInitialized || mInitializing,
1755 : "We should never try to use the editor if we're not initialized unless we're being initialized");
1756 : }
1757 : #endif
1758 :
1759 0 : nsAutoString currentValue;
1760 0 : if (!mEditorInitialized && IsSingleLineTextControl()) {
1761 : // Grab the current value directly from the text node to make sure that we
1762 : // deal with stale data correctly.
1763 0 : NS_ASSERTION(mRootNode, "We should have a root node here");
1764 0 : nsIContent *textContent = mRootNode->GetFirstChild();
1765 0 : nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(textContent);
1766 0 : if (textNode) {
1767 0 : textNode->GetData(currentValue);
1768 : }
1769 : } else {
1770 0 : mBoundFrame->GetText(currentValue);
1771 : }
1772 :
1773 0 : nsWeakFrame weakFrame(mBoundFrame);
1774 :
1775 : // this is necessary to avoid infinite recursion
1776 0 : if (!currentValue.Equals(aValue))
1777 : {
1778 : nsTextControlFrame::ValueSetter valueSetter(mBoundFrame,
1779 0 : mBoundFrame->mFocusedValue.Equals(currentValue));
1780 :
1781 : // \r is an illegal character in the dom, but people use them,
1782 : // so convert windows and mac platform linebreaks to \n:
1783 : // Unfortunately aValue is declared const, so we have to copy
1784 : // in order to do this substitution.
1785 0 : nsString newValue(aValue);
1786 0 : if (aValue.FindChar(PRUnichar('\r')) != -1) {
1787 0 : nsContentUtils::PlatformToDOMLineBreaks(newValue);
1788 : }
1789 :
1790 0 : nsCOMPtr<nsIDOMDocument> domDoc;
1791 0 : mEditor->GetDocument(getter_AddRefs(domDoc));
1792 0 : if (!domDoc) {
1793 0 : NS_WARNING("Why don't we have a document?");
1794 : return;
1795 : }
1796 :
1797 : // Time to mess with our security context... See comments in GetValue()
1798 : // for why this is needed. Note that we have to do this up here, because
1799 : // otherwise SelectAll() will fail.
1800 : { /* Scope for context pusher */
1801 0 : nsCxPusher pusher;
1802 0 : pusher.PushNull();
1803 :
1804 0 : nsCOMPtr<nsISelection> domSel;
1805 0 : nsCOMPtr<nsISelectionPrivate> selPriv;
1806 0 : mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
1807 0 : getter_AddRefs(domSel));
1808 0 : if (domSel)
1809 : {
1810 0 : selPriv = do_QueryInterface(domSel);
1811 0 : if (selPriv)
1812 0 : selPriv->StartBatchChanges();
1813 : }
1814 :
1815 0 : nsCOMPtr<nsISelectionController> kungFuDeathGrip = mSelCon.get();
1816 0 : PRUint32 currentLength = currentValue.Length();
1817 0 : PRUint32 newlength = newValue.Length();
1818 0 : if (!currentLength ||
1819 0 : !StringBeginsWith(newValue, currentValue)) {
1820 : // Replace the whole text.
1821 0 : currentLength = 0;
1822 0 : mSelCon->SelectAll();
1823 : } else {
1824 : // Collapse selection to the end so that we can append data.
1825 0 : mBoundFrame->SelectAllOrCollapseToEndOfText(false);
1826 : }
1827 : const nsAString& insertValue =
1828 0 : StringTail(newValue, newlength - currentLength);
1829 0 : nsCOMPtr<nsIPlaintextEditor> plaintextEditor = do_QueryInterface(mEditor);
1830 0 : if (!plaintextEditor || !weakFrame.IsAlive()) {
1831 0 : NS_WARNING("Somehow not a plaintext editor?");
1832 : return;
1833 : }
1834 :
1835 0 : valueSetter.Init();
1836 :
1837 : // get the flags, remove readonly and disabled, set the value,
1838 : // restore flags
1839 : PRUint32 flags, savedFlags;
1840 0 : mEditor->GetFlags(&savedFlags);
1841 0 : flags = savedFlags;
1842 0 : flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
1843 0 : flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
1844 0 : flags |= nsIPlaintextEditor::eEditorDontEchoPassword;
1845 0 : mEditor->SetFlags(flags);
1846 :
1847 0 : mTextListener->SettingValue(true);
1848 :
1849 : // Also don't enforce max-length here
1850 : PRInt32 savedMaxLength;
1851 0 : plaintextEditor->GetMaxTextLength(&savedMaxLength);
1852 0 : plaintextEditor->SetMaxTextLength(-1);
1853 :
1854 0 : if (insertValue.IsEmpty()) {
1855 0 : mEditor->DeleteSelection(nsIEditor::eNone);
1856 : } else {
1857 0 : plaintextEditor->InsertText(insertValue);
1858 : }
1859 :
1860 0 : mTextListener->SettingValue(false);
1861 :
1862 0 : if (!weakFrame.IsAlive()) {
1863 : // If the frame was destroyed because of a flush somewhere inside
1864 : // InsertText, mBoundFrame here will be false. But it's also possible
1865 : // for the frame to go away because of another reason (such as deleting
1866 : // the existing selection -- see bug 574558), in which case we don't
1867 : // need to reset the value here.
1868 0 : if (!mBoundFrame) {
1869 0 : SetValue(newValue, false);
1870 : }
1871 0 : valueSetter.Cancel();
1872 : return;
1873 : }
1874 :
1875 0 : if (!IsSingleLineTextControl()) {
1876 0 : mCachedValue = newValue;
1877 : }
1878 :
1879 0 : plaintextEditor->SetMaxTextLength(savedMaxLength);
1880 0 : mEditor->SetFlags(savedFlags);
1881 0 : if (selPriv)
1882 0 : selPriv->EndBatchChanges();
1883 : }
1884 : }
1885 :
1886 : // This second check _shouldn't_ be necessary, but let's be safe.
1887 0 : if (!weakFrame.IsAlive()) {
1888 : return;
1889 : }
1890 0 : nsIScrollableFrame* scrollableFrame = do_QueryFrame(mBoundFrame->GetFirstPrincipalChild());
1891 0 : if (scrollableFrame)
1892 : {
1893 : // Scroll the upper left corner of the text control's
1894 : // content area back into view.
1895 0 : scrollableFrame->ScrollTo(nsPoint(0, 0), nsIScrollableFrame::INSTANT);
1896 : }
1897 :
1898 0 : if (aUserInput) {
1899 0 : mBoundFrame->SetFireChangeEventState(fireChangeEvent);
1900 : }
1901 : } else {
1902 1 : if (!mValue) {
1903 1 : mValue = new nsCString;
1904 : }
1905 2 : nsString value(aValue);
1906 1 : nsContentUtils::PlatformToDOMLineBreaks(value);
1907 1 : CopyUTF16toUTF8(value, *mValue);
1908 :
1909 : // Update the frame display if needed
1910 1 : if (mBoundFrame) {
1911 0 : mBoundFrame->UpdateValueDisplay(true);
1912 : }
1913 : }
1914 :
1915 : // If we've reached the point where the root node has been created, we
1916 : // can assume that it's safe to notify.
1917 1 : ValueWasChanged(!!mRootNode);
1918 :
1919 1 : mTextCtrlElement->OnValueChanged(!!mRootNode);
1920 : }
1921 :
1922 : void
1923 0 : nsTextEditorState::InitializeKeyboardEventListeners()
1924 : {
1925 : //register key listeners
1926 0 : nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mTextCtrlElement);
1927 0 : nsEventListenerManager* manager = target->GetListenerManager(true);
1928 0 : if (manager) {
1929 : manager->AddEventListenerByType(mTextListener,
1930 0 : NS_LITERAL_STRING("keydown"),
1931 : NS_EVENT_FLAG_BUBBLE |
1932 0 : NS_EVENT_FLAG_SYSTEM_EVENT);
1933 : manager->AddEventListenerByType(mTextListener,
1934 0 : NS_LITERAL_STRING("keypress"),
1935 : NS_EVENT_FLAG_BUBBLE |
1936 0 : NS_EVENT_FLAG_SYSTEM_EVENT);
1937 : manager->AddEventListenerByType(mTextListener,
1938 0 : NS_LITERAL_STRING("keyup"),
1939 : NS_EVENT_FLAG_BUBBLE |
1940 0 : NS_EVENT_FLAG_SYSTEM_EVENT);
1941 : }
1942 :
1943 0 : mSelCon->SetScrollableFrame(do_QueryFrame(mBoundFrame->GetFirstPrincipalChild()));
1944 0 : }
1945 :
1946 : /* static */ void
1947 1403 : nsTextEditorState::ShutDown()
1948 : {
1949 1403 : NS_IF_RELEASE(sNativeTextAreaBindings);
1950 1403 : NS_IF_RELEASE(sNativeInputBindings);
1951 1403 : }
1952 :
1953 : void
1954 1 : nsTextEditorState::ValueWasChanged(bool aNotify)
1955 : {
1956 : // placeholder management
1957 1 : if (!mPlaceholderDiv) {
1958 1 : return;
1959 : }
1960 :
1961 0 : bool showPlaceholder = false;
1962 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
1963 0 : if (!nsContentUtils::IsFocusedContent(content)) {
1964 : // If the content is focused, we don't care about the changes because
1965 : // the placeholder is going to be hidden/shown on blur.
1966 0 : nsAutoString valueString;
1967 0 : GetValue(valueString, true);
1968 0 : showPlaceholder = valueString.IsEmpty();
1969 : }
1970 0 : SetPlaceholderClass(showPlaceholder, aNotify);
1971 : }
1972 :
1973 : void
1974 0 : nsTextEditorState::UpdatePlaceholderText(bool aNotify)
1975 : {
1976 0 : NS_ASSERTION(mPlaceholderDiv, "This function should not be called if "
1977 : "mPlaceholderDiv isn't set");
1978 :
1979 : // If we don't have a placeholder div, there's nothing to do.
1980 0 : if (!mPlaceholderDiv)
1981 0 : return;
1982 :
1983 0 : nsAutoString placeholderValue;
1984 :
1985 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
1986 0 : content->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, placeholderValue);
1987 0 : nsContentUtils::RemoveNewlines(placeholderValue);
1988 0 : NS_ASSERTION(mPlaceholderDiv->GetFirstChild(), "placeholder div has no child");
1989 0 : mPlaceholderDiv->GetFirstChild()->SetText(placeholderValue, aNotify);
1990 0 : ValueWasChanged(aNotify);
1991 : }
1992 :
1993 : void
1994 0 : nsTextEditorState::SetPlaceholderClass(bool aVisible,
1995 : bool aNotify)
1996 : {
1997 0 : NS_ASSERTION(mPlaceholderDiv, "This function should not be called if "
1998 : "mPlaceholderDiv isn't set");
1999 :
2000 : // No need to do anything if we don't have a frame yet
2001 0 : if (!mBoundFrame)
2002 0 : return;
2003 :
2004 0 : nsAutoString classValue;
2005 :
2006 0 : classValue.Assign(NS_LITERAL_STRING("anonymous-div placeholder"));
2007 :
2008 0 : if (!aVisible)
2009 0 : classValue.AppendLiteral(" hidden");
2010 :
2011 0 : nsIContent* placeholderDiv = GetPlaceholderNode();
2012 0 : NS_ENSURE_TRUE(placeholderDiv, );
2013 :
2014 : placeholderDiv->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
2015 0 : classValue, aNotify);
2016 : }
2017 :
2018 : void
2019 0 : nsTextEditorState::HideSelectionIfBlurred()
2020 : {
2021 0 : NS_ABORT_IF_FALSE(mSelCon, "Should have a selection controller if we have a frame!");
2022 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
2023 0 : if (!nsContentUtils::IsFocusedContent(content)) {
2024 0 : mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
2025 : }
2026 0 : }
2027 :
2028 0 : NS_IMPL_ISUPPORTS1(nsAnonDivObserver, nsIMutationObserver)
2029 :
2030 : void
2031 0 : nsAnonDivObserver::CharacterDataChanged(nsIDocument* aDocument,
2032 : nsIContent* aContent,
2033 : CharacterDataChangeInfo* aInfo)
2034 : {
2035 0 : mTextEditorState->ClearValueCache();
2036 0 : }
2037 :
2038 : void
2039 0 : nsAnonDivObserver::ContentAppended(nsIDocument* aDocument,
2040 : nsIContent* aContainer,
2041 : nsIContent* aFirstNewContent,
2042 : PRInt32 /* unused */)
2043 : {
2044 0 : mTextEditorState->ClearValueCache();
2045 0 : }
2046 :
2047 : void
2048 0 : nsAnonDivObserver::ContentInserted(nsIDocument* aDocument,
2049 : nsIContent* aContainer,
2050 : nsIContent* aChild,
2051 : PRInt32 /* unused */)
2052 : {
2053 0 : mTextEditorState->ClearValueCache();
2054 0 : }
2055 :
2056 : void
2057 0 : nsAnonDivObserver::ContentRemoved(nsIDocument* aDocument,
2058 : nsIContent* aContainer,
2059 : nsIContent* aChild,
2060 : PRInt32 aIndexInContainer,
2061 : nsIContent* aPreviousSibling)
2062 : {
2063 0 : mTextEditorState->ClearValueCache();
2064 4392 : }
|