editor/libeditor/text/nsPlaintextEditor.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/editor/libeditor/text/nsPlaintextEditor.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1614 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +
    1.10 +#include "mozilla/Assertions.h"
    1.11 +#include "mozilla/Preferences.h"
    1.12 +#include "mozilla/dom/Selection.h"
    1.13 +#include "mozilla/TextComposition.h"
    1.14 +#include "mozilla/TextEvents.h"
    1.15 +#include "mozilla/dom/Element.h"
    1.16 +#include "mozilla/mozalloc.h"
    1.17 +#include "nsAString.h"
    1.18 +#include "nsAutoPtr.h"
    1.19 +#include "nsCRT.h"
    1.20 +#include "nsCaret.h"
    1.21 +#include "nsCharTraits.h"
    1.22 +#include "nsComponentManagerUtils.h"
    1.23 +#include "nsContentCID.h"
    1.24 +#include "nsCopySupport.h"
    1.25 +#include "nsDebug.h"
    1.26 +#include "nsDependentSubstring.h"
    1.27 +#include "nsEditRules.h"
    1.28 +#include "nsEditorUtils.h"  // nsAutoEditBatch, nsAutoRules
    1.29 +#include "nsError.h"
    1.30 +#include "nsGkAtoms.h"
    1.31 +#include "nsIClipboard.h"
    1.32 +#include "nsIContent.h"
    1.33 +#include "nsIContentIterator.h"
    1.34 +#include "nsIDOMCharacterData.h"
    1.35 +#include "nsIDOMDocument.h"
    1.36 +#include "nsIDOMElement.h"
    1.37 +#include "nsIDOMEventTarget.h" 
    1.38 +#include "nsIDOMKeyEvent.h"
    1.39 +#include "nsIDOMNode.h"
    1.40 +#include "nsIDOMNodeList.h"
    1.41 +#include "nsIDocumentEncoder.h"
    1.42 +#include "nsIEditorIMESupport.h"
    1.43 +#include "nsNameSpaceManager.h"
    1.44 +#include "nsINode.h"
    1.45 +#include "nsIPresShell.h"
    1.46 +#include "nsISelection.h"
    1.47 +#include "nsISelectionController.h"
    1.48 +#include "nsISelectionPrivate.h"
    1.49 +#include "nsISupportsPrimitives.h"
    1.50 +#include "nsITransferable.h"
    1.51 +#include "nsIWeakReferenceUtils.h"
    1.52 +#include "nsInternetCiter.h"
    1.53 +#include "nsLiteralString.h"
    1.54 +#include "nsPlaintextEditor.h"
    1.55 +#include "nsReadableUtils.h"
    1.56 +#include "nsServiceManagerUtils.h"
    1.57 +#include "nsString.h"
    1.58 +#include "nsStringFwd.h"
    1.59 +#include "nsSubstringTuple.h"
    1.60 +#include "nsTextEditRules.h"
    1.61 +#include "nsTextEditUtils.h"
    1.62 +#include "nsUnicharUtils.h"
    1.63 +#include "nsXPCOM.h"
    1.64 +
    1.65 +class nsIOutputStream;
    1.66 +class nsISupports;
    1.67 +class nsISupportsArray;
    1.68 +
    1.69 +using namespace mozilla;
    1.70 +using namespace mozilla::dom;
    1.71 +
    1.72 +nsPlaintextEditor::nsPlaintextEditor()
    1.73 +: nsEditor()
    1.74 +, mRules(nullptr)
    1.75 +, mWrapToWindow(false)
    1.76 +, mWrapColumn(0)
    1.77 +, mMaxTextLength(-1)
    1.78 +, mInitTriggerCounter(0)
    1.79 +, mNewlineHandling(nsIPlaintextEditor::eNewlinesPasteToFirst)
    1.80 +#ifdef XP_WIN
    1.81 +, mCaretStyle(1)
    1.82 +#else
    1.83 +, mCaretStyle(0)
    1.84 +#endif
    1.85 +{
    1.86 +  // check the "single line editor newline handling"
    1.87 +  // and "caret behaviour in selection" prefs
    1.88 +  GetDefaultEditorPrefs(mNewlineHandling, mCaretStyle);
    1.89 +}
    1.90 +
    1.91 +nsPlaintextEditor::~nsPlaintextEditor()
    1.92 +{
    1.93 +  // Remove event listeners. Note that if we had an HTML editor,
    1.94 +  //  it installed its own instead of these
    1.95 +  RemoveEventListeners();
    1.96 +
    1.97 +  if (mRules)
    1.98 +    mRules->DetachEditor();
    1.99 +}
   1.100 +
   1.101 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsPlaintextEditor)
   1.102 +
   1.103 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsPlaintextEditor, nsEditor)
   1.104 +  if (tmp->mRules)
   1.105 +    tmp->mRules->DetachEditor();
   1.106 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRules)
   1.107 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   1.108 +
   1.109 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsPlaintextEditor, nsEditor)
   1.110 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRules)
   1.111 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1.112 +
   1.113 +NS_IMPL_ADDREF_INHERITED(nsPlaintextEditor, nsEditor)
   1.114 +NS_IMPL_RELEASE_INHERITED(nsPlaintextEditor, nsEditor)
   1.115 +
   1.116 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsPlaintextEditor)
   1.117 +  NS_INTERFACE_MAP_ENTRY(nsIPlaintextEditor)
   1.118 +  NS_INTERFACE_MAP_ENTRY(nsIEditorMailSupport)
   1.119 +NS_INTERFACE_MAP_END_INHERITING(nsEditor)
   1.120 +
   1.121 +
   1.122 +NS_IMETHODIMP nsPlaintextEditor::Init(nsIDOMDocument *aDoc, 
   1.123 +                                      nsIContent *aRoot,
   1.124 +                                      nsISelectionController *aSelCon,
   1.125 +                                      uint32_t aFlags,
   1.126 +                                      const nsAString& aInitialValue)
   1.127 +{
   1.128 +  NS_PRECONDITION(aDoc, "bad arg");
   1.129 +  NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
   1.130 +  
   1.131 +  nsresult res = NS_OK, rulesRes = NS_OK;
   1.132 +  if (mRules) {
   1.133 +    mRules->DetachEditor();
   1.134 +  }
   1.135 +  
   1.136 +  {
   1.137 +    // block to scope nsAutoEditInitRulesTrigger
   1.138 +    nsAutoEditInitRulesTrigger rulesTrigger(this, rulesRes);
   1.139 +  
   1.140 +    // Init the base editor
   1.141 +    res = nsEditor::Init(aDoc, aRoot, aSelCon, aFlags, aInitialValue);
   1.142 +  }
   1.143 +
   1.144 +  NS_ENSURE_SUCCESS(rulesRes, rulesRes);
   1.145 +
   1.146 +  // mRules may not have been initialized yet, when this is called via
   1.147 +  // nsHTMLEditor::Init.
   1.148 +  if (mRules) {
   1.149 +    mRules->SetInitialValue(aInitialValue);
   1.150 +  }
   1.151 +
   1.152 +  return res;
   1.153 +}
   1.154 +
   1.155 +static int32_t sNewlineHandlingPref = -1,
   1.156 +               sCaretStylePref = -1;
   1.157 +
   1.158 +static void
   1.159 +EditorPrefsChangedCallback(const char *aPrefName, void *)
   1.160 +{
   1.161 +  if (nsCRT::strcmp(aPrefName, "editor.singleLine.pasteNewlines") == 0) {
   1.162 +    sNewlineHandlingPref =
   1.163 +      Preferences::GetInt("editor.singleLine.pasteNewlines",
   1.164 +                          nsIPlaintextEditor::eNewlinesPasteToFirst);
   1.165 +  } else if (nsCRT::strcmp(aPrefName, "layout.selection.caret_style") == 0) {
   1.166 +    sCaretStylePref = Preferences::GetInt("layout.selection.caret_style",
   1.167 +#ifdef XP_WIN
   1.168 +                                                 1);
   1.169 +    if (sCaretStylePref == 0)
   1.170 +      sCaretStylePref = 1;
   1.171 +#else
   1.172 +                                                 0);
   1.173 +#endif
   1.174 +  }
   1.175 +}
   1.176 +
   1.177 +// static
   1.178 +void
   1.179 +nsPlaintextEditor::GetDefaultEditorPrefs(int32_t &aNewlineHandling,
   1.180 +                                         int32_t &aCaretStyle)
   1.181 +{
   1.182 +  if (sNewlineHandlingPref == -1) {
   1.183 +    Preferences::RegisterCallback(EditorPrefsChangedCallback,
   1.184 +                                  "editor.singleLine.pasteNewlines");
   1.185 +    EditorPrefsChangedCallback("editor.singleLine.pasteNewlines", nullptr);
   1.186 +    Preferences::RegisterCallback(EditorPrefsChangedCallback,
   1.187 +                                  "layout.selection.caret_style");
   1.188 +    EditorPrefsChangedCallback("layout.selection.caret_style", nullptr);
   1.189 +  }
   1.190 +
   1.191 +  aNewlineHandling = sNewlineHandlingPref;
   1.192 +  aCaretStyle = sCaretStylePref;
   1.193 +}
   1.194 +
   1.195 +void 
   1.196 +nsPlaintextEditor::BeginEditorInit()
   1.197 +{
   1.198 +  mInitTriggerCounter++;
   1.199 +}
   1.200 +
   1.201 +nsresult 
   1.202 +nsPlaintextEditor::EndEditorInit()
   1.203 +{
   1.204 +  nsresult res = NS_OK;
   1.205 +  NS_PRECONDITION(mInitTriggerCounter > 0, "ended editor init before we began?");
   1.206 +  mInitTriggerCounter--;
   1.207 +  if (mInitTriggerCounter == 0)
   1.208 +  {
   1.209 +    res = InitRules();
   1.210 +    if (NS_SUCCEEDED(res)) {
   1.211 +      // Throw away the old transaction manager if this is not the first time that
   1.212 +      // we're initializing the editor.
   1.213 +      EnableUndo(false);
   1.214 +      EnableUndo(true);
   1.215 +    }
   1.216 +  }
   1.217 +  return res;
   1.218 +}
   1.219 +
   1.220 +NS_IMETHODIMP
   1.221 +nsPlaintextEditor::SetDocumentCharacterSet(const nsACString& characterSet)
   1.222 +{
   1.223 +  nsresult rv = nsEditor::SetDocumentCharacterSet(characterSet);
   1.224 +  NS_ENSURE_SUCCESS(rv, rv);
   1.225 +
   1.226 +  // Update META charset element.
   1.227 +  nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
   1.228 +  NS_ENSURE_TRUE(domdoc, NS_ERROR_NOT_INITIALIZED);
   1.229 +
   1.230 +  if (UpdateMetaCharset(domdoc, characterSet)) {
   1.231 +    return NS_OK;
   1.232 +  }
   1.233 +
   1.234 +  nsCOMPtr<nsIDOMNodeList> headList;
   1.235 +  rv = domdoc->GetElementsByTagName(NS_LITERAL_STRING("head"), getter_AddRefs(headList));
   1.236 +  NS_ENSURE_SUCCESS(rv, rv);
   1.237 +  NS_ENSURE_TRUE(headList, NS_OK);
   1.238 +
   1.239 +  nsCOMPtr<nsIDOMNode> headNode;
   1.240 +  headList->Item(0, getter_AddRefs(headNode));
   1.241 +  NS_ENSURE_TRUE(headNode, NS_OK);
   1.242 +
   1.243 +  // Create a new meta charset tag
   1.244 +  nsCOMPtr<nsIDOMNode> resultNode;
   1.245 +  rv = CreateNode(NS_LITERAL_STRING("meta"), headNode, 0, getter_AddRefs(resultNode));
   1.246 +  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
   1.247 +  NS_ENSURE_TRUE(resultNode, NS_OK);
   1.248 +
   1.249 +  // Set attributes to the created element
   1.250 +  if (characterSet.IsEmpty()) {
   1.251 +    return NS_OK;
   1.252 +  }
   1.253 +
   1.254 +  nsCOMPtr<dom::Element> metaElement = do_QueryInterface(resultNode);
   1.255 +  if (!metaElement) {
   1.256 +    return NS_OK;
   1.257 +  }
   1.258 +
   1.259 +  // not undoable, undo should undo CreateNode
   1.260 +  metaElement->SetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv,
   1.261 +                       NS_LITERAL_STRING("Content-Type"), true);
   1.262 +  metaElement->SetAttr(kNameSpaceID_None, nsGkAtoms::content,
   1.263 +                       NS_LITERAL_STRING("text/html;charset=") +
   1.264 +                         NS_ConvertASCIItoUTF16(characterSet),
   1.265 +                       true);
   1.266 +  return NS_OK;
   1.267 +}
   1.268 +
   1.269 +bool
   1.270 +nsPlaintextEditor::UpdateMetaCharset(nsIDOMDocument* aDocument,
   1.271 +                                     const nsACString& aCharacterSet)
   1.272 +{
   1.273 +  MOZ_ASSERT(aDocument);
   1.274 +  // get a list of META tags
   1.275 +  nsCOMPtr<nsIDOMNodeList> list;
   1.276 +  nsresult rv = aDocument->GetElementsByTagName(NS_LITERAL_STRING("meta"),
   1.277 +                                                getter_AddRefs(list));
   1.278 +  NS_ENSURE_SUCCESS(rv, false);
   1.279 +  NS_ENSURE_TRUE(list, false);
   1.280 +
   1.281 +  nsCOMPtr<nsINodeList> metaList = do_QueryInterface(list);
   1.282 +
   1.283 +  uint32_t listLength = 0;
   1.284 +  metaList->GetLength(&listLength);
   1.285 +
   1.286 +  for (uint32_t i = 0; i < listLength; ++i) {
   1.287 +    nsCOMPtr<nsIContent> metaNode = metaList->Item(i);
   1.288 +    MOZ_ASSERT(metaNode);
   1.289 +
   1.290 +    if (!metaNode->IsElement()) {
   1.291 +      continue;
   1.292 +    }
   1.293 +
   1.294 +    nsAutoString currentValue;
   1.295 +    metaNode->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, currentValue);
   1.296 +
   1.297 +    if (!FindInReadable(NS_LITERAL_STRING("content-type"),
   1.298 +                        currentValue,
   1.299 +                        nsCaseInsensitiveStringComparator())) {
   1.300 +      continue;
   1.301 +    }
   1.302 +
   1.303 +    metaNode->GetAttr(kNameSpaceID_None, nsGkAtoms::content, currentValue);
   1.304 +
   1.305 +    NS_NAMED_LITERAL_STRING(charsetEquals, "charset=");
   1.306 +    nsAString::const_iterator originalStart, start, end;
   1.307 +    originalStart = currentValue.BeginReading(start);
   1.308 +    currentValue.EndReading(end);
   1.309 +    if (!FindInReadable(charsetEquals, start, end,
   1.310 +                        nsCaseInsensitiveStringComparator())) {
   1.311 +      continue;
   1.312 +    }
   1.313 +
   1.314 +    // set attribute to <original prefix> charset=text/html
   1.315 +    nsCOMPtr<nsIDOMElement> metaElement = do_QueryInterface(metaNode);
   1.316 +    MOZ_ASSERT(metaElement);
   1.317 +    rv = nsEditor::SetAttribute(metaElement, NS_LITERAL_STRING("content"),
   1.318 +                                Substring(originalStart, start) +
   1.319 +                                  charsetEquals +
   1.320 +                                  NS_ConvertASCIItoUTF16(aCharacterSet));
   1.321 +    return NS_SUCCEEDED(rv);
   1.322 +  }
   1.323 +  return false;
   1.324 +}
   1.325 +
   1.326 +NS_IMETHODIMP nsPlaintextEditor::InitRules()
   1.327 +{
   1.328 +  if (!mRules) {
   1.329 +    // instantiate the rules for this text editor
   1.330 +    mRules = new nsTextEditRules();
   1.331 +  }
   1.332 +  return mRules->Init(this);
   1.333 +}
   1.334 +
   1.335 +
   1.336 +NS_IMETHODIMP
   1.337 +nsPlaintextEditor::GetIsDocumentEditable(bool *aIsDocumentEditable)
   1.338 +{
   1.339 +  NS_ENSURE_ARG_POINTER(aIsDocumentEditable);
   1.340 +
   1.341 +  nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument();
   1.342 +  *aIsDocumentEditable = doc && IsModifiable();
   1.343 +
   1.344 +  return NS_OK;
   1.345 +}
   1.346 +
   1.347 +bool nsPlaintextEditor::IsModifiable()
   1.348 +{
   1.349 +  return !IsReadonly();
   1.350 +}
   1.351 +
   1.352 +nsresult
   1.353 +nsPlaintextEditor::HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent)
   1.354 +{
   1.355 +  // NOTE: When you change this method, you should also change:
   1.356 +  //   * editor/libeditor/text/tests/test_texteditor_keyevent_handling.html
   1.357 +  //   * editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
   1.358 +  //
   1.359 +  // And also when you add new key handling, you need to change the subclass's
   1.360 +  // HandleKeyPressEvent()'s switch statement.
   1.361 +
   1.362 +  if (IsReadonly() || IsDisabled()) {
   1.363 +    // When we're not editable, the events handled on nsEditor.
   1.364 +    return nsEditor::HandleKeyPressEvent(aKeyEvent);
   1.365 +  }
   1.366 +
   1.367 +  WidgetKeyboardEvent* nativeKeyEvent =
   1.368 +    aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
   1.369 +  NS_ENSURE_TRUE(nativeKeyEvent, NS_ERROR_UNEXPECTED);
   1.370 +  NS_ASSERTION(nativeKeyEvent->message == NS_KEY_PRESS,
   1.371 +               "HandleKeyPressEvent gets non-keypress event");
   1.372 +
   1.373 +  switch (nativeKeyEvent->keyCode) {
   1.374 +    case nsIDOMKeyEvent::DOM_VK_META:
   1.375 +    case nsIDOMKeyEvent::DOM_VK_WIN:
   1.376 +    case nsIDOMKeyEvent::DOM_VK_SHIFT:
   1.377 +    case nsIDOMKeyEvent::DOM_VK_CONTROL:
   1.378 +    case nsIDOMKeyEvent::DOM_VK_ALT:
   1.379 +    case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
   1.380 +    case nsIDOMKeyEvent::DOM_VK_DELETE:
   1.381 +      // These keys are handled on nsEditor
   1.382 +      return nsEditor::HandleKeyPressEvent(aKeyEvent);
   1.383 +    case nsIDOMKeyEvent::DOM_VK_TAB: {
   1.384 +      if (IsTabbable()) {
   1.385 +        return NS_OK; // let it be used for focus switching
   1.386 +      }
   1.387 +
   1.388 +      if (nativeKeyEvent->IsShift() || nativeKeyEvent->IsControl() ||
   1.389 +          nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta() ||
   1.390 +          nativeKeyEvent->IsOS()) {
   1.391 +        return NS_OK;
   1.392 +      }
   1.393 +
   1.394 +      // else we insert the tab straight through
   1.395 +      aKeyEvent->PreventDefault();
   1.396 +      return TypedText(NS_LITERAL_STRING("\t"), eTypedText);
   1.397 +    }
   1.398 +    case nsIDOMKeyEvent::DOM_VK_RETURN:
   1.399 +      if (IsSingleLineEditor() || nativeKeyEvent->IsControl() ||
   1.400 +          nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta() ||
   1.401 +          nativeKeyEvent->IsOS()) {
   1.402 +        return NS_OK;
   1.403 +      }
   1.404 +      aKeyEvent->PreventDefault();
   1.405 +      return TypedText(EmptyString(), eTypedBreak);
   1.406 +  }
   1.407 +
   1.408 +  // NOTE: On some keyboard layout, some characters are inputted with Control
   1.409 +  // key or Alt key, but at that time, widget sets FALSE to these keys.
   1.410 +  if (nativeKeyEvent->charCode == 0 || nativeKeyEvent->IsControl() ||
   1.411 +      nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta() ||
   1.412 +      nativeKeyEvent->IsOS()) {
   1.413 +    // we don't PreventDefault() here or keybindings like control-x won't work
   1.414 +    return NS_OK;
   1.415 +  }
   1.416 +  aKeyEvent->PreventDefault();
   1.417 +  nsAutoString str(nativeKeyEvent->charCode);
   1.418 +  return TypedText(str, eTypedText);
   1.419 +}
   1.420 +
   1.421 +/* This routine is needed to provide a bottleneck for typing for logging
   1.422 +   purposes.  Can't use HandleKeyPress() (above) for that since it takes
   1.423 +   a nsIDOMKeyEvent* parameter.  So instead we pass enough info through
   1.424 +   to TypedText() to determine what action to take, but without passing
   1.425 +   an event.
   1.426 +   */
   1.427 +NS_IMETHODIMP
   1.428 +nsPlaintextEditor::TypedText(const nsAString& aString, ETypingAction aAction)
   1.429 +{
   1.430 +  nsAutoPlaceHolderBatch batch(this, nsGkAtoms::TypingTxnName);
   1.431 +
   1.432 +  switch (aAction) {
   1.433 +    case eTypedText:
   1.434 +      return InsertText(aString);
   1.435 +    case eTypedBreak:
   1.436 +      return InsertLineBreak();
   1.437 +    default:
   1.438 +      // eTypedBR is only for HTML
   1.439 +      return NS_ERROR_FAILURE;
   1.440 +  }
   1.441 +}
   1.442 +
   1.443 +nsresult
   1.444 +nsPlaintextEditor::CreateBRImpl(nsCOMPtr<nsIDOMNode>* aInOutParent,
   1.445 +                                int32_t* aInOutOffset,
   1.446 +                                nsCOMPtr<nsIDOMNode>* outBRNode,
   1.447 +                                EDirection aSelect)
   1.448 +{
   1.449 +  NS_ENSURE_TRUE(aInOutParent && *aInOutParent && aInOutOffset && outBRNode, NS_ERROR_NULL_POINTER);
   1.450 +  *outBRNode = nullptr;
   1.451 +  nsresult res;
   1.452 +  
   1.453 +  // we need to insert a br.  unfortunately, we may have to split a text node to do it.
   1.454 +  nsCOMPtr<nsIDOMNode> node = *aInOutParent;
   1.455 +  int32_t theOffset = *aInOutOffset;
   1.456 +  nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(node);
   1.457 +  NS_NAMED_LITERAL_STRING(brType, "br");
   1.458 +  nsCOMPtr<nsIDOMNode> brNode;
   1.459 +  if (nodeAsText)  
   1.460 +  {
   1.461 +    int32_t offset;
   1.462 +    uint32_t len;
   1.463 +    nodeAsText->GetLength(&len);
   1.464 +    nsCOMPtr<nsIDOMNode> tmp = GetNodeLocation(node, &offset);
   1.465 +    NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
   1.466 +    if (!theOffset)
   1.467 +    {
   1.468 +      // we are already set to go
   1.469 +    }
   1.470 +    else if (theOffset == (int32_t)len)
   1.471 +    {
   1.472 +      // update offset to point AFTER the text node
   1.473 +      offset++;
   1.474 +    }
   1.475 +    else
   1.476 +    {
   1.477 +      // split the text node
   1.478 +      res = SplitNode(node, theOffset, getter_AddRefs(tmp));
   1.479 +      NS_ENSURE_SUCCESS(res, res);
   1.480 +      tmp = GetNodeLocation(node, &offset);
   1.481 +    }
   1.482 +    // create br
   1.483 +    res = CreateNode(brType, tmp, offset, getter_AddRefs(brNode));
   1.484 +    NS_ENSURE_SUCCESS(res, res);
   1.485 +    *aInOutParent = tmp;
   1.486 +    *aInOutOffset = offset+1;
   1.487 +  }
   1.488 +  else
   1.489 +  {
   1.490 +    res = CreateNode(brType, node, theOffset, getter_AddRefs(brNode));
   1.491 +    NS_ENSURE_SUCCESS(res, res);
   1.492 +    (*aInOutOffset)++;
   1.493 +  }
   1.494 +
   1.495 +  *outBRNode = brNode;
   1.496 +  if (*outBRNode && (aSelect != eNone))
   1.497 +  {
   1.498 +    int32_t offset;
   1.499 +    nsCOMPtr<nsIDOMNode> parent = GetNodeLocation(*outBRNode, &offset);
   1.500 +
   1.501 +    nsCOMPtr<nsISelection> selection;
   1.502 +    res = GetSelection(getter_AddRefs(selection));
   1.503 +    NS_ENSURE_SUCCESS(res, res);
   1.504 +    nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
   1.505 +    if (aSelect == eNext)
   1.506 +    {
   1.507 +      // position selection after br
   1.508 +      selPriv->SetInterlinePosition(true);
   1.509 +      res = selection->Collapse(parent, offset+1);
   1.510 +    }
   1.511 +    else if (aSelect == ePrevious)
   1.512 +    {
   1.513 +      // position selection before br
   1.514 +      selPriv->SetInterlinePosition(true);
   1.515 +      res = selection->Collapse(parent, offset);
   1.516 +    }
   1.517 +  }
   1.518 +  return NS_OK;
   1.519 +}
   1.520 +
   1.521 +
   1.522 +NS_IMETHODIMP nsPlaintextEditor::CreateBR(nsIDOMNode *aNode, int32_t aOffset, nsCOMPtr<nsIDOMNode> *outBRNode, EDirection aSelect)
   1.523 +{
   1.524 +  nsCOMPtr<nsIDOMNode> parent = aNode;
   1.525 +  int32_t offset = aOffset;
   1.526 +  return CreateBRImpl(address_of(parent), &offset, outBRNode, aSelect);
   1.527 +}
   1.528 +
   1.529 +nsresult
   1.530 +nsPlaintextEditor::InsertBR(nsCOMPtr<nsIDOMNode>* outBRNode)
   1.531 +{
   1.532 +  NS_ENSURE_TRUE(outBRNode, NS_ERROR_NULL_POINTER);
   1.533 +  *outBRNode = nullptr;
   1.534 +
   1.535 +  // calling it text insertion to trigger moz br treatment by rules
   1.536 +  nsAutoRules beginRulesSniffing(this, EditAction::insertText, nsIEditor::eNext);
   1.537 +
   1.538 +  nsCOMPtr<nsISelection> selection;
   1.539 +  nsresult res = GetSelection(getter_AddRefs(selection));
   1.540 +  NS_ENSURE_SUCCESS(res, res);
   1.541 +
   1.542 +  if (!selection->Collapsed()) {
   1.543 +    res = DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
   1.544 +    NS_ENSURE_SUCCESS(res, res);
   1.545 +  }
   1.546 +
   1.547 +  nsCOMPtr<nsIDOMNode> selNode;
   1.548 +  int32_t selOffset;
   1.549 +  res = GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset);
   1.550 +  NS_ENSURE_SUCCESS(res, res);
   1.551 +  
   1.552 +  res = CreateBR(selNode, selOffset, outBRNode);
   1.553 +  NS_ENSURE_SUCCESS(res, res);
   1.554 +    
   1.555 +  // position selection after br
   1.556 +  selNode = GetNodeLocation(*outBRNode, &selOffset);
   1.557 +  nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
   1.558 +  selPriv->SetInterlinePosition(true);
   1.559 +  return selection->Collapse(selNode, selOffset+1);
   1.560 +}
   1.561 +
   1.562 +nsresult
   1.563 +nsPlaintextEditor::ExtendSelectionForDelete(nsISelection *aSelection,
   1.564 +                                            nsIEditor::EDirection *aAction)
   1.565 +{
   1.566 +  nsresult result = NS_OK;
   1.567 +
   1.568 +  bool bCollapsed = aSelection->Collapsed();
   1.569 +
   1.570 +  if (*aAction == eNextWord || *aAction == ePreviousWord
   1.571 +      || (*aAction == eNext && bCollapsed)
   1.572 +      || (*aAction == ePrevious && bCollapsed)
   1.573 +      || *aAction == eToBeginningOfLine || *aAction == eToEndOfLine)
   1.574 +  {
   1.575 +    nsCOMPtr<nsISelectionController> selCont;
   1.576 +    GetSelectionController(getter_AddRefs(selCont));
   1.577 +    NS_ENSURE_TRUE(selCont, NS_ERROR_NO_INTERFACE);
   1.578 +
   1.579 +    switch (*aAction)
   1.580 +    {
   1.581 +      case eNextWord:
   1.582 +        result = selCont->WordExtendForDelete(true);
   1.583 +        // DeleteSelectionImpl doesn't handle these actions
   1.584 +        // because it's inside batching, so don't confuse it:
   1.585 +        *aAction = eNone;
   1.586 +        break;
   1.587 +      case ePreviousWord:
   1.588 +        result = selCont->WordExtendForDelete(false);
   1.589 +        *aAction = eNone;
   1.590 +        break;
   1.591 +      case eNext:
   1.592 +        result = selCont->CharacterExtendForDelete();
   1.593 +        // Don't set aAction to eNone (see Bug 502259)
   1.594 +        break;
   1.595 +      case ePrevious: {
   1.596 +        // Only extend the selection where the selection is after a UTF-16
   1.597 +        // surrogate pair.  For other cases we don't want to do that, in order
   1.598 +        // to make sure that pressing backspace will only delete the last
   1.599 +        // typed character.
   1.600 +        nsCOMPtr<nsIDOMNode> node;
   1.601 +        int32_t offset;
   1.602 +        result = GetStartNodeAndOffset(aSelection, getter_AddRefs(node), &offset);
   1.603 +        NS_ENSURE_SUCCESS(result, result);
   1.604 +        NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
   1.605 +
   1.606 +        if (IsTextNode(node)) {
   1.607 +          nsCOMPtr<nsIDOMCharacterData> charData = do_QueryInterface(node);
   1.608 +          if (charData) {
   1.609 +            nsAutoString data;
   1.610 +            result = charData->GetData(data);
   1.611 +            NS_ENSURE_SUCCESS(result, result);
   1.612 +
   1.613 +            if (offset > 1 &&
   1.614 +                NS_IS_LOW_SURROGATE(data[offset - 1]) &&
   1.615 +                NS_IS_HIGH_SURROGATE(data[offset - 2])) {
   1.616 +              result = selCont->CharacterExtendForBackspace();
   1.617 +            }
   1.618 +          }
   1.619 +        }
   1.620 +        break;
   1.621 +      }
   1.622 +      case eToBeginningOfLine:
   1.623 +        selCont->IntraLineMove(true, false);          // try to move to end
   1.624 +        result = selCont->IntraLineMove(false, true); // select to beginning
   1.625 +        *aAction = eNone;
   1.626 +        break;
   1.627 +      case eToEndOfLine:
   1.628 +        result = selCont->IntraLineMove(true, true);
   1.629 +        *aAction = eNext;
   1.630 +        break;
   1.631 +      default:       // avoid several compiler warnings
   1.632 +        result = NS_OK;
   1.633 +        break;
   1.634 +    }
   1.635 +  }
   1.636 +  return result;
   1.637 +}
   1.638 +
   1.639 +nsresult
   1.640 +nsPlaintextEditor::DeleteSelection(EDirection aAction,
   1.641 +                                   EStripWrappers aStripWrappers)
   1.642 +{
   1.643 +  MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
   1.644 +
   1.645 +  if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
   1.646 +
   1.647 +  // Protect the edit rules object from dying
   1.648 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
   1.649 +
   1.650 +  nsresult result;
   1.651 +
   1.652 +  // delete placeholder txns merge.
   1.653 +  nsAutoPlaceHolderBatch batch(this, nsGkAtoms::DeleteTxnName);
   1.654 +  nsAutoRules beginRulesSniffing(this, EditAction::deleteSelection, aAction);
   1.655 +
   1.656 +  // pre-process
   1.657 +  nsRefPtr<Selection> selection = GetSelection();
   1.658 +  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
   1.659 +
   1.660 +  // If there is an existing selection when an extended delete is requested,
   1.661 +  //  platforms that use "caret-style" caret positioning collapse the
   1.662 +  //  selection to the  start and then create a new selection.
   1.663 +  //  Platforms that use "selection-style" caret positioning just delete the
   1.664 +  //  existing selection without extending it.
   1.665 +  if (!selection->Collapsed() &&
   1.666 +      (aAction == eNextWord || aAction == ePreviousWord ||
   1.667 +       aAction == eToBeginningOfLine || aAction == eToEndOfLine))
   1.668 +  {
   1.669 +    if (mCaretStyle == 1)
   1.670 +    {
   1.671 +      result = selection->CollapseToStart();
   1.672 +      NS_ENSURE_SUCCESS(result, result);
   1.673 +    }
   1.674 +    else
   1.675 +    { 
   1.676 +      aAction = eNone;
   1.677 +    }
   1.678 +  }
   1.679 +
   1.680 +  nsTextRulesInfo ruleInfo(EditAction::deleteSelection);
   1.681 +  ruleInfo.collapsedAction = aAction;
   1.682 +  ruleInfo.stripWrappers = aStripWrappers;
   1.683 +  bool cancel, handled;
   1.684 +  result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   1.685 +  NS_ENSURE_SUCCESS(result, result);
   1.686 +  if (!cancel && !handled)
   1.687 +  {
   1.688 +    result = DeleteSelectionImpl(aAction, aStripWrappers);
   1.689 +  }
   1.690 +  if (!cancel)
   1.691 +  {
   1.692 +    // post-process 
   1.693 +    result = mRules->DidDoAction(selection, &ruleInfo, result);
   1.694 +  }
   1.695 +
   1.696 +  return result;
   1.697 +}
   1.698 +
   1.699 +NS_IMETHODIMP nsPlaintextEditor::InsertText(const nsAString &aStringToInsert)
   1.700 +{
   1.701 +  if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
   1.702 +
   1.703 +  // Protect the edit rules object from dying
   1.704 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
   1.705 +
   1.706 +  EditAction opID = EditAction::insertText;
   1.707 +  if (mComposition) {
   1.708 +    opID = EditAction::insertIMEText;
   1.709 +  }
   1.710 +  nsAutoPlaceHolderBatch batch(this, nullptr); 
   1.711 +  nsAutoRules beginRulesSniffing(this, opID, nsIEditor::eNext);
   1.712 +
   1.713 +  // pre-process
   1.714 +  nsRefPtr<Selection> selection = GetSelection();
   1.715 +  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
   1.716 +  nsAutoString resultString;
   1.717 +  // XXX can we trust instring to outlive ruleInfo,
   1.718 +  // XXX and ruleInfo not to refer to instring in its dtor?
   1.719 +  //nsAutoString instring(aStringToInsert);
   1.720 +  nsTextRulesInfo ruleInfo(opID);
   1.721 +  ruleInfo.inString = &aStringToInsert;
   1.722 +  ruleInfo.outString = &resultString;
   1.723 +  ruleInfo.maxLength = mMaxTextLength;
   1.724 +
   1.725 +  bool cancel, handled;
   1.726 +  nsresult res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   1.727 +  NS_ENSURE_SUCCESS(res, res);
   1.728 +  if (!cancel && !handled)
   1.729 +  {
   1.730 +    // we rely on rules code for now - no default implementation
   1.731 +  }
   1.732 +  if (!cancel)
   1.733 +  {
   1.734 +    // post-process 
   1.735 +    res = mRules->DidDoAction(selection, &ruleInfo, res);
   1.736 +  }
   1.737 +  return res;
   1.738 +}
   1.739 +
   1.740 +NS_IMETHODIMP nsPlaintextEditor::InsertLineBreak()
   1.741 +{
   1.742 +  if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
   1.743 +
   1.744 +  // Protect the edit rules object from dying
   1.745 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
   1.746 +
   1.747 +  nsAutoEditBatch beginBatching(this);
   1.748 +  nsAutoRules beginRulesSniffing(this, EditAction::insertBreak, nsIEditor::eNext);
   1.749 +
   1.750 +  // pre-process
   1.751 +  nsRefPtr<Selection> selection = GetSelection();
   1.752 +  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
   1.753 +
   1.754 +  // Batching the selection and moving nodes out from under the caret causes
   1.755 +  // caret turds. Ask the shell to invalidate the caret now to avoid the turds.
   1.756 +  nsCOMPtr<nsIPresShell> shell = GetPresShell();
   1.757 +  NS_ENSURE_TRUE(shell, NS_ERROR_NOT_INITIALIZED);
   1.758 +  shell->MaybeInvalidateCaretPosition();
   1.759 +
   1.760 +  nsTextRulesInfo ruleInfo(EditAction::insertBreak);
   1.761 +  ruleInfo.maxLength = mMaxTextLength;
   1.762 +  bool cancel, handled;
   1.763 +  nsresult res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   1.764 +  NS_ENSURE_SUCCESS(res, res);
   1.765 +  if (!cancel && !handled)
   1.766 +  {
   1.767 +    // get the (collapsed) selection location
   1.768 +    nsCOMPtr<nsIDOMNode> selNode;
   1.769 +    int32_t selOffset;
   1.770 +    res = GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset);
   1.771 +    NS_ENSURE_SUCCESS(res, res);
   1.772 +
   1.773 +    // don't put text in places that can't have it
   1.774 +    if (!IsTextNode(selNode) && !CanContainTag(selNode, nsGkAtoms::textTagName)) {
   1.775 +      return NS_ERROR_FAILURE;
   1.776 +    }
   1.777 +
   1.778 +    // we need to get the doc
   1.779 +    nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument();
   1.780 +    NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
   1.781 +
   1.782 +    // don't spaz my selection in subtransactions
   1.783 +    nsAutoTxnsConserveSelection dontSpazMySelection(this);
   1.784 +
   1.785 +    // insert a linefeed character
   1.786 +    res = InsertTextImpl(NS_LITERAL_STRING("\n"), address_of(selNode),
   1.787 +                         &selOffset, doc);
   1.788 +    if (!selNode) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called
   1.789 +    if (NS_SUCCEEDED(res))
   1.790 +    {
   1.791 +      // set the selection to the correct location
   1.792 +      res = selection->Collapse(selNode, selOffset);
   1.793 +
   1.794 +      if (NS_SUCCEEDED(res))
   1.795 +      {
   1.796 +        // see if we're at the end of the editor range
   1.797 +        nsCOMPtr<nsIDOMNode> endNode;
   1.798 +        int32_t endOffset;
   1.799 +        res = GetEndNodeAndOffset(selection, getter_AddRefs(endNode), &endOffset);
   1.800 +
   1.801 +        if (NS_SUCCEEDED(res) && endNode == selNode && endOffset == selOffset)
   1.802 +        {
   1.803 +          // SetInterlinePosition(true) means we want the caret to stick to the content on the "right".
   1.804 +          // We want the caret to stick to whatever is past the break.  This is
   1.805 +          // because the break is on the same line we were on, but the next content
   1.806 +          // will be on the following line.
   1.807 +          selection->SetInterlinePosition(true);
   1.808 +        }
   1.809 +      }
   1.810 +    }
   1.811 +  }
   1.812 +  if (!cancel)
   1.813 +  {
   1.814 +    // post-process, always called if WillInsertBreak didn't return cancel==true
   1.815 +    res = mRules->DidDoAction(selection, &ruleInfo, res);
   1.816 +  }
   1.817 +
   1.818 +  return res;
   1.819 +}
   1.820 +
   1.821 +nsresult
   1.822 +nsPlaintextEditor::BeginIMEComposition(WidgetCompositionEvent* aEvent)
   1.823 +{
   1.824 +  NS_ENSURE_TRUE(!mComposition, NS_OK);
   1.825 +
   1.826 +  if (IsPasswordEditor()) {
   1.827 +    NS_ENSURE_TRUE(mRules, NS_ERROR_NULL_POINTER);
   1.828 +    // Protect the edit rules object from dying
   1.829 +    nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
   1.830 +
   1.831 +    nsTextEditRules *textEditRules =
   1.832 +      static_cast<nsTextEditRules*>(mRules.get());
   1.833 +    textEditRules->ResetIMETextPWBuf();
   1.834 +  }
   1.835 +
   1.836 +  return nsEditor::BeginIMEComposition(aEvent);
   1.837 +}
   1.838 +
   1.839 +nsresult
   1.840 +nsPlaintextEditor::UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent)
   1.841 +{
   1.842 +  NS_ABORT_IF_FALSE(aDOMTextEvent, "aDOMTextEvent must not be nullptr");
   1.843 +
   1.844 +  WidgetTextEvent* widgetTextEvent =
   1.845 +    aDOMTextEvent->GetInternalNSEvent()->AsTextEvent();
   1.846 +  NS_ENSURE_TRUE(widgetTextEvent, NS_ERROR_INVALID_ARG);
   1.847 +
   1.848 +  EnsureComposition(widgetTextEvent);
   1.849 +
   1.850 +  nsCOMPtr<nsIPresShell> ps = GetPresShell();
   1.851 +  NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
   1.852 +
   1.853 +  nsCOMPtr<nsISelection> selection;
   1.854 +  nsresult rv = GetSelection(getter_AddRefs(selection));
   1.855 +  NS_ENSURE_SUCCESS(rv, rv);
   1.856 +
   1.857 +  nsRefPtr<nsCaret> caretP = ps->GetCaret();
   1.858 +
   1.859 +  {
   1.860 +    TextComposition::TextEventHandlingMarker
   1.861 +      textEventHandlingMarker(mComposition, widgetTextEvent);
   1.862 +
   1.863 +    nsAutoPlaceHolderBatch batch(this, nsGkAtoms::IMETxnName);
   1.864 +
   1.865 +    rv = InsertText(widgetTextEvent->theText);
   1.866 +
   1.867 +    if (caretP) {
   1.868 +      caretP->SetCaretDOMSelection(selection);
   1.869 +    }
   1.870 +  }
   1.871 +
   1.872 +  // If still composing, we should fire input event via observer.
   1.873 +  // Note that if committed, we don't need to notify it since it will be
   1.874 +  // notified at followed compositionend event.
   1.875 +  // NOTE: We must notify after the auto batch will be gone.
   1.876 +  if (IsIMEComposing()) {
   1.877 +    NotifyEditorObservers();
   1.878 +  }
   1.879 +
   1.880 +  return rv;
   1.881 +}
   1.882 +
   1.883 +already_AddRefed<nsIContent>
   1.884 +nsPlaintextEditor::GetInputEventTargetContent()
   1.885 +{
   1.886 +  nsCOMPtr<nsIContent> target = do_QueryInterface(mEventTarget);
   1.887 +  return target.forget();
   1.888 +}
   1.889 +
   1.890 +NS_IMETHODIMP
   1.891 +nsPlaintextEditor::GetDocumentIsEmpty(bool *aDocumentIsEmpty)
   1.892 +{
   1.893 +  NS_ENSURE_TRUE(aDocumentIsEmpty, NS_ERROR_NULL_POINTER);
   1.894 +  
   1.895 +  NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
   1.896 +
   1.897 +  // Protect the edit rules object from dying
   1.898 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
   1.899 +  
   1.900 +  return mRules->DocumentIsEmpty(aDocumentIsEmpty);
   1.901 +}
   1.902 +
   1.903 +NS_IMETHODIMP
   1.904 +nsPlaintextEditor::GetTextLength(int32_t *aCount)
   1.905 +{
   1.906 +  NS_ASSERTION(aCount, "null pointer");
   1.907 +
   1.908 +  // initialize out params
   1.909 +  *aCount = 0;
   1.910 +  
   1.911 +  // special-case for empty document, to account for the bogus node
   1.912 +  bool docEmpty;
   1.913 +  nsresult rv = GetDocumentIsEmpty(&docEmpty);
   1.914 +  NS_ENSURE_SUCCESS(rv, rv);
   1.915 +  if (docEmpty)
   1.916 +    return NS_OK;
   1.917 +
   1.918 +  dom::Element *rootElement = GetRoot();
   1.919 +  NS_ENSURE_TRUE(rootElement, NS_ERROR_NULL_POINTER);
   1.920 +
   1.921 +  nsCOMPtr<nsIContentIterator> iter =
   1.922 +    do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &rv);
   1.923 +  NS_ENSURE_SUCCESS(rv, rv);
   1.924 +
   1.925 +  uint32_t totalLength = 0;
   1.926 +  iter->Init(rootElement);
   1.927 +  for (; !iter->IsDone(); iter->Next()) {
   1.928 +    nsCOMPtr<nsIDOMNode> currentNode = do_QueryInterface(iter->GetCurrentNode());
   1.929 +    nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(currentNode);
   1.930 +    if (textNode && IsEditable(currentNode)) {
   1.931 +      uint32_t length;
   1.932 +      textNode->GetLength(&length);
   1.933 +      totalLength += length;
   1.934 +    }
   1.935 +  }
   1.936 +
   1.937 +  *aCount = totalLength;
   1.938 +  return NS_OK;
   1.939 +}
   1.940 +
   1.941 +NS_IMETHODIMP
   1.942 +nsPlaintextEditor::SetMaxTextLength(int32_t aMaxTextLength)
   1.943 +{
   1.944 +  mMaxTextLength = aMaxTextLength;
   1.945 +  return NS_OK;
   1.946 +}
   1.947 +
   1.948 +NS_IMETHODIMP
   1.949 +nsPlaintextEditor::GetMaxTextLength(int32_t* aMaxTextLength)
   1.950 +{
   1.951 +  NS_ENSURE_TRUE(aMaxTextLength, NS_ERROR_INVALID_POINTER);
   1.952 +  *aMaxTextLength = mMaxTextLength;
   1.953 +  return NS_OK;
   1.954 +}
   1.955 +
   1.956 +//
   1.957 +// Get the wrap width
   1.958 +//
   1.959 +NS_IMETHODIMP 
   1.960 +nsPlaintextEditor::GetWrapWidth(int32_t *aWrapColumn)
   1.961 +{
   1.962 +  NS_ENSURE_TRUE( aWrapColumn, NS_ERROR_NULL_POINTER);
   1.963 +
   1.964 +  *aWrapColumn = mWrapColumn;
   1.965 +  return NS_OK;
   1.966 +}
   1.967 +
   1.968 +//
   1.969 +// See if the style value includes this attribute, and if it does,
   1.970 +// cut out everything from the attribute to the next semicolon.
   1.971 +//
   1.972 +static void CutStyle(const char* stylename, nsString& styleValue)
   1.973 +{
   1.974 +  // Find the current wrapping type:
   1.975 +  int32_t styleStart = styleValue.Find(stylename, true);
   1.976 +  if (styleStart >= 0)
   1.977 +  {
   1.978 +    int32_t styleEnd = styleValue.Find(";", false, styleStart);
   1.979 +    if (styleEnd > styleStart)
   1.980 +      styleValue.Cut(styleStart, styleEnd - styleStart + 1);
   1.981 +    else
   1.982 +      styleValue.Cut(styleStart, styleValue.Length() - styleStart);
   1.983 +  }
   1.984 +}
   1.985 +
   1.986 +//
   1.987 +// Change the wrap width on the root of this document.
   1.988 +// 
   1.989 +NS_IMETHODIMP 
   1.990 +nsPlaintextEditor::SetWrapWidth(int32_t aWrapColumn)
   1.991 +{
   1.992 +  SetWrapColumn(aWrapColumn);
   1.993 +
   1.994 +  // Make sure we're a plaintext editor, otherwise we shouldn't
   1.995 +  // do the rest of this.
   1.996 +  if (!IsPlaintextEditor())
   1.997 +    return NS_OK;
   1.998 +
   1.999 +  // Ought to set a style sheet here ...
  1.1000 +  // Probably should keep around an mPlaintextStyleSheet for this purpose.
  1.1001 +  dom::Element *rootElement = GetRoot();
  1.1002 +  NS_ENSURE_TRUE(rootElement, NS_ERROR_NULL_POINTER);
  1.1003 +
  1.1004 +  // Get the current style for this root element:
  1.1005 +  nsAutoString styleValue;
  1.1006 +  rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::style, styleValue);
  1.1007 +
  1.1008 +  // We'll replace styles for these values:
  1.1009 +  CutStyle("white-space", styleValue);
  1.1010 +  CutStyle("width", styleValue);
  1.1011 +  CutStyle("font-family", styleValue);
  1.1012 +
  1.1013 +  // If we have other style left, trim off any existing semicolons
  1.1014 +  // or whitespace, then add a known semicolon-space:
  1.1015 +  if (!styleValue.IsEmpty())
  1.1016 +  {
  1.1017 +    styleValue.Trim("; \t", false, true);
  1.1018 +    styleValue.AppendLiteral("; ");
  1.1019 +  }
  1.1020 +
  1.1021 +  // Make sure we have fixed-width font.  This should be done for us,
  1.1022 +  // but it isn't, see bug 22502, so we have to add "font: -moz-fixed;".
  1.1023 +  // Only do this if we're wrapping.
  1.1024 +  if (IsWrapHackEnabled() && aWrapColumn >= 0)
  1.1025 +    styleValue.AppendLiteral("font-family: -moz-fixed; ");
  1.1026 +
  1.1027 +  // If "mail.compose.wrap_to_window_width" is set, and we're a mail editor,
  1.1028 +  // then remember our wrap width (for output purposes) but set the visual
  1.1029 +  // wrapping to window width.
  1.1030 +  // We may reset mWrapToWindow here, based on the pref's current value.
  1.1031 +  if (IsMailEditor())
  1.1032 +  {
  1.1033 +    mWrapToWindow =
  1.1034 +      Preferences::GetBool("mail.compose.wrap_to_window_width", mWrapToWindow);
  1.1035 +  }
  1.1036 +
  1.1037 +  // and now we're ready to set the new whitespace/wrapping style.
  1.1038 +  if (aWrapColumn > 0 && !mWrapToWindow)        // Wrap to a fixed column
  1.1039 +  {
  1.1040 +    styleValue.AppendLiteral("white-space: pre-wrap; width: ");
  1.1041 +    styleValue.AppendInt(aWrapColumn);
  1.1042 +    styleValue.AppendLiteral("ch;");
  1.1043 +  }
  1.1044 +  else if (mWrapToWindow || aWrapColumn == 0)
  1.1045 +    styleValue.AppendLiteral("white-space: pre-wrap;");
  1.1046 +  else
  1.1047 +    styleValue.AppendLiteral("white-space: pre;");
  1.1048 +
  1.1049 +  return rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::style, styleValue, true);
  1.1050 +}
  1.1051 +
  1.1052 +NS_IMETHODIMP 
  1.1053 +nsPlaintextEditor::SetWrapColumn(int32_t aWrapColumn)
  1.1054 +{
  1.1055 +  mWrapColumn = aWrapColumn;
  1.1056 +  return NS_OK;
  1.1057 +}
  1.1058 +
  1.1059 +//
  1.1060 +// Get the newline handling for this editor
  1.1061 +//
  1.1062 +NS_IMETHODIMP 
  1.1063 +nsPlaintextEditor::GetNewlineHandling(int32_t *aNewlineHandling)
  1.1064 +{
  1.1065 +  NS_ENSURE_ARG_POINTER(aNewlineHandling);
  1.1066 +
  1.1067 +  *aNewlineHandling = mNewlineHandling;
  1.1068 +  return NS_OK;
  1.1069 +}
  1.1070 +
  1.1071 +//
  1.1072 +// Change the newline handling for this editor
  1.1073 +// 
  1.1074 +NS_IMETHODIMP 
  1.1075 +nsPlaintextEditor::SetNewlineHandling(int32_t aNewlineHandling)
  1.1076 +{
  1.1077 +  mNewlineHandling = aNewlineHandling;
  1.1078 +  
  1.1079 +  return NS_OK;
  1.1080 +}
  1.1081 +
  1.1082 +NS_IMETHODIMP 
  1.1083 +nsPlaintextEditor::Undo(uint32_t aCount)
  1.1084 +{
  1.1085 +  // Protect the edit rules object from dying
  1.1086 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
  1.1087 +
  1.1088 +  nsAutoUpdateViewBatch beginViewBatching(this);
  1.1089 +
  1.1090 +  ForceCompositionEnd();
  1.1091 +
  1.1092 +  nsAutoRules beginRulesSniffing(this, EditAction::undo, nsIEditor::eNone);
  1.1093 +
  1.1094 +  nsTextRulesInfo ruleInfo(EditAction::undo);
  1.1095 +  nsRefPtr<Selection> selection = GetSelection();
  1.1096 +  bool cancel, handled;
  1.1097 +  nsresult result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
  1.1098 +  
  1.1099 +  if (!cancel && NS_SUCCEEDED(result))
  1.1100 +  {
  1.1101 +    result = nsEditor::Undo(aCount);
  1.1102 +    result = mRules->DidDoAction(selection, &ruleInfo, result);
  1.1103 +  } 
  1.1104 +   
  1.1105 +  NotifyEditorObservers();
  1.1106 +  return result;
  1.1107 +}
  1.1108 +
  1.1109 +NS_IMETHODIMP 
  1.1110 +nsPlaintextEditor::Redo(uint32_t aCount)
  1.1111 +{
  1.1112 +  // Protect the edit rules object from dying
  1.1113 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
  1.1114 +
  1.1115 +  nsAutoUpdateViewBatch beginViewBatching(this);
  1.1116 +
  1.1117 +  ForceCompositionEnd();
  1.1118 +
  1.1119 +  nsAutoRules beginRulesSniffing(this, EditAction::redo, nsIEditor::eNone);
  1.1120 +
  1.1121 +  nsTextRulesInfo ruleInfo(EditAction::redo);
  1.1122 +  nsRefPtr<Selection> selection = GetSelection();
  1.1123 +  bool cancel, handled;
  1.1124 +  nsresult result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
  1.1125 +  
  1.1126 +  if (!cancel && NS_SUCCEEDED(result))
  1.1127 +  {
  1.1128 +    result = nsEditor::Redo(aCount);
  1.1129 +    result = mRules->DidDoAction(selection, &ruleInfo, result);
  1.1130 +  } 
  1.1131 +   
  1.1132 +  NotifyEditorObservers();
  1.1133 +  return result;
  1.1134 +}
  1.1135 +
  1.1136 +bool
  1.1137 +nsPlaintextEditor::CanCutOrCopy()
  1.1138 +{
  1.1139 +  nsCOMPtr<nsISelection> selection;
  1.1140 +  if (NS_FAILED(GetSelection(getter_AddRefs(selection))))
  1.1141 +    return false;
  1.1142 +
  1.1143 +  return !selection->Collapsed();
  1.1144 +}
  1.1145 +
  1.1146 +bool
  1.1147 +nsPlaintextEditor::FireClipboardEvent(int32_t aType, int32_t aSelectionType)
  1.1148 +{
  1.1149 +  if (aType == NS_PASTE)
  1.1150 +    ForceCompositionEnd();
  1.1151 +
  1.1152 +  nsCOMPtr<nsIPresShell> presShell = GetPresShell();
  1.1153 +  NS_ENSURE_TRUE(presShell, false);
  1.1154 +
  1.1155 +  nsCOMPtr<nsISelection> selection;
  1.1156 +  if (NS_FAILED(GetSelection(getter_AddRefs(selection))))
  1.1157 +    return false;
  1.1158 +
  1.1159 +  if (!nsCopySupport::FireClipboardEvent(aType, aSelectionType, presShell, selection))
  1.1160 +    return false;
  1.1161 +
  1.1162 +  // If the event handler caused the editor to be destroyed, return false.
  1.1163 +  // Otherwise return true to indicate that the event was not cancelled.
  1.1164 +  return !mDidPreDestroy;
  1.1165 +}
  1.1166 +
  1.1167 +NS_IMETHODIMP nsPlaintextEditor::Cut()
  1.1168 +{
  1.1169 +  if (FireClipboardEvent(NS_CUT, nsIClipboard::kGlobalClipboard))
  1.1170 +    return DeleteSelection(eNone, eStrip);
  1.1171 +  return NS_OK;
  1.1172 +}
  1.1173 +
  1.1174 +NS_IMETHODIMP nsPlaintextEditor::CanCut(bool *aCanCut)
  1.1175 +{
  1.1176 +  NS_ENSURE_ARG_POINTER(aCanCut);
  1.1177 +  *aCanCut = IsModifiable() && CanCutOrCopy();
  1.1178 +  return NS_OK;
  1.1179 +}
  1.1180 +
  1.1181 +NS_IMETHODIMP nsPlaintextEditor::Copy()
  1.1182 +{
  1.1183 +  FireClipboardEvent(NS_COPY, nsIClipboard::kGlobalClipboard);
  1.1184 +  return NS_OK;
  1.1185 +}
  1.1186 +
  1.1187 +NS_IMETHODIMP nsPlaintextEditor::CanCopy(bool *aCanCopy)
  1.1188 +{
  1.1189 +  NS_ENSURE_ARG_POINTER(aCanCopy);
  1.1190 +  *aCanCopy = CanCutOrCopy();
  1.1191 +  return NS_OK;
  1.1192 +}
  1.1193 +
  1.1194 +// Shared between OutputToString and OutputToStream
  1.1195 +NS_IMETHODIMP
  1.1196 +nsPlaintextEditor::GetAndInitDocEncoder(const nsAString& aFormatType,
  1.1197 +                                        uint32_t aFlags,
  1.1198 +                                        const nsACString& aCharset,
  1.1199 +                                        nsIDocumentEncoder** encoder)
  1.1200 +{
  1.1201 +  nsresult rv = NS_OK;
  1.1202 +
  1.1203 +  nsAutoCString formatType(NS_DOC_ENCODER_CONTRACTID_BASE);
  1.1204 +  LossyAppendUTF16toASCII(aFormatType, formatType);
  1.1205 +  nsCOMPtr<nsIDocumentEncoder> docEncoder (do_CreateInstance(formatType.get(), &rv));
  1.1206 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1207 +
  1.1208 +  nsCOMPtr<nsIDOMDocument> domDoc = do_QueryReferent(mDocWeak);
  1.1209 +  NS_ASSERTION(domDoc, "Need a document");
  1.1210 +
  1.1211 +  rv = docEncoder->Init(domDoc, aFormatType, aFlags);
  1.1212 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1213 +
  1.1214 +  if (!aCharset.IsEmpty() && !aCharset.EqualsLiteral("null")) {
  1.1215 +    docEncoder->SetCharset(aCharset);
  1.1216 +  }
  1.1217 +
  1.1218 +  int32_t wc;
  1.1219 +  (void) GetWrapWidth(&wc);
  1.1220 +  if (wc >= 0)
  1.1221 +    (void) docEncoder->SetWrapColumn(wc);
  1.1222 +
  1.1223 +  // Set the selection, if appropriate.
  1.1224 +  // We do this either if the OutputSelectionOnly flag is set,
  1.1225 +  // in which case we use our existing selection ...
  1.1226 +  if (aFlags & nsIDocumentEncoder::OutputSelectionOnly)
  1.1227 +  {
  1.1228 +    nsCOMPtr<nsISelection> selection;
  1.1229 +    rv = GetSelection(getter_AddRefs(selection));
  1.1230 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1231 +    if (selection) {
  1.1232 +      rv = docEncoder->SetSelection(selection);
  1.1233 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1234 +    }
  1.1235 +  }
  1.1236 +  // ... or if the root element is not a body,
  1.1237 +  // in which case we set the selection to encompass the root.
  1.1238 +  else
  1.1239 +  {
  1.1240 +    dom::Element* rootElement = GetRoot();
  1.1241 +    NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE);
  1.1242 +    if (!rootElement->IsHTML(nsGkAtoms::body)) {
  1.1243 +      rv = docEncoder->SetNativeContainerNode(rootElement);
  1.1244 +      NS_ENSURE_SUCCESS(rv, rv);
  1.1245 +    }
  1.1246 +  }
  1.1247 +
  1.1248 +  docEncoder.forget(encoder);
  1.1249 +  return NS_OK;
  1.1250 +}
  1.1251 +
  1.1252 +
  1.1253 +NS_IMETHODIMP 
  1.1254 +nsPlaintextEditor::OutputToString(const nsAString& aFormatType,
  1.1255 +                                  uint32_t aFlags,
  1.1256 +                                  nsAString& aOutputString)
  1.1257 +{
  1.1258 +  // Protect the edit rules object from dying
  1.1259 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
  1.1260 +
  1.1261 +  nsString resultString;
  1.1262 +  nsTextRulesInfo ruleInfo(EditAction::outputText);
  1.1263 +  ruleInfo.outString = &resultString;
  1.1264 +  // XXX Struct should store a nsAReadable*
  1.1265 +  nsAutoString str(aFormatType);
  1.1266 +  ruleInfo.outputFormat = &str;
  1.1267 +  bool cancel, handled;
  1.1268 +  nsresult rv = mRules->WillDoAction(nullptr, &ruleInfo, &cancel, &handled);
  1.1269 +  if (cancel || NS_FAILED(rv)) { return rv; }
  1.1270 +  if (handled)
  1.1271 +  { // this case will get triggered by password fields
  1.1272 +    aOutputString.Assign(*(ruleInfo.outString));
  1.1273 +    return rv;
  1.1274 +  }
  1.1275 +
  1.1276 +  nsAutoCString charsetStr;
  1.1277 +  rv = GetDocumentCharacterSet(charsetStr);
  1.1278 +  if(NS_FAILED(rv) || charsetStr.IsEmpty())
  1.1279 +    charsetStr.AssignLiteral("ISO-8859-1");
  1.1280 +
  1.1281 +  nsCOMPtr<nsIDocumentEncoder> encoder;
  1.1282 +  rv = GetAndInitDocEncoder(aFormatType, aFlags, charsetStr, getter_AddRefs(encoder));
  1.1283 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1284 +  return encoder->EncodeToString(aOutputString);
  1.1285 +}
  1.1286 +
  1.1287 +NS_IMETHODIMP
  1.1288 +nsPlaintextEditor::OutputToStream(nsIOutputStream* aOutputStream,
  1.1289 +                             const nsAString& aFormatType,
  1.1290 +                             const nsACString& aCharset,
  1.1291 +                             uint32_t aFlags)
  1.1292 +{
  1.1293 +  nsresult rv;
  1.1294 +
  1.1295 +  // special-case for empty document when requesting plain text,
  1.1296 +  // to account for the bogus text node.
  1.1297 +  // XXX Should there be a similar test in OutputToString?
  1.1298 +  if (aFormatType.EqualsLiteral("text/plain"))
  1.1299 +  {
  1.1300 +    bool docEmpty;
  1.1301 +    rv = GetDocumentIsEmpty(&docEmpty);
  1.1302 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1303 +    
  1.1304 +    if (docEmpty)
  1.1305 +       return NS_OK;    // output nothing
  1.1306 +  }
  1.1307 +
  1.1308 +  nsCOMPtr<nsIDocumentEncoder> encoder;
  1.1309 +  rv = GetAndInitDocEncoder(aFormatType, aFlags, aCharset,
  1.1310 +                            getter_AddRefs(encoder));
  1.1311 +
  1.1312 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1313 +
  1.1314 +  return encoder->EncodeToStream(aOutputStream);
  1.1315 +}
  1.1316 +
  1.1317 +NS_IMETHODIMP
  1.1318 +nsPlaintextEditor::InsertTextWithQuotations(const nsAString &aStringToInsert)
  1.1319 +{
  1.1320 +  return InsertText(aStringToInsert);
  1.1321 +}
  1.1322 +
  1.1323 +NS_IMETHODIMP
  1.1324 +nsPlaintextEditor::PasteAsQuotation(int32_t aSelectionType)
  1.1325 +{
  1.1326 +  // Get Clipboard Service
  1.1327 +  nsresult rv;
  1.1328 +  nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
  1.1329 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1330 +
  1.1331 +  // Get the nsITransferable interface for getting the data from the clipboard
  1.1332 +  nsCOMPtr<nsITransferable> trans;
  1.1333 +  rv = PrepareTransferable(getter_AddRefs(trans));
  1.1334 +  if (NS_SUCCEEDED(rv) && trans)
  1.1335 +  {
  1.1336 +    // Get the Data from the clipboard
  1.1337 +    clipboard->GetData(trans, aSelectionType);
  1.1338 +
  1.1339 +    // Now we ask the transferable for the data
  1.1340 +    // it still owns the data, we just have a pointer to it.
  1.1341 +    // If it can't support a "text" output of the data the call will fail
  1.1342 +    nsCOMPtr<nsISupports> genericDataObj;
  1.1343 +    uint32_t len;
  1.1344 +    char* flav = nullptr;
  1.1345 +    rv = trans->GetAnyTransferData(&flav, getter_AddRefs(genericDataObj),
  1.1346 +                                   &len);
  1.1347 +    if (NS_FAILED(rv) || !flav)
  1.1348 +    {
  1.1349 +#ifdef DEBUG_akkana
  1.1350 +      printf("PasteAsPlaintextQuotation: GetAnyTransferData failed, %d\n", rv);
  1.1351 +#endif
  1.1352 +      return rv;
  1.1353 +    }
  1.1354 +#ifdef DEBUG_clipboard
  1.1355 +    printf("Got flavor [%s]\n", flav);
  1.1356 +#endif
  1.1357 +    if (0 == nsCRT::strcmp(flav, kUnicodeMime) ||
  1.1358 +        0 == nsCRT::strcmp(flav, kMozTextInternal))
  1.1359 +    {
  1.1360 +      nsCOMPtr<nsISupportsString> textDataObj ( do_QueryInterface(genericDataObj) );
  1.1361 +      if (textDataObj && len > 0)
  1.1362 +      {
  1.1363 +        nsAutoString stuffToPaste;
  1.1364 +        textDataObj->GetData ( stuffToPaste );
  1.1365 +        nsAutoEditBatch beginBatching(this);
  1.1366 +        rv = InsertAsQuotation(stuffToPaste, 0);
  1.1367 +      }
  1.1368 +    }
  1.1369 +    NS_Free(flav);
  1.1370 +  }
  1.1371 +
  1.1372 +  return rv;
  1.1373 +}
  1.1374 +
  1.1375 +NS_IMETHODIMP
  1.1376 +nsPlaintextEditor::InsertAsQuotation(const nsAString& aQuotedText,
  1.1377 +                                     nsIDOMNode **aNodeInserted)
  1.1378 +{
  1.1379 +  // Protect the edit rules object from dying
  1.1380 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
  1.1381 +
  1.1382 +  // Let the citer quote it for us:
  1.1383 +  nsString quotedStuff;
  1.1384 +  nsresult rv = nsInternetCiter::GetCiteString(aQuotedText, quotedStuff);
  1.1385 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1386 +
  1.1387 +  // It's best to put a blank line after the quoted text so that mails
  1.1388 +  // written without thinking won't be so ugly.
  1.1389 +  if (!aQuotedText.IsEmpty() && (aQuotedText.Last() != char16_t('\n')))
  1.1390 +    quotedStuff.Append(char16_t('\n'));
  1.1391 +
  1.1392 +  // get selection
  1.1393 +  nsRefPtr<Selection> selection = GetSelection();
  1.1394 +  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  1.1395 +
  1.1396 +  nsAutoEditBatch beginBatching(this);
  1.1397 +  nsAutoRules beginRulesSniffing(this, EditAction::insertText, nsIEditor::eNext);
  1.1398 +
  1.1399 +  // give rules a chance to handle or cancel
  1.1400 +  nsTextRulesInfo ruleInfo(EditAction::insertElement);
  1.1401 +  bool cancel, handled;
  1.1402 +  rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
  1.1403 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1404 +  if (cancel) return NS_OK; // rules canceled the operation
  1.1405 +  if (!handled)
  1.1406 +  {
  1.1407 +    rv = InsertText(quotedStuff);
  1.1408 +
  1.1409 +    // XXX Should set *aNodeInserted to the first node inserted
  1.1410 +    if (aNodeInserted && NS_SUCCEEDED(rv))
  1.1411 +    {
  1.1412 +      *aNodeInserted = 0;
  1.1413 +      //NS_IF_ADDREF(*aNodeInserted);
  1.1414 +    }
  1.1415 +  }
  1.1416 +  return rv;
  1.1417 +}
  1.1418 +
  1.1419 +NS_IMETHODIMP
  1.1420 +nsPlaintextEditor::PasteAsCitedQuotation(const nsAString& aCitation,
  1.1421 +                                         int32_t aSelectionType)
  1.1422 +{
  1.1423 +  return NS_ERROR_NOT_IMPLEMENTED;
  1.1424 +}
  1.1425 +
  1.1426 +NS_IMETHODIMP
  1.1427 +nsPlaintextEditor::InsertAsCitedQuotation(const nsAString& aQuotedText,
  1.1428 +                                          const nsAString& aCitation,
  1.1429 +                                          bool aInsertHTML,
  1.1430 +                                          nsIDOMNode **aNodeInserted)
  1.1431 +{
  1.1432 +  return InsertAsQuotation(aQuotedText, aNodeInserted);
  1.1433 +}
  1.1434 +
  1.1435 +nsresult
  1.1436 +nsPlaintextEditor::SharedOutputString(uint32_t aFlags,
  1.1437 +                                      bool* aIsCollapsed,
  1.1438 +                                      nsAString& aResult)
  1.1439 +{
  1.1440 +  nsCOMPtr<nsISelection> selection;
  1.1441 +  nsresult rv = GetSelection(getter_AddRefs(selection));
  1.1442 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1443 +  NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED);
  1.1444 +
  1.1445 +  *aIsCollapsed = selection->Collapsed();
  1.1446 +
  1.1447 +  if (!*aIsCollapsed)
  1.1448 +    aFlags |= nsIDocumentEncoder::OutputSelectionOnly;
  1.1449 +  // If the selection isn't collapsed, we'll use the whole document.
  1.1450 +
  1.1451 +  return OutputToString(NS_LITERAL_STRING("text/plain"), aFlags, aResult);
  1.1452 +}
  1.1453 +
  1.1454 +NS_IMETHODIMP
  1.1455 +nsPlaintextEditor::Rewrap(bool aRespectNewlines)
  1.1456 +{
  1.1457 +  int32_t wrapCol;
  1.1458 +  nsresult rv = GetWrapWidth(&wrapCol);
  1.1459 +  NS_ENSURE_SUCCESS(rv, NS_OK);
  1.1460 +
  1.1461 +  // Rewrap makes no sense if there's no wrap column; default to 72.
  1.1462 +  if (wrapCol <= 0)
  1.1463 +    wrapCol = 72;
  1.1464 +
  1.1465 +#ifdef DEBUG_akkana
  1.1466 +  printf("nsPlaintextEditor::Rewrap to %ld columns\n", (long)wrapCol);
  1.1467 +#endif
  1.1468 +
  1.1469 +  nsAutoString current;
  1.1470 +  bool isCollapsed;
  1.1471 +  rv = SharedOutputString(nsIDocumentEncoder::OutputFormatted
  1.1472 +                          | nsIDocumentEncoder::OutputLFLineBreak,
  1.1473 +                          &isCollapsed, current);
  1.1474 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1475 +
  1.1476 +  nsString wrapped;
  1.1477 +  uint32_t firstLineOffset = 0;   // XXX need to reset this if there is a selection
  1.1478 +  rv = nsInternetCiter::Rewrap(current, wrapCol, firstLineOffset, aRespectNewlines,
  1.1479 +                     wrapped);
  1.1480 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1481 +
  1.1482 +  if (isCollapsed)    // rewrap the whole document
  1.1483 +    SelectAll();
  1.1484 +
  1.1485 +  return InsertTextWithQuotations(wrapped);
  1.1486 +}
  1.1487 +
  1.1488 +NS_IMETHODIMP    
  1.1489 +nsPlaintextEditor::StripCites()
  1.1490 +{
  1.1491 +#ifdef DEBUG_akkana
  1.1492 +  printf("nsPlaintextEditor::StripCites()\n");
  1.1493 +#endif
  1.1494 +
  1.1495 +  nsAutoString current;
  1.1496 +  bool isCollapsed;
  1.1497 +  nsresult rv = SharedOutputString(nsIDocumentEncoder::OutputFormatted,
  1.1498 +                                   &isCollapsed, current);
  1.1499 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1500 +
  1.1501 +  nsString stripped;
  1.1502 +  rv = nsInternetCiter::StripCites(current, stripped);
  1.1503 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1504 +
  1.1505 +  if (isCollapsed)    // rewrap the whole document
  1.1506 +  {
  1.1507 +    rv = SelectAll();
  1.1508 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1509 +  }
  1.1510 +
  1.1511 +  return InsertText(stripped);
  1.1512 +}
  1.1513 +
  1.1514 +NS_IMETHODIMP
  1.1515 +nsPlaintextEditor::GetEmbeddedObjects(nsISupportsArray** aNodeList)
  1.1516 +{
  1.1517 +  *aNodeList = 0;
  1.1518 +  return NS_OK;
  1.1519 +}
  1.1520 +
  1.1521 +
  1.1522 +/** All editor operations which alter the doc should be prefaced
  1.1523 + *  with a call to StartOperation, naming the action and direction */
  1.1524 +NS_IMETHODIMP
  1.1525 +nsPlaintextEditor::StartOperation(EditAction opID,
  1.1526 +                                  nsIEditor::EDirection aDirection)
  1.1527 +{
  1.1528 +  // Protect the edit rules object from dying
  1.1529 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
  1.1530 +
  1.1531 +  nsEditor::StartOperation(opID, aDirection);  // will set mAction, mDirection
  1.1532 +  if (mRules) return mRules->BeforeEdit(mAction, mDirection);
  1.1533 +  return NS_OK;
  1.1534 +}
  1.1535 +
  1.1536 +
  1.1537 +/** All editor operations which alter the doc should be followed
  1.1538 + *  with a call to EndOperation */
  1.1539 +NS_IMETHODIMP
  1.1540 +nsPlaintextEditor::EndOperation()
  1.1541 +{
  1.1542 +  // Protect the edit rules object from dying
  1.1543 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
  1.1544 +
  1.1545 +  // post processing
  1.1546 +  nsresult res = NS_OK;
  1.1547 +  if (mRules) res = mRules->AfterEdit(mAction, mDirection);
  1.1548 +  nsEditor::EndOperation();  // will clear mAction, mDirection
  1.1549 +  return res;
  1.1550 +}  
  1.1551 +
  1.1552 +
  1.1553 +NS_IMETHODIMP 
  1.1554 +nsPlaintextEditor::SelectEntireDocument(nsISelection *aSelection)
  1.1555 +{
  1.1556 +  if (!aSelection || !mRules) { return NS_ERROR_NULL_POINTER; }
  1.1557 +
  1.1558 +  // Protect the edit rules object from dying
  1.1559 +  nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
  1.1560 +
  1.1561 +  // is doc empty?
  1.1562 +  bool bDocIsEmpty;
  1.1563 +  if (NS_SUCCEEDED(mRules->DocumentIsEmpty(&bDocIsEmpty)) && bDocIsEmpty)
  1.1564 +  {
  1.1565 +    // get root node
  1.1566 +    nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(GetRoot());
  1.1567 +    NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE);
  1.1568 +
  1.1569 +    // if it's empty don't select entire doc - that would select the bogus node
  1.1570 +    return aSelection->Collapse(rootElement, 0);
  1.1571 +  }
  1.1572 +
  1.1573 +  nsresult rv = nsEditor::SelectEntireDocument(aSelection);
  1.1574 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1575 +
  1.1576 +  // Don't select the trailing BR node if we have one
  1.1577 +  int32_t selOffset;
  1.1578 +  nsCOMPtr<nsIDOMNode> selNode;
  1.1579 +  rv = GetEndNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
  1.1580 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1581 +
  1.1582 +  nsCOMPtr<nsIDOMNode> childNode = GetChildAt(selNode, selOffset - 1);
  1.1583 +
  1.1584 +  if (childNode && nsTextEditUtils::IsMozBR(childNode)) {
  1.1585 +    int32_t parentOffset;
  1.1586 +    nsCOMPtr<nsIDOMNode> parentNode = GetNodeLocation(childNode, &parentOffset);
  1.1587 +
  1.1588 +    return aSelection->Extend(parentNode, parentOffset);
  1.1589 +  }
  1.1590 +
  1.1591 +  return NS_OK;
  1.1592 +}
  1.1593 +
  1.1594 +already_AddRefed<mozilla::dom::EventTarget>
  1.1595 +nsPlaintextEditor::GetDOMEventTarget()
  1.1596 +{
  1.1597 +  nsCOMPtr<mozilla::dom::EventTarget> copy = mEventTarget;
  1.1598 +  return copy.forget();
  1.1599 +}
  1.1600 +
  1.1601 +
  1.1602 +nsresult
  1.1603 +nsPlaintextEditor::SetAttributeOrEquivalent(nsIDOMElement * aElement,
  1.1604 +                                            const nsAString & aAttribute,
  1.1605 +                                            const nsAString & aValue,
  1.1606 +                                            bool aSuppressTransaction)
  1.1607 +{
  1.1608 +  return nsEditor::SetAttribute(aElement, aAttribute, aValue);
  1.1609 +}
  1.1610 +
  1.1611 +nsresult
  1.1612 +nsPlaintextEditor::RemoveAttributeOrEquivalent(nsIDOMElement * aElement,
  1.1613 +                                               const nsAString & aAttribute,
  1.1614 +                                               bool aSuppressTransaction)
  1.1615 +{
  1.1616 +  return nsEditor::RemoveAttribute(aElement, aAttribute);
  1.1617 +}

mercurial