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 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsTextEditRules.h"
39 :
40 : #include "nsEditor.h"
41 : #include "nsTextEditUtils.h"
42 : #include "nsCRT.h"
43 :
44 : #include "nsCOMPtr.h"
45 : #include "nsIServiceManager.h"
46 : #include "nsIDOMNode.h"
47 : #include "nsIDOMElement.h"
48 : #include "nsIDOMText.h"
49 : #include "nsIDOMNodeList.h"
50 : #include "nsISelection.h"
51 : #include "nsISelectionPrivate.h"
52 : #include "nsISelectionController.h"
53 : #include "nsIDOMRange.h"
54 : #include "nsIDOMCharacterData.h"
55 : #include "nsIContent.h"
56 : #include "nsIContentIterator.h"
57 : #include "nsEditorUtils.h"
58 : #include "EditTxn.h"
59 : #include "nsEditProperty.h"
60 : #include "nsUnicharUtils.h"
61 : #include "DeleteTextTxn.h"
62 : #include "nsNodeIterator.h"
63 : #include "nsIDOMNodeFilter.h"
64 :
65 : // for IBMBIDI
66 : #include "nsFrameSelection.h"
67 :
68 : #include "mozilla/Preferences.h"
69 : #include "mozilla/LookAndFeel.h"
70 : #include "mozilla/dom/Element.h"
71 :
72 : using namespace mozilla;
73 :
74 : #define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \
75 : if (IsReadonly() || IsDisabled()) \
76 : { \
77 : *aCancel = true; \
78 : return NS_OK; \
79 : };
80 :
81 :
82 : /********************************************************
83 : * Constructor/Destructor
84 : ********************************************************/
85 :
86 0 : nsTextEditRules::nsTextEditRules()
87 : : mEditor(nsnull)
88 : , mPasswordText()
89 : , mPasswordIMEText()
90 : , mPasswordIMEIndex(0)
91 : , mActionNesting(0)
92 : , mLockRulesSniffing(false)
93 : , mDidExplicitlySetInterline(false)
94 : , mTheAction(0)
95 : , mLastStart(0)
96 0 : , mLastLength(0)
97 : {
98 0 : }
99 :
100 0 : nsTextEditRules::~nsTextEditRules()
101 : {
102 : // do NOT delete mEditor here. We do not hold a ref count to mEditor. mEditor owns our lifespan.
103 :
104 0 : if (mTimer)
105 0 : mTimer->Cancel();
106 0 : }
107 :
108 : /********************************************************
109 : * XPCOM Cruft
110 : ********************************************************/
111 :
112 1464 : NS_IMPL_CYCLE_COLLECTION_2(nsTextEditRules, mBogusNode, mCachedSelectionNode)
113 :
114 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTextEditRules)
115 0 : NS_INTERFACE_MAP_ENTRY(nsIEditRules)
116 0 : NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
117 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditRules)
118 0 : NS_INTERFACE_MAP_END
119 :
120 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTextEditRules)
121 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTextEditRules)
122 :
123 : /********************************************************
124 : * Public methods
125 : ********************************************************/
126 :
127 : NS_IMETHODIMP
128 0 : nsTextEditRules::Init(nsPlaintextEditor *aEditor)
129 : {
130 0 : if (!aEditor) { return NS_ERROR_NULL_POINTER; }
131 :
132 0 : mEditor = aEditor; // we hold a non-refcounted reference back to our editor
133 0 : nsCOMPtr<nsISelection> selection;
134 0 : mEditor->GetSelection(getter_AddRefs(selection));
135 0 : NS_ASSERTION(selection, "editor cannot get selection");
136 :
137 : // Put in a magic br if needed. This method handles null selection,
138 : // which should never happen anyway
139 0 : nsresult res = CreateBogusNodeIfNeeded(selection);
140 0 : NS_ENSURE_SUCCESS(res, res);
141 :
142 : // If the selection hasn't been set up yet, set it up collapsed to the end of
143 : // our editable content.
144 : PRInt32 rangeCount;
145 0 : res = selection->GetRangeCount(&rangeCount);
146 0 : NS_ENSURE_SUCCESS(res, res);
147 0 : if (!rangeCount) {
148 0 : res = mEditor->EndOfDocument();
149 0 : NS_ENSURE_SUCCESS(res, res);
150 : }
151 :
152 0 : if (IsPlaintextEditor())
153 : {
154 : // ensure trailing br node
155 0 : res = CreateTrailingBRIfNeeded();
156 0 : NS_ENSURE_SUCCESS(res, res);
157 : }
158 :
159 : mDeleteBidiImmediately =
160 0 : Preferences::GetBool("bidi.edit.delete_immediately", false);
161 :
162 0 : return res;
163 : }
164 :
165 : NS_IMETHODIMP
166 0 : nsTextEditRules::DetachEditor()
167 : {
168 0 : if (mTimer)
169 0 : mTimer->Cancel();
170 :
171 0 : mEditor = nsnull;
172 0 : return NS_OK;
173 : }
174 :
175 : NS_IMETHODIMP
176 0 : nsTextEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection)
177 : {
178 0 : if (mLockRulesSniffing) return NS_OK;
179 :
180 0 : nsAutoLockRulesSniffing lockIt(this);
181 0 : mDidExplicitlySetInterline = false;
182 0 : if (!mActionNesting)
183 : {
184 : // let rules remember the top level action
185 0 : mTheAction = action;
186 : }
187 0 : mActionNesting++;
188 :
189 : // get the selection and cache the position before editing
190 0 : nsCOMPtr<nsISelection> selection;
191 0 : nsresult res = mEditor->GetSelection(getter_AddRefs(selection));
192 0 : NS_ENSURE_SUCCESS(res, res);
193 :
194 0 : selection->GetAnchorNode(getter_AddRefs(mCachedSelectionNode));
195 0 : selection->GetAnchorOffset(&mCachedSelectionOffset);
196 :
197 0 : return NS_OK;
198 : }
199 :
200 :
201 : NS_IMETHODIMP
202 0 : nsTextEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
203 : {
204 0 : if (mLockRulesSniffing) return NS_OK;
205 :
206 0 : nsAutoLockRulesSniffing lockIt(this);
207 :
208 0 : NS_PRECONDITION(mActionNesting>0, "bad action nesting!");
209 0 : nsresult res = NS_OK;
210 0 : if (!--mActionNesting)
211 : {
212 0 : nsCOMPtr<nsISelection>selection;
213 0 : res = mEditor->GetSelection(getter_AddRefs(selection));
214 0 : NS_ENSURE_SUCCESS(res, res);
215 :
216 : res = mEditor->HandleInlineSpellCheck(action, selection,
217 : mCachedSelectionNode, mCachedSelectionOffset,
218 0 : nsnull, 0, nsnull, 0);
219 0 : NS_ENSURE_SUCCESS(res, res);
220 :
221 : // if only trailing <br> remaining remove it
222 0 : res = RemoveRedundantTrailingBR();
223 0 : if (NS_FAILED(res))
224 0 : return res;
225 :
226 : // detect empty doc
227 0 : res = CreateBogusNodeIfNeeded(selection);
228 0 : NS_ENSURE_SUCCESS(res, res);
229 :
230 : // ensure trailing br node
231 0 : res = CreateTrailingBRIfNeeded();
232 0 : NS_ENSURE_SUCCESS(res, res);
233 :
234 : // collapse the selection to the trailing BR if it's at the end of our text node
235 0 : CollapseSelectionToTrailingBRIfNeeded(selection);
236 : }
237 0 : return res;
238 : }
239 :
240 :
241 : NS_IMETHODIMP
242 0 : nsTextEditRules::WillDoAction(nsISelection *aSelection,
243 : nsRulesInfo *aInfo,
244 : bool *aCancel,
245 : bool *aHandled)
246 : {
247 : // null selection is legal
248 0 : if (!aInfo || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
249 : #if defined(DEBUG_ftang)
250 : printf("nsTextEditRules::WillDoAction action= %d", aInfo->action);
251 : #endif
252 :
253 0 : *aCancel = false;
254 0 : *aHandled = false;
255 :
256 : // my kingdom for dynamic cast
257 0 : nsTextRulesInfo *info = static_cast<nsTextRulesInfo*>(aInfo);
258 :
259 0 : switch (info->action)
260 : {
261 : case kInsertBreak:
262 0 : return WillInsertBreak(aSelection, aCancel, aHandled, info->maxLength);
263 : case kInsertText:
264 : case kInsertTextIME:
265 : return WillInsertText(info->action,
266 : aSelection,
267 : aCancel,
268 : aHandled,
269 : info->inString,
270 : info->outString,
271 0 : info->maxLength);
272 : case kDeleteSelection:
273 0 : return WillDeleteSelection(aSelection, info->collapsedAction, aCancel, aHandled);
274 : case kUndo:
275 0 : return WillUndo(aSelection, aCancel, aHandled);
276 : case kRedo:
277 0 : return WillRedo(aSelection, aCancel, aHandled);
278 : case kSetTextProperty:
279 0 : return WillSetTextProperty(aSelection, aCancel, aHandled);
280 : case kRemoveTextProperty:
281 0 : return WillRemoveTextProperty(aSelection, aCancel, aHandled);
282 : case kOutputText:
283 : return WillOutputText(aSelection,
284 : info->outputFormat,
285 : info->outString,
286 : aCancel,
287 0 : aHandled);
288 : case kInsertElement: // i had thought this would be html rules only. but we put pre elements
289 : // into plaintext mail when doing quoting for reply! doh!
290 0 : return WillInsert(aSelection, aCancel);
291 : }
292 0 : return NS_ERROR_FAILURE;
293 : }
294 :
295 : NS_IMETHODIMP
296 0 : nsTextEditRules::DidDoAction(nsISelection *aSelection,
297 : nsRulesInfo *aInfo, nsresult aResult)
298 : {
299 : // don't let any txns in here move the selection around behind our back.
300 : // Note that this won't prevent explicit selection setting from working.
301 0 : nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
302 :
303 0 : NS_ENSURE_TRUE(aSelection && aInfo, NS_ERROR_NULL_POINTER);
304 :
305 : // my kingdom for dynamic cast
306 0 : nsTextRulesInfo *info = static_cast<nsTextRulesInfo*>(aInfo);
307 :
308 0 : switch (info->action)
309 : {
310 : case kInsertBreak:
311 0 : return DidInsertBreak(aSelection, aResult);
312 : case kInsertText:
313 : case kInsertTextIME:
314 0 : return DidInsertText(aSelection, aResult);
315 : case kDeleteSelection:
316 0 : return DidDeleteSelection(aSelection, info->collapsedAction, aResult);
317 : case kUndo:
318 0 : return DidUndo(aSelection, aResult);
319 : case kRedo:
320 0 : return DidRedo(aSelection, aResult);
321 : case kSetTextProperty:
322 0 : return DidSetTextProperty(aSelection, aResult);
323 : case kRemoveTextProperty:
324 0 : return DidRemoveTextProperty(aSelection, aResult);
325 : case kOutputText:
326 0 : return DidOutputText(aSelection, aResult);
327 : }
328 : // Don't fail on transactions we don't handle here!
329 0 : return NS_OK;
330 : }
331 :
332 :
333 : NS_IMETHODIMP
334 0 : nsTextEditRules::DocumentIsEmpty(bool *aDocumentIsEmpty)
335 : {
336 0 : NS_ENSURE_TRUE(aDocumentIsEmpty, NS_ERROR_NULL_POINTER);
337 :
338 0 : *aDocumentIsEmpty = (mBogusNode != nsnull);
339 0 : return NS_OK;
340 : }
341 :
342 : /********************************************************
343 : * Protected methods
344 : ********************************************************/
345 :
346 :
347 : nsresult
348 0 : nsTextEditRules::WillInsert(nsISelection *aSelection, bool *aCancel)
349 : {
350 0 : NS_ENSURE_TRUE(aSelection && aCancel, NS_ERROR_NULL_POINTER);
351 :
352 0 : CANCEL_OPERATION_IF_READONLY_OR_DISABLED
353 :
354 : // initialize out param
355 0 : *aCancel = false;
356 :
357 : // check for the magic content node and delete it if it exists
358 0 : if (mBogusNode)
359 : {
360 0 : mEditor->DeleteNode(mBogusNode);
361 0 : mBogusNode = nsnull;
362 : }
363 :
364 0 : return NS_OK;
365 : }
366 :
367 : nsresult
368 0 : nsTextEditRules::DidInsert(nsISelection *aSelection, nsresult aResult)
369 : {
370 0 : return NS_OK;
371 : }
372 :
373 : nsresult
374 0 : nsTextEditRules::WillInsertBreak(nsISelection *aSelection,
375 : bool *aCancel,
376 : bool *aHandled,
377 : PRInt32 aMaxLength)
378 : {
379 0 : if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
380 0 : CANCEL_OPERATION_IF_READONLY_OR_DISABLED
381 0 : *aHandled = false;
382 0 : if (IsSingleLineEditor()) {
383 0 : *aCancel = true;
384 : }
385 : else
386 : {
387 : // handle docs with a max length
388 : // NOTE, this function copies inString into outString for us.
389 0 : NS_NAMED_LITERAL_STRING(inString, "\n");
390 0 : nsAutoString outString;
391 : bool didTruncate;
392 : nsresult res = TruncateInsertionIfNeeded(aSelection, &inString, &outString,
393 0 : aMaxLength, &didTruncate);
394 0 : NS_ENSURE_SUCCESS(res, res);
395 0 : if (didTruncate) {
396 0 : *aCancel = true;
397 0 : return NS_OK;
398 : }
399 :
400 0 : *aCancel = false;
401 :
402 : // if the selection isn't collapsed, delete it.
403 : bool bCollapsed;
404 0 : res = aSelection->GetIsCollapsed(&bCollapsed);
405 0 : NS_ENSURE_SUCCESS(res, res);
406 0 : if (!bCollapsed)
407 : {
408 0 : res = mEditor->DeleteSelection(nsIEditor::eNone);
409 0 : NS_ENSURE_SUCCESS(res, res);
410 : }
411 :
412 0 : res = WillInsert(aSelection, aCancel);
413 0 : NS_ENSURE_SUCCESS(res, res);
414 : // initialize out param
415 : // we want to ignore result of WillInsert()
416 0 : *aCancel = false;
417 :
418 : }
419 0 : return NS_OK;
420 : }
421 :
422 : nsresult
423 0 : nsTextEditRules::DidInsertBreak(nsISelection *aSelection, nsresult aResult)
424 : {
425 0 : return NS_OK;
426 : }
427 :
428 : nsresult
429 0 : nsTextEditRules::CollapseSelectionToTrailingBRIfNeeded(nsISelection* aSelection)
430 : {
431 : // we only need to execute the stuff below if we are a plaintext editor.
432 : // html editors have a different mechanism for putting in mozBR's
433 : // (because there are a bunch more places you have to worry about it in html)
434 0 : if (!IsPlaintextEditor()) {
435 0 : return NS_OK;
436 : }
437 :
438 : // if we are at the end of the textarea, we need to set the
439 : // selection to stick to the mozBR at the end of the textarea.
440 : PRInt32 selOffset;
441 0 : nsCOMPtr<nsIDOMNode> selNode;
442 : nsresult res;
443 0 : res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
444 0 : NS_ENSURE_SUCCESS(res, res);
445 :
446 0 : nsCOMPtr<nsIDOMText> nodeAsText = do_QueryInterface(selNode);
447 0 : if (!nodeAsText) return NS_OK; // nothing to do if we're not at a text node
448 :
449 : PRUint32 length;
450 0 : res = nodeAsText->GetLength(&length);
451 0 : NS_ENSURE_SUCCESS(res, res);
452 :
453 : // nothing to do if we're not at the end of the text node
454 0 : if (selOffset != PRInt32(length))
455 0 : return NS_OK;
456 :
457 0 : nsCOMPtr<nsIDOMNode> parentNode;
458 : PRInt32 parentOffset;
459 : res = nsEditor::GetNodeLocation(selNode, address_of(parentNode),
460 0 : &parentOffset);
461 0 : NS_ENSURE_SUCCESS(res, res);
462 :
463 0 : nsCOMPtr<nsIDOMNode> root = do_QueryInterface(mEditor->GetRoot());
464 0 : NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
465 0 : if (parentNode != root) return NS_OK;
466 :
467 : nsCOMPtr<nsIDOMNode> nextNode = mEditor->GetChildAt(parentNode,
468 0 : parentOffset + 1);
469 0 : if (nextNode && nsTextEditUtils::IsMozBR(nextNode))
470 : {
471 0 : res = aSelection->Collapse(parentNode, parentOffset + 1);
472 0 : NS_ENSURE_SUCCESS(res, res);
473 : }
474 0 : return res;
475 : }
476 :
477 : static inline already_AddRefed<nsIDOMNode>
478 0 : GetTextNode(nsISelection *selection, nsEditor *editor) {
479 : PRInt32 selOffset;
480 0 : nsCOMPtr<nsIDOMNode> selNode;
481 0 : nsresult res = editor->GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset);
482 0 : NS_ENSURE_SUCCESS(res, nsnull);
483 0 : if (!editor->IsTextNode(selNode)) {
484 : // Get an nsINode from the nsIDOMNode
485 0 : nsCOMPtr<nsINode> node = do_QueryInterface(selNode);
486 : // if node is null, return it to indicate there's no text
487 0 : NS_ENSURE_TRUE(node, nsnull);
488 : // This should be the root node, walk the tree looking for text nodes
489 0 : nsNodeIterator iter(node, nsIDOMNodeFilter::SHOW_TEXT, nsnull);
490 0 : while (!editor->IsTextNode(selNode)) {
491 0 : if (NS_FAILED(res = iter.NextNode(getter_AddRefs(selNode))) || !selNode) {
492 0 : return nsnull;
493 : }
494 : }
495 : }
496 0 : return selNode.forget();
497 : }
498 : #ifdef DEBUG
499 : #define ASSERT_PASSWORD_LENGTHS_EQUAL() \
500 : if (IsPasswordEditor()) { \
501 : PRInt32 txtLen; \
502 : mEditor->GetTextLength(&txtLen); \
503 : NS_ASSERTION(mPasswordText.Length() == PRUint32(txtLen), \
504 : "password length not equal to number of asterisks"); \
505 : }
506 : #else
507 : #define ASSERT_PASSWORD_LENGTHS_EQUAL()
508 : #endif
509 :
510 : // static
511 : void
512 0 : nsTextEditRules::HandleNewLines(nsString &aString,
513 : PRInt32 aNewlineHandling)
514 : {
515 0 : if (aNewlineHandling < 0) {
516 : PRInt32 caretStyle;
517 0 : nsPlaintextEditor::GetDefaultEditorPrefs(aNewlineHandling, caretStyle);
518 : }
519 :
520 0 : switch(aNewlineHandling)
521 : {
522 : case nsIPlaintextEditor::eNewlinesReplaceWithSpaces:
523 : // Strip trailing newlines first so we don't wind up with trailing spaces
524 0 : aString.Trim(CRLF, false, true);
525 0 : aString.ReplaceChar(CRLF, ' ');
526 0 : break;
527 : case nsIPlaintextEditor::eNewlinesStrip:
528 0 : aString.StripChars(CRLF);
529 0 : break;
530 : case nsIPlaintextEditor::eNewlinesPasteToFirst:
531 : default:
532 : {
533 0 : PRInt32 firstCRLF = aString.FindCharInSet(CRLF);
534 :
535 : // we get first *non-empty* line.
536 0 : PRInt32 offset = 0;
537 0 : while (firstCRLF == offset)
538 : {
539 0 : offset++;
540 0 : firstCRLF = aString.FindCharInSet(CRLF, offset);
541 : }
542 0 : if (firstCRLF > 0)
543 0 : aString.Truncate(firstCRLF);
544 0 : if (offset > 0)
545 0 : aString.Cut(0, offset);
546 : }
547 0 : break;
548 : case nsIPlaintextEditor::eNewlinesReplaceWithCommas:
549 0 : aString.Trim(CRLF, true, true);
550 0 : aString.ReplaceChar(CRLF, ',');
551 0 : break;
552 : case nsIPlaintextEditor::eNewlinesStripSurroundingWhitespace:
553 : {
554 : // find each newline, and strip all the whitespace before
555 : // and after it
556 0 : PRInt32 firstCRLF = aString.FindCharInSet(CRLF);
557 0 : while (firstCRLF >= 0)
558 : {
559 0 : PRUint32 wsBegin = firstCRLF, wsEnd = firstCRLF + 1;
560 : // look backwards for the first non-whitespace char
561 0 : while (wsBegin > 0 && NS_IS_SPACE(aString[wsBegin - 1]))
562 0 : --wsBegin;
563 0 : while (wsEnd < aString.Length() && NS_IS_SPACE(aString[wsEnd]))
564 0 : ++wsEnd;
565 : // now cut this range out of the string
566 0 : aString.Cut(wsBegin, wsEnd - wsBegin);
567 : // look for another CR or LF
568 0 : firstCRLF = aString.FindCharInSet(CRLF);
569 : }
570 : }
571 0 : break;
572 : case nsIPlaintextEditor::eNewlinesPasteIntact:
573 : // even if we're pasting newlines, don't paste leading/trailing ones
574 0 : aString.Trim(CRLF, true, true);
575 0 : break;
576 : }
577 0 : }
578 :
579 : nsresult
580 0 : nsTextEditRules::WillInsertText(PRInt32 aAction,
581 : nsISelection *aSelection,
582 : bool *aCancel,
583 : bool *aHandled,
584 : const nsAString *inString,
585 : nsAString *outString,
586 : PRInt32 aMaxLength)
587 : {
588 0 : if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
589 :
590 0 : if (inString->IsEmpty() && (aAction != kInsertTextIME))
591 : {
592 : // HACK: this is a fix for bug 19395
593 : // I can't outlaw all empty insertions
594 : // because IME transaction depend on them
595 : // There is more work to do to make the
596 : // world safe for IME.
597 0 : *aCancel = true;
598 0 : *aHandled = false;
599 0 : return NS_OK;
600 : }
601 :
602 : // initialize out param
603 0 : *aCancel = false;
604 0 : *aHandled = true;
605 :
606 : // handle docs with a max length
607 : // NOTE, this function copies inString into outString for us.
608 0 : bool truncated = false;
609 : nsresult res = TruncateInsertionIfNeeded(aSelection, inString, outString,
610 0 : aMaxLength, &truncated);
611 0 : NS_ENSURE_SUCCESS(res, res);
612 : // If we're exceeding the maxlength when composing IME, we need to clean up
613 : // the composing text, so we shouldn't return early.
614 0 : if (truncated && outString->IsEmpty() && aAction != kInsertTextIME) {
615 0 : *aCancel = true;
616 0 : return NS_OK;
617 : }
618 :
619 0 : PRUint32 start = 0;
620 0 : PRUint32 end = 0;
621 :
622 : // handle password field docs
623 0 : if (IsPasswordEditor())
624 : {
625 0 : res = mEditor->GetTextSelectionOffsets(aSelection, start, end);
626 0 : NS_ASSERTION((NS_SUCCEEDED(res)), "getTextSelectionOffsets failed!");
627 0 : NS_ENSURE_SUCCESS(res, res);
628 : }
629 :
630 : // if the selection isn't collapsed, delete it.
631 : bool bCollapsed;
632 0 : res = aSelection->GetIsCollapsed(&bCollapsed);
633 0 : NS_ENSURE_SUCCESS(res, res);
634 0 : if (!bCollapsed)
635 : {
636 0 : res = mEditor->DeleteSelection(nsIEditor::eNone);
637 0 : NS_ENSURE_SUCCESS(res, res);
638 : }
639 :
640 0 : res = WillInsert(aSelection, aCancel);
641 0 : NS_ENSURE_SUCCESS(res, res);
642 : // initialize out param
643 : // we want to ignore result of WillInsert()
644 0 : *aCancel = false;
645 :
646 : // handle password field data
647 : // this has the side effect of changing all the characters in aOutString
648 : // to the replacement character
649 0 : if (IsPasswordEditor())
650 : {
651 0 : if (aAction == kInsertTextIME) {
652 0 : res = RemoveIMETextFromPWBuf(start, outString);
653 0 : NS_ENSURE_SUCCESS(res, res);
654 : }
655 : }
656 :
657 : // People have lots of different ideas about what text fields
658 : // should do with multiline pastes. See bugs 21032, 23485, 23485, 50935.
659 : // The six possible options are:
660 : // 0. paste newlines intact
661 : // 1. paste up to the first newline (default)
662 : // 2. replace newlines with spaces
663 : // 3. strip newlines
664 : // 4. replace with commas
665 : // 5. strip newlines and surrounding whitespace
666 : // So find out what we're expected to do:
667 0 : if (IsSingleLineEditor())
668 : {
669 0 : nsAutoString tString(*outString);
670 :
671 0 : HandleNewLines(tString, mEditor->mNewlineHandling);
672 :
673 0 : outString->Assign(tString);
674 : }
675 :
676 0 : if (IsPasswordEditor())
677 : {
678 : // manage the password buffer
679 0 : mPasswordText.Insert(*outString, start);
680 :
681 0 : if (LookAndFeel::GetEchoPassword() && !DontEchoPassword()) {
682 0 : HideLastPWInput();
683 0 : mLastStart = start;
684 0 : mLastLength = outString->Length();
685 0 : if (mTimer)
686 : {
687 0 : mTimer->Cancel();
688 : }
689 : else
690 : {
691 0 : mTimer = do_CreateInstance("@mozilla.org/timer;1", &res);
692 0 : NS_ENSURE_SUCCESS(res, res);
693 : }
694 0 : mTimer->InitWithCallback(this, 600, nsITimer::TYPE_ONE_SHOT);
695 : }
696 : else
697 : {
698 0 : res = FillBufWithPWChars(outString, outString->Length());
699 0 : NS_ENSURE_SUCCESS(res, res);
700 : }
701 : }
702 :
703 : // get the (collapsed) selection location
704 0 : nsCOMPtr<nsIDOMNode> selNode;
705 : PRInt32 selOffset;
706 0 : res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
707 0 : NS_ENSURE_SUCCESS(res, res);
708 :
709 : // don't put text in places that can't have it
710 0 : if (!mEditor->IsTextNode(selNode) && !mEditor->CanContainTag(selNode, NS_LITERAL_STRING("#text")))
711 0 : return NS_ERROR_FAILURE;
712 :
713 : // we need to get the doc
714 0 : nsCOMPtr<nsIDOMDocument>doc;
715 0 : res = mEditor->GetDocument(getter_AddRefs(doc));
716 0 : NS_ENSURE_SUCCESS(res, res);
717 0 : NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);
718 :
719 0 : if (aAction == kInsertTextIME)
720 : {
721 0 : res = mEditor->InsertTextImpl(*outString, address_of(selNode), &selOffset, doc);
722 0 : NS_ENSURE_SUCCESS(res, res);
723 : }
724 : else // aAction == kInsertText
725 : {
726 : // find where we are
727 0 : nsCOMPtr<nsIDOMNode> curNode = selNode;
728 0 : PRInt32 curOffset = selOffset;
729 :
730 : // don't spaz my selection in subtransactions
731 0 : nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
732 :
733 : res = mEditor->InsertTextImpl(*outString, address_of(curNode),
734 0 : &curOffset, doc);
735 0 : NS_ENSURE_SUCCESS(res, res);
736 :
737 0 : if (curNode)
738 : {
739 : // Make the caret attach to the inserted text, unless this text ends with a LF,
740 : // in which case make the caret attach to the next line.
741 : bool endsWithLF =
742 0 : !outString->IsEmpty() && outString->Last() == nsCRT::LF;
743 0 : nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(aSelection));
744 0 : selPrivate->SetInterlinePosition(endsWithLF);
745 :
746 0 : aSelection->Collapse(curNode, curOffset);
747 : }
748 : }
749 0 : ASSERT_PASSWORD_LENGTHS_EQUAL()
750 0 : return res;
751 : }
752 :
753 : nsresult
754 0 : nsTextEditRules::DidInsertText(nsISelection *aSelection,
755 : nsresult aResult)
756 : {
757 0 : return DidInsert(aSelection, aResult);
758 : }
759 :
760 :
761 :
762 : nsresult
763 0 : nsTextEditRules::WillSetTextProperty(nsISelection *aSelection, bool *aCancel, bool *aHandled)
764 : {
765 0 : if (!aSelection || !aCancel || !aHandled)
766 0 : { return NS_ERROR_NULL_POINTER; }
767 :
768 : // XXX: should probably return a success value other than NS_OK that means "not allowed"
769 0 : if (IsPlaintextEditor()) {
770 0 : *aCancel = true;
771 : }
772 0 : return NS_OK;
773 : }
774 :
775 : nsresult
776 0 : nsTextEditRules::DidSetTextProperty(nsISelection *aSelection, nsresult aResult)
777 : {
778 0 : return NS_OK;
779 : }
780 :
781 : nsresult
782 0 : nsTextEditRules::WillRemoveTextProperty(nsISelection *aSelection, bool *aCancel, bool *aHandled)
783 : {
784 0 : if (!aSelection || !aCancel || !aHandled)
785 0 : { return NS_ERROR_NULL_POINTER; }
786 :
787 : // XXX: should probably return a success value other than NS_OK that means "not allowed"
788 0 : if (IsPlaintextEditor()) {
789 0 : *aCancel = true;
790 : }
791 0 : return NS_OK;
792 : }
793 :
794 : nsresult
795 0 : nsTextEditRules::DidRemoveTextProperty(nsISelection *aSelection, nsresult aResult)
796 : {
797 0 : return NS_OK;
798 : }
799 :
800 : nsresult
801 0 : nsTextEditRules::WillDeleteSelection(nsISelection *aSelection,
802 : nsIEditor::EDirection aCollapsedAction,
803 : bool *aCancel,
804 : bool *aHandled)
805 : {
806 0 : if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
807 0 : CANCEL_OPERATION_IF_READONLY_OR_DISABLED
808 :
809 : // initialize out param
810 0 : *aCancel = false;
811 0 : *aHandled = false;
812 :
813 : // if there is only bogus content, cancel the operation
814 0 : if (mBogusNode) {
815 0 : *aCancel = true;
816 0 : return NS_OK;
817 : }
818 :
819 0 : nsresult res = NS_OK;
820 :
821 0 : if (IsPasswordEditor())
822 : {
823 0 : res = mEditor->ExtendSelectionForDelete(aSelection, &aCollapsedAction);
824 0 : NS_ENSURE_SUCCESS(res, res);
825 :
826 : // manage the password buffer
827 : PRUint32 start, end;
828 0 : mEditor->GetTextSelectionOffsets(aSelection, start, end);
829 0 : NS_ENSURE_SUCCESS(res, res);
830 :
831 0 : if (LookAndFeel::GetEchoPassword()) {
832 0 : HideLastPWInput();
833 0 : mLastStart = start;
834 0 : mLastLength = 0;
835 0 : if (mTimer)
836 : {
837 0 : mTimer->Cancel();
838 : }
839 : }
840 :
841 0 : if (end == start)
842 : { // collapsed selection
843 0 : if (nsIEditor::ePrevious==aCollapsedAction && 0<start) { // del back
844 0 : mPasswordText.Cut(start-1, 1);
845 : }
846 0 : else if (nsIEditor::eNext==aCollapsedAction) { // del forward
847 0 : mPasswordText.Cut(start, 1);
848 : }
849 : // otherwise nothing to do for this collapsed selection
850 : }
851 : else { // extended selection
852 0 : mPasswordText.Cut(start, end-start);
853 : }
854 : }
855 : else
856 : {
857 0 : nsCOMPtr<nsIDOMNode> startNode;
858 : PRInt32 startOffset;
859 0 : res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset);
860 0 : NS_ENSURE_SUCCESS(res, res);
861 0 : NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
862 :
863 : bool bCollapsed;
864 0 : res = aSelection->GetIsCollapsed(&bCollapsed);
865 0 : NS_ENSURE_SUCCESS(res, res);
866 :
867 0 : if (!bCollapsed)
868 0 : return NS_OK;
869 :
870 : // Test for distance between caret and text that will be deleted
871 0 : res = CheckBidiLevelForDeletion(aSelection, startNode, startOffset, aCollapsedAction, aCancel);
872 0 : NS_ENSURE_SUCCESS(res, res);
873 0 : if (*aCancel) return NS_OK;
874 :
875 0 : res = mEditor->ExtendSelectionForDelete(aSelection, &aCollapsedAction);
876 0 : NS_ENSURE_SUCCESS(res, res);
877 : }
878 :
879 0 : res = mEditor->DeleteSelectionImpl(aCollapsedAction);
880 0 : NS_ENSURE_SUCCESS(res, res);
881 :
882 0 : *aHandled = true;
883 0 : ASSERT_PASSWORD_LENGTHS_EQUAL()
884 0 : return NS_OK;
885 : }
886 :
887 : nsresult
888 0 : nsTextEditRules::DidDeleteSelection(nsISelection *aSelection,
889 : nsIEditor::EDirection aCollapsedAction,
890 : nsresult aResult)
891 : {
892 0 : nsCOMPtr<nsIDOMNode> startNode;
893 : PRInt32 startOffset;
894 0 : nsresult res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset);
895 0 : NS_ENSURE_SUCCESS(res, res);
896 0 : NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
897 :
898 : // delete empty text nodes at selection
899 0 : if (mEditor->IsTextNode(startNode))
900 : {
901 0 : nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(startNode);
902 : PRUint32 strLength;
903 0 : res = textNode->GetLength(&strLength);
904 0 : NS_ENSURE_SUCCESS(res, res);
905 :
906 : // are we in an empty text node?
907 0 : if (!strLength)
908 : {
909 0 : res = mEditor->DeleteNode(startNode);
910 0 : NS_ENSURE_SUCCESS(res, res);
911 : }
912 : }
913 0 : if (!mDidExplicitlySetInterline)
914 : {
915 : // We prevent the caret from sticking on the left of prior BR
916 : // (i.e. the end of previous line) after this deletion. Bug 92124
917 0 : nsCOMPtr<nsISelectionPrivate> selPriv = do_QueryInterface(aSelection);
918 0 : if (selPriv) res = selPriv->SetInterlinePosition(true);
919 : }
920 0 : return res;
921 : }
922 :
923 : nsresult
924 0 : nsTextEditRules::WillUndo(nsISelection *aSelection, bool *aCancel, bool *aHandled)
925 : {
926 0 : if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
927 0 : CANCEL_OPERATION_IF_READONLY_OR_DISABLED
928 : // initialize out param
929 0 : *aCancel = false;
930 0 : *aHandled = false;
931 0 : return NS_OK;
932 : }
933 :
934 : /* the idea here is to see if the magic empty node has suddenly reappeared as the result of the undo.
935 : * if it has, set our state so we remember it.
936 : * There is a tradeoff between doing here and at redo, or doing it everywhere else that might care.
937 : * Since undo and redo are relatively rare, it makes sense to take the (small) performance hit here.
938 : */
939 : nsresult
940 0 : nsTextEditRules::DidUndo(nsISelection *aSelection, nsresult aResult)
941 : {
942 0 : NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
943 : // If aResult is an error, we return it.
944 0 : NS_ENSURE_SUCCESS(aResult, aResult);
945 :
946 0 : dom::Element* theRoot = mEditor->GetRoot();
947 0 : NS_ENSURE_TRUE(theRoot, NS_ERROR_FAILURE);
948 0 : nsIContent* node = mEditor->GetLeftmostChild(theRoot);
949 0 : if (node && mEditor->IsMozEditorBogusNode(node)) {
950 0 : mBogusNode = do_QueryInterface(node);
951 : } else {
952 0 : mBogusNode = nsnull;
953 : }
954 0 : return aResult;
955 : }
956 :
957 : nsresult
958 0 : nsTextEditRules::WillRedo(nsISelection *aSelection, bool *aCancel, bool *aHandled)
959 : {
960 0 : if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
961 0 : CANCEL_OPERATION_IF_READONLY_OR_DISABLED
962 : // initialize out param
963 0 : *aCancel = false;
964 0 : *aHandled = false;
965 0 : return NS_OK;
966 : }
967 :
968 : nsresult
969 0 : nsTextEditRules::DidRedo(nsISelection *aSelection, nsresult aResult)
970 : {
971 0 : nsresult res = aResult; // if aResult is an error, we return it.
972 0 : if (!aSelection) { return NS_ERROR_NULL_POINTER; }
973 0 : if (NS_SUCCEEDED(res))
974 : {
975 0 : nsCOMPtr<nsIDOMElement> theRoot = do_QueryInterface(mEditor->GetRoot());
976 0 : NS_ENSURE_TRUE(theRoot, NS_ERROR_FAILURE);
977 :
978 0 : nsCOMPtr<nsIDOMNodeList> nodeList;
979 0 : res = theRoot->GetElementsByTagName(NS_LITERAL_STRING("br"),
980 0 : getter_AddRefs(nodeList));
981 0 : NS_ENSURE_SUCCESS(res, res);
982 0 : if (nodeList)
983 : {
984 : PRUint32 len;
985 0 : nodeList->GetLength(&len);
986 :
987 0 : if (len != 1) {
988 : // only in the case of one br could there be the bogus node
989 0 : mBogusNode = nsnull;
990 0 : return NS_OK;
991 : }
992 :
993 0 : nsCOMPtr<nsIContent> content = nodeList->GetNodeAt(0);
994 0 : MOZ_ASSERT(content);
995 0 : if (mEditor->IsMozEditorBogusNode(content)) {
996 0 : mBogusNode = do_QueryInterface(content);
997 : } else {
998 0 : mBogusNode = nsnull;
999 : }
1000 : }
1001 : }
1002 0 : return res;
1003 : }
1004 :
1005 : nsresult
1006 0 : nsTextEditRules::WillOutputText(nsISelection *aSelection,
1007 : const nsAString *aOutputFormat,
1008 : nsAString *aOutString,
1009 : bool *aCancel,
1010 : bool *aHandled)
1011 : {
1012 : // null selection ok
1013 0 : if (!aOutString || !aOutputFormat || !aCancel || !aHandled)
1014 0 : { return NS_ERROR_NULL_POINTER; }
1015 :
1016 : // initialize out param
1017 0 : *aCancel = false;
1018 0 : *aHandled = false;
1019 :
1020 0 : nsAutoString outputFormat(*aOutputFormat);
1021 0 : ToLowerCase(outputFormat);
1022 0 : if (outputFormat.EqualsLiteral("text/plain"))
1023 : { // only use these rules for plain text output
1024 0 : if (IsPasswordEditor())
1025 : {
1026 0 : *aOutString = mPasswordText;
1027 0 : *aHandled = true;
1028 : }
1029 0 : else if (mBogusNode)
1030 : { // this means there's no content, so output null string
1031 0 : aOutString->Truncate();
1032 0 : *aHandled = true;
1033 : }
1034 : }
1035 0 : return NS_OK;
1036 : }
1037 :
1038 : nsresult
1039 0 : nsTextEditRules::DidOutputText(nsISelection *aSelection, nsresult aResult)
1040 : {
1041 0 : return NS_OK;
1042 : }
1043 :
1044 : nsresult
1045 0 : nsTextEditRules::RemoveRedundantTrailingBR()
1046 : {
1047 : // If the bogus node exists, we have no work to do
1048 0 : if (mBogusNode)
1049 0 : return NS_OK;
1050 :
1051 : // Likewise, nothing to be done if we could never have inserted a trailing br
1052 0 : if (IsSingleLineEditor())
1053 0 : return NS_OK;
1054 :
1055 0 : nsRefPtr<dom::Element> body = mEditor->GetRoot();
1056 0 : if (!body)
1057 0 : return NS_ERROR_NULL_POINTER;
1058 :
1059 0 : PRUint32 childCount = body->GetChildCount();
1060 0 : if (childCount > 1) {
1061 : // The trailing br is redundant if it is the only remaining child node
1062 0 : return NS_OK;
1063 : }
1064 :
1065 0 : nsRefPtr<nsIContent> child = body->GetFirstChild();
1066 0 : if (!child || !child->IsElement()) {
1067 0 : return NS_OK;
1068 : }
1069 :
1070 0 : dom::Element* elem = child->AsElement();
1071 0 : if (!nsTextEditUtils::IsMozBR(elem)) {
1072 0 : return NS_OK;
1073 : }
1074 :
1075 : // Rather than deleting this node from the DOM tree we should instead
1076 : // morph this br into the bogus node
1077 0 : elem->UnsetAttr(kNameSpaceID_None, nsGkAtoms::type, true);
1078 :
1079 : // set mBogusNode to be this <br>
1080 0 : mBogusNode = do_QueryInterface(elem);
1081 :
1082 : // give it the bogus node attribute
1083 : elem->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
1084 0 : kMOZEditorBogusNodeValue, false);
1085 0 : return NS_OK;
1086 : }
1087 :
1088 : nsresult
1089 0 : nsTextEditRules::CreateTrailingBRIfNeeded()
1090 : {
1091 : // but only if we aren't a single line edit field
1092 0 : if (IsSingleLineEditor())
1093 0 : return NS_OK;
1094 0 : nsCOMPtr<nsIDOMNode> body = do_QueryInterface(mEditor->GetRoot());
1095 0 : NS_ENSURE_TRUE(body, NS_ERROR_NULL_POINTER);
1096 0 : nsCOMPtr<nsIDOMNode> lastChild;
1097 0 : nsresult res = body->GetLastChild(getter_AddRefs(lastChild));
1098 : // assuming CreateBogusNodeIfNeeded() has been called first
1099 0 : NS_ENSURE_SUCCESS(res, res);
1100 0 : NS_ENSURE_TRUE(lastChild, NS_ERROR_NULL_POINTER);
1101 :
1102 0 : if (!nsTextEditUtils::IsBreak(lastChild))
1103 : {
1104 0 : nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
1105 : PRUint32 rootLen;
1106 0 : res = mEditor->GetLengthOfDOMNode(body, rootLen);
1107 0 : NS_ENSURE_SUCCESS(res, res);
1108 0 : nsCOMPtr<nsIDOMNode> unused;
1109 0 : res = CreateMozBR(body, rootLen, address_of(unused));
1110 : }
1111 0 : return res;
1112 : }
1113 :
1114 : nsresult
1115 0 : nsTextEditRules::CreateBogusNodeIfNeeded(nsISelection *aSelection)
1116 : {
1117 0 : NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
1118 0 : NS_ENSURE_TRUE(mEditor, NS_ERROR_NULL_POINTER);
1119 :
1120 0 : if (mBogusNode) {
1121 : // Let's not create more than one, ok?
1122 0 : return NS_OK;
1123 : }
1124 :
1125 : // tell rules system to not do any post-processing
1126 0 : nsAutoRules beginRulesSniffing(mEditor, nsEditor::kOpIgnore, nsIEditor::eNone);
1127 :
1128 0 : nsCOMPtr<dom::Element> body = mEditor->GetRoot();
1129 0 : if (!body) {
1130 : // We don't even have a body yet, don't insert any bogus nodes at
1131 : // this point.
1132 0 : return NS_OK;
1133 : }
1134 :
1135 : // Now we've got the body element. Iterate over the body element's children,
1136 : // looking for editable content. If no editable content is found, insert the
1137 : // bogus node.
1138 0 : for (nsCOMPtr<nsIContent> bodyChild = body->GetFirstChild();
1139 0 : bodyChild;
1140 0 : bodyChild = bodyChild->GetNextSibling()) {
1141 0 : if (mEditor->IsMozEditorBogusNode(bodyChild) ||
1142 0 : !mEditor->IsEditable(body) || // XXX hoist out of the loop?
1143 0 : mEditor->IsEditable(bodyChild)) {
1144 0 : return NS_OK;
1145 : }
1146 : }
1147 :
1148 : // Skip adding the bogus node if body is read-only.
1149 0 : if (!mEditor->IsModifiableNode(body)) {
1150 0 : return NS_OK;
1151 : }
1152 :
1153 : // Create a br.
1154 0 : nsCOMPtr<nsIContent> newContent;
1155 0 : nsresult rv = mEditor->CreateHTMLContent(NS_LITERAL_STRING("br"), getter_AddRefs(newContent));
1156 0 : NS_ENSURE_SUCCESS(rv, rv);
1157 :
1158 : // set mBogusNode to be the newly created <br>
1159 0 : mBogusNode = do_QueryInterface(newContent);
1160 0 : NS_ENSURE_TRUE(mBogusNode, NS_ERROR_NULL_POINTER);
1161 :
1162 : // Give it a special attribute.
1163 : newContent->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
1164 0 : kMOZEditorBogusNodeValue, false);
1165 :
1166 : // Put the node in the document.
1167 0 : nsCOMPtr<nsIDOMNode> bodyNode = do_QueryInterface(body);
1168 0 : rv = mEditor->InsertNode(mBogusNode, bodyNode, 0);
1169 0 : NS_ENSURE_SUCCESS(rv, rv);
1170 :
1171 : // Set selection.
1172 0 : aSelection->CollapseNative(body, 0);
1173 0 : return NS_OK;
1174 : }
1175 :
1176 :
1177 : nsresult
1178 0 : nsTextEditRules::TruncateInsertionIfNeeded(nsISelection *aSelection,
1179 : const nsAString *aInString,
1180 : nsAString *aOutString,
1181 : PRInt32 aMaxLength,
1182 : bool *aTruncated)
1183 : {
1184 0 : if (!aSelection || !aInString || !aOutString) {return NS_ERROR_NULL_POINTER;}
1185 :
1186 0 : nsresult res = NS_OK;
1187 0 : *aOutString = *aInString;
1188 0 : if (aTruncated) {
1189 0 : *aTruncated = false;
1190 : }
1191 :
1192 0 : if ((-1 != aMaxLength) && IsPlaintextEditor() && !mEditor->IsIMEComposing() )
1193 : {
1194 : // Get the current text length.
1195 : // Get the length of inString.
1196 : // Get the length of the selection.
1197 : // If selection is collapsed, it is length 0.
1198 : // Subtract the length of the selection from the len(doc)
1199 : // since we'll delete the selection on insert.
1200 : // This is resultingDocLength.
1201 : // Get old length of IME composing string
1202 : // which will be replaced by new one.
1203 : // If (resultingDocLength) is at or over max, cancel the insert
1204 : // If (resultingDocLength) + (length of input) > max,
1205 : // set aOutString to subset of inString so length = max
1206 : PRInt32 docLength;
1207 0 : res = mEditor->GetTextLength(&docLength);
1208 0 : if (NS_FAILED(res)) { return res; }
1209 :
1210 : PRUint32 start, end;
1211 0 : res = mEditor->GetTextSelectionOffsets(aSelection, start, end);
1212 0 : if (NS_FAILED(res)) { return res; }
1213 :
1214 : PRInt32 oldCompStrLength;
1215 0 : res = mEditor->GetIMEBufferLength(&oldCompStrLength);
1216 0 : if (NS_FAILED(res)) { return res; }
1217 :
1218 0 : const PRInt32 selectionLength = end - start;
1219 0 : const PRInt32 resultingDocLength = docLength - selectionLength - oldCompStrLength;
1220 0 : if (resultingDocLength >= aMaxLength)
1221 : {
1222 0 : aOutString->Truncate();
1223 0 : if (aTruncated) {
1224 0 : *aTruncated = true;
1225 : }
1226 : }
1227 : else
1228 : {
1229 0 : PRInt32 inCount = aOutString->Length();
1230 0 : if (inCount + resultingDocLength > aMaxLength)
1231 : {
1232 0 : aOutString->Truncate(aMaxLength - resultingDocLength);
1233 0 : if (aTruncated) {
1234 0 : *aTruncated = true;
1235 : }
1236 : }
1237 : }
1238 : }
1239 0 : return res;
1240 : }
1241 :
1242 : nsresult
1243 0 : nsTextEditRules::ResetIMETextPWBuf()
1244 : {
1245 0 : mPasswordIMEText.Truncate();
1246 0 : return NS_OK;
1247 : }
1248 :
1249 : nsresult
1250 0 : nsTextEditRules::RemoveIMETextFromPWBuf(PRUint32 &aStart, nsAString *aIMEString)
1251 : {
1252 0 : if (!aIMEString) {
1253 0 : return NS_ERROR_NULL_POINTER;
1254 : }
1255 :
1256 : // initialize PasswordIME
1257 0 : if (mPasswordIMEText.IsEmpty()) {
1258 0 : mPasswordIMEIndex = aStart;
1259 : }
1260 : else {
1261 : // manage the password buffer
1262 0 : mPasswordText.Cut(mPasswordIMEIndex, mPasswordIMEText.Length());
1263 0 : aStart = mPasswordIMEIndex;
1264 : }
1265 :
1266 0 : mPasswordIMEText.Assign(*aIMEString);
1267 0 : return NS_OK;
1268 : }
1269 :
1270 0 : NS_IMETHODIMP nsTextEditRules::Notify(class nsITimer *) {
1271 0 : nsresult res = HideLastPWInput();
1272 0 : ASSERT_PASSWORD_LENGTHS_EQUAL();
1273 0 : mLastLength = 0;
1274 0 : return res;
1275 : }
1276 :
1277 0 : nsresult nsTextEditRules::HideLastPWInput() {
1278 0 : if (!mLastLength) {
1279 : // Special case, we're trying to replace a range that no longer exists
1280 0 : return NS_OK;
1281 : }
1282 :
1283 0 : nsAutoString hiddenText;
1284 0 : FillBufWithPWChars(&hiddenText, mLastLength);
1285 :
1286 0 : nsCOMPtr<nsISelection> selection;
1287 : PRUint32 start, end;
1288 0 : nsresult res = mEditor->GetSelection(getter_AddRefs(selection));
1289 0 : NS_ENSURE_SUCCESS(res, res);
1290 0 : res = mEditor->GetTextSelectionOffsets(selection, start, end);
1291 0 : NS_ENSURE_SUCCESS(res, res);
1292 :
1293 0 : nsCOMPtr<nsIDOMNode> selNode = GetTextNode(selection, mEditor);
1294 0 : NS_ENSURE_TRUE(selNode, NS_OK);
1295 :
1296 0 : nsCOMPtr<nsIDOMCharacterData> nodeAsText(do_QueryInterface(selNode));
1297 0 : NS_ENSURE_TRUE(nodeAsText, NS_OK);
1298 :
1299 0 : nodeAsText->ReplaceData(mLastStart, mLastLength, hiddenText);
1300 0 : selection->Collapse(selNode, start);
1301 0 : if (start != end)
1302 0 : selection->Extend(selNode, end);
1303 0 : return NS_OK;
1304 : }
1305 :
1306 : // static
1307 : nsresult
1308 0 : nsTextEditRules::FillBufWithPWChars(nsAString *aOutString, PRInt32 aLength)
1309 : {
1310 0 : if (!aOutString) {return NS_ERROR_NULL_POINTER;}
1311 :
1312 : // change the output to the platform password character
1313 0 : PRUnichar passwordChar = LookAndFeel::GetPasswordCharacter();
1314 :
1315 : PRInt32 i;
1316 0 : aOutString->Truncate();
1317 0 : for (i=0; i < aLength; i++)
1318 0 : aOutString->Append(passwordChar);
1319 :
1320 0 : return NS_OK;
1321 : }
1322 :
1323 :
1324 : ///////////////////////////////////////////////////////////////////////////
1325 : // CreateMozBR: put a BR node with moz attribute at {aNode, aOffset}
1326 : //
1327 : nsresult
1328 0 : nsTextEditRules::CreateMozBR(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outBRNode)
1329 : {
1330 0 : NS_ENSURE_TRUE(inParent && outBRNode, NS_ERROR_NULL_POINTER);
1331 :
1332 0 : nsresult res = mEditor->CreateBR(inParent, inOffset, outBRNode);
1333 0 : NS_ENSURE_SUCCESS(res, res);
1334 :
1335 : // give it special moz attr
1336 0 : nsCOMPtr<nsIDOMElement> brElem = do_QueryInterface(*outBRNode);
1337 0 : if (brElem)
1338 : {
1339 0 : res = mEditor->SetAttribute(brElem, NS_LITERAL_STRING("type"), NS_LITERAL_STRING("_moz"));
1340 0 : NS_ENSURE_SUCCESS(res, res);
1341 : }
1342 0 : return res;
1343 : }
1344 :
1345 : NS_IMETHODIMP
1346 0 : nsTextEditRules::DocumentModified()
1347 : {
1348 0 : return NS_ERROR_NOT_IMPLEMENTED;
1349 4392 : }
|