editor/libeditor/text/nsPlaintextEditor.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial