editor/libeditor/text/nsPlaintextEditor.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial