LCOV - code coverage report
Current view: top level - editor/libeditor - TextEditRules.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 253 779 32.5 %
Date: 2017-07-14 16:53:18 Functions: 27 59 45.8 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       5             : 
       6             : #include "mozilla/TextEditRules.h"
       7             : 
       8             : #include "TextEditUtils.h"
       9             : #include "mozilla/Assertions.h"
      10             : #include "mozilla/EditorUtils.h"
      11             : #include "mozilla/LookAndFeel.h"
      12             : #include "mozilla/Preferences.h"
      13             : #include "mozilla/TextComposition.h"
      14             : #include "mozilla/TextEditor.h"
      15             : #include "mozilla/dom/Element.h"
      16             : #include "mozilla/dom/NodeIterator.h"
      17             : #include "mozilla/dom/Selection.h"
      18             : #include "nsAString.h"
      19             : #include "nsCOMPtr.h"
      20             : #include "nsCRT.h"
      21             : #include "nsCRTGlue.h"
      22             : #include "nsComponentManagerUtils.h"
      23             : #include "nsContentUtils.h"
      24             : #include "nsDebug.h"
      25             : #include "nsError.h"
      26             : #include "nsGkAtoms.h"
      27             : #include "nsIContent.h"
      28             : #include "nsIDocumentEncoder.h"
      29             : #include "nsIDOMDocument.h"
      30             : #include "nsIDOMNode.h"
      31             : #include "nsIDOMNodeFilter.h"
      32             : #include "nsNameSpaceManager.h"
      33             : #include "nsINode.h"
      34             : #include "nsIPlaintextEditor.h"
      35             : #include "nsISupportsBase.h"
      36             : #include "nsLiteralString.h"
      37             : #include "nsTextNode.h"
      38             : #include "nsUnicharUtils.h"
      39             : #include "nsIHTMLCollection.h"
      40             : #include "nsPrintfCString.h"
      41             : 
      42             : namespace mozilla {
      43             : 
      44             : using namespace dom;
      45             : 
      46             : #define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \
      47             :   if (IsReadonly() || IsDisabled()) \
      48             :   {                     \
      49             :     *aCancel = true; \
      50             :     return NS_OK;       \
      51             :   };
      52             : 
      53             : /********************************************************
      54             :  * mozilla::TextEditRules
      55             :  ********************************************************/
      56             : 
      57           1 : TextEditRules::TextEditRules()
      58             :   : mTextEditor(nullptr)
      59             :   , mPasswordIMEIndex(0)
      60             :   , mCachedSelectionOffset(0)
      61             :   , mActionNesting(0)
      62             :   , mLockRulesSniffing(false)
      63             :   , mDidExplicitlySetInterline(false)
      64             :   , mDeleteBidiImmediately(false)
      65             :   , mTheAction(EditAction::none)
      66             :   , mLastStart(0)
      67           1 :   , mLastLength(0)
      68             : {
      69           1 :   InitFields();
      70           1 : }
      71             : 
      72             : void
      73           3 : TextEditRules::InitFields()
      74             : {
      75           3 :   mTextEditor = nullptr;
      76           3 :   mPasswordText.Truncate();
      77           3 :   mPasswordIMEText.Truncate();
      78           3 :   mPasswordIMEIndex = 0;
      79           3 :   mBogusNode = nullptr;
      80           3 :   mCachedSelectionNode = nullptr;
      81           3 :   mCachedSelectionOffset = 0;
      82           3 :   mActionNesting = 0;
      83           3 :   mLockRulesSniffing = false;
      84           3 :   mDidExplicitlySetInterline = false;
      85           3 :   mDeleteBidiImmediately = false;
      86           3 :   mTheAction = EditAction::none;
      87           3 :   mTimer = nullptr;
      88           3 :   mLastStart = 0;
      89           3 :   mLastLength = 0;
      90           3 : }
      91             : 
      92           0 : TextEditRules::~TextEditRules()
      93             : {
      94             :    // do NOT delete mTextEditor here.  We do not hold a ref count to
      95             :    // mTextEditor.  mTextEditor owns our lifespan.
      96             : 
      97           0 :   if (mTimer) {
      98           0 :     mTimer->Cancel();
      99             :   }
     100           0 : }
     101             : 
     102           0 : NS_IMPL_CYCLE_COLLECTION(TextEditRules, mBogusNode, mCachedSelectionNode)
     103             : 
     104           2 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextEditRules)
     105           1 :   NS_INTERFACE_MAP_ENTRY(nsIEditRules)
     106           0 :   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
     107           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditRules)
     108           0 : NS_INTERFACE_MAP_END
     109             : 
     110          17 : NS_IMPL_CYCLE_COLLECTING_ADDREF(TextEditRules)
     111          16 : NS_IMPL_CYCLE_COLLECTING_RELEASE(TextEditRules)
     112             : 
     113             : NS_IMETHODIMP
     114           2 : TextEditRules::Init(TextEditor* aTextEditor)
     115             : {
     116           2 :   if (!aTextEditor) {
     117           0 :     return NS_ERROR_NULL_POINTER;
     118             :   }
     119             : 
     120           2 :   InitFields();
     121             : 
     122             :   // We hold a non-refcounted reference back to our editor.
     123           2 :   mTextEditor = aTextEditor;
     124           4 :   RefPtr<Selection> selection = mTextEditor->GetSelection();
     125           2 :   NS_WARNING_ASSERTION(selection, "editor cannot get selection");
     126             : 
     127             :   // Put in a magic br if needed. This method handles null selection,
     128             :   // which should never happen anyway
     129           2 :   nsresult rv = CreateBogusNodeIfNeeded(selection);
     130           2 :   NS_ENSURE_SUCCESS(rv, rv);
     131             : 
     132             :   // If the selection hasn't been set up yet, set it up collapsed to the end of
     133             :   // our editable content.
     134             :   int32_t rangeCount;
     135           2 :   rv = selection->GetRangeCount(&rangeCount);
     136           2 :   NS_ENSURE_SUCCESS(rv, rv);
     137           2 :   if (!rangeCount) {
     138           0 :     rv = mTextEditor->EndOfDocument();
     139           0 :     NS_ENSURE_SUCCESS(rv, rv);
     140             :   }
     141             : 
     142           2 :   if (IsPlaintextEditor()) {
     143             :     // ensure trailing br node
     144           2 :     rv = CreateTrailingBRIfNeeded();
     145           2 :     NS_ENSURE_SUCCESS(rv, rv);
     146             :   }
     147             : 
     148           2 :   mDeleteBidiImmediately =
     149           2 :     Preferences::GetBool("bidi.edit.delete_immediately", false);
     150             : 
     151           2 :   return NS_OK;
     152             : }
     153             : 
     154             : NS_IMETHODIMP
     155           2 : TextEditRules::SetInitialValue(const nsAString& aValue)
     156             : {
     157           2 :   if (IsPasswordEditor()) {
     158           0 :     mPasswordText = aValue;
     159             :   }
     160           2 :   return NS_OK;
     161             : }
     162             : 
     163             : NS_IMETHODIMP
     164           1 : TextEditRules::DetachEditor()
     165             : {
     166           1 :   if (mTimer) {
     167           0 :     mTimer->Cancel();
     168             :   }
     169           1 :   mTextEditor = nullptr;
     170           1 :   return NS_OK;
     171             : }
     172             : 
     173             : NS_IMETHODIMP
     174           3 : TextEditRules::BeforeEdit(EditAction action,
     175             :                           nsIEditor::EDirection aDirection)
     176             : {
     177           3 :   if (mLockRulesSniffing) {
     178           0 :     return NS_OK;
     179             :   }
     180             : 
     181           6 :   AutoLockRulesSniffing lockIt(this);
     182           3 :   mDidExplicitlySetInterline = false;
     183           3 :   if (!mActionNesting) {
     184             :     // let rules remember the top level action
     185           3 :     mTheAction = action;
     186             :   }
     187           3 :   mActionNesting++;
     188             : 
     189             :   // get the selection and cache the position before editing
     190           3 :   if (NS_WARN_IF(!mTextEditor)) {
     191           0 :     return NS_ERROR_FAILURE;
     192             :   }
     193           6 :   RefPtr<TextEditor> textEditor = mTextEditor;
     194           6 :   RefPtr<Selection> selection = textEditor->GetSelection();
     195           3 :   NS_ENSURE_STATE(selection);
     196             : 
     197           3 :   if (action == EditAction::setText) {
     198             :     // setText replaces all text, so mCachedSelectionNode might be invalid on
     199             :     // AfterEdit.
     200             :     // Since this will be used as start position of spellchecker, we should
     201             :     // use root instead.
     202           1 :     mCachedSelectionNode = textEditor->GetRoot();
     203           1 :     mCachedSelectionOffset = 0;
     204             :   } else {
     205           2 :     mCachedSelectionNode = selection->GetAnchorNode();
     206           2 :     selection->GetAnchorOffset(&mCachedSelectionOffset);
     207             :   }
     208             : 
     209           3 :   return NS_OK;
     210             : }
     211             : 
     212             : NS_IMETHODIMP
     213           3 : TextEditRules::AfterEdit(EditAction action,
     214             :                          nsIEditor::EDirection aDirection)
     215             : {
     216           3 :   if (mLockRulesSniffing) {
     217           0 :     return NS_OK;
     218             :   }
     219             : 
     220           6 :   AutoLockRulesSniffing lockIt(this);
     221             : 
     222           3 :   NS_PRECONDITION(mActionNesting>0, "bad action nesting!");
     223           3 :   if (!--mActionNesting) {
     224           3 :     NS_ENSURE_STATE(mTextEditor);
     225           6 :     RefPtr<Selection> selection = mTextEditor->GetSelection();
     226           3 :     NS_ENSURE_STATE(selection);
     227             : 
     228           3 :     NS_ENSURE_STATE(mTextEditor);
     229             :     nsresult rv =
     230           3 :       mTextEditor->HandleInlineSpellCheck(action, selection,
     231             :                                           GetAsDOMNode(mCachedSelectionNode),
     232             :                                           mCachedSelectionOffset,
     233           3 :                                           nullptr, 0, nullptr, 0);
     234           3 :     NS_ENSURE_SUCCESS(rv, rv);
     235             : 
     236             :     // no longer uses mCachedSelectionNode, so release it.
     237           3 :     mCachedSelectionNode = nullptr;
     238             : 
     239             :     // if only trailing <br> remaining remove it
     240           3 :     rv = RemoveRedundantTrailingBR();
     241           3 :     if (NS_FAILED(rv)) {
     242           0 :       return rv;
     243             :     }
     244             : 
     245             :     // detect empty doc
     246           3 :     rv = CreateBogusNodeIfNeeded(selection);
     247           3 :     NS_ENSURE_SUCCESS(rv, rv);
     248             : 
     249             :     // ensure trailing br node
     250           3 :     rv = CreateTrailingBRIfNeeded();
     251           3 :     NS_ENSURE_SUCCESS(rv, rv);
     252             : 
     253             :     // collapse the selection to the trailing BR if it's at the end of our text node
     254           3 :     CollapseSelectionToTrailingBRIfNeeded(selection);
     255             :   }
     256           3 :   return NS_OK;
     257             : }
     258             : 
     259             : NS_IMETHODIMP
     260           5 : TextEditRules::WillDoAction(Selection* aSelection,
     261             :                             RulesInfo* aInfo,
     262             :                             bool* aCancel,
     263             :                             bool* aHandled)
     264             : {
     265             :   // null selection is legal
     266           5 :   MOZ_ASSERT(aInfo && aCancel && aHandled);
     267             : 
     268           5 :   *aCancel = false;
     269           5 :   *aHandled = false;
     270             : 
     271             :   // my kingdom for dynamic cast
     272           5 :   TextRulesInfo* info = static_cast<TextRulesInfo*>(aInfo);
     273             : 
     274           5 :   switch (info->action) {
     275             :     case EditAction::insertBreak:
     276           0 :       UndefineCaretBidiLevel(aSelection);
     277           0 :       return WillInsertBreak(aSelection, aCancel, aHandled, info->maxLength);
     278             :     case EditAction::insertText:
     279             :     case EditAction::insertIMEText:
     280           0 :       UndefineCaretBidiLevel(aSelection);
     281           0 :       return WillInsertText(info->action, aSelection, aCancel, aHandled,
     282           0 :                             info->inString, info->outString, info->maxLength);
     283             :     case EditAction::setText:
     284           1 :       UndefineCaretBidiLevel(aSelection);
     285           1 :       return WillSetText(*aSelection, aCancel, aHandled, info->inString,
     286           1 :                          info->maxLength);
     287             :     case EditAction::deleteSelection:
     288           0 :       return WillDeleteSelection(aSelection, info->collapsedAction,
     289           0 :                                  aCancel, aHandled);
     290             :     case EditAction::undo:
     291           0 :       return WillUndo(aSelection, aCancel, aHandled);
     292             :     case EditAction::redo:
     293           0 :       return WillRedo(aSelection, aCancel, aHandled);
     294             :     case EditAction::setTextProperty:
     295           0 :       return WillSetTextProperty(aSelection, aCancel, aHandled);
     296             :     case EditAction::removeTextProperty:
     297           0 :       return WillRemoveTextProperty(aSelection, aCancel, aHandled);
     298             :     case EditAction::outputText:
     299           4 :       return WillOutputText(aSelection, info->outputFormat, info->outString,
     300           4 :                             info->flags, aCancel, aHandled);
     301             :     case EditAction::insertElement:
     302             :       // i had thought this would be html rules only.  but we put pre elements
     303             :       // into plaintext mail when doing quoting for reply!  doh!
     304           0 :       WillInsert(*aSelection, aCancel);
     305           0 :       return NS_OK;
     306             :     default:
     307           0 :       return NS_ERROR_FAILURE;
     308             :   }
     309             : }
     310             : 
     311             : NS_IMETHODIMP
     312           1 : TextEditRules::DidDoAction(Selection* aSelection,
     313             :                            RulesInfo* aInfo,
     314             :                            nsresult aResult)
     315             : {
     316           1 :   NS_ENSURE_STATE(mTextEditor);
     317             :   // don't let any txns in here move the selection around behind our back.
     318             :   // Note that this won't prevent explicit selection setting from working.
     319           2 :   AutoTransactionsConserveSelection dontSpazMySelection(mTextEditor);
     320             : 
     321           1 :   NS_ENSURE_TRUE(aSelection && aInfo, NS_ERROR_NULL_POINTER);
     322             : 
     323             :   // my kingdom for dynamic cast
     324           1 :   TextRulesInfo* info = static_cast<TextRulesInfo*>(aInfo);
     325             : 
     326           1 :   switch (info->action) {
     327             :     case EditAction::insertBreak:
     328           0 :       return DidInsertBreak(aSelection, aResult);
     329             :     case EditAction::insertText:
     330             :     case EditAction::insertIMEText:
     331           0 :       return DidInsertText(aSelection, aResult);
     332             :     case EditAction::setText:
     333           1 :       return DidSetText(*aSelection, aResult);
     334             :     case EditAction::deleteSelection:
     335           0 :       return DidDeleteSelection(aSelection, info->collapsedAction, aResult);
     336             :     case EditAction::undo:
     337           0 :       return DidUndo(aSelection, aResult);
     338             :     case EditAction::redo:
     339           0 :       return DidRedo(aSelection, aResult);
     340             :     case EditAction::setTextProperty:
     341           0 :       return DidSetTextProperty(aSelection, aResult);
     342             :     case EditAction::removeTextProperty:
     343           0 :       return DidRemoveTextProperty(aSelection, aResult);
     344             :     case EditAction::outputText:
     345           0 :       return DidOutputText(aSelection, aResult);
     346             :     default:
     347             :       // Don't fail on transactions we don't handle here!
     348           0 :       return NS_OK;
     349             :   }
     350             : }
     351             : 
     352             : /**
     353             :  * Return false if the editor has non-empty text nodes or non-text
     354             :  * nodes.  Otherwise, i.e., there is no meaningful content,
     355             :  * return true.
     356             :  */
     357             : NS_IMETHODIMP_(bool)
     358           4 : TextEditRules::DocumentIsEmpty()
     359             : {
     360           4 :   if (mBogusNode) {
     361           1 :     return true;
     362             :   }
     363             : 
     364             :   // Even if there is no bogus node, we should detect as empty document
     365             :   // if all children are text node and these are no content.
     366           3 :   if (NS_WARN_IF(!mTextEditor)) {
     367           0 :     return true;
     368             :   }
     369           3 :   Element* rootElement = mTextEditor->GetRoot();
     370           3 :   if (!rootElement) {
     371           0 :     return true;
     372             :   }
     373             : 
     374           3 :   uint32_t childCount = rootElement->GetChildCount();
     375           3 :   for (uint32_t i = 0; i < childCount; i++) {
     376           3 :     nsINode* node = rootElement->GetChildAt(i);
     377           6 :     if (!EditorBase::IsTextNode(node) ||
     378           3 :         node->Length()) {
     379           3 :       return false;
     380             :     }
     381             :   }
     382           0 :   return true;
     383             : }
     384             : 
     385             : void
     386           1 : TextEditRules::WillInsert(Selection& aSelection, bool* aCancel)
     387             : {
     388           1 :   MOZ_ASSERT(aCancel);
     389             : 
     390           1 :   if (IsReadonly() || IsDisabled()) {
     391           0 :     *aCancel = true;
     392           0 :     return;
     393             :   }
     394             : 
     395             :   // initialize out param
     396           1 :   *aCancel = false;
     397             : 
     398             :   // check for the magic content node and delete it if it exists
     399           1 :   if (mBogusNode) {
     400           1 :     NS_ENSURE_TRUE_VOID(mTextEditor);
     401           1 :     mTextEditor->DeleteNode(mBogusNode);
     402           1 :     mBogusNode = nullptr;
     403             :   }
     404             : }
     405             : 
     406             : nsresult
     407           0 : TextEditRules::DidInsert(Selection* aSelection,
     408             :                          nsresult aResult)
     409             : {
     410           0 :   return NS_OK;
     411             : }
     412             : 
     413             : nsresult
     414           0 : TextEditRules::WillInsertBreak(Selection* aSelection,
     415             :                                bool* aCancel,
     416             :                                bool* aHandled,
     417             :                                int32_t aMaxLength)
     418             : {
     419           0 :   if (!aSelection || !aCancel || !aHandled) {
     420           0 :     return NS_ERROR_NULL_POINTER;
     421             :   }
     422           0 :   CANCEL_OPERATION_IF_READONLY_OR_DISABLED
     423           0 :   *aHandled = false;
     424           0 :   if (IsSingleLineEditor()) {
     425           0 :     *aCancel = true;
     426             :   } else {
     427             :     // handle docs with a max length
     428             :     // NOTE, this function copies inString into outString for us.
     429           0 :     NS_NAMED_LITERAL_STRING(inString, "\n");
     430           0 :     nsAutoString outString;
     431             :     bool didTruncate;
     432           0 :     nsresult rv = TruncateInsertionIfNeeded(aSelection, &inString.AsString(),
     433             :                                             &outString, aMaxLength,
     434           0 :                                             &didTruncate);
     435           0 :     NS_ENSURE_SUCCESS(rv, rv);
     436           0 :     if (didTruncate) {
     437           0 :       *aCancel = true;
     438           0 :       return NS_OK;
     439             :     }
     440             : 
     441           0 :     *aCancel = false;
     442             : 
     443             :     // if the selection isn't collapsed, delete it.
     444             :     bool bCollapsed;
     445           0 :     rv = aSelection->GetIsCollapsed(&bCollapsed);
     446           0 :     NS_ENSURE_SUCCESS(rv, rv);
     447           0 :     if (!bCollapsed) {
     448           0 :       NS_ENSURE_STATE(mTextEditor);
     449           0 :       rv = mTextEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
     450           0 :       NS_ENSURE_SUCCESS(rv, rv);
     451             :     }
     452             : 
     453           0 :     WillInsert(*aSelection, aCancel);
     454             :     // initialize out param
     455             :     // we want to ignore result of WillInsert()
     456           0 :     *aCancel = false;
     457             :   }
     458           0 :   return NS_OK;
     459             : }
     460             : 
     461             : nsresult
     462           0 : TextEditRules::DidInsertBreak(Selection* aSelection,
     463             :                               nsresult aResult)
     464             : {
     465           0 :   return NS_OK;
     466             : }
     467             : 
     468             : nsresult
     469           3 : TextEditRules::CollapseSelectionToTrailingBRIfNeeded(Selection* aSelection)
     470             : {
     471             :   // we only need to execute the stuff below if we are a plaintext editor.
     472             :   // html editors have a different mechanism for putting in mozBR's
     473             :   // (because there are a bunch more places you have to worry about it in html)
     474           3 :   if (!IsPlaintextEditor()) {
     475           0 :     return NS_OK;
     476             :   }
     477             : 
     478           3 :   NS_ENSURE_STATE(mTextEditor);
     479             : 
     480             :   // If there is no selection ranges, we should set to the end of the editor.
     481             :   // This is usually performed in TextEditRules::Init(), however, if the
     482             :   // editor is reframed, this may be called by AfterEdit().
     483           3 :   if (!aSelection->RangeCount()) {
     484           0 :     mTextEditor->EndOfDocument();
     485             :   }
     486             : 
     487             :   // if we are at the end of the textarea, we need to set the
     488             :   // selection to stick to the mozBR at the end of the textarea.
     489             :   int32_t selOffset;
     490           6 :   nsCOMPtr<nsINode> selNode;
     491             :   nsresult rv =
     492           3 :     EditorBase::GetStartNodeAndOffset(aSelection,
     493           6 :                                       getter_AddRefs(selNode), &selOffset);
     494           3 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     495           0 :     return rv;
     496             :   }
     497             : 
     498           3 :   if (!EditorBase::IsTextNode(selNode)) {
     499           3 :     return NS_OK; // Nothing to do if we're not at a text node.
     500             :   }
     501             : 
     502             :   // nothing to do if we're not at the end of the text node
     503           0 :   if (selOffset != static_cast<int32_t>(selNode->Length())) {
     504           0 :     return NS_OK;
     505             :   }
     506             : 
     507             :   int32_t parentOffset;
     508             :   nsINode* parentNode =
     509           0 :     EditorBase::GetNodeLocation(selNode, &parentOffset);
     510             : 
     511           0 :   NS_ENSURE_STATE(mTextEditor);
     512           0 :   nsINode* root = mTextEditor->GetRoot();
     513           0 :   if (NS_WARN_IF(!root)) {
     514           0 :     return NS_ERROR_NULL_POINTER;
     515             :   }
     516           0 :   if (parentNode != root) {
     517           0 :     return NS_OK;
     518             :   }
     519             : 
     520           0 :   nsINode* nextNode = parentNode->GetChildAt(parentOffset + 1);
     521           0 :   if (nextNode && TextEditUtils::IsMozBR(nextNode)) {
     522           0 :     rv = aSelection->Collapse(parentNode, parentOffset + 1);
     523           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     524           0 :       return rv;
     525             :     }
     526             :   }
     527           0 :   return NS_OK;
     528             : }
     529             : 
     530             : static inline already_AddRefed<nsINode>
     531           0 : GetTextNode(Selection* selection)
     532             : {
     533             :   int32_t selOffset;
     534           0 :   nsCOMPtr<nsINode> selNode;
     535             :   nsresult rv =
     536           0 :     EditorBase::GetStartNodeAndOffset(selection,
     537           0 :                                       getter_AddRefs(selNode), &selOffset);
     538           0 :   NS_ENSURE_SUCCESS(rv, nullptr);
     539           0 :   if (!EditorBase::IsTextNode(selNode)) {
     540             :     // This should be the root node, walk the tree looking for text nodes
     541             :     RefPtr<NodeIterator> iter =
     542             :       new NodeIterator(selNode, nsIDOMNodeFilter::SHOW_TEXT,
     543           0 :                        NodeFilterHolder());
     544           0 :     while (!EditorBase::IsTextNode(selNode)) {
     545           0 :       IgnoredErrorResult rv;
     546           0 :       selNode = iter->NextNode(rv);
     547           0 :       if (!selNode) {
     548           0 :         return nullptr;
     549             :       }
     550             :     }
     551             :   }
     552           0 :   return selNode.forget();
     553             : }
     554             : #ifdef DEBUG
     555             : #define ASSERT_PASSWORD_LENGTHS_EQUAL()                                \
     556             :   if (IsPasswordEditor() && mTextEditor->GetRoot()) {                  \
     557             :     int32_t txtLen;                                                    \
     558             :     mTextEditor->GetTextLength(&txtLen);                               \
     559             :     NS_ASSERTION(mPasswordText.Length() == uint32_t(txtLen),           \
     560             :                  "password length not equal to number of asterisks");  \
     561             :   }
     562             : #else
     563             : #define ASSERT_PASSWORD_LENGTHS_EQUAL()
     564             : #endif
     565             : 
     566             : // static
     567             : void
     568           1 : TextEditRules::HandleNewLines(nsString& aString,
     569             :                               int32_t aNewlineHandling)
     570             : {
     571           1 :   if (aNewlineHandling < 0) {
     572             :     int32_t caretStyle;
     573           0 :     TextEditor::GetDefaultEditorPrefs(aNewlineHandling, caretStyle);
     574             :   }
     575             : 
     576           1 :   switch(aNewlineHandling) {
     577             :     case nsIPlaintextEditor::eNewlinesReplaceWithSpaces:
     578             :       // Strip trailing newlines first so we don't wind up with trailing spaces
     579           0 :       aString.Trim(CRLF, false, true);
     580           0 :       aString.ReplaceChar(CRLF, ' ');
     581           0 :       break;
     582             :     case nsIPlaintextEditor::eNewlinesStrip:
     583           0 :       aString.StripCRLF();
     584           0 :       break;
     585             :     case nsIPlaintextEditor::eNewlinesPasteToFirst:
     586             :     default: {
     587           0 :       int32_t firstCRLF = aString.FindCharInSet(CRLF);
     588             : 
     589             :       // we get first *non-empty* line.
     590           0 :       int32_t offset = 0;
     591           0 :       while (firstCRLF == offset) {
     592           0 :         offset++;
     593           0 :         firstCRLF = aString.FindCharInSet(CRLF, offset);
     594             :       }
     595           0 :       if (firstCRLF > 0) {
     596           0 :         aString.Truncate(firstCRLF);
     597             :       }
     598           0 :       if (offset > 0) {
     599           0 :         aString.Cut(0, offset);
     600             :       }
     601           0 :       break;
     602             :     }
     603             :     case nsIPlaintextEditor::eNewlinesReplaceWithCommas:
     604           0 :       aString.Trim(CRLF, true, true);
     605           0 :       aString.ReplaceChar(CRLF, ',');
     606           0 :       break;
     607             :     case nsIPlaintextEditor::eNewlinesStripSurroundingWhitespace: {
     608           2 :       nsAutoString result;
     609           1 :       uint32_t offset = 0;
     610           1 :       while (offset < aString.Length()) {
     611           1 :         int32_t nextCRLF = aString.FindCharInSet(CRLF, offset);
     612           1 :         if (nextCRLF < 0) {
     613           1 :           result.Append(nsDependentSubstring(aString, offset));
     614           1 :           break;
     615             :         }
     616           0 :         uint32_t wsBegin = nextCRLF;
     617             :         // look backwards for the first non-whitespace char
     618           0 :         while (wsBegin > offset && NS_IS_SPACE(aString[wsBegin - 1])) {
     619           0 :           --wsBegin;
     620             :         }
     621           0 :         result.Append(nsDependentSubstring(aString, offset, wsBegin - offset));
     622           0 :         offset = nextCRLF + 1;
     623           0 :         while (offset < aString.Length() && NS_IS_SPACE(aString[offset])) {
     624           0 :           ++offset;
     625             :         }
     626             :       }
     627           1 :       aString = result;
     628           1 :       break;
     629             :     }
     630             :     case nsIPlaintextEditor::eNewlinesPasteIntact:
     631             :       // even if we're pasting newlines, don't paste leading/trailing ones
     632           0 :       aString.Trim(CRLF, true, true);
     633           0 :       break;
     634             :   }
     635           1 : }
     636             : 
     637             : nsresult
     638           0 : TextEditRules::WillInsertText(EditAction aAction,
     639             :                               Selection* aSelection,
     640             :                               bool* aCancel,
     641             :                               bool* aHandled,
     642             :                               const nsAString* inString,
     643             :                               nsAString* outString,
     644             :                               int32_t aMaxLength)
     645             : {
     646           0 :   if (!aSelection || !aCancel || !aHandled) {
     647           0 :     return NS_ERROR_NULL_POINTER;
     648             :   }
     649             : 
     650           0 :   if (inString->IsEmpty() && aAction != EditAction::insertIMEText) {
     651             :     // HACK: this is a fix for bug 19395
     652             :     // I can't outlaw all empty insertions
     653             :     // because IME transaction depend on them
     654             :     // There is more work to do to make the
     655             :     // world safe for IME.
     656           0 :     *aCancel = true;
     657           0 :     *aHandled = false;
     658           0 :     return NS_OK;
     659             :   }
     660             : 
     661             :   // initialize out param
     662           0 :   *aCancel = false;
     663           0 :   *aHandled = true;
     664             : 
     665             :   // handle docs with a max length
     666             :   // NOTE, this function copies inString into outString for us.
     667           0 :   bool truncated = false;
     668           0 :   nsresult rv = TruncateInsertionIfNeeded(aSelection, inString, outString,
     669           0 :                                           aMaxLength, &truncated);
     670           0 :   NS_ENSURE_SUCCESS(rv, rv);
     671             :   // If we're exceeding the maxlength when composing IME, we need to clean up
     672             :   // the composing text, so we shouldn't return early.
     673           0 :   if (truncated && outString->IsEmpty() &&
     674             :       aAction != EditAction::insertIMEText) {
     675           0 :     *aCancel = true;
     676           0 :     return NS_OK;
     677             :   }
     678             : 
     679           0 :   uint32_t start = 0;
     680           0 :   uint32_t end = 0;
     681             : 
     682             :   // handle password field docs
     683           0 :   if (IsPasswordEditor()) {
     684           0 :     NS_ENSURE_STATE(mTextEditor);
     685           0 :     nsContentUtils::GetSelectionInTextControl(aSelection,
     686           0 :                                               mTextEditor->GetRoot(),
     687           0 :                                               start, end);
     688             :   }
     689             : 
     690             :   // if the selection isn't collapsed, delete it.
     691             :   bool bCollapsed;
     692           0 :   rv = aSelection->GetIsCollapsed(&bCollapsed);
     693           0 :   NS_ENSURE_SUCCESS(rv, rv);
     694           0 :   if (!bCollapsed) {
     695           0 :     NS_ENSURE_STATE(mTextEditor);
     696           0 :     rv = mTextEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
     697           0 :     NS_ENSURE_SUCCESS(rv, rv);
     698             :   }
     699             : 
     700           0 :   WillInsert(*aSelection, aCancel);
     701             :   // initialize out param
     702             :   // we want to ignore result of WillInsert()
     703           0 :   *aCancel = false;
     704             : 
     705             :   // handle password field data
     706             :   // this has the side effect of changing all the characters in aOutString
     707             :   // to the replacement character
     708           0 :   if (IsPasswordEditor() &&
     709             :       aAction == EditAction::insertIMEText) {
     710           0 :     RemoveIMETextFromPWBuf(start, outString);
     711             :   }
     712             : 
     713             :   // People have lots of different ideas about what text fields
     714             :   // should do with multiline pastes.  See bugs 21032, 23485, 23485, 50935.
     715             :   // The six possible options are:
     716             :   // 0. paste newlines intact
     717             :   // 1. paste up to the first newline (default)
     718             :   // 2. replace newlines with spaces
     719             :   // 3. strip newlines
     720             :   // 4. replace with commas
     721             :   // 5. strip newlines and surrounding whitespace
     722             :   // So find out what we're expected to do:
     723           0 :   if (IsSingleLineEditor()) {
     724           0 :     nsAutoString tString(*outString);
     725             : 
     726           0 :     NS_ENSURE_STATE(mTextEditor);
     727           0 :     HandleNewLines(tString, mTextEditor->mNewlineHandling);
     728             : 
     729           0 :     outString->Assign(tString);
     730             :   }
     731             : 
     732           0 :   if (IsPasswordEditor()) {
     733             :     // manage the password buffer
     734           0 :     mPasswordText.Insert(*outString, start);
     735             : 
     736           0 :     if (LookAndFeel::GetEchoPassword() && !DontEchoPassword()) {
     737           0 :       HideLastPWInput();
     738           0 :       mLastStart = start;
     739           0 :       mLastLength = outString->Length();
     740           0 :       if (mTimer) {
     741           0 :         mTimer->Cancel();
     742             :       } else {
     743           0 :         mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
     744           0 :         NS_ENSURE_SUCCESS(rv, rv);
     745             :       }
     746           0 :       mTimer->InitWithCallback(this, LookAndFeel::GetPasswordMaskDelay(),
     747           0 :                                nsITimer::TYPE_ONE_SHOT);
     748             :     } else {
     749           0 :       FillBufWithPWChars(outString, outString->Length());
     750             :     }
     751             :   }
     752             : 
     753             :   // get the (collapsed) selection location
     754           0 :   NS_ENSURE_STATE(aSelection->GetRangeAt(0));
     755           0 :   nsCOMPtr<nsINode> selNode = aSelection->GetRangeAt(0)->GetStartContainer();
     756           0 :   int32_t selOffset = aSelection->GetRangeAt(0)->StartOffset();
     757           0 :   NS_ENSURE_STATE(selNode);
     758             : 
     759             :   // don't put text in places that can't have it
     760           0 :   NS_ENSURE_STATE(mTextEditor);
     761           0 :   if (!EditorBase::IsTextNode(selNode) &&
     762           0 :       !mTextEditor->CanContainTag(*selNode, *nsGkAtoms::textTagName)) {
     763           0 :     return NS_ERROR_FAILURE;
     764             :   }
     765             : 
     766             :   // we need to get the doc
     767           0 :   NS_ENSURE_STATE(mTextEditor);
     768           0 :   nsCOMPtr<nsIDocument> doc = mTextEditor->GetDocument();
     769           0 :   NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
     770             : 
     771           0 :   if (aAction == EditAction::insertIMEText) {
     772           0 :     NS_ENSURE_STATE(mTextEditor);
     773             :     // Find better insertion point to insert text.
     774           0 :     mTextEditor->FindBetterInsertionPoint(selNode, selOffset);
     775             :     // If there is one or more IME selections, its minimum offset should be
     776             :     // the insertion point.
     777             :     int32_t IMESelectionOffset =
     778           0 :       mTextEditor->GetIMESelectionStartOffsetIn(selNode);
     779           0 :     if (IMESelectionOffset >= 0) {
     780           0 :       selOffset = IMESelectionOffset;
     781             :     }
     782           0 :     rv = mTextEditor->InsertTextImpl(*outString, address_of(selNode),
     783           0 :                                      &selOffset, doc);
     784           0 :     NS_ENSURE_SUCCESS(rv, rv);
     785             :   } else {
     786             :     // aAction == EditAction::insertText; find where we are
     787           0 :     nsCOMPtr<nsINode> curNode = selNode;
     788           0 :     int32_t curOffset = selOffset;
     789             : 
     790             :     // don't spaz my selection in subtransactions
     791           0 :     NS_ENSURE_STATE(mTextEditor);
     792           0 :     AutoTransactionsConserveSelection dontSpazMySelection(mTextEditor);
     793             : 
     794           0 :     rv = mTextEditor->InsertTextImpl(*outString, address_of(curNode),
     795           0 :                                      &curOffset, doc);
     796           0 :     NS_ENSURE_SUCCESS(rv, rv);
     797             : 
     798           0 :     if (curNode) {
     799             :       // Make the caret attach to the inserted text, unless this text ends with a LF,
     800             :       // in which case make the caret attach to the next line.
     801             :       bool endsWithLF =
     802           0 :         !outString->IsEmpty() && outString->Last() == nsCRT::LF;
     803           0 :       aSelection->SetInterlinePosition(endsWithLF);
     804             : 
     805           0 :       aSelection->Collapse(curNode, curOffset);
     806             :     }
     807             :   }
     808           0 :   ASSERT_PASSWORD_LENGTHS_EQUAL()
     809           0 :   return NS_OK;
     810             : }
     811             : 
     812             : nsresult
     813           0 : TextEditRules::DidInsertText(Selection* aSelection,
     814             :                              nsresult aResult)
     815             : {
     816           0 :   return DidInsert(aSelection, aResult);
     817             : }
     818             : 
     819             : nsresult
     820           1 : TextEditRules::WillSetText(Selection& aSelection,
     821             :                            bool* aCancel,
     822             :                            bool* aHandled,
     823             :                            const nsAString* aString,
     824             :                            int32_t aMaxLength)
     825             : {
     826           1 :   MOZ_ASSERT(aCancel);
     827           1 :   MOZ_ASSERT(aHandled);
     828           1 :   MOZ_ASSERT(aString);
     829             : 
     830           1 :   CANCEL_OPERATION_IF_READONLY_OR_DISABLED
     831             : 
     832           1 :   *aHandled = false;
     833           1 :   *aCancel = false;
     834             : 
     835           1 :   if (NS_WARN_IF(!mTextEditor)) {
     836           0 :     return NS_ERROR_FAILURE;
     837             :   }
     838           2 :   RefPtr<TextEditor> textEditor = mTextEditor;
     839             : 
     840           1 :   if (!IsPlaintextEditor() || textEditor->IsIMEComposing() ||
     841             :       aMaxLength != -1) {
     842             :     // SetTextTransaction only supports plain text editor without IME.
     843           0 :     return NS_OK;
     844             :   }
     845             : 
     846           1 :   if (IsPasswordEditor() && LookAndFeel::GetEchoPassword() &&
     847           0 :       !DontEchoPassword()) {
     848             :     // Echo password timer will implement on InsertText.
     849           0 :     return NS_OK;
     850             :   }
     851             : 
     852           1 :   WillInsert(aSelection, aCancel);
     853             :   // we want to ignore result of WillInsert()
     854           1 :   *aCancel = false;
     855             : 
     856           2 :   RefPtr<Element> rootElement = textEditor->GetRoot();
     857           1 :   uint32_t count = rootElement->GetChildCount();
     858             : 
     859             :   // handles only when there is only one node and it's a text node, or empty.
     860             : 
     861           1 :   if (count > 1) {
     862           0 :     return NS_OK;
     863             :   }
     864             : 
     865           2 :   nsAutoString tString(*aString);
     866             : 
     867           1 :   if (IsPasswordEditor()) {
     868           0 :     mPasswordText.Assign(tString);
     869           0 :     FillBufWithPWChars(&tString, tString.Length());
     870           1 :   } else if (IsSingleLineEditor()) {
     871           1 :     HandleNewLines(tString, textEditor->mNewlineHandling);
     872             :   }
     873             : 
     874           1 :   if (!count) {
     875           1 :     if (tString.IsEmpty()) {
     876           0 :       *aHandled = true;
     877           0 :       return NS_OK;
     878             :     }
     879           2 :     RefPtr<nsIDocument> doc = textEditor->GetDocument();
     880           1 :     if (NS_WARN_IF(!doc)) {
     881           0 :       return NS_OK;
     882             :     }
     883           2 :     RefPtr<nsTextNode> newNode = doc->CreateTextNode(tString);
     884           1 :     if (NS_WARN_IF(!newNode)) {
     885           0 :       return NS_OK;
     886             :     }
     887           1 :     nsresult rv = textEditor->InsertNode(*newNode, *rootElement, 0);
     888           1 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     889           0 :       return rv;
     890             :     }
     891           1 :     *aHandled = true;
     892             : 
     893           1 :     ASSERT_PASSWORD_LENGTHS_EQUAL();
     894             : 
     895           1 :     return NS_OK;
     896             :   }
     897             : 
     898           0 :   nsINode* curNode = rootElement->GetFirstChild();
     899           0 :   if (NS_WARN_IF(!EditorBase::IsTextNode(curNode))) {
     900           0 :     return NS_OK;
     901             :   }
     902             : 
     903             :   // Even if empty text, we don't remove text node and set empty text
     904             :   // for performance
     905           0 :   nsresult rv = textEditor->SetTextImpl(tString, *curNode->GetAsText());
     906           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     907           0 :     return rv;
     908             :   }
     909             : 
     910           0 :   *aHandled = true;
     911             : 
     912           0 :   ASSERT_PASSWORD_LENGTHS_EQUAL();
     913             : 
     914           0 :   return NS_OK;
     915             : }
     916             : 
     917             : nsresult
     918           1 : TextEditRules::DidSetText(Selection& aSelection,
     919             :                           nsresult aResult)
     920             : {
     921           1 :   return NS_OK;
     922             : }
     923             : 
     924             : nsresult
     925           0 : TextEditRules::WillSetTextProperty(Selection* aSelection,
     926             :                                    bool* aCancel,
     927             :                                    bool* aHandled)
     928             : {
     929           0 :   if (!aSelection || !aCancel || !aHandled) {
     930           0 :     return NS_ERROR_NULL_POINTER;
     931             :   }
     932             : 
     933             :   // XXX: should probably return a success value other than NS_OK that means "not allowed"
     934           0 :   if (IsPlaintextEditor()) {
     935           0 :     *aCancel = true;
     936             :   }
     937           0 :   return NS_OK;
     938             : }
     939             : 
     940             : nsresult
     941           0 : TextEditRules::DidSetTextProperty(Selection* aSelection,
     942             :                                   nsresult aResult)
     943             : {
     944           0 :   return NS_OK;
     945             : }
     946             : 
     947             : nsresult
     948           0 : TextEditRules::WillRemoveTextProperty(Selection* aSelection,
     949             :                                       bool* aCancel,
     950             :                                       bool* aHandled)
     951             : {
     952           0 :   if (!aSelection || !aCancel || !aHandled) {
     953           0 :     return NS_ERROR_NULL_POINTER;
     954             :   }
     955             : 
     956             :   // XXX: should probably return a success value other than NS_OK that means "not allowed"
     957           0 :   if (IsPlaintextEditor()) {
     958           0 :     *aCancel = true;
     959             :   }
     960           0 :   return NS_OK;
     961             : }
     962             : 
     963             : nsresult
     964           0 : TextEditRules::DidRemoveTextProperty(Selection* aSelection,
     965             :                                      nsresult aResult)
     966             : {
     967           0 :   return NS_OK;
     968             : }
     969             : 
     970             : nsresult
     971           0 : TextEditRules::WillDeleteSelection(Selection* aSelection,
     972             :                                    nsIEditor::EDirection aCollapsedAction,
     973             :                                    bool* aCancel,
     974             :                                    bool* aHandled)
     975             : {
     976           0 :   if (!aSelection || !aCancel || !aHandled) {
     977           0 :     return NS_ERROR_NULL_POINTER;
     978             :   }
     979           0 :   CANCEL_OPERATION_IF_READONLY_OR_DISABLED
     980             : 
     981             :   // initialize out param
     982           0 :   *aCancel = false;
     983           0 :   *aHandled = false;
     984             : 
     985             :   // if there is only bogus content, cancel the operation
     986           0 :   if (mBogusNode) {
     987           0 :     *aCancel = true;
     988           0 :     return NS_OK;
     989             :   }
     990             : 
     991             :   // If the current selection is empty (e.g the user presses backspace with
     992             :   // a collapsed selection), then we want to avoid sending the selectstart
     993             :   // event to the user, so we hide selection changes. However, we still
     994             :   // want to send a single selectionchange event to the document, so we
     995             :   // batch the selectionchange events, such that a single event fires after
     996             :   // the AutoHideSelectionChanges destructor has been run.
     997           0 :   SelectionBatcher selectionBatcher(aSelection);
     998           0 :   AutoHideSelectionChanges hideSelection(aSelection);
     999           0 :   nsAutoScriptBlocker scriptBlocker;
    1000             : 
    1001           0 :   if (IsPasswordEditor()) {
    1002           0 :     NS_ENSURE_STATE(mTextEditor);
    1003             :     nsresult rv =
    1004           0 :       mTextEditor->ExtendSelectionForDelete(aSelection, &aCollapsedAction);
    1005           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1006             : 
    1007             :     // manage the password buffer
    1008             :     uint32_t start, end;
    1009           0 :     nsContentUtils::GetSelectionInTextControl(aSelection,
    1010           0 :                                               mTextEditor->GetRoot(),
    1011           0 :                                               start, end);
    1012             : 
    1013           0 :     if (LookAndFeel::GetEchoPassword()) {
    1014           0 :       HideLastPWInput();
    1015           0 :       mLastStart = start;
    1016           0 :       mLastLength = 0;
    1017           0 :       if (mTimer) {
    1018           0 :         mTimer->Cancel();
    1019             :       }
    1020             :     }
    1021             : 
    1022             :     // Collapsed selection.
    1023           0 :     if (end == start) {
    1024             :       // Deleting back.
    1025           0 :       if (nsIEditor::ePrevious == aCollapsedAction && start > 0) {
    1026           0 :         mPasswordText.Cut(start-1, 1);
    1027             :       }
    1028             :       // Deleting forward.
    1029           0 :       else if (nsIEditor::eNext == aCollapsedAction) {
    1030           0 :         mPasswordText.Cut(start, 1);
    1031             :       }
    1032             :       // Otherwise nothing to do for this collapsed selection.
    1033             :     }
    1034             :     // Extended selection.
    1035             :     else {
    1036           0 :       mPasswordText.Cut(start, end-start);
    1037             :     }
    1038             :   } else {
    1039           0 :     nsCOMPtr<nsIDOMNode> startNode;
    1040             :     int32_t startOffset;
    1041             :     nsresult rv =
    1042           0 :       EditorBase::GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode),
    1043           0 :                                         &startOffset);
    1044           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1045           0 :     NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
    1046             : 
    1047             :     bool bCollapsed;
    1048           0 :     rv = aSelection->GetIsCollapsed(&bCollapsed);
    1049           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1050             : 
    1051           0 :     if (!bCollapsed) {
    1052           0 :       return NS_OK;
    1053             :     }
    1054             : 
    1055             :     // Test for distance between caret and text that will be deleted
    1056           0 :     rv = CheckBidiLevelForDeletion(aSelection, startNode, startOffset,
    1057           0 :                                    aCollapsedAction, aCancel);
    1058           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1059           0 :     if (*aCancel) {
    1060           0 :       return NS_OK;
    1061             :     }
    1062             : 
    1063           0 :     NS_ENSURE_STATE(mTextEditor);
    1064           0 :     rv = mTextEditor->ExtendSelectionForDelete(aSelection, &aCollapsedAction);
    1065           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1066             :   }
    1067             : 
    1068           0 :   NS_ENSURE_STATE(mTextEditor);
    1069             :   nsresult rv =
    1070           0 :     mTextEditor->DeleteSelectionImpl(aCollapsedAction, nsIEditor::eStrip);
    1071           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1072             : 
    1073           0 :   *aHandled = true;
    1074           0 :   ASSERT_PASSWORD_LENGTHS_EQUAL()
    1075           0 :   return NS_OK;
    1076             : }
    1077             : 
    1078             : nsresult
    1079           0 : TextEditRules::DidDeleteSelection(Selection* aSelection,
    1080             :                                   nsIEditor::EDirection aCollapsedAction,
    1081             :                                   nsresult aResult)
    1082             : {
    1083           0 :   nsCOMPtr<nsINode> startNode;
    1084             :   int32_t startOffset;
    1085             :   nsresult rv =
    1086           0 :     EditorBase::GetStartNodeAndOffset(aSelection,
    1087           0 :                                       getter_AddRefs(startNode), &startOffset);
    1088           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1089           0 :     return rv;
    1090             :   }
    1091           0 :   if (NS_WARN_IF(!startNode)) {
    1092           0 :     return NS_ERROR_FAILURE;
    1093             :   }
    1094             : 
    1095             :   // delete empty text nodes at selection
    1096           0 :   if (EditorBase::IsTextNode(startNode)) {
    1097             :     // are we in an empty text node?
    1098           0 :     if (!startNode->Length()) {
    1099           0 :       NS_ENSURE_STATE(mTextEditor);
    1100           0 :       rv = mTextEditor->DeleteNode(startNode);
    1101           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1102           0 :         return rv;
    1103             :       }
    1104             :     }
    1105             :   }
    1106           0 :   if (mDidExplicitlySetInterline) {
    1107           0 :     return NS_OK;
    1108             :   }
    1109             :   // We prevent the caret from sticking on the left of prior BR
    1110             :   // (i.e. the end of previous line) after this deletion.  Bug 92124
    1111           0 :   return aSelection->SetInterlinePosition(true);
    1112             : }
    1113             : 
    1114             : nsresult
    1115           0 : TextEditRules::WillUndo(Selection* aSelection,
    1116             :                         bool* aCancel,
    1117             :                         bool* aHandled)
    1118             : {
    1119           0 :   if (!aSelection || !aCancel || !aHandled) {
    1120           0 :     return NS_ERROR_NULL_POINTER;
    1121             :   }
    1122           0 :   CANCEL_OPERATION_IF_READONLY_OR_DISABLED
    1123             :   // initialize out param
    1124           0 :   *aCancel = false;
    1125           0 :   *aHandled = false;
    1126           0 :   return NS_OK;
    1127             : }
    1128             : 
    1129             : /**
    1130             :  * The idea here is to see if the magic empty node has suddenly reappeared as
    1131             :  * the result of the undo.  If it has, set our state so we remember it.
    1132             :  * There is a tradeoff between doing here and at redo, or doing it everywhere
    1133             :  * else that might care.  Since undo and redo are relatively rare, it makes
    1134             :  * sense to take the (small) performance hit here.
    1135             :  */
    1136             : nsresult
    1137           0 : TextEditRules::DidUndo(Selection* aSelection,
    1138             :                        nsresult aResult)
    1139             : {
    1140           0 :   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
    1141             :   // If aResult is an error, we return it.
    1142           0 :   NS_ENSURE_SUCCESS(aResult, aResult);
    1143             : 
    1144           0 :   NS_ENSURE_STATE(mTextEditor);
    1145           0 :   dom::Element* theRoot = mTextEditor->GetRoot();
    1146           0 :   NS_ENSURE_TRUE(theRoot, NS_ERROR_FAILURE);
    1147           0 :   nsIContent* node = mTextEditor->GetLeftmostChild(theRoot);
    1148           0 :   if (node && mTextEditor->IsMozEditorBogusNode(node)) {
    1149           0 :     mBogusNode = node;
    1150             :   } else {
    1151           0 :     mBogusNode = nullptr;
    1152             :   }
    1153           0 :   return aResult;
    1154             : }
    1155             : 
    1156             : nsresult
    1157           0 : TextEditRules::WillRedo(Selection* aSelection,
    1158             :                         bool* aCancel,
    1159             :                         bool* aHandled)
    1160             : {
    1161           0 :   if (!aSelection || !aCancel || !aHandled) {
    1162           0 :     return NS_ERROR_NULL_POINTER;
    1163             :   }
    1164           0 :   CANCEL_OPERATION_IF_READONLY_OR_DISABLED
    1165             :   // initialize out param
    1166           0 :   *aCancel = false;
    1167           0 :   *aHandled = false;
    1168           0 :   return NS_OK;
    1169             : }
    1170             : 
    1171             : nsresult
    1172           0 : TextEditRules::DidRedo(Selection* aSelection,
    1173             :                        nsresult aResult)
    1174             : {
    1175           0 :   if (!aSelection) {
    1176           0 :     return NS_ERROR_NULL_POINTER;
    1177             :   }
    1178           0 :   if (NS_FAILED(aResult)) {
    1179           0 :     return aResult; // if aResult is an error, we return it.
    1180             :   }
    1181             : 
    1182           0 :   NS_ENSURE_STATE(mTextEditor);
    1183           0 :   RefPtr<Element> theRoot = mTextEditor->GetRoot();
    1184           0 :   NS_ENSURE_TRUE(theRoot, NS_ERROR_FAILURE);
    1185             : 
    1186             :   nsCOMPtr<nsIHTMLCollection> nodeList =
    1187           0 :     theRoot->GetElementsByTagName(NS_LITERAL_STRING("br"));
    1188           0 :   MOZ_ASSERT(nodeList);
    1189           0 :   uint32_t len = nodeList->Length();
    1190             : 
    1191           0 :   if (len != 1) {
    1192             :     // only in the case of one br could there be the bogus node
    1193           0 :     mBogusNode = nullptr;
    1194           0 :     return NS_OK;
    1195             :   }
    1196             : 
    1197           0 :   RefPtr<Element> node = nodeList->Item(0);
    1198           0 :   if (mTextEditor->IsMozEditorBogusNode(node)) {
    1199           0 :     mBogusNode = node;
    1200             :   } else {
    1201           0 :     mBogusNode = nullptr;
    1202             :   }
    1203           0 :   return NS_OK;
    1204             : }
    1205             : 
    1206             : nsresult
    1207           4 : TextEditRules::WillOutputText(Selection* aSelection,
    1208             :                               const nsAString* aOutputFormat,
    1209             :                               nsAString* aOutString,
    1210             :                               uint32_t aFlags,
    1211             :                               bool* aCancel,
    1212             :                               bool* aHandled)
    1213             : {
    1214             :   // null selection ok
    1215          16 :   if (NS_WARN_IF(!aOutString) || NS_WARN_IF(!aOutputFormat) ||
    1216          12 :       NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
    1217           0 :     return NS_ERROR_NULL_POINTER;
    1218             :   }
    1219             : 
    1220             :   // initialize out param
    1221           4 :   *aCancel = false;
    1222           4 :   *aHandled = false;
    1223             : 
    1224           4 :   if (!aOutputFormat->LowerCaseEqualsLiteral("text/plain")) {
    1225           0 :     return NS_OK;
    1226             :   }
    1227             : 
    1228             :   // XXX Looks like that even if it's password field, we need to use the
    1229             :   //     expensive path if the caller requests some complicated handling.
    1230             :   //     However, changing the behavior for password field might cause
    1231             :   //     security issue.  So, be careful when you touch here.
    1232           4 :   if (IsPasswordEditor()) {
    1233           0 :     *aOutString = mPasswordText;
    1234           0 :     *aHandled = true;
    1235           0 :     return NS_OK;
    1236             :   }
    1237             : 
    1238             :   // If there is a bogus node, there's no content.  So output empty string.
    1239           4 :   if (mBogusNode) {
    1240           3 :     aOutString->Truncate();
    1241           3 :     *aHandled = true;
    1242           3 :     return NS_OK;
    1243             :   }
    1244             : 
    1245             :   // If it's necessary to check selection range or the editor wraps hard,
    1246             :   // we need some complicated handling.  In such case, we need to use the
    1247             :   // expensive path.
    1248             :   // XXX Anything else what we cannot return plain text simply?
    1249           2 :   if (aFlags & nsIDocumentEncoder::OutputSelectionOnly ||
    1250           1 :       aFlags & nsIDocumentEncoder::OutputWrap) {
    1251           0 :     return NS_OK;
    1252             :   }
    1253             : 
    1254             :   // If it's neither <input type="text"> nor <textarea>, e.g., an HTML editor
    1255             :   // which is in plaintext mode (e.g., plaintext email composer on Thunderbird),
    1256             :   // it should be handled by the expensive path.
    1257           1 :   if (NS_WARN_IF(!mTextEditor) || mTextEditor->AsHTMLEditor()) {
    1258           0 :     return NS_OK;
    1259             :   }
    1260             : 
    1261           2 :   RefPtr<Element> root = mTextEditor->GetRoot();
    1262           1 :   if (!root) { // Don't warn it, this is possible, e.g., 997805.html
    1263           0 :     aOutString->Truncate();
    1264           0 :     *aHandled = true;
    1265           0 :     return NS_OK;
    1266             :   }
    1267             : 
    1268           1 :   nsIContent* firstChild = root->GetFirstChild();
    1269           1 :   if (!firstChild) {
    1270           0 :     aOutString->Truncate();
    1271           0 :     *aHandled = true;
    1272           0 :     return NS_OK;
    1273             :   }
    1274             : 
    1275             :   // If it's an <input type="text"> element, the DOM tree should be:
    1276             :   // <div class="anonymous-div">
    1277             :   //   #text
    1278             :   // </div>
    1279             :   //
    1280             :   // If it's a <textarea> element, the DOM tree should be:
    1281             :   // <div class="anonymous-div">
    1282             :   //   #text (if there is)
    1283             :   //   <br type="_moz">
    1284             :   //   <scrollbar orient="horizontal">
    1285             :   //   ...
    1286             :   // </div>
    1287             : 
    1288           1 :   Text* text = firstChild->GetAsText();
    1289             :   nsIContent* firstChildExceptText =
    1290           1 :     text ? firstChild->GetNextSibling() : firstChild;
    1291             :   // If the DOM tree is unexpected, fall back to the expensive path.
    1292           1 :   bool isInput = IsSingleLineEditor();
    1293           1 :   bool isTextarea = !isInput;
    1294           3 :   if (NS_WARN_IF(isInput && firstChildExceptText) ||
    1295           2 :       NS_WARN_IF(isTextarea && !firstChildExceptText) ||
    1296           1 :       NS_WARN_IF(isTextarea &&
    1297             :                  !TextEditUtils::IsMozBR(firstChildExceptText) &&
    1298             :                  !firstChildExceptText->IsXULElement(nsGkAtoms::scrollbar))) {
    1299           0 :     return NS_OK;
    1300             :   }
    1301             : 
    1302             :   // If there is no text node in the expected DOM tree, we can say that it's
    1303             :   // just empty.
    1304           1 :   if (!text) {
    1305           0 :     aOutString->Truncate();
    1306           0 :     *aHandled = true;
    1307           0 :     return NS_OK;
    1308             :   }
    1309             : 
    1310             :   // Otherwise, the text is the value.
    1311           1 :   nsresult rv = text->GetData(*aOutString);
    1312           1 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1313             :     // Fall back to the expensive path if it fails.
    1314           0 :     return NS_OK;
    1315             :   }
    1316             : 
    1317           1 :   *aHandled = true;
    1318           1 :   return NS_OK;
    1319             : }
    1320             : 
    1321             : nsresult
    1322           0 : TextEditRules::DidOutputText(Selection* aSelection,
    1323             :                              nsresult aResult)
    1324             : {
    1325           0 :   return NS_OK;
    1326             : }
    1327             : 
    1328             : nsresult
    1329           3 : TextEditRules::RemoveRedundantTrailingBR()
    1330             : {
    1331             :   // If the bogus node exists, we have no work to do
    1332           3 :   if (mBogusNode) {
    1333           2 :     return NS_OK;
    1334             :   }
    1335             : 
    1336             :   // Likewise, nothing to be done if we could never have inserted a trailing br
    1337           1 :   if (IsSingleLineEditor()) {
    1338           1 :     return NS_OK;
    1339             :   }
    1340             : 
    1341           0 :   NS_ENSURE_STATE(mTextEditor);
    1342           0 :   RefPtr<dom::Element> body = mTextEditor->GetRoot();
    1343           0 :   if (!body) {
    1344           0 :     return NS_ERROR_NULL_POINTER;
    1345             :   }
    1346             : 
    1347           0 :   uint32_t childCount = body->GetChildCount();
    1348           0 :   if (childCount > 1) {
    1349             :     // The trailing br is redundant if it is the only remaining child node
    1350           0 :     return NS_OK;
    1351             :   }
    1352             : 
    1353           0 :   RefPtr<nsIContent> child = body->GetFirstChild();
    1354           0 :   if (!child || !child->IsElement()) {
    1355           0 :     return NS_OK;
    1356             :   }
    1357             : 
    1358           0 :   dom::Element* elem = child->AsElement();
    1359           0 :   if (!TextEditUtils::IsMozBR(elem)) {
    1360           0 :     return NS_OK;
    1361             :   }
    1362             : 
    1363             :   // Rather than deleting this node from the DOM tree we should instead
    1364             :   // morph this br into the bogus node
    1365           0 :   elem->UnsetAttr(kNameSpaceID_None, nsGkAtoms::type, true);
    1366             : 
    1367             :   // set mBogusNode to be this <br>
    1368           0 :   mBogusNode = elem;
    1369             : 
    1370             :   // give it the bogus node attribute
    1371           0 :   elem->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
    1372           0 :                 kMOZEditorBogusNodeValue, false);
    1373           0 :   return NS_OK;
    1374             : }
    1375             : 
    1376             : nsresult
    1377           5 : TextEditRules::CreateTrailingBRIfNeeded()
    1378             : {
    1379             :   // but only if we aren't a single line edit field
    1380           5 :   if (IsSingleLineEditor()) {
    1381           5 :     return NS_OK;
    1382             :   }
    1383             : 
    1384           0 :   NS_ENSURE_STATE(mTextEditor);
    1385           0 :   dom::Element* body = mTextEditor->GetRoot();
    1386           0 :   NS_ENSURE_TRUE(body, NS_ERROR_NULL_POINTER);
    1387             : 
    1388           0 :   nsIContent* lastChild = body->GetLastChild();
    1389             :   // assuming CreateBogusNodeIfNeeded() has been called first
    1390           0 :   NS_ENSURE_TRUE(lastChild, NS_ERROR_NULL_POINTER);
    1391             : 
    1392           0 :   if (!lastChild->IsHTMLElement(nsGkAtoms::br)) {
    1393           0 :     AutoTransactionsConserveSelection dontSpazMySelection(mTextEditor);
    1394           0 :     nsCOMPtr<nsIDOMNode> domBody = do_QueryInterface(body);
    1395           0 :     return CreateMozBR(domBody, body->Length());
    1396             :   }
    1397             : 
    1398             :   // Check to see if the trailing BR is a former bogus node - this will have
    1399             :   // stuck around if we previously morphed a trailing node into a bogus node.
    1400           0 :   if (!mTextEditor->IsMozEditorBogusNode(lastChild)) {
    1401           0 :     return NS_OK;
    1402             :   }
    1403             : 
    1404             :   // Morph it back to a mozBR
    1405           0 :   lastChild->UnsetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom, false);
    1406           0 :   lastChild->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
    1407           0 :                      NS_LITERAL_STRING("_moz"), true);
    1408           0 :   return NS_OK;
    1409             : }
    1410             : 
    1411             : nsresult
    1412           5 : TextEditRules::CreateBogusNodeIfNeeded(Selection* aSelection)
    1413             : {
    1414           5 :   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
    1415           5 :   NS_ENSURE_TRUE(mTextEditor, NS_ERROR_NULL_POINTER);
    1416             : 
    1417           5 :   if (mBogusNode) {
    1418             :     // Let's not create more than one, ok?
    1419           2 :     return NS_OK;
    1420             :   }
    1421             : 
    1422             :   // tell rules system to not do any post-processing
    1423           3 :   AutoRules beginRulesSniffing(mTextEditor, EditAction::ignore,
    1424           9 :                                nsIEditor::eNone);
    1425             : 
    1426           6 :   nsCOMPtr<dom::Element> body = mTextEditor->GetRoot();
    1427           3 :   if (!body) {
    1428             :     // We don't even have a body yet, don't insert any bogus nodes at
    1429             :     // this point.
    1430           0 :     return NS_OK;
    1431             :   }
    1432             : 
    1433             :   // Now we've got the body element. Iterate over the body element's children,
    1434             :   // looking for editable content. If no editable content is found, insert the
    1435             :   // bogus node.
    1436           3 :   for (nsCOMPtr<nsIContent> bodyChild = body->GetFirstChild();
    1437             :        bodyChild;
    1438           0 :        bodyChild = bodyChild->GetNextSibling()) {
    1439           3 :     if (mTextEditor->IsMozEditorBogusNode(bodyChild) ||
    1440           2 :         !mTextEditor->IsEditable(body) || // XXX hoist out of the loop?
    1441           2 :         mTextEditor->IsEditable(bodyChild) ||
    1442           0 :         mTextEditor->IsBlockNode(bodyChild)) {
    1443           1 :       return NS_OK;
    1444             :     }
    1445             :   }
    1446             : 
    1447             :   // Skip adding the bogus node if body is read-only.
    1448           2 :   if (!mTextEditor->IsModifiableNode(body)) {
    1449           0 :     return NS_OK;
    1450             :   }
    1451             : 
    1452             :   // Create a br.
    1453           4 :   nsCOMPtr<Element> newContent = mTextEditor->CreateHTMLContent(nsGkAtoms::br);
    1454           2 :   NS_ENSURE_STATE(newContent);
    1455             : 
    1456             :   // set mBogusNode to be the newly created <br>
    1457           2 :   mBogusNode = newContent;
    1458             : 
    1459             :   // Give it a special attribute.
    1460           4 :   newContent->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
    1461           6 :                       kMOZEditorBogusNodeValue, false);
    1462             : 
    1463             :   // Put the node in the document.
    1464           2 :   nsresult rv = mTextEditor->InsertNode(*mBogusNode, *body, 0);
    1465           2 :   NS_ENSURE_SUCCESS(rv, rv);
    1466             : 
    1467             :   // Set selection.
    1468           2 :   aSelection->Collapse(body, 0);
    1469           2 :   return NS_OK;
    1470             : }
    1471             : 
    1472             : 
    1473             : nsresult
    1474           0 : TextEditRules::TruncateInsertionIfNeeded(Selection* aSelection,
    1475             :                                          const nsAString* aInString,
    1476             :                                          nsAString* aOutString,
    1477             :                                          int32_t aMaxLength,
    1478             :                                          bool* aTruncated)
    1479             : {
    1480           0 :   if (!aSelection || !aInString || !aOutString) {
    1481           0 :     return NS_ERROR_NULL_POINTER;
    1482             :   }
    1483             : 
    1484           0 :   if (!aOutString->Assign(*aInString, mozilla::fallible)) {
    1485           0 :     return NS_ERROR_OUT_OF_MEMORY;
    1486             :   }
    1487           0 :   if (aTruncated) {
    1488           0 :     *aTruncated = false;
    1489             :   }
    1490             : 
    1491           0 :   NS_ENSURE_STATE(mTextEditor);
    1492           0 :   if (-1 != aMaxLength && IsPlaintextEditor() &&
    1493           0 :       !mTextEditor->IsIMEComposing()) {
    1494             :     // Get the current text length.
    1495             :     // Get the length of inString.
    1496             :     // Get the length of the selection.
    1497             :     //   If selection is collapsed, it is length 0.
    1498             :     //   Subtract the length of the selection from the len(doc)
    1499             :     //   since we'll delete the selection on insert.
    1500             :     //   This is resultingDocLength.
    1501             :     // Get old length of IME composing string
    1502             :     //   which will be replaced by new one.
    1503             :     // If (resultingDocLength) is at or over max, cancel the insert
    1504             :     // If (resultingDocLength) + (length of input) > max,
    1505             :     //    set aOutString to subset of inString so length = max
    1506             :     int32_t docLength;
    1507           0 :     nsresult rv = mTextEditor->GetTextLength(&docLength);
    1508           0 :     if (NS_FAILED(rv)) {
    1509           0 :       return rv;
    1510             :     }
    1511             : 
    1512             :     uint32_t start, end;
    1513           0 :     nsContentUtils::GetSelectionInTextControl(aSelection,
    1514           0 :                                               mTextEditor->GetRoot(),
    1515           0 :                                               start, end);
    1516             : 
    1517           0 :     TextComposition* composition = mTextEditor->GetComposition();
    1518           0 :     uint32_t oldCompStrLength = composition ? composition->String().Length() : 0;
    1519             : 
    1520           0 :     const uint32_t selectionLength = end - start;
    1521           0 :     const int32_t resultingDocLength = docLength - selectionLength - oldCompStrLength;
    1522           0 :     if (resultingDocLength >= aMaxLength) {
    1523             :       // This call is guaranteed to reduce the capacity of the string, so it
    1524             :       // cannot cause an OOM.
    1525           0 :       aOutString->Truncate();
    1526           0 :       if (aTruncated) {
    1527           0 :         *aTruncated = true;
    1528             :       }
    1529             :     } else {
    1530           0 :       int32_t oldLength = aOutString->Length();
    1531           0 :       if (oldLength + resultingDocLength > aMaxLength) {
    1532           0 :         int32_t newLength = aMaxLength - resultingDocLength;
    1533           0 :         MOZ_ASSERT(newLength > 0);
    1534           0 :         char16_t newLastChar = aOutString->CharAt(newLength - 1);
    1535           0 :         char16_t removingFirstChar = aOutString->CharAt(newLength);
    1536             :         // Don't separate the string between a surrogate pair.
    1537           0 :         if (NS_IS_HIGH_SURROGATE(newLastChar) &&
    1538           0 :             NS_IS_LOW_SURROGATE(removingFirstChar)) {
    1539           0 :           newLength--;
    1540             :         }
    1541             :         // XXX What should we do if we're removing IVS and its preceding
    1542             :         //     character won't be removed?
    1543             :         // This call is guaranteed to reduce the capacity of the string, so it
    1544             :         // cannot cause an OOM.
    1545           0 :         aOutString->Truncate(newLength);
    1546           0 :         if (aTruncated) {
    1547           0 :           *aTruncated = true;
    1548             :         }
    1549             :       }
    1550             :     }
    1551             :   }
    1552           0 :   return NS_OK;
    1553             : }
    1554             : 
    1555             : void
    1556           0 : TextEditRules::ResetIMETextPWBuf()
    1557             : {
    1558           0 :   mPasswordIMEText.Truncate();
    1559           0 : }
    1560             : 
    1561             : void
    1562           0 : TextEditRules::RemoveIMETextFromPWBuf(uint32_t& aStart,
    1563             :                                       nsAString* aIMEString)
    1564             : {
    1565           0 :   MOZ_ASSERT(aIMEString);
    1566             : 
    1567             :   // initialize PasswordIME
    1568           0 :   if (mPasswordIMEText.IsEmpty()) {
    1569           0 :     mPasswordIMEIndex = aStart;
    1570             :   } else {
    1571             :     // manage the password buffer
    1572           0 :     mPasswordText.Cut(mPasswordIMEIndex, mPasswordIMEText.Length());
    1573           0 :     aStart = mPasswordIMEIndex;
    1574             :   }
    1575             : 
    1576           0 :   mPasswordIMEText.Assign(*aIMEString);
    1577           0 : }
    1578             : 
    1579             : NS_IMETHODIMP
    1580           0 : TextEditRules::Notify(nsITimer* aTimer)
    1581             : {
    1582           0 :   MOZ_ASSERT(mTimer);
    1583             : 
    1584             :   // Check whether our text editor's password flag was changed before this
    1585             :   // "hide password character" timer actually fires.
    1586           0 :   nsresult rv = IsPasswordEditor() ? HideLastPWInput() : NS_OK;
    1587           0 :   ASSERT_PASSWORD_LENGTHS_EQUAL();
    1588           0 :   mLastLength = 0;
    1589           0 :   return rv;
    1590             : }
    1591             : 
    1592             : nsresult
    1593           0 : TextEditRules::HideLastPWInput()
    1594             : {
    1595           0 :   if (!mLastLength) {
    1596             :     // Special case, we're trying to replace a range that no longer exists
    1597           0 :     return NS_OK;
    1598             :   }
    1599             : 
    1600           0 :   nsAutoString hiddenText;
    1601           0 :   FillBufWithPWChars(&hiddenText, mLastLength);
    1602             : 
    1603           0 :   NS_ENSURE_STATE(mTextEditor);
    1604           0 :   RefPtr<Selection> selection = mTextEditor->GetSelection();
    1605           0 :   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
    1606             :   uint32_t start, end;
    1607           0 :   nsContentUtils::GetSelectionInTextControl(selection, mTextEditor->GetRoot(),
    1608           0 :                                             start, end);
    1609             : 
    1610           0 :   nsCOMPtr<nsINode> selNode = GetTextNode(selection);
    1611           0 :   NS_ENSURE_TRUE(selNode, NS_OK);
    1612             : 
    1613           0 :   selNode->GetAsText()->ReplaceData(mLastStart, mLastLength, hiddenText);
    1614             :   // XXXbz Selection::Collapse/Extend take int32_t, but there are tons of
    1615             :   // callsites... Converting all that is a battle for another day.
    1616           0 :   selection->Collapse(selNode, start);
    1617           0 :   if (start != end) {
    1618           0 :     selection->Extend(selNode, end);
    1619             :   }
    1620           0 :   return NS_OK;
    1621             : }
    1622             : 
    1623             : // static
    1624             : void
    1625           0 : TextEditRules::FillBufWithPWChars(nsAString* aOutString,
    1626             :                                   int32_t aLength)
    1627             : {
    1628           0 :   MOZ_ASSERT(aOutString);
    1629             : 
    1630             :   // change the output to the platform password character
    1631           0 :   char16_t passwordChar = LookAndFeel::GetPasswordCharacter();
    1632             : 
    1633           0 :   aOutString->Truncate();
    1634           0 :   for (int32_t i = 0; i < aLength; i++) {
    1635           0 :     aOutString->Append(passwordChar);
    1636             :   }
    1637           0 : }
    1638             : 
    1639             : /**
    1640             :  * CreateMozBR() puts a BR node with moz attribute at {inParent, inOffset}.
    1641             :  */
    1642             : nsresult
    1643           0 : TextEditRules::CreateMozBR(nsIDOMNode* inParent,
    1644             :                            int32_t inOffset,
    1645             :                            nsIDOMNode** outBRNode)
    1646             : {
    1647           0 :   NS_ENSURE_TRUE(inParent, NS_ERROR_NULL_POINTER);
    1648             : 
    1649           0 :   nsCOMPtr<nsIDOMNode> brNode;
    1650           0 :   NS_ENSURE_STATE(mTextEditor);
    1651           0 :   nsresult rv = mTextEditor->CreateBR(inParent, inOffset, address_of(brNode));
    1652           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1653             : 
    1654             :   // give it special moz attr
    1655           0 :   nsCOMPtr<Element> brElem = do_QueryInterface(brNode);
    1656           0 :   if (brElem) {
    1657           0 :     rv = mTextEditor->SetAttribute(brElem, nsGkAtoms::type,
    1658           0 :                                    NS_LITERAL_STRING("_moz"));
    1659           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1660             :   }
    1661             : 
    1662           0 :   if (outBRNode) {
    1663           0 :     brNode.forget(outBRNode);
    1664             :   }
    1665           0 :   return NS_OK;
    1666             : }
    1667             : 
    1668             : NS_IMETHODIMP
    1669           0 : TextEditRules::DocumentModified()
    1670             : {
    1671           0 :   return NS_ERROR_NOT_IMPLEMENTED;
    1672             : }
    1673             : 
    1674             : bool
    1675           9 : TextEditRules::IsPasswordEditor() const
    1676             : {
    1677           9 :   return mTextEditor ? mTextEditor->IsPasswordEditor() : false;
    1678             : }
    1679             : 
    1680             : bool
    1681           8 : TextEditRules::IsSingleLineEditor() const
    1682             : {
    1683           8 :   return mTextEditor ? mTextEditor->IsSingleLineEditor() : false;
    1684             : }
    1685             : 
    1686             : bool
    1687           6 : TextEditRules::IsPlaintextEditor() const
    1688             : {
    1689           6 :   return mTextEditor ? mTextEditor->IsPlaintextEditor() : false;
    1690             : }
    1691             : 
    1692             : bool
    1693           2 : TextEditRules::IsReadonly() const
    1694             : {
    1695           2 :   return mTextEditor ? mTextEditor->IsReadonly() : false;
    1696             : }
    1697             : 
    1698             : bool
    1699           2 : TextEditRules::IsDisabled() const
    1700             : {
    1701           2 :   return mTextEditor ? mTextEditor->IsDisabled() : false;
    1702             : }
    1703             : bool
    1704           0 : TextEditRules::IsMailEditor() const
    1705             : {
    1706           0 :   return mTextEditor ? mTextEditor->IsMailEditor() : false;
    1707             : }
    1708             : 
    1709             : bool
    1710           0 : TextEditRules::DontEchoPassword() const
    1711             : {
    1712           0 :   return mTextEditor ? mTextEditor->DontEchoPassword() : false;
    1713             : }
    1714             : 
    1715             : } // namespace mozilla

Generated by: LCOV version 1.13