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 : * Daniel Glazman <glazman@netscape.com>
24 : * Mats Palmgren <matspal@gmail.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 :
41 : #include "nsPlaintextEditor.h"
42 : #include "nsCaret.h"
43 : #include "nsTextEditUtils.h"
44 : #include "nsTextEditRules.h"
45 : #include "nsIEditActionListener.h"
46 : #include "nsIDOMNodeList.h"
47 : #include "nsIDOMDocument.h"
48 : #include "nsIDocument.h"
49 : #include "nsIDOMEventTarget.h"
50 : #include "nsIDOMKeyEvent.h"
51 : #include "nsISelection.h"
52 : #include "nsISelectionPrivate.h"
53 : #include "nsISelectionController.h"
54 : #include "nsGUIEvent.h"
55 : #include "nsCRT.h"
56 :
57 : #include "nsIEnumerator.h"
58 : #include "nsIContent.h"
59 : #include "nsIContentIterator.h"
60 : #include "nsIDOMRange.h"
61 : #include "nsISupportsArray.h"
62 : #include "nsIComponentManager.h"
63 : #include "nsIServiceManager.h"
64 : #include "nsIDocumentEncoder.h"
65 : #include "nsIPresShell.h"
66 : #include "nsISupportsPrimitives.h"
67 : #include "nsReadableUtils.h"
68 :
69 : // Misc
70 : #include "nsEditorUtils.h" // nsAutoEditBatch, nsAutoRules
71 : #include "nsUnicharUtils.h"
72 : #include "nsContentCID.h"
73 : #include "nsInternetCiter.h"
74 : #include "nsEventDispatcher.h"
75 : #include "nsGkAtoms.h"
76 : #include "nsDebug.h"
77 : #include "mozilla/Preferences.h"
78 : #include "mozilla/dom/Element.h"
79 :
80 : // Drag & Drop, Clipboard
81 : #include "nsIClipboard.h"
82 : #include "nsITransferable.h"
83 : #include "nsCopySupport.h"
84 :
85 : #include "mozilla/FunctionTimer.h"
86 :
87 : using namespace mozilla;
88 :
89 0 : nsPlaintextEditor::nsPlaintextEditor()
90 : : nsEditor()
91 : , mRules(nsnull)
92 : , mWrapToWindow(false)
93 : , mWrapColumn(0)
94 : , mMaxTextLength(-1)
95 : , mInitTriggerCounter(0)
96 : , mNewlineHandling(nsIPlaintextEditor::eNewlinesPasteToFirst)
97 : #ifdef XP_WIN
98 : , mCaretStyle(1)
99 : #else
100 0 : , mCaretStyle(0)
101 : #endif
102 : {
103 0 : }
104 :
105 0 : nsPlaintextEditor::~nsPlaintextEditor()
106 : {
107 : // Remove event listeners. Note that if we had an HTML editor,
108 : // it installed its own instead of these
109 0 : RemoveEventListeners();
110 :
111 0 : if (mRules)
112 0 : mRules->DetachEditor();
113 0 : }
114 :
115 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsPlaintextEditor)
116 :
117 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsPlaintextEditor, nsEditor)
118 0 : if (tmp->mRules)
119 0 : tmp->mRules->DetachEditor();
120 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRules)
121 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
122 :
123 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsPlaintextEditor, nsEditor)
124 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRules)
125 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
126 :
127 0 : NS_IMPL_ADDREF_INHERITED(nsPlaintextEditor, nsEditor)
128 0 : NS_IMPL_RELEASE_INHERITED(nsPlaintextEditor, nsEditor)
129 :
130 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsPlaintextEditor)
131 0 : NS_INTERFACE_MAP_ENTRY(nsIPlaintextEditor)
132 0 : NS_INTERFACE_MAP_ENTRY(nsIEditorMailSupport)
133 0 : NS_INTERFACE_MAP_END_INHERITING(nsEditor)
134 :
135 :
136 0 : NS_IMETHODIMP nsPlaintextEditor::Init(nsIDOMDocument *aDoc,
137 : nsIContent *aRoot,
138 : nsISelectionController *aSelCon,
139 : PRUint32 aFlags)
140 : {
141 : NS_TIME_FUNCTION;
142 :
143 0 : NS_PRECONDITION(aDoc, "bad arg");
144 0 : NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
145 :
146 0 : nsresult res = NS_OK, rulesRes = NS_OK;
147 :
148 : if (1)
149 : {
150 : // block to scope nsAutoEditInitRulesTrigger
151 0 : nsAutoEditInitRulesTrigger rulesTrigger(this, rulesRes);
152 :
153 : // Init the base editor
154 0 : res = nsEditor::Init(aDoc, aRoot, aSelCon, aFlags);
155 : }
156 :
157 : // check the "single line editor newline handling"
158 : // and "caret behaviour in selection" prefs
159 0 : GetDefaultEditorPrefs(mNewlineHandling, mCaretStyle);
160 :
161 0 : NS_ENSURE_SUCCESS(rulesRes, rulesRes);
162 0 : return res;
163 : }
164 :
165 : static PRInt32 sNewlineHandlingPref = -1,
166 : sCaretStylePref = -1;
167 :
168 : static int
169 0 : EditorPrefsChangedCallback(const char *aPrefName, void *)
170 : {
171 0 : if (nsCRT::strcmp(aPrefName, "editor.singleLine.pasteNewlines") == 0) {
172 : sNewlineHandlingPref =
173 : Preferences::GetInt("editor.singleLine.pasteNewlines",
174 0 : nsIPlaintextEditor::eNewlinesPasteToFirst);
175 0 : } else if (nsCRT::strcmp(aPrefName, "layout.selection.caret_style") == 0) {
176 : sCaretStylePref = Preferences::GetInt("layout.selection.caret_style",
177 : #ifdef XP_WIN
178 : 1);
179 : if (sCaretStylePref == 0)
180 : sCaretStylePref = 1;
181 : #else
182 0 : 0);
183 : #endif
184 : }
185 0 : return 0;
186 : }
187 :
188 : // static
189 : void
190 0 : nsPlaintextEditor::GetDefaultEditorPrefs(PRInt32 &aNewlineHandling,
191 : PRInt32 &aCaretStyle)
192 : {
193 0 : if (sNewlineHandlingPref == -1) {
194 : Preferences::RegisterCallback(EditorPrefsChangedCallback,
195 0 : "editor.singleLine.pasteNewlines");
196 0 : EditorPrefsChangedCallback("editor.singleLine.pasteNewlines", nsnull);
197 : Preferences::RegisterCallback(EditorPrefsChangedCallback,
198 0 : "layout.selection.caret_style");
199 0 : EditorPrefsChangedCallback("layout.selection.caret_style", nsnull);
200 : }
201 :
202 0 : aNewlineHandling = sNewlineHandlingPref;
203 0 : aCaretStyle = sCaretStylePref;
204 0 : }
205 :
206 : void
207 0 : nsPlaintextEditor::BeginEditorInit()
208 : {
209 0 : mInitTriggerCounter++;
210 0 : }
211 :
212 : nsresult
213 0 : nsPlaintextEditor::EndEditorInit()
214 : {
215 0 : nsresult res = NS_OK;
216 0 : NS_PRECONDITION(mInitTriggerCounter > 0, "ended editor init before we began?");
217 0 : mInitTriggerCounter--;
218 0 : if (mInitTriggerCounter == 0)
219 : {
220 0 : res = InitRules();
221 0 : if (NS_SUCCEEDED(res)) {
222 : // Throw away the old transaction manager if this is not the first time that
223 : // we're initializing the editor.
224 0 : EnableUndo(false);
225 0 : EnableUndo(true);
226 : }
227 : }
228 0 : return res;
229 : }
230 :
231 : NS_IMETHODIMP
232 0 : nsPlaintextEditor::SetDocumentCharacterSet(const nsACString& characterSet)
233 : {
234 0 : nsresult rv = nsEditor::SetDocumentCharacterSet(characterSet);
235 0 : NS_ENSURE_SUCCESS(rv, rv);
236 :
237 : // Update META charset element.
238 0 : nsCOMPtr<nsIDOMDocument> domdoc;
239 0 : rv = GetDocument(getter_AddRefs(domdoc));
240 0 : NS_ENSURE_SUCCESS(rv, rv);
241 0 : NS_ENSURE_TRUE(domdoc, NS_ERROR_FAILURE);
242 :
243 0 : if (UpdateMetaCharset(domdoc, characterSet)) {
244 0 : return NS_OK;
245 : }
246 :
247 0 : nsCOMPtr<nsIDOMNodeList> headList;
248 0 : rv = domdoc->GetElementsByTagName(NS_LITERAL_STRING("head"), getter_AddRefs(headList));
249 0 : NS_ENSURE_SUCCESS(rv, rv);
250 0 : NS_ENSURE_TRUE(headList, NS_OK);
251 :
252 0 : nsCOMPtr<nsIDOMNode> headNode;
253 0 : headList->Item(0, getter_AddRefs(headNode));
254 0 : NS_ENSURE_TRUE(headNode, NS_OK);
255 :
256 : // Create a new meta charset tag
257 0 : nsCOMPtr<nsIDOMNode> resultNode;
258 0 : rv = CreateNode(NS_LITERAL_STRING("meta"), headNode, 0, getter_AddRefs(resultNode));
259 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
260 0 : NS_ENSURE_TRUE(resultNode, NS_OK);
261 :
262 : // Set attributes to the created element
263 0 : if (characterSet.IsEmpty()) {
264 0 : return NS_OK;
265 : }
266 :
267 0 : nsCOMPtr<dom::Element> metaElement = do_QueryInterface(resultNode);
268 0 : if (!metaElement) {
269 0 : return NS_OK;
270 : }
271 :
272 : // not undoable, undo should undo CreateNode
273 0 : metaElement->SetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv,
274 0 : NS_LITERAL_STRING("Content-Type"), true);
275 0 : metaElement->SetAttr(kNameSpaceID_None, nsGkAtoms::content,
276 0 : NS_LITERAL_STRING("text/html;charset=") +
277 0 : NS_ConvertASCIItoUTF16(characterSet),
278 0 : true);
279 0 : return NS_OK;
280 : }
281 :
282 : bool
283 0 : nsPlaintextEditor::UpdateMetaCharset(nsIDOMDocument* aDocument,
284 : const nsACString& aCharacterSet)
285 : {
286 : // get a list of META tags
287 0 : nsCOMPtr<nsIDOMNodeList> metaList;
288 0 : nsresult rv = aDocument->GetElementsByTagName(NS_LITERAL_STRING("meta"),
289 0 : getter_AddRefs(metaList));
290 0 : NS_ENSURE_SUCCESS(rv, false);
291 0 : NS_ENSURE_TRUE(metaList, false);
292 :
293 0 : PRUint32 listLength = 0;
294 0 : metaList->GetLength(&listLength);
295 :
296 0 : for (PRUint32 i = 0; i < listLength; ++i) {
297 0 : nsCOMPtr<nsIContent> metaNode = metaList->GetNodeAt(i);
298 0 : MOZ_ASSERT(metaNode);
299 :
300 0 : if (!metaNode->IsElement()) {
301 0 : continue;
302 : }
303 :
304 0 : nsAutoString currentValue;
305 0 : metaNode->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, currentValue);
306 :
307 0 : if (!FindInReadable(NS_LITERAL_STRING("content-type"),
308 : currentValue,
309 0 : nsCaseInsensitiveStringComparator())) {
310 0 : continue;
311 : }
312 :
313 0 : metaNode->GetAttr(kNameSpaceID_None, nsGkAtoms::content, currentValue);
314 :
315 0 : NS_NAMED_LITERAL_STRING(charsetEquals, "charset=");
316 0 : nsAString::const_iterator originalStart, start, end;
317 0 : originalStart = currentValue.BeginReading(start);
318 0 : currentValue.EndReading(end);
319 0 : if (!FindInReadable(charsetEquals, start, end,
320 0 : nsCaseInsensitiveStringComparator())) {
321 0 : continue;
322 : }
323 :
324 : // set attribute to <original prefix> charset=text/html
325 0 : nsCOMPtr<nsIDOMElement> metaElement = do_QueryInterface(metaNode);
326 0 : MOZ_ASSERT(metaElement);
327 0 : rv = nsEditor::SetAttribute(metaElement, NS_LITERAL_STRING("content"),
328 0 : Substring(originalStart, start) +
329 0 : charsetEquals +
330 0 : NS_ConvertASCIItoUTF16(aCharacterSet));
331 0 : return NS_SUCCEEDED(rv);
332 : }
333 0 : return false;
334 : }
335 :
336 0 : NS_IMETHODIMP nsPlaintextEditor::InitRules()
337 : {
338 : // instantiate the rules for this text editor
339 0 : mRules = new nsTextEditRules();
340 0 : return mRules->Init(this);
341 : }
342 :
343 :
344 : NS_IMETHODIMP
345 0 : nsPlaintextEditor::GetIsDocumentEditable(bool *aIsDocumentEditable)
346 : {
347 0 : NS_ENSURE_ARG_POINTER(aIsDocumentEditable);
348 :
349 0 : nsCOMPtr<nsIDOMDocument> doc;
350 0 : GetDocument(getter_AddRefs(doc));
351 0 : *aIsDocumentEditable = doc ? IsModifiable() : false;
352 :
353 0 : return NS_OK;
354 : }
355 :
356 0 : bool nsPlaintextEditor::IsModifiable()
357 : {
358 0 : return !IsReadonly();
359 : }
360 :
361 : nsresult
362 0 : nsPlaintextEditor::HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent)
363 : {
364 : // NOTE: When you change this method, you should also change:
365 : // * editor/libeditor/text/tests/test_texteditor_keyevent_handling.html
366 : // * editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
367 : //
368 : // And also when you add new key handling, you need to change the subclass's
369 : // HandleKeyPressEvent()'s switch statement.
370 :
371 0 : if (IsReadonly() || IsDisabled()) {
372 : // When we're not editable, the events handled on nsEditor.
373 0 : return nsEditor::HandleKeyPressEvent(aKeyEvent);
374 : }
375 :
376 0 : nsKeyEvent* nativeKeyEvent = GetNativeKeyEvent(aKeyEvent);
377 0 : NS_ENSURE_TRUE(nativeKeyEvent, NS_ERROR_UNEXPECTED);
378 0 : NS_ASSERTION(nativeKeyEvent->message == NS_KEY_PRESS,
379 : "HandleKeyPressEvent gets non-keypress event");
380 :
381 0 : switch (nativeKeyEvent->keyCode) {
382 : case nsIDOMKeyEvent::DOM_VK_META:
383 : case nsIDOMKeyEvent::DOM_VK_SHIFT:
384 : case nsIDOMKeyEvent::DOM_VK_CONTROL:
385 : case nsIDOMKeyEvent::DOM_VK_ALT:
386 : case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
387 : case nsIDOMKeyEvent::DOM_VK_DELETE:
388 : // These keys are handled on nsEditor
389 0 : return nsEditor::HandleKeyPressEvent(aKeyEvent);
390 : case nsIDOMKeyEvent::DOM_VK_TAB: {
391 0 : if (IsTabbable()) {
392 0 : return NS_OK; // let it be used for focus switching
393 : }
394 :
395 0 : if (nativeKeyEvent->isShift || nativeKeyEvent->isControl ||
396 : nativeKeyEvent->isAlt || nativeKeyEvent->isMeta) {
397 0 : return NS_OK;
398 : }
399 :
400 : // else we insert the tab straight through
401 0 : aKeyEvent->PreventDefault();
402 0 : return TypedText(NS_LITERAL_STRING("\t"), eTypedText);
403 : }
404 : case nsIDOMKeyEvent::DOM_VK_RETURN:
405 : case nsIDOMKeyEvent::DOM_VK_ENTER:
406 0 : if (IsSingleLineEditor() || nativeKeyEvent->isControl ||
407 : nativeKeyEvent->isAlt || nativeKeyEvent->isMeta) {
408 0 : return NS_OK;
409 : }
410 0 : aKeyEvent->PreventDefault();
411 0 : return TypedText(EmptyString(), eTypedBreak);
412 : }
413 :
414 : // NOTE: On some keyboard layout, some characters are inputted with Control
415 : // key or Alt key, but at that time, widget sets FALSE to these keys.
416 0 : if (nativeKeyEvent->charCode == 0 || nativeKeyEvent->isControl ||
417 : nativeKeyEvent->isAlt || nativeKeyEvent->isMeta) {
418 : // we don't PreventDefault() here or keybindings like control-x won't work
419 0 : return NS_OK;
420 : }
421 0 : aKeyEvent->PreventDefault();
422 0 : nsAutoString str(nativeKeyEvent->charCode);
423 0 : return TypedText(str, eTypedText);
424 : }
425 :
426 : /* This routine is needed to provide a bottleneck for typing for logging
427 : purposes. Can't use HandleKeyPress() (above) for that since it takes
428 : a nsIDOMKeyEvent* parameter. So instead we pass enough info through
429 : to TypedText() to determine what action to take, but without passing
430 : an event.
431 : */
432 0 : NS_IMETHODIMP nsPlaintextEditor::TypedText(const nsAString& aString,
433 : PRInt32 aAction)
434 : {
435 0 : nsAutoPlaceHolderBatch batch(this, nsGkAtoms::TypingTxnName);
436 :
437 0 : switch (aAction)
438 : {
439 : case eTypedText:
440 : {
441 0 : return InsertText(aString);
442 : }
443 : case eTypedBreak:
444 : {
445 0 : return InsertLineBreak();
446 : }
447 : }
448 0 : return NS_ERROR_FAILURE;
449 : }
450 :
451 : nsresult
452 0 : nsPlaintextEditor::CreateBRImpl(nsCOMPtr<nsIDOMNode>* aInOutParent,
453 : PRInt32* aInOutOffset,
454 : nsCOMPtr<nsIDOMNode>* outBRNode,
455 : EDirection aSelect)
456 : {
457 0 : NS_ENSURE_TRUE(aInOutParent && *aInOutParent && aInOutOffset && outBRNode, NS_ERROR_NULL_POINTER);
458 0 : *outBRNode = nsnull;
459 : nsresult res;
460 :
461 : // we need to insert a br. unfortunately, we may have to split a text node to do it.
462 0 : nsCOMPtr<nsIDOMNode> node = *aInOutParent;
463 0 : PRInt32 theOffset = *aInOutOffset;
464 0 : nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(node);
465 0 : NS_NAMED_LITERAL_STRING(brType, "br");
466 0 : nsCOMPtr<nsIDOMNode> brNode;
467 0 : if (nodeAsText)
468 : {
469 0 : nsCOMPtr<nsIDOMNode> tmp;
470 : PRInt32 offset;
471 : PRUint32 len;
472 0 : nodeAsText->GetLength(&len);
473 0 : GetNodeLocation(node, address_of(tmp), &offset);
474 0 : NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
475 0 : if (!theOffset)
476 : {
477 : // we are already set to go
478 : }
479 0 : else if (theOffset == (PRInt32)len)
480 : {
481 : // update offset to point AFTER the text node
482 0 : offset++;
483 : }
484 : else
485 : {
486 : // split the text node
487 0 : res = SplitNode(node, theOffset, getter_AddRefs(tmp));
488 0 : NS_ENSURE_SUCCESS(res, res);
489 0 : res = GetNodeLocation(node, address_of(tmp), &offset);
490 0 : NS_ENSURE_SUCCESS(res, res);
491 : }
492 : // create br
493 0 : res = CreateNode(brType, tmp, offset, getter_AddRefs(brNode));
494 0 : NS_ENSURE_SUCCESS(res, res);
495 0 : *aInOutParent = tmp;
496 0 : *aInOutOffset = offset+1;
497 : }
498 : else
499 : {
500 0 : res = CreateNode(brType, node, theOffset, getter_AddRefs(brNode));
501 0 : NS_ENSURE_SUCCESS(res, res);
502 0 : (*aInOutOffset)++;
503 : }
504 :
505 0 : *outBRNode = brNode;
506 0 : if (*outBRNode && (aSelect != eNone))
507 : {
508 0 : nsCOMPtr<nsIDOMNode> parent;
509 : PRInt32 offset;
510 0 : res = GetNodeLocation(*outBRNode, address_of(parent), &offset);
511 0 : NS_ENSURE_SUCCESS(res, res);
512 :
513 0 : nsCOMPtr<nsISelection> selection;
514 0 : res = GetSelection(getter_AddRefs(selection));
515 0 : NS_ENSURE_SUCCESS(res, res);
516 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
517 0 : if (aSelect == eNext)
518 : {
519 : // position selection after br
520 0 : selPriv->SetInterlinePosition(true);
521 0 : res = selection->Collapse(parent, offset+1);
522 : }
523 0 : else if (aSelect == ePrevious)
524 : {
525 : // position selection before br
526 0 : selPriv->SetInterlinePosition(true);
527 0 : res = selection->Collapse(parent, offset);
528 : }
529 : }
530 0 : return NS_OK;
531 : }
532 :
533 :
534 0 : NS_IMETHODIMP nsPlaintextEditor::CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPtr<nsIDOMNode> *outBRNode, EDirection aSelect)
535 : {
536 0 : nsCOMPtr<nsIDOMNode> parent = aNode;
537 0 : PRInt32 offset = aOffset;
538 0 : return CreateBRImpl(address_of(parent), &offset, outBRNode, aSelect);
539 : }
540 :
541 : nsresult
542 0 : nsPlaintextEditor::InsertBR(nsCOMPtr<nsIDOMNode>* outBRNode)
543 : {
544 0 : NS_ENSURE_TRUE(outBRNode, NS_ERROR_NULL_POINTER);
545 0 : *outBRNode = nsnull;
546 :
547 : // calling it text insertion to trigger moz br treatment by rules
548 0 : nsAutoRules beginRulesSniffing(this, kOpInsertText, nsIEditor::eNext);
549 :
550 0 : nsCOMPtr<nsISelection> selection;
551 0 : nsresult res = GetSelection(getter_AddRefs(selection));
552 0 : NS_ENSURE_SUCCESS(res, res);
553 : bool bCollapsed;
554 0 : res = selection->GetIsCollapsed(&bCollapsed);
555 0 : NS_ENSURE_SUCCESS(res, res);
556 0 : if (!bCollapsed)
557 : {
558 0 : res = DeleteSelection(nsIEditor::eNone);
559 0 : NS_ENSURE_SUCCESS(res, res);
560 : }
561 0 : nsCOMPtr<nsIDOMNode> selNode;
562 : PRInt32 selOffset;
563 0 : res = GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset);
564 0 : NS_ENSURE_SUCCESS(res, res);
565 :
566 0 : res = CreateBR(selNode, selOffset, outBRNode);
567 0 : NS_ENSURE_SUCCESS(res, res);
568 :
569 : // position selection after br
570 0 : res = GetNodeLocation(*outBRNode, address_of(selNode), &selOffset);
571 0 : NS_ENSURE_SUCCESS(res, res);
572 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
573 0 : selPriv->SetInterlinePosition(true);
574 0 : return selection->Collapse(selNode, selOffset+1);
575 : }
576 :
577 : nsresult
578 0 : nsPlaintextEditor::GetTextSelectionOffsets(nsISelection *aSelection,
579 : PRUint32 &aOutStartOffset,
580 : PRUint32 &aOutEndOffset)
581 : {
582 0 : NS_ASSERTION(aSelection, "null selection");
583 :
584 : nsresult rv;
585 0 : nsCOMPtr<nsIDOMNode> startNode, endNode;
586 : PRInt32 startNodeOffset, endNodeOffset;
587 0 : aSelection->GetAnchorNode(getter_AddRefs(startNode));
588 0 : aSelection->GetAnchorOffset(&startNodeOffset);
589 0 : aSelection->GetFocusNode(getter_AddRefs(endNode));
590 0 : aSelection->GetFocusOffset(&endNodeOffset);
591 :
592 0 : dom::Element *rootElement = GetRoot();
593 0 : nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(rootElement);
594 0 : NS_ENSURE_TRUE(rootNode, NS_ERROR_NULL_POINTER);
595 :
596 0 : PRInt32 startOffset = -1;
597 0 : PRInt32 endOffset = -1;
598 :
599 : nsCOMPtr<nsIContentIterator> iter =
600 0 : do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &rv);
601 0 : NS_ENSURE_SUCCESS(rv, rv);
602 :
603 : #ifdef NS_DEBUG
604 0 : PRInt32 nodeCount = 0; // only needed for the assertions below
605 : #endif
606 0 : PRUint32 totalLength = 0;
607 0 : iter->Init(rootElement);
608 0 : for (; !iter->IsDone() && (startOffset == -1 || endOffset == -1); iter->Next()) {
609 0 : nsCOMPtr<nsIDOMNode> currentNode = do_QueryInterface(iter->GetCurrentNode());
610 0 : nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(currentNode);
611 0 : if (textNode) {
612 : // Note that sometimes we have an empty #text-node as start/endNode,
613 : // which we regard as not editable because the frame width == 0,
614 : // see nsEditor::IsEditable().
615 0 : bool editable = IsEditable(currentNode);
616 0 : if (currentNode == startNode) {
617 0 : startOffset = totalLength + (editable ? startNodeOffset : 0);
618 : }
619 0 : if (currentNode == endNode) {
620 0 : endOffset = totalLength + (editable ? endNodeOffset : 0);
621 : }
622 0 : if (editable) {
623 : PRUint32 length;
624 0 : textNode->GetLength(&length);
625 0 : totalLength += length;
626 : }
627 : }
628 : #ifdef NS_DEBUG
629 : // The post content iterator might return the parent node (which is the
630 : // editor's root node) as the last item. Don't count the root node itself
631 : // as one of its children!
632 0 : if (!SameCOMIdentity(currentNode, rootNode)) {
633 0 : ++nodeCount;
634 : }
635 : #endif
636 : }
637 :
638 0 : if (endOffset == -1) {
639 0 : NS_ASSERTION(endNode == rootNode, "failed to find the end node");
640 0 : NS_ASSERTION(IsPasswordEditor() ||
641 : (endNodeOffset == nodeCount-1 || endNodeOffset == 0),
642 : "invalid end node offset");
643 0 : endOffset = endNodeOffset == 0 ? 0 : totalLength;
644 : }
645 0 : if (startOffset == -1) {
646 0 : NS_ASSERTION(startNode == rootNode, "failed to find the start node");
647 0 : NS_ASSERTION(startNodeOffset == nodeCount-1 || startNodeOffset == 0,
648 : "invalid start node offset");
649 0 : startOffset = startNodeOffset == 0 ? 0 : totalLength;
650 : }
651 :
652 : // Make sure aOutStartOffset <= aOutEndOffset.
653 0 : if (startOffset <= endOffset) {
654 0 : aOutStartOffset = startOffset;
655 0 : aOutEndOffset = endOffset;
656 : }
657 : else {
658 0 : aOutStartOffset = endOffset;
659 0 : aOutEndOffset = startOffset;
660 : }
661 :
662 0 : return NS_OK;
663 : }
664 :
665 : nsresult
666 0 : nsPlaintextEditor::ExtendSelectionForDelete(nsISelection *aSelection,
667 : nsIEditor::EDirection *aAction)
668 : {
669 : nsresult result;
670 :
671 : bool bCollapsed;
672 0 : result = aSelection->GetIsCollapsed(&bCollapsed);
673 0 : NS_ENSURE_SUCCESS(result, result);
674 :
675 0 : if (*aAction == eNextWord || *aAction == ePreviousWord
676 : || (*aAction == eNext && bCollapsed)
677 : || (*aAction == ePrevious && bCollapsed)
678 : || *aAction == eToBeginningOfLine || *aAction == eToEndOfLine)
679 : {
680 0 : nsCOMPtr<nsISelectionController> selCont;
681 0 : GetSelectionController(getter_AddRefs(selCont));
682 0 : NS_ENSURE_TRUE(selCont, NS_ERROR_NO_INTERFACE);
683 :
684 0 : switch (*aAction)
685 : {
686 : case eNextWord:
687 0 : result = selCont->WordExtendForDelete(true);
688 : // DeleteSelectionImpl doesn't handle these actions
689 : // because it's inside batching, so don't confuse it:
690 0 : *aAction = eNone;
691 0 : break;
692 : case ePreviousWord:
693 0 : result = selCont->WordExtendForDelete(false);
694 0 : *aAction = eNone;
695 0 : break;
696 : case eNext:
697 0 : result = selCont->CharacterExtendForDelete();
698 : // Don't set aAction to eNone (see Bug 502259)
699 0 : break;
700 : case ePrevious: {
701 : // Only extend the selection where the selection is after a UTF-16
702 : // surrogate pair. For other cases we don't want to do that, in order
703 : // to make sure that pressing backspace will only delete the last
704 : // typed character.
705 0 : nsCOMPtr<nsIDOMNode> node;
706 : PRInt32 offset;
707 0 : result = GetStartNodeAndOffset(aSelection, getter_AddRefs(node), &offset);
708 0 : NS_ENSURE_SUCCESS(result, result);
709 0 : NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
710 :
711 0 : if (IsTextNode(node)) {
712 0 : nsCOMPtr<nsIDOMCharacterData> charData = do_QueryInterface(node);
713 0 : if (charData) {
714 0 : nsAutoString data;
715 0 : result = charData->GetData(data);
716 0 : NS_ENSURE_SUCCESS(result, result);
717 :
718 0 : if (offset > 1 &&
719 0 : NS_IS_LOW_SURROGATE(data[offset - 1]) &&
720 0 : NS_IS_HIGH_SURROGATE(data[offset - 2])) {
721 0 : result = selCont->CharacterExtendForBackspace();
722 : }
723 : }
724 : }
725 0 : break;
726 : }
727 : case eToBeginningOfLine:
728 0 : selCont->IntraLineMove(true, false); // try to move to end
729 0 : result = selCont->IntraLineMove(false, true); // select to beginning
730 0 : *aAction = eNone;
731 0 : break;
732 : case eToEndOfLine:
733 0 : result = selCont->IntraLineMove(true, true);
734 0 : *aAction = eNext;
735 0 : break;
736 : default: // avoid several compiler warnings
737 0 : result = NS_OK;
738 0 : break;
739 : }
740 : }
741 0 : return result;
742 : }
743 :
744 0 : NS_IMETHODIMP nsPlaintextEditor::DeleteSelection(nsIEditor::EDirection aAction)
745 : {
746 0 : if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
747 :
748 : // Protect the edit rules object from dying
749 0 : nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
750 :
751 : nsresult result;
752 :
753 0 : FireTrustedInputEvent trusted(this, aAction != eNone);
754 :
755 : // delete placeholder txns merge.
756 0 : nsAutoPlaceHolderBatch batch(this, nsGkAtoms::DeleteTxnName);
757 0 : nsAutoRules beginRulesSniffing(this, kOpDeleteSelection, aAction);
758 :
759 : // pre-process
760 0 : nsCOMPtr<nsISelection> selection;
761 0 : result = GetSelection(getter_AddRefs(selection));
762 0 : NS_ENSURE_SUCCESS(result, result);
763 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
764 :
765 : // If there is an existing selection when an extended delete is requested,
766 : // platforms that use "caret-style" caret positioning collapse the
767 : // selection to the start and then create a new selection.
768 : // Platforms that use "selection-style" caret positioning just delete the
769 : // existing selection without extending it.
770 : bool bCollapsed;
771 0 : result = selection->GetIsCollapsed(&bCollapsed);
772 0 : NS_ENSURE_SUCCESS(result, result);
773 0 : if (!bCollapsed &&
774 : (aAction == eNextWord || aAction == ePreviousWord ||
775 : aAction == eToBeginningOfLine || aAction == eToEndOfLine))
776 : {
777 0 : if (mCaretStyle == 1)
778 : {
779 0 : result = selection->CollapseToStart();
780 0 : NS_ENSURE_SUCCESS(result, result);
781 : }
782 : else
783 : {
784 0 : aAction = eNone;
785 : }
786 : }
787 :
788 0 : nsTextRulesInfo ruleInfo(nsTextEditRules::kDeleteSelection);
789 0 : ruleInfo.collapsedAction = aAction;
790 : bool cancel, handled;
791 0 : result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
792 0 : NS_ENSURE_SUCCESS(result, result);
793 0 : if (!cancel && !handled)
794 : {
795 0 : result = DeleteSelectionImpl(aAction);
796 : }
797 0 : if (!cancel)
798 : {
799 : // post-process
800 0 : result = mRules->DidDoAction(selection, &ruleInfo, result);
801 : }
802 :
803 0 : return result;
804 : }
805 :
806 0 : NS_IMETHODIMP nsPlaintextEditor::InsertText(const nsAString &aStringToInsert)
807 : {
808 0 : if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
809 :
810 : // Protect the edit rules object from dying
811 0 : nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
812 :
813 0 : PRInt32 theAction = nsTextEditRules::kInsertText;
814 0 : PRInt32 opID = kOpInsertText;
815 0 : if (mInIMEMode)
816 : {
817 0 : theAction = nsTextEditRules::kInsertTextIME;
818 0 : opID = kOpInsertIMEText;
819 : }
820 0 : nsAutoPlaceHolderBatch batch(this, nsnull);
821 0 : nsAutoRules beginRulesSniffing(this, opID, nsIEditor::eNext);
822 :
823 : // pre-process
824 0 : nsCOMPtr<nsISelection> selection;
825 0 : nsresult result = GetSelection(getter_AddRefs(selection));
826 0 : NS_ENSURE_SUCCESS(result, result);
827 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
828 0 : nsAutoString resultString;
829 : // XXX can we trust instring to outlive ruleInfo,
830 : // XXX and ruleInfo not to refer to instring in its dtor?
831 : //nsAutoString instring(aStringToInsert);
832 0 : nsTextRulesInfo ruleInfo(theAction);
833 0 : ruleInfo.inString = &aStringToInsert;
834 0 : ruleInfo.outString = &resultString;
835 0 : ruleInfo.maxLength = mMaxTextLength;
836 :
837 : bool cancel, handled;
838 0 : result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
839 0 : NS_ENSURE_SUCCESS(result, result);
840 0 : if (!cancel && !handled)
841 : {
842 : // we rely on rules code for now - no default implementation
843 : }
844 0 : if (!cancel)
845 : {
846 : // post-process
847 0 : result = mRules->DidDoAction(selection, &ruleInfo, result);
848 : }
849 0 : return result;
850 : }
851 :
852 0 : NS_IMETHODIMP nsPlaintextEditor::InsertLineBreak()
853 : {
854 0 : if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
855 :
856 : // Protect the edit rules object from dying
857 0 : nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
858 :
859 0 : nsAutoEditBatch beginBatching(this);
860 0 : nsAutoRules beginRulesSniffing(this, kOpInsertBreak, nsIEditor::eNext);
861 :
862 : // pre-process
863 0 : nsCOMPtr<nsISelection> selection;
864 : nsresult res;
865 0 : res = GetSelection(getter_AddRefs(selection));
866 0 : NS_ENSURE_SUCCESS(res, res);
867 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
868 :
869 : // Batching the selection and moving nodes out from under the caret causes
870 : // caret turds. Ask the shell to invalidate the caret now to avoid the turds.
871 0 : nsCOMPtr<nsIPresShell> shell = GetPresShell();
872 0 : NS_ENSURE_TRUE(shell, NS_ERROR_NOT_INITIALIZED);
873 0 : shell->MaybeInvalidateCaretPosition();
874 :
875 0 : nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertBreak);
876 0 : ruleInfo.maxLength = mMaxTextLength;
877 : bool cancel, handled;
878 0 : res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
879 0 : NS_ENSURE_SUCCESS(res, res);
880 0 : if (!cancel && !handled)
881 : {
882 : // get the (collapsed) selection location
883 0 : nsCOMPtr<nsIDOMNode> selNode;
884 : PRInt32 selOffset;
885 0 : res = GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset);
886 0 : NS_ENSURE_SUCCESS(res, res);
887 :
888 : // don't put text in places that can't have it
889 0 : if (!IsTextNode(selNode) && !CanContainTag(selNode, NS_LITERAL_STRING("#text")))
890 0 : return NS_ERROR_FAILURE;
891 :
892 : // we need to get the doc
893 0 : nsCOMPtr<nsIDOMDocument> doc;
894 0 : res = GetDocument(getter_AddRefs(doc));
895 0 : NS_ENSURE_SUCCESS(res, res);
896 0 : NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);
897 :
898 : // don't spaz my selection in subtransactions
899 0 : nsAutoTxnsConserveSelection dontSpazMySelection(this);
900 :
901 : // insert a linefeed character
902 0 : res = InsertTextImpl(NS_LITERAL_STRING("\n"), address_of(selNode),
903 0 : &selOffset, doc);
904 0 : if (!selNode) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called
905 0 : if (NS_SUCCEEDED(res))
906 : {
907 : // set the selection to the correct location
908 0 : res = selection->Collapse(selNode, selOffset);
909 :
910 0 : if (NS_SUCCEEDED(res))
911 : {
912 : // see if we're at the end of the editor range
913 0 : nsCOMPtr<nsIDOMNode> endNode;
914 : PRInt32 endOffset;
915 0 : res = GetEndNodeAndOffset(selection, getter_AddRefs(endNode), &endOffset);
916 :
917 0 : if (NS_SUCCEEDED(res) && endNode == selNode && endOffset == selOffset)
918 : {
919 : // SetInterlinePosition(true) means we want the caret to stick to the content on the "right".
920 : // We want the caret to stick to whatever is past the break. This is
921 : // because the break is on the same line we were on, but the next content
922 : // will be on the following line.
923 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
924 0 : selPriv->SetInterlinePosition(true);
925 : }
926 : }
927 : }
928 : }
929 0 : if (!cancel)
930 : {
931 : // post-process, always called if WillInsertBreak didn't return cancel==true
932 0 : res = mRules->DidDoAction(selection, &ruleInfo, res);
933 : }
934 :
935 0 : return res;
936 : }
937 :
938 : nsresult
939 0 : nsPlaintextEditor::BeginIMEComposition()
940 : {
941 0 : NS_ENSURE_TRUE(!mInIMEMode, NS_OK);
942 :
943 0 : if (IsPasswordEditor()) {
944 0 : NS_ENSURE_TRUE(mRules, NS_ERROR_NULL_POINTER);
945 : // Protect the edit rules object from dying
946 0 : nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
947 :
948 : nsTextEditRules *textEditRules =
949 0 : static_cast<nsTextEditRules*>(mRules.get());
950 0 : textEditRules->ResetIMETextPWBuf();
951 : }
952 :
953 0 : return nsEditor::BeginIMEComposition();
954 : }
955 :
956 : nsresult
957 0 : nsPlaintextEditor::UpdateIMEComposition(const nsAString& aCompositionString,
958 : nsIPrivateTextRangeList* aTextRangeList)
959 : {
960 0 : NS_ABORT_IF_FALSE(aTextRangeList, "aTextRangeList must not be NULL");
961 :
962 0 : nsCOMPtr<nsIPresShell> ps = GetPresShell();
963 0 : NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
964 :
965 0 : nsCOMPtr<nsISelection> selection;
966 0 : nsresult rv = GetSelection(getter_AddRefs(selection));
967 0 : NS_ENSURE_SUCCESS(rv, rv);
968 :
969 0 : nsRefPtr<nsCaret> caretP = ps->GetCaret();
970 :
971 : // Update information of clauses in the new composition string.
972 : // This will be refered by followed methods.
973 0 : mIMETextRangeList = aTextRangeList;
974 :
975 : // We set mIsIMEComposing properly.
976 0 : SetIsIMEComposing();
977 :
978 : {
979 0 : nsAutoPlaceHolderBatch batch(this, nsGkAtoms::IMETxnName);
980 :
981 0 : rv = InsertText(aCompositionString);
982 :
983 0 : mIMEBufferLength = aCompositionString.Length();
984 :
985 0 : if (caretP) {
986 0 : caretP->SetCaretDOMSelection(selection);
987 : }
988 : }
989 :
990 : // If still composing, we should fire input event via observer.
991 : // Note that if committed, we don't need to notify it since it will be
992 : // notified at followed compositionend event.
993 : // NOTE: We must notify after the auto batch will be gone.
994 0 : if (mIsIMEComposing) {
995 0 : NotifyEditorObservers();
996 : }
997 :
998 0 : return rv;
999 : }
1000 :
1001 : NS_IMETHODIMP
1002 0 : nsPlaintextEditor::GetDocumentIsEmpty(bool *aDocumentIsEmpty)
1003 : {
1004 0 : NS_ENSURE_TRUE(aDocumentIsEmpty, NS_ERROR_NULL_POINTER);
1005 :
1006 0 : NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
1007 :
1008 : // Protect the edit rules object from dying
1009 0 : nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
1010 :
1011 0 : return mRules->DocumentIsEmpty(aDocumentIsEmpty);
1012 : }
1013 :
1014 : NS_IMETHODIMP
1015 0 : nsPlaintextEditor::GetTextLength(PRInt32 *aCount)
1016 : {
1017 0 : NS_ASSERTION(aCount, "null pointer");
1018 :
1019 : // initialize out params
1020 0 : *aCount = 0;
1021 :
1022 : // special-case for empty document, to account for the bogus node
1023 : bool docEmpty;
1024 0 : nsresult rv = GetDocumentIsEmpty(&docEmpty);
1025 0 : NS_ENSURE_SUCCESS(rv, rv);
1026 0 : if (docEmpty)
1027 0 : return NS_OK;
1028 :
1029 0 : dom::Element *rootElement = GetRoot();
1030 0 : NS_ENSURE_TRUE(rootElement, NS_ERROR_NULL_POINTER);
1031 :
1032 : nsCOMPtr<nsIContentIterator> iter =
1033 0 : do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &rv);
1034 0 : NS_ENSURE_SUCCESS(rv, rv);
1035 :
1036 0 : PRUint32 totalLength = 0;
1037 0 : iter->Init(rootElement);
1038 0 : for (; !iter->IsDone(); iter->Next()) {
1039 0 : nsCOMPtr<nsIDOMNode> currentNode = do_QueryInterface(iter->GetCurrentNode());
1040 0 : nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(currentNode);
1041 0 : if (textNode && IsEditable(currentNode)) {
1042 : PRUint32 length;
1043 0 : textNode->GetLength(&length);
1044 0 : totalLength += length;
1045 : }
1046 : }
1047 :
1048 0 : *aCount = totalLength;
1049 0 : return NS_OK;
1050 : }
1051 :
1052 : NS_IMETHODIMP
1053 0 : nsPlaintextEditor::SetMaxTextLength(PRInt32 aMaxTextLength)
1054 : {
1055 0 : mMaxTextLength = aMaxTextLength;
1056 0 : return NS_OK;
1057 : }
1058 :
1059 : NS_IMETHODIMP
1060 0 : nsPlaintextEditor::GetMaxTextLength(PRInt32* aMaxTextLength)
1061 : {
1062 0 : NS_ENSURE_TRUE(aMaxTextLength, NS_ERROR_INVALID_POINTER);
1063 0 : *aMaxTextLength = mMaxTextLength;
1064 0 : return NS_OK;
1065 : }
1066 :
1067 : //
1068 : // Get the wrap width
1069 : //
1070 : NS_IMETHODIMP
1071 0 : nsPlaintextEditor::GetWrapWidth(PRInt32 *aWrapColumn)
1072 : {
1073 0 : NS_ENSURE_TRUE( aWrapColumn, NS_ERROR_NULL_POINTER);
1074 :
1075 0 : *aWrapColumn = mWrapColumn;
1076 0 : return NS_OK;
1077 : }
1078 :
1079 : //
1080 : // See if the style value includes this attribute, and if it does,
1081 : // cut out everything from the attribute to the next semicolon.
1082 : //
1083 0 : static void CutStyle(const char* stylename, nsString& styleValue)
1084 : {
1085 : // Find the current wrapping type:
1086 0 : PRInt32 styleStart = styleValue.Find(stylename, true);
1087 0 : if (styleStart >= 0)
1088 : {
1089 0 : PRInt32 styleEnd = styleValue.Find(";", false, styleStart);
1090 0 : if (styleEnd > styleStart)
1091 0 : styleValue.Cut(styleStart, styleEnd - styleStart + 1);
1092 : else
1093 0 : styleValue.Cut(styleStart, styleValue.Length() - styleStart);
1094 : }
1095 0 : }
1096 :
1097 : //
1098 : // Change the wrap width on the root of this document.
1099 : //
1100 : NS_IMETHODIMP
1101 0 : nsPlaintextEditor::SetWrapWidth(PRInt32 aWrapColumn)
1102 : {
1103 0 : SetWrapColumn(aWrapColumn);
1104 :
1105 : // Make sure we're a plaintext editor, otherwise we shouldn't
1106 : // do the rest of this.
1107 0 : if (!IsPlaintextEditor())
1108 0 : return NS_OK;
1109 :
1110 : // Ought to set a style sheet here ...
1111 : // Probably should keep around an mPlaintextStyleSheet for this purpose.
1112 0 : dom::Element *rootElement = GetRoot();
1113 0 : NS_ENSURE_TRUE(rootElement, NS_ERROR_NULL_POINTER);
1114 :
1115 : // Get the current style for this root element:
1116 0 : nsAutoString styleValue;
1117 0 : nsresult res = rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::style, styleValue);
1118 0 : NS_ENSURE_SUCCESS(res, res);
1119 :
1120 : // We'll replace styles for these values:
1121 0 : CutStyle("white-space", styleValue);
1122 0 : CutStyle("width", styleValue);
1123 0 : CutStyle("font-family", styleValue);
1124 :
1125 : // If we have other style left, trim off any existing semicolons
1126 : // or whitespace, then add a known semicolon-space:
1127 0 : if (!styleValue.IsEmpty())
1128 : {
1129 0 : styleValue.Trim("; \t", false, true);
1130 0 : styleValue.AppendLiteral("; ");
1131 : }
1132 :
1133 : // Make sure we have fixed-width font. This should be done for us,
1134 : // but it isn't, see bug 22502, so we have to add "font: -moz-fixed;".
1135 : // Only do this if we're wrapping.
1136 0 : if (IsWrapHackEnabled() && aWrapColumn >= 0)
1137 0 : styleValue.AppendLiteral("font-family: -moz-fixed; ");
1138 :
1139 : // If "mail.compose.wrap_to_window_width" is set, and we're a mail editor,
1140 : // then remember our wrap width (for output purposes) but set the visual
1141 : // wrapping to window width.
1142 : // We may reset mWrapToWindow here, based on the pref's current value.
1143 0 : if (IsMailEditor())
1144 : {
1145 : mWrapToWindow =
1146 0 : Preferences::GetBool("mail.compose.wrap_to_window_width", mWrapToWindow);
1147 : }
1148 :
1149 : // and now we're ready to set the new whitespace/wrapping style.
1150 0 : if (aWrapColumn > 0 && !mWrapToWindow) // Wrap to a fixed column
1151 : {
1152 0 : styleValue.AppendLiteral("white-space: pre-wrap; width: ");
1153 0 : styleValue.AppendInt(aWrapColumn);
1154 0 : styleValue.AppendLiteral("ch;");
1155 : }
1156 0 : else if (mWrapToWindow || aWrapColumn == 0)
1157 0 : styleValue.AppendLiteral("white-space: pre-wrap;");
1158 : else
1159 0 : styleValue.AppendLiteral("white-space: pre;");
1160 :
1161 0 : return rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::style, styleValue, true);
1162 : }
1163 :
1164 : NS_IMETHODIMP
1165 0 : nsPlaintextEditor::SetWrapColumn(PRInt32 aWrapColumn)
1166 : {
1167 0 : mWrapColumn = aWrapColumn;
1168 0 : return NS_OK;
1169 : }
1170 :
1171 : //
1172 : // Get the newline handling for this editor
1173 : //
1174 : NS_IMETHODIMP
1175 0 : nsPlaintextEditor::GetNewlineHandling(PRInt32 *aNewlineHandling)
1176 : {
1177 0 : NS_ENSURE_ARG_POINTER(aNewlineHandling);
1178 :
1179 0 : *aNewlineHandling = mNewlineHandling;
1180 0 : return NS_OK;
1181 : }
1182 :
1183 : //
1184 : // Change the newline handling for this editor
1185 : //
1186 : NS_IMETHODIMP
1187 0 : nsPlaintextEditor::SetNewlineHandling(PRInt32 aNewlineHandling)
1188 : {
1189 0 : mNewlineHandling = aNewlineHandling;
1190 :
1191 0 : return NS_OK;
1192 : }
1193 :
1194 : NS_IMETHODIMP
1195 0 : nsPlaintextEditor::Undo(PRUint32 aCount)
1196 : {
1197 : // Protect the edit rules object from dying
1198 0 : nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
1199 :
1200 0 : FireTrustedInputEvent trusted(this);
1201 :
1202 0 : nsAutoUpdateViewBatch beginViewBatching(this);
1203 :
1204 0 : ForceCompositionEnd();
1205 :
1206 0 : nsAutoRules beginRulesSniffing(this, kOpUndo, nsIEditor::eNone);
1207 :
1208 0 : nsTextRulesInfo ruleInfo(nsTextEditRules::kUndo);
1209 0 : nsCOMPtr<nsISelection> selection;
1210 0 : GetSelection(getter_AddRefs(selection));
1211 : bool cancel, handled;
1212 0 : nsresult result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
1213 :
1214 0 : if (!cancel && NS_SUCCEEDED(result))
1215 : {
1216 0 : result = nsEditor::Undo(aCount);
1217 0 : result = mRules->DidDoAction(selection, &ruleInfo, result);
1218 : }
1219 :
1220 0 : NotifyEditorObservers();
1221 0 : return result;
1222 : }
1223 :
1224 : NS_IMETHODIMP
1225 0 : nsPlaintextEditor::Redo(PRUint32 aCount)
1226 : {
1227 : // Protect the edit rules object from dying
1228 0 : nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
1229 :
1230 0 : FireTrustedInputEvent trusted(this);
1231 :
1232 0 : nsAutoUpdateViewBatch beginViewBatching(this);
1233 :
1234 0 : ForceCompositionEnd();
1235 :
1236 0 : nsAutoRules beginRulesSniffing(this, kOpRedo, nsIEditor::eNone);
1237 :
1238 0 : nsTextRulesInfo ruleInfo(nsTextEditRules::kRedo);
1239 0 : nsCOMPtr<nsISelection> selection;
1240 0 : GetSelection(getter_AddRefs(selection));
1241 : bool cancel, handled;
1242 0 : nsresult result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
1243 :
1244 0 : if (!cancel && NS_SUCCEEDED(result))
1245 : {
1246 0 : result = nsEditor::Redo(aCount);
1247 0 : result = mRules->DidDoAction(selection, &ruleInfo, result);
1248 : }
1249 :
1250 0 : NotifyEditorObservers();
1251 0 : return result;
1252 : }
1253 :
1254 : bool
1255 0 : nsPlaintextEditor::CanCutOrCopy()
1256 : {
1257 0 : nsCOMPtr<nsISelection> selection;
1258 0 : if (NS_FAILED(GetSelection(getter_AddRefs(selection))))
1259 0 : return false;
1260 :
1261 : bool isCollapsed;
1262 0 : selection->GetIsCollapsed(&isCollapsed);
1263 0 : return !isCollapsed;
1264 : }
1265 :
1266 : bool
1267 0 : nsPlaintextEditor::FireClipboardEvent(PRInt32 aType)
1268 : {
1269 0 : if (aType == NS_PASTE)
1270 0 : ForceCompositionEnd();
1271 :
1272 0 : nsCOMPtr<nsIPresShell> presShell = GetPresShell();
1273 0 : NS_ENSURE_TRUE(presShell, false);
1274 :
1275 0 : nsCOMPtr<nsISelection> selection;
1276 0 : if (NS_FAILED(GetSelection(getter_AddRefs(selection))))
1277 0 : return false;
1278 :
1279 0 : if (!nsCopySupport::FireClipboardEvent(aType, presShell, selection))
1280 0 : return false;
1281 :
1282 : // If the event handler caused the editor to be destroyed, return false.
1283 : // Otherwise return true to indicate that the event was not cancelled.
1284 0 : return !mDidPreDestroy;
1285 : }
1286 :
1287 0 : NS_IMETHODIMP nsPlaintextEditor::Cut()
1288 : {
1289 0 : FireTrustedInputEvent trusted(this);
1290 :
1291 0 : if (FireClipboardEvent(NS_CUT))
1292 0 : return DeleteSelection(eNone);
1293 0 : return NS_OK;
1294 : }
1295 :
1296 0 : NS_IMETHODIMP nsPlaintextEditor::CanCut(bool *aCanCut)
1297 : {
1298 0 : NS_ENSURE_ARG_POINTER(aCanCut);
1299 0 : *aCanCut = IsModifiable() && CanCutOrCopy();
1300 0 : return NS_OK;
1301 : }
1302 :
1303 0 : NS_IMETHODIMP nsPlaintextEditor::Copy()
1304 : {
1305 0 : FireClipboardEvent(NS_COPY);
1306 0 : return NS_OK;
1307 : }
1308 :
1309 0 : NS_IMETHODIMP nsPlaintextEditor::CanCopy(bool *aCanCopy)
1310 : {
1311 0 : NS_ENSURE_ARG_POINTER(aCanCopy);
1312 0 : *aCanCopy = CanCutOrCopy();
1313 0 : return NS_OK;
1314 : }
1315 :
1316 : // Shared between OutputToString and OutputToStream
1317 : NS_IMETHODIMP
1318 0 : nsPlaintextEditor::GetAndInitDocEncoder(const nsAString& aFormatType,
1319 : PRUint32 aFlags,
1320 : const nsACString& aCharset,
1321 : nsIDocumentEncoder** encoder)
1322 : {
1323 0 : nsresult rv = NS_OK;
1324 :
1325 0 : nsCAutoString formatType(NS_DOC_ENCODER_CONTRACTID_BASE);
1326 0 : formatType.AppendWithConversion(aFormatType);
1327 0 : nsCOMPtr<nsIDocumentEncoder> docEncoder (do_CreateInstance(formatType.get(), &rv));
1328 0 : NS_ENSURE_SUCCESS(rv, rv);
1329 :
1330 0 : nsCOMPtr<nsIDOMDocument> domDoc = do_QueryReferent(mDocWeak);
1331 0 : NS_ASSERTION(domDoc, "Need a document");
1332 :
1333 0 : rv = docEncoder->Init(domDoc, aFormatType, aFlags);
1334 0 : NS_ENSURE_SUCCESS(rv, rv);
1335 :
1336 0 : if (!aCharset.IsEmpty() && !aCharset.EqualsLiteral("null")) {
1337 0 : docEncoder->SetCharset(aCharset);
1338 : }
1339 :
1340 : PRInt32 wc;
1341 0 : (void) GetWrapWidth(&wc);
1342 0 : if (wc >= 0)
1343 0 : (void) docEncoder->SetWrapColumn(wc);
1344 :
1345 : // Set the selection, if appropriate.
1346 : // We do this either if the OutputSelectionOnly flag is set,
1347 : // in which case we use our existing selection ...
1348 0 : if (aFlags & nsIDocumentEncoder::OutputSelectionOnly)
1349 : {
1350 0 : nsCOMPtr<nsISelection> selection;
1351 0 : rv = GetSelection(getter_AddRefs(selection));
1352 0 : NS_ENSURE_SUCCESS(rv, rv);
1353 0 : if (selection) {
1354 0 : rv = docEncoder->SetSelection(selection);
1355 0 : NS_ENSURE_SUCCESS(rv, rv);
1356 : }
1357 : }
1358 : // ... or if the root element is not a body,
1359 : // in which case we set the selection to encompass the root.
1360 : else
1361 : {
1362 0 : dom::Element* rootElement = GetRoot();
1363 0 : NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE);
1364 0 : if (!rootElement->IsHTML(nsGkAtoms::body)) {
1365 0 : rv = docEncoder->SetNativeContainerNode(rootElement);
1366 0 : NS_ENSURE_SUCCESS(rv, rv);
1367 : }
1368 : }
1369 :
1370 0 : docEncoder.forget(encoder);
1371 0 : return NS_OK;
1372 : }
1373 :
1374 :
1375 : NS_IMETHODIMP
1376 0 : nsPlaintextEditor::OutputToString(const nsAString& aFormatType,
1377 : PRUint32 aFlags,
1378 : nsAString& aOutputString)
1379 : {
1380 : // Protect the edit rules object from dying
1381 0 : nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
1382 :
1383 0 : nsString resultString;
1384 0 : nsTextRulesInfo ruleInfo(nsTextEditRules::kOutputText);
1385 0 : ruleInfo.outString = &resultString;
1386 : // XXX Struct should store a nsAReadable*
1387 0 : nsAutoString str(aFormatType);
1388 0 : ruleInfo.outputFormat = &str;
1389 : bool cancel, handled;
1390 0 : nsresult rv = mRules->WillDoAction(nsnull, &ruleInfo, &cancel, &handled);
1391 0 : if (cancel || NS_FAILED(rv)) { return rv; }
1392 0 : if (handled)
1393 : { // this case will get triggered by password fields
1394 0 : aOutputString.Assign(*(ruleInfo.outString));
1395 0 : return rv;
1396 : }
1397 :
1398 0 : nsCAutoString charsetStr;
1399 0 : rv = GetDocumentCharacterSet(charsetStr);
1400 0 : if(NS_FAILED(rv) || charsetStr.IsEmpty())
1401 0 : charsetStr.AssignLiteral("ISO-8859-1");
1402 :
1403 0 : nsCOMPtr<nsIDocumentEncoder> encoder;
1404 0 : rv = GetAndInitDocEncoder(aFormatType, aFlags, charsetStr, getter_AddRefs(encoder));
1405 0 : NS_ENSURE_SUCCESS(rv, rv);
1406 0 : return encoder->EncodeToString(aOutputString);
1407 : }
1408 :
1409 : NS_IMETHODIMP
1410 0 : nsPlaintextEditor::OutputToStream(nsIOutputStream* aOutputStream,
1411 : const nsAString& aFormatType,
1412 : const nsACString& aCharset,
1413 : PRUint32 aFlags)
1414 : {
1415 : nsresult rv;
1416 :
1417 : // special-case for empty document when requesting plain text,
1418 : // to account for the bogus text node.
1419 : // XXX Should there be a similar test in OutputToString?
1420 0 : if (aFormatType.EqualsLiteral("text/plain"))
1421 : {
1422 : bool docEmpty;
1423 0 : rv = GetDocumentIsEmpty(&docEmpty);
1424 0 : NS_ENSURE_SUCCESS(rv, rv);
1425 :
1426 0 : if (docEmpty)
1427 0 : return NS_OK; // output nothing
1428 : }
1429 :
1430 0 : nsCOMPtr<nsIDocumentEncoder> encoder;
1431 : rv = GetAndInitDocEncoder(aFormatType, aFlags, aCharset,
1432 0 : getter_AddRefs(encoder));
1433 :
1434 0 : NS_ENSURE_SUCCESS(rv, rv);
1435 :
1436 0 : return encoder->EncodeToStream(aOutputStream);
1437 : }
1438 :
1439 : NS_IMETHODIMP
1440 0 : nsPlaintextEditor::InsertTextWithQuotations(const nsAString &aStringToInsert)
1441 : {
1442 0 : return InsertText(aStringToInsert);
1443 : }
1444 :
1445 : NS_IMETHODIMP
1446 0 : nsPlaintextEditor::PasteAsQuotation(PRInt32 aSelectionType)
1447 : {
1448 : // Get Clipboard Service
1449 : nsresult rv;
1450 0 : nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
1451 0 : NS_ENSURE_SUCCESS(rv, rv);
1452 :
1453 : // Create generic Transferable for getting the data
1454 0 : nsCOMPtr<nsITransferable> trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
1455 0 : if (NS_SUCCEEDED(rv) && trans)
1456 : {
1457 : // We only handle plaintext pastes here
1458 0 : trans->AddDataFlavor(kUnicodeMime);
1459 :
1460 : // Get the Data from the clipboard
1461 0 : clipboard->GetData(trans, aSelectionType);
1462 :
1463 : // Now we ask the transferable for the data
1464 : // it still owns the data, we just have a pointer to it.
1465 : // If it can't support a "text" output of the data the call will fail
1466 0 : nsCOMPtr<nsISupports> genericDataObj;
1467 : PRUint32 len;
1468 0 : char* flav = nsnull;
1469 0 : rv = trans->GetAnyTransferData(&flav, getter_AddRefs(genericDataObj),
1470 0 : &len);
1471 0 : if (NS_FAILED(rv) || !flav)
1472 : {
1473 : #ifdef DEBUG_akkana
1474 : printf("PasteAsPlaintextQuotation: GetAnyTransferData failed, %d\n", rv);
1475 : #endif
1476 0 : return rv;
1477 : }
1478 : #ifdef DEBUG_clipboard
1479 : printf("Got flavor [%s]\n", flav);
1480 : #endif
1481 0 : if (0 == nsCRT::strcmp(flav, kUnicodeMime))
1482 : {
1483 0 : nsCOMPtr<nsISupportsString> textDataObj ( do_QueryInterface(genericDataObj) );
1484 0 : if (textDataObj && len > 0)
1485 : {
1486 0 : nsAutoString stuffToPaste;
1487 0 : textDataObj->GetData ( stuffToPaste );
1488 0 : nsAutoEditBatch beginBatching(this);
1489 0 : rv = InsertAsQuotation(stuffToPaste, 0);
1490 : }
1491 : }
1492 0 : NS_Free(flav);
1493 : }
1494 :
1495 0 : return rv;
1496 : }
1497 :
1498 : NS_IMETHODIMP
1499 0 : nsPlaintextEditor::InsertAsQuotation(const nsAString& aQuotedText,
1500 : nsIDOMNode **aNodeInserted)
1501 : {
1502 : // Protect the edit rules object from dying
1503 0 : nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
1504 :
1505 : // Let the citer quote it for us:
1506 0 : nsString quotedStuff;
1507 0 : nsresult rv = nsInternetCiter::GetCiteString(aQuotedText, quotedStuff);
1508 0 : NS_ENSURE_SUCCESS(rv, rv);
1509 :
1510 : // It's best to put a blank line after the quoted text so that mails
1511 : // written without thinking won't be so ugly.
1512 0 : if (!aQuotedText.IsEmpty() && (aQuotedText.Last() != PRUnichar('\n')))
1513 0 : quotedStuff.Append(PRUnichar('\n'));
1514 :
1515 : // get selection
1516 0 : nsCOMPtr<nsISelection> selection;
1517 0 : rv = GetSelection(getter_AddRefs(selection));
1518 0 : NS_ENSURE_SUCCESS(rv, rv);
1519 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
1520 :
1521 0 : nsAutoEditBatch beginBatching(this);
1522 0 : nsAutoRules beginRulesSniffing(this, kOpInsertText, nsIEditor::eNext);
1523 :
1524 : // give rules a chance to handle or cancel
1525 0 : nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertElement);
1526 : bool cancel, handled;
1527 0 : rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
1528 0 : NS_ENSURE_SUCCESS(rv, rv);
1529 0 : if (cancel) return NS_OK; // rules canceled the operation
1530 0 : if (!handled)
1531 : {
1532 0 : rv = InsertText(quotedStuff);
1533 :
1534 : // XXX Should set *aNodeInserted to the first node inserted
1535 0 : if (aNodeInserted && NS_SUCCEEDED(rv))
1536 : {
1537 0 : *aNodeInserted = 0;
1538 : //NS_IF_ADDREF(*aNodeInserted);
1539 : }
1540 : }
1541 0 : return rv;
1542 : }
1543 :
1544 : NS_IMETHODIMP
1545 0 : nsPlaintextEditor::PasteAsCitedQuotation(const nsAString& aCitation,
1546 : PRInt32 aSelectionType)
1547 : {
1548 0 : return NS_ERROR_NOT_IMPLEMENTED;
1549 : }
1550 :
1551 : NS_IMETHODIMP
1552 0 : nsPlaintextEditor::InsertAsCitedQuotation(const nsAString& aQuotedText,
1553 : const nsAString& aCitation,
1554 : bool aInsertHTML,
1555 : nsIDOMNode **aNodeInserted)
1556 : {
1557 0 : return InsertAsQuotation(aQuotedText, aNodeInserted);
1558 : }
1559 :
1560 : nsresult
1561 0 : nsPlaintextEditor::SharedOutputString(PRUint32 aFlags,
1562 : bool* aIsCollapsed,
1563 : nsAString& aResult)
1564 : {
1565 0 : nsCOMPtr<nsISelection> selection;
1566 0 : nsresult rv = GetSelection(getter_AddRefs(selection));
1567 0 : NS_ENSURE_SUCCESS(rv, rv);
1568 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED);
1569 :
1570 0 : rv = selection->GetIsCollapsed(aIsCollapsed);
1571 0 : NS_ENSURE_SUCCESS(rv, rv);
1572 :
1573 0 : if (!*aIsCollapsed)
1574 0 : aFlags |= nsIDocumentEncoder::OutputSelectionOnly;
1575 : // If the selection isn't collapsed, we'll use the whole document.
1576 :
1577 0 : return OutputToString(NS_LITERAL_STRING("text/plain"), aFlags, aResult);
1578 : }
1579 :
1580 : NS_IMETHODIMP
1581 0 : nsPlaintextEditor::Rewrap(bool aRespectNewlines)
1582 : {
1583 : PRInt32 wrapCol;
1584 0 : nsresult rv = GetWrapWidth(&wrapCol);
1585 0 : NS_ENSURE_SUCCESS(rv, NS_OK);
1586 :
1587 : // Rewrap makes no sense if there's no wrap column; default to 72.
1588 0 : if (wrapCol <= 0)
1589 0 : wrapCol = 72;
1590 :
1591 : #ifdef DEBUG_akkana
1592 : printf("nsPlaintextEditor::Rewrap to %ld columns\n", (long)wrapCol);
1593 : #endif
1594 :
1595 0 : nsAutoString current;
1596 : bool isCollapsed;
1597 : rv = SharedOutputString(nsIDocumentEncoder::OutputFormatted
1598 : | nsIDocumentEncoder::OutputLFLineBreak,
1599 0 : &isCollapsed, current);
1600 0 : NS_ENSURE_SUCCESS(rv, rv);
1601 :
1602 0 : nsString wrapped;
1603 0 : PRUint32 firstLineOffset = 0; // XXX need to reset this if there is a selection
1604 : rv = nsInternetCiter::Rewrap(current, wrapCol, firstLineOffset, aRespectNewlines,
1605 0 : wrapped);
1606 0 : NS_ENSURE_SUCCESS(rv, rv);
1607 :
1608 0 : if (isCollapsed) // rewrap the whole document
1609 0 : SelectAll();
1610 :
1611 0 : return InsertTextWithQuotations(wrapped);
1612 : }
1613 :
1614 : NS_IMETHODIMP
1615 0 : nsPlaintextEditor::StripCites()
1616 : {
1617 : #ifdef DEBUG_akkana
1618 : printf("nsPlaintextEditor::StripCites()\n");
1619 : #endif
1620 :
1621 0 : nsAutoString current;
1622 : bool isCollapsed;
1623 : nsresult rv = SharedOutputString(nsIDocumentEncoder::OutputFormatted,
1624 0 : &isCollapsed, current);
1625 0 : NS_ENSURE_SUCCESS(rv, rv);
1626 :
1627 0 : nsString stripped;
1628 0 : rv = nsInternetCiter::StripCites(current, stripped);
1629 0 : NS_ENSURE_SUCCESS(rv, rv);
1630 :
1631 0 : if (isCollapsed) // rewrap the whole document
1632 : {
1633 0 : rv = SelectAll();
1634 0 : NS_ENSURE_SUCCESS(rv, rv);
1635 : }
1636 :
1637 0 : return InsertText(stripped);
1638 : }
1639 :
1640 : NS_IMETHODIMP
1641 0 : nsPlaintextEditor::GetEmbeddedObjects(nsISupportsArray** aNodeList)
1642 : {
1643 0 : *aNodeList = 0;
1644 0 : return NS_OK;
1645 : }
1646 :
1647 :
1648 : /** All editor operations which alter the doc should be prefaced
1649 : * with a call to StartOperation, naming the action and direction */
1650 : NS_IMETHODIMP
1651 0 : nsPlaintextEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection)
1652 : {
1653 : // Protect the edit rules object from dying
1654 0 : nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
1655 :
1656 0 : nsEditor::StartOperation(opID, aDirection); // will set mAction, mDirection
1657 0 : if (mRules) return mRules->BeforeEdit(mAction, mDirection);
1658 0 : return NS_OK;
1659 : }
1660 :
1661 :
1662 : /** All editor operations which alter the doc should be followed
1663 : * with a call to EndOperation */
1664 : NS_IMETHODIMP
1665 0 : nsPlaintextEditor::EndOperation()
1666 : {
1667 : // Protect the edit rules object from dying
1668 0 : nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
1669 :
1670 : // post processing
1671 0 : nsresult res = NS_OK;
1672 0 : if (mRules) res = mRules->AfterEdit(mAction, mDirection);
1673 0 : nsEditor::EndOperation(); // will clear mAction, mDirection
1674 0 : return res;
1675 : }
1676 :
1677 :
1678 : NS_IMETHODIMP
1679 0 : nsPlaintextEditor::SelectEntireDocument(nsISelection *aSelection)
1680 : {
1681 0 : if (!aSelection || !mRules) { return NS_ERROR_NULL_POINTER; }
1682 :
1683 : // Protect the edit rules object from dying
1684 0 : nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
1685 :
1686 : // is doc empty?
1687 : bool bDocIsEmpty;
1688 0 : if (NS_SUCCEEDED(mRules->DocumentIsEmpty(&bDocIsEmpty)) && bDocIsEmpty)
1689 : {
1690 : // get root node
1691 0 : nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(GetRoot());
1692 0 : NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE);
1693 :
1694 : // if it's empty don't select entire doc - that would select the bogus node
1695 0 : return aSelection->Collapse(rootElement, 0);
1696 : }
1697 :
1698 0 : nsresult rv = nsEditor::SelectEntireDocument(aSelection);
1699 0 : NS_ENSURE_SUCCESS(rv, rv);
1700 :
1701 : // Don't select the trailing BR node if we have one
1702 : PRInt32 selOffset;
1703 0 : nsCOMPtr<nsIDOMNode> selNode;
1704 0 : rv = GetEndNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
1705 0 : NS_ENSURE_SUCCESS(rv, rv);
1706 :
1707 0 : nsCOMPtr<nsIDOMNode> childNode = GetChildAt(selNode, selOffset - 1);
1708 :
1709 0 : if (childNode && nsTextEditUtils::IsMozBR(childNode)) {
1710 0 : nsCOMPtr<nsIDOMNode> parentNode;
1711 : PRInt32 parentOffset;
1712 0 : rv = GetNodeLocation(childNode, address_of(parentNode), &parentOffset);
1713 0 : NS_ENSURE_SUCCESS(rv, rv);
1714 :
1715 0 : return aSelection->Extend(parentNode, parentOffset);
1716 : }
1717 :
1718 0 : return NS_OK;
1719 : }
1720 :
1721 : already_AddRefed<nsIDOMEventTarget>
1722 0 : nsPlaintextEditor::GetDOMEventTarget()
1723 : {
1724 0 : nsCOMPtr<nsIDOMEventTarget> copy = mEventTarget;
1725 0 : return copy.forget();
1726 : }
1727 :
1728 :
1729 : nsresult
1730 0 : nsPlaintextEditor::SetAttributeOrEquivalent(nsIDOMElement * aElement,
1731 : const nsAString & aAttribute,
1732 : const nsAString & aValue,
1733 : bool aSuppressTransaction)
1734 : {
1735 0 : return nsEditor::SetAttribute(aElement, aAttribute, aValue);
1736 : }
1737 :
1738 : nsresult
1739 0 : nsPlaintextEditor::RemoveAttributeOrEquivalent(nsIDOMElement * aElement,
1740 : const nsAString & aAttribute,
1741 : bool aSuppressTransaction)
1742 : {
1743 0 : return nsEditor::RemoveAttribute(aElement, aAttribute);
1744 4392 : }
|