editor/libeditor/text/nsTextEditRules.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/. */
     6 #include "mozilla/Assertions.h"
     7 #include "mozilla/LookAndFeel.h"
     8 #include "mozilla/Preferences.h"
     9 #include "mozilla/dom/Selection.h"
    10 #include "mozilla/TextComposition.h"
    11 #include "mozilla/dom/Element.h"
    12 #include "nsAString.h"
    13 #include "nsAutoPtr.h"
    14 #include "nsCOMPtr.h"
    15 #include "nsCRT.h"
    16 #include "nsCRTGlue.h"
    17 #include "nsComponentManagerUtils.h"
    18 #include "nsContentUtils.h"
    19 #include "nsDebug.h"
    20 #include "nsEditor.h"
    21 #include "nsEditorUtils.h"
    22 #include "nsError.h"
    23 #include "nsGkAtoms.h"
    24 #include "nsIContent.h"
    25 #include "nsIDOMCharacterData.h"
    26 #include "nsIDOMDocument.h"
    27 #include "nsIDOMElement.h"
    28 #include "nsIDOMNode.h"
    29 #include "nsIDOMNodeFilter.h"
    30 #include "nsIDOMNodeIterator.h"
    31 #include "nsIDOMNodeList.h"
    32 #include "nsIDOMText.h"
    33 #include "nsNameSpaceManager.h"
    34 #include "nsINode.h"
    35 #include "nsIPlaintextEditor.h"
    36 #include "nsISelection.h"
    37 #include "nsISelectionPrivate.h"
    38 #include "nsISupportsBase.h"
    39 #include "nsLiteralString.h"
    40 #include "mozilla/dom/NodeIterator.h"
    41 #include "nsTextEditRules.h"
    42 #include "nsTextEditUtils.h"
    43 #include "nsUnicharUtils.h"
    45 using namespace mozilla;
    46 using namespace mozilla::dom;
    48 #define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \
    49   if (IsReadonly() || IsDisabled()) \
    50   {                     \
    51     *aCancel = true; \
    52     return NS_OK;       \
    53   };
    56 /********************************************************
    57  *  Constructor/Destructor 
    58  ********************************************************/
    60 nsTextEditRules::nsTextEditRules()
    61 {
    62   InitFields();
    63 }
    65 void
    66 nsTextEditRules::InitFields()
    67 {
    68   mEditor = nullptr;
    69   mPasswordText.Truncate();
    70   mPasswordIMEText.Truncate();
    71   mPasswordIMEIndex = 0;
    72   mBogusNode = nullptr;
    73   mCachedSelectionNode = nullptr;
    74   mCachedSelectionOffset = 0;
    75   mActionNesting = 0;
    76   mLockRulesSniffing = false;
    77   mDidExplicitlySetInterline = false;
    78   mDeleteBidiImmediately = false;
    79   mTheAction = EditAction::none;
    80   mTimer = nullptr;
    81   mLastStart = 0;
    82   mLastLength = 0;
    83 }
    85 nsTextEditRules::~nsTextEditRules()
    86 {
    87    // do NOT delete mEditor here.  We do not hold a ref count to mEditor.  mEditor owns our lifespan.
    89   if (mTimer)
    90     mTimer->Cancel();
    91 }
    93 /********************************************************
    94  *  XPCOM Cruft
    95  ********************************************************/
    97 NS_IMPL_CYCLE_COLLECTION(nsTextEditRules, mBogusNode, mCachedSelectionNode)
    99 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTextEditRules)
   100   NS_INTERFACE_MAP_ENTRY(nsIEditRules)
   101   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
   102   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditRules)
   103 NS_INTERFACE_MAP_END
   105 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTextEditRules)
   106 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTextEditRules)
   108 /********************************************************
   109  *  Public methods 
   110  ********************************************************/
   112 NS_IMETHODIMP
   113 nsTextEditRules::Init(nsPlaintextEditor *aEditor)
   114 {
   115   if (!aEditor) { return NS_ERROR_NULL_POINTER; }
   117   InitFields();
   119   mEditor = aEditor;  // we hold a non-refcounted reference back to our editor
   120   nsCOMPtr<nsISelection> selection;
   121   mEditor->GetSelection(getter_AddRefs(selection));
   122   NS_WARN_IF_FALSE(selection, "editor cannot get selection");
   124   // Put in a magic br if needed. This method handles null selection,
   125   // which should never happen anyway
   126   nsresult res = CreateBogusNodeIfNeeded(selection);
   127   NS_ENSURE_SUCCESS(res, res);
   129   // If the selection hasn't been set up yet, set it up collapsed to the end of
   130   // our editable content.
   131   int32_t rangeCount;
   132   res = selection->GetRangeCount(&rangeCount);
   133   NS_ENSURE_SUCCESS(res, res);
   134   if (!rangeCount) {
   135     res = mEditor->EndOfDocument();
   136     NS_ENSURE_SUCCESS(res, res);
   137   }
   139   if (IsPlaintextEditor())
   140   {
   141     // ensure trailing br node
   142     res = CreateTrailingBRIfNeeded();
   143     NS_ENSURE_SUCCESS(res, res);
   144   }
   146   mDeleteBidiImmediately =
   147     Preferences::GetBool("bidi.edit.delete_immediately", false);
   149   return res;
   150 }
   152 NS_IMETHODIMP
   153 nsTextEditRules::SetInitialValue(const nsAString& aValue)
   154 {
   155   if (IsPasswordEditor()) {
   156     mPasswordText = aValue;
   157   }
   158   return NS_OK;
   159 }
   161 NS_IMETHODIMP
   162 nsTextEditRules::DetachEditor()
   163 {
   164   if (mTimer)
   165     mTimer->Cancel();
   167   mEditor = nullptr;
   168   return NS_OK;
   169 }
   171 NS_IMETHODIMP
   172 nsTextEditRules::BeforeEdit(EditAction action,
   173                             nsIEditor::EDirection aDirection)
   174 {
   175   if (mLockRulesSniffing) return NS_OK;
   177   nsAutoLockRulesSniffing lockIt(this);
   178   mDidExplicitlySetInterline = false;
   179   if (!mActionNesting)
   180   {
   181     // let rules remember the top level action
   182     mTheAction = action;
   183   }
   184   mActionNesting++;
   186   // get the selection and cache the position before editing
   187   nsCOMPtr<nsISelection> selection;
   188   NS_ENSURE_STATE(mEditor);
   189   nsresult res = mEditor->GetSelection(getter_AddRefs(selection));
   190   NS_ENSURE_SUCCESS(res, res);
   192   selection->GetAnchorNode(getter_AddRefs(mCachedSelectionNode));
   193   selection->GetAnchorOffset(&mCachedSelectionOffset);
   195   return NS_OK;
   196 }
   199 NS_IMETHODIMP
   200 nsTextEditRules::AfterEdit(EditAction action,
   201                            nsIEditor::EDirection aDirection)
   202 {
   203   if (mLockRulesSniffing) return NS_OK;
   205   nsAutoLockRulesSniffing lockIt(this);
   207   NS_PRECONDITION(mActionNesting>0, "bad action nesting!");
   208   nsresult res = NS_OK;
   209   if (!--mActionNesting)
   210   {
   211     nsCOMPtr<nsISelection>selection;
   212     NS_ENSURE_STATE(mEditor);
   213     res = mEditor->GetSelection(getter_AddRefs(selection));
   214     NS_ENSURE_SUCCESS(res, res);
   216     NS_ENSURE_STATE(mEditor);
   217     res = mEditor->HandleInlineSpellCheck(action, selection,
   218                                           mCachedSelectionNode, mCachedSelectionOffset,
   219                                           nullptr, 0, nullptr, 0);
   220     NS_ENSURE_SUCCESS(res, res);
   222     // if only trailing <br> remaining remove it
   223     res = RemoveRedundantTrailingBR();
   224     if (NS_FAILED(res))
   225       return res;
   227     // detect empty doc
   228     res = CreateBogusNodeIfNeeded(selection);
   229     NS_ENSURE_SUCCESS(res, res);
   231     // ensure trailing br node
   232     res = CreateTrailingBRIfNeeded();
   233     NS_ENSURE_SUCCESS(res, res);
   235     // collapse the selection to the trailing BR if it's at the end of our text node
   236     CollapseSelectionToTrailingBRIfNeeded(selection);
   237   }
   238   return res;
   239 }
   242 NS_IMETHODIMP
   243 nsTextEditRules::WillDoAction(Selection* aSelection,
   244                               nsRulesInfo* aInfo,
   245                               bool* aCancel,
   246                               bool* aHandled)
   247 {
   248   // null selection is legal
   249   MOZ_ASSERT(aInfo && aCancel && aHandled);
   251   *aCancel = false;
   252   *aHandled = false;
   254   // my kingdom for dynamic cast
   255   nsTextRulesInfo *info = static_cast<nsTextRulesInfo*>(aInfo);
   257   switch (info->action) {
   258     case EditAction::insertBreak:
   259       return WillInsertBreak(aSelection, aCancel, aHandled, info->maxLength);
   260     case EditAction::insertText:
   261     case EditAction::insertIMEText:
   262       return WillInsertText(info->action, aSelection, aCancel, aHandled,
   263                             info->inString, info->outString, info->maxLength);
   264     case EditAction::deleteSelection:
   265       return WillDeleteSelection(aSelection, info->collapsedAction,
   266                                  aCancel, aHandled);
   267     case EditAction::undo:
   268       return WillUndo(aSelection, aCancel, aHandled);
   269     case EditAction::redo:
   270       return WillRedo(aSelection, aCancel, aHandled);
   271     case EditAction::setTextProperty:
   272       return WillSetTextProperty(aSelection, aCancel, aHandled);
   273     case EditAction::removeTextProperty:
   274       return WillRemoveTextProperty(aSelection, aCancel, aHandled);
   275     case EditAction::outputText:
   276       return WillOutputText(aSelection, info->outputFormat, info->outString,
   277                             aCancel, aHandled);
   278     case EditAction::insertElement:
   279       // i had thought this would be html rules only.  but we put pre elements
   280       // into plaintext mail when doing quoting for reply!  doh!
   281       return WillInsert(aSelection, aCancel);
   282     default:
   283       return NS_ERROR_FAILURE;
   284   }
   285 }
   287 NS_IMETHODIMP 
   288 nsTextEditRules::DidDoAction(nsISelection *aSelection,
   289                              nsRulesInfo *aInfo, nsresult aResult)
   290 {
   291   NS_ENSURE_STATE(mEditor);
   292   // don't let any txns in here move the selection around behind our back.
   293   // Note that this won't prevent explicit selection setting from working.
   294   nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
   296   NS_ENSURE_TRUE(aSelection && aInfo, NS_ERROR_NULL_POINTER);
   298   // my kingdom for dynamic cast
   299   nsTextRulesInfo *info = static_cast<nsTextRulesInfo*>(aInfo);
   301   switch (info->action)
   302   {
   303     case EditAction::insertBreak:
   304       return DidInsertBreak(aSelection, aResult);
   305     case EditAction::insertText:
   306     case EditAction::insertIMEText:
   307       return DidInsertText(aSelection, aResult);
   308     case EditAction::deleteSelection:
   309       return DidDeleteSelection(aSelection, info->collapsedAction, aResult);
   310     case EditAction::undo:
   311       return DidUndo(aSelection, aResult);
   312     case EditAction::redo:
   313       return DidRedo(aSelection, aResult);
   314     case EditAction::setTextProperty:
   315       return DidSetTextProperty(aSelection, aResult);
   316     case EditAction::removeTextProperty:
   317       return DidRemoveTextProperty(aSelection, aResult);
   318     case EditAction::outputText:
   319       return DidOutputText(aSelection, aResult);
   320     default:
   321       // Don't fail on transactions we don't handle here!
   322       return NS_OK;
   323   }
   324 }
   327 NS_IMETHODIMP
   328 nsTextEditRules::DocumentIsEmpty(bool *aDocumentIsEmpty)
   329 {
   330   NS_ENSURE_TRUE(aDocumentIsEmpty, NS_ERROR_NULL_POINTER);
   332   *aDocumentIsEmpty = (mBogusNode != nullptr);
   333   return NS_OK;
   334 }
   336 /********************************************************
   337  *  Protected methods 
   338  ********************************************************/
   341 nsresult
   342 nsTextEditRules::WillInsert(nsISelection *aSelection, bool *aCancel)
   343 {
   344   NS_ENSURE_TRUE(aSelection && aCancel, NS_ERROR_NULL_POINTER);
   346   CANCEL_OPERATION_IF_READONLY_OR_DISABLED
   348   // initialize out param
   349   *aCancel = false;
   351   // check for the magic content node and delete it if it exists
   352   if (mBogusNode)
   353   {
   354     NS_ENSURE_STATE(mEditor);
   355     mEditor->DeleteNode(mBogusNode);
   356     mBogusNode = nullptr;
   357   }
   359   return NS_OK;
   360 }
   362 nsresult
   363 nsTextEditRules::DidInsert(nsISelection *aSelection, nsresult aResult)
   364 {
   365   return NS_OK;
   366 }
   368 nsresult
   369 nsTextEditRules::WillInsertBreak(Selection* aSelection,
   370                                  bool *aCancel,
   371                                  bool *aHandled,
   372                                  int32_t aMaxLength)
   373 {
   374   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
   375   CANCEL_OPERATION_IF_READONLY_OR_DISABLED
   376   *aHandled = false;
   377   if (IsSingleLineEditor()) {
   378     *aCancel = true;
   379   }
   380   else 
   381   {
   382     // handle docs with a max length
   383     // NOTE, this function copies inString into outString for us.
   384     NS_NAMED_LITERAL_STRING(inString, "\n");
   385     nsAutoString outString;
   386     bool didTruncate;
   387     nsresult res = TruncateInsertionIfNeeded(aSelection, &inString, &outString,
   388                                              aMaxLength, &didTruncate);
   389     NS_ENSURE_SUCCESS(res, res);
   390     if (didTruncate) {
   391       *aCancel = true;
   392       return NS_OK;
   393     }
   395     *aCancel = false;
   397     // if the selection isn't collapsed, delete it.
   398     bool bCollapsed;
   399     res = aSelection->GetIsCollapsed(&bCollapsed);
   400     NS_ENSURE_SUCCESS(res, res);
   401     if (!bCollapsed)
   402     {
   403       NS_ENSURE_STATE(mEditor);
   404       res = mEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
   405       NS_ENSURE_SUCCESS(res, res);
   406     }
   408     res = WillInsert(aSelection, aCancel);
   409     NS_ENSURE_SUCCESS(res, res);
   410     // initialize out param
   411     // we want to ignore result of WillInsert()
   412     *aCancel = false;
   414   }
   415   return NS_OK;
   416 }
   418 nsresult
   419 nsTextEditRules::DidInsertBreak(nsISelection *aSelection, nsresult aResult)
   420 {
   421   return NS_OK;
   422 }
   424 nsresult
   425 nsTextEditRules::CollapseSelectionToTrailingBRIfNeeded(nsISelection* aSelection)
   426 {
   427   // we only need to execute the stuff below if we are a plaintext editor.
   428   // html editors have a different mechanism for putting in mozBR's
   429   // (because there are a bunch more places you have to worry about it in html) 
   430   if (!IsPlaintextEditor()) {
   431     return NS_OK;
   432   }
   434   // if we are at the end of the textarea, we need to set the
   435   // selection to stick to the mozBR at the end of the textarea.
   436   int32_t selOffset;
   437   nsCOMPtr<nsIDOMNode> selNode;
   438   nsresult res;
   439   NS_ENSURE_STATE(mEditor);
   440   res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
   441   NS_ENSURE_SUCCESS(res, res);
   443   nsCOMPtr<nsIDOMText> nodeAsText = do_QueryInterface(selNode);
   444   if (!nodeAsText) return NS_OK; // nothing to do if we're not at a text node
   446   uint32_t length;
   447   res = nodeAsText->GetLength(&length);
   448   NS_ENSURE_SUCCESS(res, res);
   450   // nothing to do if we're not at the end of the text node
   451   if (selOffset != int32_t(length))
   452     return NS_OK;
   454   int32_t parentOffset;
   455   nsCOMPtr<nsIDOMNode> parentNode = nsEditor::GetNodeLocation(selNode, &parentOffset);
   457   NS_ENSURE_STATE(mEditor);
   458   nsCOMPtr<nsIDOMNode> root = do_QueryInterface(mEditor->GetRoot());
   459   NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
   460   if (parentNode != root) return NS_OK;
   462   nsCOMPtr<nsIDOMNode> nextNode = mEditor->GetChildAt(parentNode,
   463                                                       parentOffset + 1);
   464   if (nextNode && nsTextEditUtils::IsMozBR(nextNode))
   465   {
   466     res = aSelection->Collapse(parentNode, parentOffset + 1);
   467     NS_ENSURE_SUCCESS(res, res);
   468   }
   469   return res;
   470 }
   472 static inline already_AddRefed<nsIDOMNode>
   473 GetTextNode(nsISelection *selection, nsEditor *editor) {
   474   int32_t selOffset;
   475   nsCOMPtr<nsIDOMNode> selNode;
   476   nsresult res = editor->GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset);
   477   NS_ENSURE_SUCCESS(res, nullptr);
   478   if (!editor->IsTextNode(selNode)) {
   479     // Get an nsINode from the nsIDOMNode
   480     nsCOMPtr<nsINode> node = do_QueryInterface(selNode);
   481     // if node is null, return it to indicate there's no text
   482     NS_ENSURE_TRUE(node, nullptr);
   483     // This should be the root node, walk the tree looking for text nodes
   484     mozilla::dom::NodeFilterHolder filter;
   485     mozilla::dom::NodeIterator iter(node, nsIDOMNodeFilter::SHOW_TEXT, filter);
   486     while (!editor->IsTextNode(selNode)) {
   487       if (NS_FAILED(res = iter.NextNode(getter_AddRefs(selNode))) || !selNode) {
   488         return nullptr;
   489       }
   490     }
   491   }
   492   return selNode.forget();
   493 }
   494 #ifdef DEBUG
   495 #define ASSERT_PASSWORD_LENGTHS_EQUAL()                                \
   496   if (IsPasswordEditor() && mEditor->GetRoot()) {                      \
   497     int32_t txtLen;                                                    \
   498     mEditor->GetTextLength(&txtLen);                                   \
   499     NS_ASSERTION(mPasswordText.Length() == uint32_t(txtLen),           \
   500                  "password length not equal to number of asterisks");  \
   501   }
   502 #else
   503 #define ASSERT_PASSWORD_LENGTHS_EQUAL()
   504 #endif
   506 // static
   507 void
   508 nsTextEditRules::HandleNewLines(nsString &aString,
   509                                 int32_t aNewlineHandling)
   510 {
   511   if (aNewlineHandling < 0) {
   512     int32_t caretStyle;
   513     nsPlaintextEditor::GetDefaultEditorPrefs(aNewlineHandling, caretStyle);
   514   }
   516   switch(aNewlineHandling)
   517   {
   518   case nsIPlaintextEditor::eNewlinesReplaceWithSpaces:
   519     // Strip trailing newlines first so we don't wind up with trailing spaces
   520     aString.Trim(CRLF, false, true);
   521     aString.ReplaceChar(CRLF, ' ');
   522     break;
   523   case nsIPlaintextEditor::eNewlinesStrip:
   524     aString.StripChars(CRLF);
   525     break;
   526   case nsIPlaintextEditor::eNewlinesPasteToFirst:
   527   default:
   528     {
   529       int32_t firstCRLF = aString.FindCharInSet(CRLF);
   531       // we get first *non-empty* line.
   532       int32_t offset = 0;
   533       while (firstCRLF == offset)
   534       {
   535         offset++;
   536         firstCRLF = aString.FindCharInSet(CRLF, offset);
   537       }
   538       if (firstCRLF > 0)
   539         aString.Truncate(firstCRLF);
   540       if (offset > 0)
   541         aString.Cut(0, offset);
   542     }
   543     break;
   544   case nsIPlaintextEditor::eNewlinesReplaceWithCommas:
   545     aString.Trim(CRLF, true, true);
   546     aString.ReplaceChar(CRLF, ',');
   547     break;
   548   case nsIPlaintextEditor::eNewlinesStripSurroundingWhitespace:
   549     {
   550       nsString result;
   551       uint32_t offset = 0;
   552       while (offset < aString.Length())
   553       {
   554         int32_t nextCRLF = aString.FindCharInSet(CRLF, offset);
   555         if (nextCRLF < 0) {
   556           result.Append(nsDependentSubstring(aString, offset));
   557           break;
   558         }
   559         uint32_t wsBegin = nextCRLF;
   560         // look backwards for the first non-whitespace char
   561         while (wsBegin > offset && NS_IS_SPACE(aString[wsBegin - 1]))
   562           --wsBegin;
   563         result.Append(nsDependentSubstring(aString, offset, wsBegin - offset));
   564         offset = nextCRLF + 1;
   565         while (offset < aString.Length() && NS_IS_SPACE(aString[offset]))
   566           ++offset;
   567       }
   568       aString = result;
   569     }
   570     break;
   571   case nsIPlaintextEditor::eNewlinesPasteIntact:
   572     // even if we're pasting newlines, don't paste leading/trailing ones
   573     aString.Trim(CRLF, true, true);
   574     break;
   575   }
   576 }
   578 nsresult
   579 nsTextEditRules::WillInsertText(EditAction aAction,
   580                                 Selection* aSelection,
   581                                 bool            *aCancel,
   582                                 bool            *aHandled,
   583                                 const nsAString *inString,
   584                                 nsAString *outString,
   585                                 int32_t          aMaxLength)
   586 {  
   587   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
   589   if (inString->IsEmpty() && aAction != EditAction::insertIMEText) {
   590     // HACK: this is a fix for bug 19395
   591     // I can't outlaw all empty insertions
   592     // because IME transaction depend on them
   593     // There is more work to do to make the 
   594     // world safe for IME.
   595     *aCancel = true;
   596     *aHandled = false;
   597     return NS_OK;
   598   }
   600   // initialize out param
   601   *aCancel = false;
   602   *aHandled = true;
   604   // handle docs with a max length
   605   // NOTE, this function copies inString into outString for us.
   606   bool truncated = false;
   607   nsresult res = TruncateInsertionIfNeeded(aSelection, inString, outString,
   608                                            aMaxLength, &truncated);
   609   NS_ENSURE_SUCCESS(res, res);
   610   // If we're exceeding the maxlength when composing IME, we need to clean up
   611   // the composing text, so we shouldn't return early.
   612   if (truncated && outString->IsEmpty() &&
   613       aAction != EditAction::insertIMEText) {
   614     *aCancel = true;
   615     return NS_OK;
   616   }
   618   int32_t start = 0;
   619   int32_t end = 0;
   621   // handle password field docs
   622   if (IsPasswordEditor()) {
   623     NS_ENSURE_STATE(mEditor);
   624     nsContentUtils::GetSelectionInTextControl(aSelection, mEditor->GetRoot(),
   625                                               start, end);
   626   }
   628   // if the selection isn't collapsed, delete it.
   629   bool bCollapsed;
   630   res = aSelection->GetIsCollapsed(&bCollapsed);
   631   NS_ENSURE_SUCCESS(res, res);
   632   if (!bCollapsed)
   633   {
   634     NS_ENSURE_STATE(mEditor);
   635     res = mEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
   636     NS_ENSURE_SUCCESS(res, res);
   637   }
   639   res = WillInsert(aSelection, aCancel);
   640   NS_ENSURE_SUCCESS(res, res);
   641   // initialize out param
   642   // we want to ignore result of WillInsert()
   643   *aCancel = false;
   645   // handle password field data
   646   // this has the side effect of changing all the characters in aOutString
   647   // to the replacement character
   648   if (IsPasswordEditor())
   649   {
   650     if (aAction == EditAction::insertIMEText) {
   651       RemoveIMETextFromPWBuf(start, outString);
   652     }
   653   }
   655   // People have lots of different ideas about what text fields
   656   // should do with multiline pastes.  See bugs 21032, 23485, 23485, 50935.
   657   // The six possible options are:
   658   // 0. paste newlines intact
   659   // 1. paste up to the first newline (default)
   660   // 2. replace newlines with spaces
   661   // 3. strip newlines
   662   // 4. replace with commas
   663   // 5. strip newlines and surrounding whitespace
   664   // So find out what we're expected to do:
   665   if (IsSingleLineEditor())
   666   {
   667     nsAutoString tString(*outString);
   669     NS_ENSURE_STATE(mEditor);
   670     HandleNewLines(tString, mEditor->mNewlineHandling);
   672     outString->Assign(tString);
   673   }
   675   if (IsPasswordEditor())
   676   {
   677     // manage the password buffer
   678     mPasswordText.Insert(*outString, start);
   680     if (LookAndFeel::GetEchoPassword() && !DontEchoPassword()) {
   681       HideLastPWInput();
   682       mLastStart = start;
   683       mLastLength = outString->Length();
   684       if (mTimer)
   685       {
   686         mTimer->Cancel();
   687       }
   688       else
   689       {
   690         mTimer = do_CreateInstance("@mozilla.org/timer;1", &res);
   691         NS_ENSURE_SUCCESS(res, res);
   692       }
   693       mTimer->InitWithCallback(this, LookAndFeel::GetPasswordMaskDelay(),
   694                                nsITimer::TYPE_ONE_SHOT);
   695     } 
   696     else 
   697     {
   698       FillBufWithPWChars(outString, outString->Length());
   699     }
   700   }
   702   // get the (collapsed) selection location
   703   nsCOMPtr<nsIDOMNode> selNode;
   704   int32_t selOffset;
   705   NS_ENSURE_STATE(mEditor);
   706   res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
   707   NS_ENSURE_SUCCESS(res, res);
   709   // don't put text in places that can't have it
   710   NS_ENSURE_STATE(mEditor);
   711   if (!mEditor->IsTextNode(selNode) &&
   712       !mEditor->CanContainTag(selNode, nsGkAtoms::textTagName)) {
   713     return NS_ERROR_FAILURE;
   714   }
   716   // we need to get the doc
   717   NS_ENSURE_STATE(mEditor);
   718   nsCOMPtr<nsIDOMDocument> doc = mEditor->GetDOMDocument();
   719   NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
   721   if (aAction == EditAction::insertIMEText) {
   722     NS_ENSURE_STATE(mEditor);
   723     res = mEditor->InsertTextImpl(*outString, address_of(selNode), &selOffset, doc);
   724     NS_ENSURE_SUCCESS(res, res);
   725   } else {
   726     // aAction == EditAction::insertText; find where we are
   727     nsCOMPtr<nsIDOMNode> curNode = selNode;
   728     int32_t curOffset = selOffset;
   730     // don't spaz my selection in subtransactions
   731     NS_ENSURE_STATE(mEditor);
   732     nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
   734     res = mEditor->InsertTextImpl(*outString, address_of(curNode),
   735                                   &curOffset, doc);
   736     NS_ENSURE_SUCCESS(res, res);
   738     if (curNode) 
   739     {
   740       // Make the caret attach to the inserted text, unless this text ends with a LF, 
   741       // in which case make the caret attach to the next line.
   742       bool endsWithLF =
   743         !outString->IsEmpty() && outString->Last() == nsCRT::LF;
   744       aSelection->SetInterlinePosition(endsWithLF);
   746       aSelection->Collapse(curNode, curOffset);
   747     }
   748   }
   749   ASSERT_PASSWORD_LENGTHS_EQUAL()
   750   return res;
   751 }
   753 nsresult
   754 nsTextEditRules::DidInsertText(nsISelection *aSelection, 
   755                                nsresult aResult)
   756 {
   757   return DidInsert(aSelection, aResult);
   758 }
   762 nsresult
   763 nsTextEditRules::WillSetTextProperty(nsISelection *aSelection, bool *aCancel, bool *aHandled)
   764 {
   765   if (!aSelection || !aCancel || !aHandled) 
   766     { return NS_ERROR_NULL_POINTER; }
   768   // XXX: should probably return a success value other than NS_OK that means "not allowed"
   769   if (IsPlaintextEditor()) {
   770     *aCancel = true;
   771   }
   772   return NS_OK;
   773 }
   775 nsresult
   776 nsTextEditRules::DidSetTextProperty(nsISelection *aSelection, nsresult aResult)
   777 {
   778   return NS_OK;
   779 }
   781 nsresult
   782 nsTextEditRules::WillRemoveTextProperty(nsISelection *aSelection, bool *aCancel, bool *aHandled)
   783 {
   784   if (!aSelection || !aCancel || !aHandled) 
   785     { return NS_ERROR_NULL_POINTER; }
   787   // XXX: should probably return a success value other than NS_OK that means "not allowed"
   788   if (IsPlaintextEditor()) {
   789     *aCancel = true;
   790   }
   791   return NS_OK;
   792 }
   794 nsresult
   795 nsTextEditRules::DidRemoveTextProperty(nsISelection *aSelection, nsresult aResult)
   796 {
   797   return NS_OK;
   798 }
   800 nsresult
   801 nsTextEditRules::WillDeleteSelection(Selection* aSelection,
   802                                      nsIEditor::EDirection aCollapsedAction, 
   803                                      bool *aCancel,
   804                                      bool *aHandled)
   805 {
   806   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
   807   CANCEL_OPERATION_IF_READONLY_OR_DISABLED
   809   // initialize out param
   810   *aCancel = false;
   811   *aHandled = false;
   813   // if there is only bogus content, cancel the operation
   814   if (mBogusNode) {
   815     *aCancel = true;
   816     return NS_OK;
   817   }
   819   nsresult res = NS_OK;
   820   nsAutoScriptBlocker scriptBlocker;
   822   if (IsPasswordEditor())
   823   {
   824     NS_ENSURE_STATE(mEditor);
   825     res = mEditor->ExtendSelectionForDelete(aSelection, &aCollapsedAction);
   826     NS_ENSURE_SUCCESS(res, res);
   828     // manage the password buffer
   829     int32_t start, end;
   830     nsContentUtils::GetSelectionInTextControl(aSelection, mEditor->GetRoot(),
   831                                               start, end);
   833     if (LookAndFeel::GetEchoPassword()) {
   834       HideLastPWInput();
   835       mLastStart = start;
   836       mLastLength = 0;
   837       if (mTimer)
   838       {
   839         mTimer->Cancel();
   840       }
   841     }
   843     if (end == start)
   844     { // collapsed selection
   845       if (nsIEditor::ePrevious==aCollapsedAction && 0<start) { // del back
   846         mPasswordText.Cut(start-1, 1);
   847       }
   848       else if (nsIEditor::eNext==aCollapsedAction) {      // del forward
   849         mPasswordText.Cut(start, 1);
   850       }
   851       // otherwise nothing to do for this collapsed selection
   852     }
   853     else {  // extended selection
   854       mPasswordText.Cut(start, end-start);
   855     }
   856   }
   857   else
   858   {
   859     nsCOMPtr<nsIDOMNode> startNode;
   860     int32_t startOffset;
   861     NS_ENSURE_STATE(mEditor);
   862     res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset);
   863     NS_ENSURE_SUCCESS(res, res);
   864     NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
   866     bool bCollapsed;
   867     res = aSelection->GetIsCollapsed(&bCollapsed);
   868     NS_ENSURE_SUCCESS(res, res);
   870     if (!bCollapsed)
   871       return NS_OK;
   873     // Test for distance between caret and text that will be deleted
   874     res = CheckBidiLevelForDeletion(aSelection, startNode, startOffset, aCollapsedAction, aCancel);
   875     NS_ENSURE_SUCCESS(res, res);
   876     if (*aCancel) return NS_OK;
   878     NS_ENSURE_STATE(mEditor);
   879     res = mEditor->ExtendSelectionForDelete(aSelection, &aCollapsedAction);
   880     NS_ENSURE_SUCCESS(res, res);
   881   }
   883   NS_ENSURE_STATE(mEditor);
   884   res = mEditor->DeleteSelectionImpl(aCollapsedAction, nsIEditor::eStrip);
   885   NS_ENSURE_SUCCESS(res, res);
   887   *aHandled = true;
   888   ASSERT_PASSWORD_LENGTHS_EQUAL()
   889   return NS_OK;
   890 }
   892 nsresult
   893 nsTextEditRules::DidDeleteSelection(nsISelection *aSelection, 
   894                                     nsIEditor::EDirection aCollapsedAction, 
   895                                     nsresult aResult)
   896 {
   897   nsCOMPtr<nsIDOMNode> startNode;
   898   int32_t startOffset;
   899   NS_ENSURE_STATE(mEditor);
   900   nsresult res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset);
   901   NS_ENSURE_SUCCESS(res, res);
   902   NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
   904   // delete empty text nodes at selection
   905   if (mEditor->IsTextNode(startNode))
   906   {
   907     nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(startNode);
   908     uint32_t strLength;
   909     res = textNode->GetLength(&strLength);
   910     NS_ENSURE_SUCCESS(res, res);
   912     // are we in an empty text node?
   913     if (!strLength)
   914     {
   915       res = mEditor->DeleteNode(startNode);
   916       NS_ENSURE_SUCCESS(res, res);
   917     }
   918   }
   919   if (!mDidExplicitlySetInterline)
   920   {
   921     // We prevent the caret from sticking on the left of prior BR
   922     // (i.e. the end of previous line) after this deletion.  Bug 92124
   923     nsCOMPtr<nsISelectionPrivate> selPriv = do_QueryInterface(aSelection);
   924     if (selPriv) res = selPriv->SetInterlinePosition(true);
   925   }
   926   return res;
   927 }
   929 nsresult
   930 nsTextEditRules::WillUndo(nsISelection *aSelection, bool *aCancel, bool *aHandled)
   931 {
   932   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
   933   CANCEL_OPERATION_IF_READONLY_OR_DISABLED
   934   // initialize out param
   935   *aCancel = false;
   936   *aHandled = false;
   937   return NS_OK;
   938 }
   940 /* the idea here is to see if the magic empty node has suddenly reappeared as the result of the undo.
   941  * if it has, set our state so we remember it.
   942  * There is a tradeoff between doing here and at redo, or doing it everywhere else that might care.
   943  * Since undo and redo are relatively rare, it makes sense to take the (small) performance hit here.
   944  */
   945 nsresult
   946 nsTextEditRules::DidUndo(nsISelection *aSelection, nsresult aResult)
   947 {
   948   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
   949   // If aResult is an error, we return it.
   950   NS_ENSURE_SUCCESS(aResult, aResult);
   952   NS_ENSURE_STATE(mEditor);
   953   dom::Element* theRoot = mEditor->GetRoot();
   954   NS_ENSURE_TRUE(theRoot, NS_ERROR_FAILURE);
   955   nsIContent* node = mEditor->GetLeftmostChild(theRoot);
   956   if (node && mEditor->IsMozEditorBogusNode(node)) {
   957     mBogusNode = do_QueryInterface(node);
   958   } else {
   959     mBogusNode = nullptr;
   960   }
   961   return aResult;
   962 }
   964 nsresult
   965 nsTextEditRules::WillRedo(nsISelection *aSelection, bool *aCancel, bool *aHandled)
   966 {
   967   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
   968   CANCEL_OPERATION_IF_READONLY_OR_DISABLED
   969   // initialize out param
   970   *aCancel = false;
   971   *aHandled = false;
   972   return NS_OK;
   973 }
   975 nsresult
   976 nsTextEditRules::DidRedo(nsISelection *aSelection, nsresult aResult)
   977 {
   978   nsresult res = aResult;  // if aResult is an error, we return it.
   979   if (!aSelection) { return NS_ERROR_NULL_POINTER; }
   980   if (NS_SUCCEEDED(res)) 
   981   {
   982     NS_ENSURE_STATE(mEditor);
   983     nsCOMPtr<nsIDOMElement> theRoot = do_QueryInterface(mEditor->GetRoot());
   984     NS_ENSURE_TRUE(theRoot, NS_ERROR_FAILURE);
   986     nsCOMPtr<nsIDOMHTMLCollection> nodeList;
   987     res = theRoot->GetElementsByTagName(NS_LITERAL_STRING("br"),
   988                                         getter_AddRefs(nodeList));
   989     NS_ENSURE_SUCCESS(res, res);
   990     if (nodeList)
   991     {
   992       uint32_t len;
   993       nodeList->GetLength(&len);
   995       if (len != 1) {
   996         // only in the case of one br could there be the bogus node
   997         mBogusNode = nullptr;
   998         return NS_OK;  
   999       }
  1001       nsCOMPtr<nsIDOMNode> node;
  1002       nodeList->Item(0, getter_AddRefs(node));
  1003       nsCOMPtr<nsIContent> content = do_QueryInterface(node);
  1004       MOZ_ASSERT(content);
  1005       if (mEditor->IsMozEditorBogusNode(content)) {
  1006         mBogusNode = node;
  1007       } else {
  1008         mBogusNode = nullptr;
  1012   return res;
  1015 nsresult
  1016 nsTextEditRules::WillOutputText(nsISelection *aSelection, 
  1017                                 const nsAString  *aOutputFormat,
  1018                                 nsAString *aOutString,                                
  1019                                 bool     *aCancel,
  1020                                 bool     *aHandled)
  1022   // null selection ok
  1023   if (!aOutString || !aOutputFormat || !aCancel || !aHandled) 
  1024     { return NS_ERROR_NULL_POINTER; }
  1026   // initialize out param
  1027   *aCancel = false;
  1028   *aHandled = false;
  1030   nsAutoString outputFormat(*aOutputFormat);
  1031   ToLowerCase(outputFormat);
  1032   if (outputFormat.EqualsLiteral("text/plain"))
  1033   { // only use these rules for plain text output
  1034     if (IsPasswordEditor())
  1036       *aOutString = mPasswordText;
  1037       *aHandled = true;
  1039     else if (mBogusNode)
  1040     { // this means there's no content, so output null string
  1041       aOutString->Truncate();
  1042       *aHandled = true;
  1045   return NS_OK;
  1048 nsresult
  1049 nsTextEditRules::DidOutputText(nsISelection *aSelection, nsresult aResult)
  1051   return NS_OK;
  1054 nsresult
  1055 nsTextEditRules::RemoveRedundantTrailingBR()
  1057   // If the bogus node exists, we have no work to do
  1058   if (mBogusNode)
  1059     return NS_OK;
  1061   // Likewise, nothing to be done if we could never have inserted a trailing br
  1062   if (IsSingleLineEditor())
  1063     return NS_OK;
  1065   NS_ENSURE_STATE(mEditor);
  1066   nsRefPtr<dom::Element> body = mEditor->GetRoot();
  1067   if (!body)
  1068     return NS_ERROR_NULL_POINTER;
  1070   uint32_t childCount = body->GetChildCount();
  1071   if (childCount > 1) {
  1072     // The trailing br is redundant if it is the only remaining child node
  1073     return NS_OK;
  1076   nsRefPtr<nsIContent> child = body->GetFirstChild();
  1077   if (!child || !child->IsElement()) {
  1078     return NS_OK;
  1081   dom::Element* elem = child->AsElement();
  1082   if (!nsTextEditUtils::IsMozBR(elem)) {
  1083     return NS_OK;
  1086   // Rather than deleting this node from the DOM tree we should instead
  1087   // morph this br into the bogus node
  1088   elem->UnsetAttr(kNameSpaceID_None, nsGkAtoms::type, true);
  1090   // set mBogusNode to be this <br>
  1091   mBogusNode = do_QueryInterface(elem);
  1093   // give it the bogus node attribute
  1094   elem->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
  1095                 kMOZEditorBogusNodeValue, false);
  1096   return NS_OK;
  1099 nsresult
  1100 nsTextEditRules::CreateTrailingBRIfNeeded()
  1102   // but only if we aren't a single line edit field
  1103   if (IsSingleLineEditor()) {
  1104     return NS_OK;
  1107   NS_ENSURE_STATE(mEditor);
  1108   dom::Element* body = mEditor->GetRoot();
  1109   NS_ENSURE_TRUE(body, NS_ERROR_NULL_POINTER);
  1111   nsIContent* lastChild = body->GetLastChild();
  1112   // assuming CreateBogusNodeIfNeeded() has been called first
  1113   NS_ENSURE_TRUE(lastChild, NS_ERROR_NULL_POINTER);
  1115   if (!lastChild->IsHTML(nsGkAtoms::br)) {
  1116     nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
  1117     nsCOMPtr<nsIDOMNode> domBody = do_QueryInterface(body);
  1118     return CreateMozBR(domBody, body->Length());
  1121   // Check to see if the trailing BR is a former bogus node - this will have
  1122   // stuck around if we previously morphed a trailing node into a bogus node.
  1123   if (!mEditor->IsMozEditorBogusNode(lastChild)) {
  1124     return NS_OK;
  1127   // Morph it back to a mozBR
  1128   lastChild->UnsetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom, false);
  1129   lastChild->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
  1130                      NS_LITERAL_STRING("_moz"), true);
  1131   return NS_OK;
  1134 nsresult
  1135 nsTextEditRules::CreateBogusNodeIfNeeded(nsISelection *aSelection)
  1137   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
  1138   NS_ENSURE_TRUE(mEditor, NS_ERROR_NULL_POINTER);
  1140   if (mBogusNode) {
  1141     // Let's not create more than one, ok?
  1142     return NS_OK;
  1145   // tell rules system to not do any post-processing
  1146   nsAutoRules beginRulesSniffing(mEditor, EditAction::ignore, nsIEditor::eNone);
  1148   nsCOMPtr<dom::Element> body = mEditor->GetRoot();
  1149   if (!body) {
  1150     // We don't even have a body yet, don't insert any bogus nodes at
  1151     // this point.
  1152     return NS_OK;
  1155   // Now we've got the body element. Iterate over the body element's children,
  1156   // looking for editable content. If no editable content is found, insert the
  1157   // bogus node.
  1158   for (nsCOMPtr<nsIContent> bodyChild = body->GetFirstChild();
  1159        bodyChild;
  1160        bodyChild = bodyChild->GetNextSibling()) {
  1161     if (mEditor->IsMozEditorBogusNode(bodyChild) ||
  1162         !mEditor->IsEditable(body) || // XXX hoist out of the loop?
  1163         mEditor->IsEditable(bodyChild)) {
  1164       return NS_OK;
  1168   // Skip adding the bogus node if body is read-only.
  1169   if (!mEditor->IsModifiableNode(body)) {
  1170     return NS_OK;
  1173   // Create a br.
  1174   nsCOMPtr<dom::Element> newContent;
  1175   nsresult rv = mEditor->CreateHTMLContent(NS_LITERAL_STRING("br"), getter_AddRefs(newContent));
  1176   NS_ENSURE_SUCCESS(rv, rv);
  1178   // set mBogusNode to be the newly created <br>
  1179   mBogusNode = do_QueryInterface(newContent);
  1180   NS_ENSURE_TRUE(mBogusNode, NS_ERROR_NULL_POINTER);
  1182   // Give it a special attribute.
  1183   newContent->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
  1184                       kMOZEditorBogusNodeValue, false);
  1186   // Put the node in the document.
  1187   nsCOMPtr<nsIDOMNode> bodyNode = do_QueryInterface(body);
  1188   rv = mEditor->InsertNode(mBogusNode, bodyNode, 0);
  1189   NS_ENSURE_SUCCESS(rv, rv);
  1191   // Set selection.
  1192   aSelection->CollapseNative(body, 0);
  1193   return NS_OK;
  1197 nsresult
  1198 nsTextEditRules::TruncateInsertionIfNeeded(Selection* aSelection,
  1199                                            const nsAString  *aInString,
  1200                                            nsAString  *aOutString,
  1201                                            int32_t          aMaxLength,
  1202                                            bool *aTruncated)
  1204   if (!aSelection || !aInString || !aOutString) {return NS_ERROR_NULL_POINTER;}
  1206   nsresult res = NS_OK;
  1207   *aOutString = *aInString;
  1208   if (aTruncated) {
  1209     *aTruncated = false;
  1212   NS_ENSURE_STATE(mEditor);
  1213   if ((-1 != aMaxLength) && IsPlaintextEditor() && !mEditor->IsIMEComposing() )
  1215     // Get the current text length.
  1216     // Get the length of inString.
  1217     // Get the length of the selection.
  1218     //   If selection is collapsed, it is length 0.
  1219     //   Subtract the length of the selection from the len(doc) 
  1220     //   since we'll delete the selection on insert.
  1221     //   This is resultingDocLength.
  1222     // Get old length of IME composing string
  1223     //   which will be replaced by new one.
  1224     // If (resultingDocLength) is at or over max, cancel the insert
  1225     // If (resultingDocLength) + (length of input) > max, 
  1226     //    set aOutString to subset of inString so length = max
  1227     int32_t docLength;
  1228     res = mEditor->GetTextLength(&docLength);
  1229     if (NS_FAILED(res)) { return res; }
  1231     int32_t start, end;
  1232     nsContentUtils::GetSelectionInTextControl(aSelection, mEditor->GetRoot(),
  1233                                               start, end);
  1235     TextComposition* composition = mEditor->GetComposition();
  1236     int32_t oldCompStrLength = composition ? composition->String().Length() : 0;
  1238     const int32_t selectionLength = end - start;
  1239     const int32_t resultingDocLength = docLength - selectionLength - oldCompStrLength;
  1240     if (resultingDocLength >= aMaxLength)
  1242       aOutString->Truncate();
  1243       if (aTruncated) {
  1244         *aTruncated = true;
  1247     else
  1249       int32_t inCount = aOutString->Length();
  1250       if (inCount + resultingDocLength > aMaxLength)
  1252         aOutString->Truncate(aMaxLength - resultingDocLength);
  1253         if (aTruncated) {
  1254           *aTruncated = true;
  1259   return res;
  1262 void
  1263 nsTextEditRules::ResetIMETextPWBuf()
  1265   mPasswordIMEText.Truncate();
  1268 void
  1269 nsTextEditRules::RemoveIMETextFromPWBuf(int32_t &aStart, nsAString *aIMEString)
  1271   MOZ_ASSERT(aIMEString);
  1273   // initialize PasswordIME
  1274   if (mPasswordIMEText.IsEmpty()) {
  1275     mPasswordIMEIndex = aStart;
  1277   else {
  1278     // manage the password buffer
  1279     mPasswordText.Cut(mPasswordIMEIndex, mPasswordIMEText.Length());
  1280     aStart = mPasswordIMEIndex;
  1283   mPasswordIMEText.Assign(*aIMEString);
  1286 NS_IMETHODIMP nsTextEditRules::Notify(nsITimer *)
  1288   MOZ_ASSERT(mTimer);
  1290   // Check whether our text editor's password flag was changed before this
  1291   // "hide password character" timer actually fires.
  1292   nsresult res = IsPasswordEditor() ? HideLastPWInput() : NS_OK;
  1293   ASSERT_PASSWORD_LENGTHS_EQUAL();
  1294   mLastLength = 0;
  1295   return res;
  1298 nsresult nsTextEditRules::HideLastPWInput() {
  1299   if (!mLastLength) {
  1300     // Special case, we're trying to replace a range that no longer exists
  1301     return NS_OK;
  1304   nsAutoString hiddenText;
  1305   FillBufWithPWChars(&hiddenText, mLastLength);
  1307   NS_ENSURE_STATE(mEditor);
  1308   nsRefPtr<Selection> selection = mEditor->GetSelection();
  1309   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  1310   int32_t start, end;
  1311   nsContentUtils::GetSelectionInTextControl(selection, mEditor->GetRoot(),
  1312                                             start, end);
  1314   nsCOMPtr<nsIDOMNode> selNode = GetTextNode(selection, mEditor);
  1315   NS_ENSURE_TRUE(selNode, NS_OK);
  1317   nsCOMPtr<nsIDOMCharacterData> nodeAsText(do_QueryInterface(selNode));
  1318   NS_ENSURE_TRUE(nodeAsText, NS_OK);
  1320   nodeAsText->ReplaceData(mLastStart, mLastLength, hiddenText);
  1321   selection->Collapse(selNode, start);
  1322   if (start != end)
  1323     selection->Extend(selNode, end);
  1324   return NS_OK;
  1327 // static
  1328 void
  1329 nsTextEditRules::FillBufWithPWChars(nsAString *aOutString, int32_t aLength)
  1331   MOZ_ASSERT(aOutString);
  1333   // change the output to the platform password character
  1334   char16_t passwordChar = LookAndFeel::GetPasswordCharacter();
  1336   int32_t i;
  1337   aOutString->Truncate();
  1338   for (i=0; i < aLength; i++)
  1339     aOutString->Append(passwordChar);
  1343 ///////////////////////////////////////////////////////////////////////////
  1344 // CreateMozBR: put a BR node with moz attribute at {aNode, aOffset}
  1345 //                       
  1346 nsresult 
  1347 nsTextEditRules::CreateMozBR(nsIDOMNode* inParent, int32_t inOffset,
  1348                              nsIDOMNode** outBRNode)
  1350   NS_ENSURE_TRUE(inParent, NS_ERROR_NULL_POINTER);
  1352   nsCOMPtr<nsIDOMNode> brNode;
  1353   NS_ENSURE_STATE(mEditor);
  1354   nsresult res = mEditor->CreateBR(inParent, inOffset, address_of(brNode));
  1355   NS_ENSURE_SUCCESS(res, res);
  1357   // give it special moz attr
  1358   nsCOMPtr<nsIDOMElement> brElem = do_QueryInterface(brNode);
  1359   if (brElem) {
  1360     res = mEditor->SetAttribute(brElem, NS_LITERAL_STRING("type"), NS_LITERAL_STRING("_moz"));
  1361     NS_ENSURE_SUCCESS(res, res);
  1364   if (outBRNode) {
  1365     brNode.forget(outBRNode);
  1367   return NS_OK;
  1370 NS_IMETHODIMP
  1371 nsTextEditRules::DocumentModified()
  1373   return NS_ERROR_NOT_IMPLEMENTED;

mercurial