LCOV - code coverage report
Current view: directory - editor/libeditor/text - nsTextEditRules.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 567 2 0.4 %
Date: 2012-06-02 Functions: 50 2 4.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      26                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : #include "nsTextEditRules.h"
      39                 : 
      40                 : #include "nsEditor.h"
      41                 : #include "nsTextEditUtils.h"
      42                 : #include "nsCRT.h"
      43                 : 
      44                 : #include "nsCOMPtr.h"
      45                 : #include "nsIServiceManager.h"
      46                 : #include "nsIDOMNode.h"
      47                 : #include "nsIDOMElement.h"
      48                 : #include "nsIDOMText.h"
      49                 : #include "nsIDOMNodeList.h"
      50                 : #include "nsISelection.h"
      51                 : #include "nsISelectionPrivate.h"
      52                 : #include "nsISelectionController.h"
      53                 : #include "nsIDOMRange.h"
      54                 : #include "nsIDOMCharacterData.h"
      55                 : #include "nsIContent.h"
      56                 : #include "nsIContentIterator.h"
      57                 : #include "nsEditorUtils.h"
      58                 : #include "EditTxn.h"
      59                 : #include "nsEditProperty.h"
      60                 : #include "nsUnicharUtils.h"
      61                 : #include "DeleteTextTxn.h"
      62                 : #include "nsNodeIterator.h"
      63                 : #include "nsIDOMNodeFilter.h"
      64                 : 
      65                 : // for IBMBIDI
      66                 : #include "nsFrameSelection.h"
      67                 : 
      68                 : #include "mozilla/Preferences.h"
      69                 : #include "mozilla/LookAndFeel.h"
      70                 : #include "mozilla/dom/Element.h"
      71                 : 
      72                 : using namespace mozilla;
      73                 : 
      74                 : #define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \
      75                 :   if (IsReadonly() || IsDisabled()) \
      76                 :   {                     \
      77                 :     *aCancel = true; \
      78                 :     return NS_OK;       \
      79                 :   };
      80                 : 
      81                 : 
      82                 : /********************************************************
      83                 :  *  Constructor/Destructor 
      84                 :  ********************************************************/
      85                 : 
      86               0 : nsTextEditRules::nsTextEditRules()
      87                 : : mEditor(nsnull)
      88                 : , mPasswordText()
      89                 : , mPasswordIMEText()
      90                 : , mPasswordIMEIndex(0)
      91                 : , mActionNesting(0)
      92                 : , mLockRulesSniffing(false)
      93                 : , mDidExplicitlySetInterline(false)
      94                 : , mTheAction(0)
      95                 : , mLastStart(0)
      96               0 : , mLastLength(0)
      97                 : {
      98               0 : }
      99                 : 
     100               0 : nsTextEditRules::~nsTextEditRules()
     101                 : {
     102                 :    // do NOT delete mEditor here.  We do not hold a ref count to mEditor.  mEditor owns our lifespan.
     103                 : 
     104               0 :   if (mTimer)
     105               0 :     mTimer->Cancel();
     106               0 : }
     107                 : 
     108                 : /********************************************************
     109                 :  *  XPCOM Cruft
     110                 :  ********************************************************/
     111                 : 
     112            1464 : NS_IMPL_CYCLE_COLLECTION_2(nsTextEditRules, mBogusNode, mCachedSelectionNode)
     113                 : 
     114               0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTextEditRules)
     115               0 :   NS_INTERFACE_MAP_ENTRY(nsIEditRules)
     116               0 :   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
     117               0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditRules)
     118               0 : NS_INTERFACE_MAP_END
     119                 : 
     120               0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTextEditRules)
     121               0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTextEditRules)
     122                 : 
     123                 : /********************************************************
     124                 :  *  Public methods 
     125                 :  ********************************************************/
     126                 : 
     127                 : NS_IMETHODIMP
     128               0 : nsTextEditRules::Init(nsPlaintextEditor *aEditor)
     129                 : {
     130               0 :   if (!aEditor) { return NS_ERROR_NULL_POINTER; }
     131                 : 
     132               0 :   mEditor = aEditor;  // we hold a non-refcounted reference back to our editor
     133               0 :   nsCOMPtr<nsISelection> selection;
     134               0 :   mEditor->GetSelection(getter_AddRefs(selection));
     135               0 :   NS_ASSERTION(selection, "editor cannot get selection");
     136                 : 
     137                 :   // Put in a magic br if needed. This method handles null selection,
     138                 :   // which should never happen anyway
     139               0 :   nsresult res = CreateBogusNodeIfNeeded(selection);
     140               0 :   NS_ENSURE_SUCCESS(res, res);
     141                 : 
     142                 :   // If the selection hasn't been set up yet, set it up collapsed to the end of
     143                 :   // our editable content.
     144                 :   PRInt32 rangeCount;
     145               0 :   res = selection->GetRangeCount(&rangeCount);
     146               0 :   NS_ENSURE_SUCCESS(res, res);
     147               0 :   if (!rangeCount) {
     148               0 :     res = mEditor->EndOfDocument();
     149               0 :     NS_ENSURE_SUCCESS(res, res);
     150                 :   }
     151                 : 
     152               0 :   if (IsPlaintextEditor())
     153                 :   {
     154                 :     // ensure trailing br node
     155               0 :     res = CreateTrailingBRIfNeeded();
     156               0 :     NS_ENSURE_SUCCESS(res, res);
     157                 :   }
     158                 : 
     159                 :   mDeleteBidiImmediately =
     160               0 :     Preferences::GetBool("bidi.edit.delete_immediately", false);
     161                 : 
     162               0 :   return res;
     163                 : }
     164                 : 
     165                 : NS_IMETHODIMP
     166               0 : nsTextEditRules::DetachEditor()
     167                 : {
     168               0 :   if (mTimer)
     169               0 :     mTimer->Cancel();
     170                 : 
     171               0 :   mEditor = nsnull;
     172               0 :   return NS_OK;
     173                 : }
     174                 : 
     175                 : NS_IMETHODIMP
     176               0 : nsTextEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection)
     177                 : {
     178               0 :   if (mLockRulesSniffing) return NS_OK;
     179                 :   
     180               0 :   nsAutoLockRulesSniffing lockIt(this);
     181               0 :   mDidExplicitlySetInterline = false;
     182               0 :   if (!mActionNesting)
     183                 :   {
     184                 :     // let rules remember the top level action
     185               0 :     mTheAction = action;
     186                 :   }
     187               0 :   mActionNesting++;
     188                 :   
     189                 :   // get the selection and cache the position before editing
     190               0 :   nsCOMPtr<nsISelection> selection;
     191               0 :   nsresult res = mEditor->GetSelection(getter_AddRefs(selection));
     192               0 :   NS_ENSURE_SUCCESS(res, res);
     193                 : 
     194               0 :   selection->GetAnchorNode(getter_AddRefs(mCachedSelectionNode));
     195               0 :   selection->GetAnchorOffset(&mCachedSelectionOffset);
     196                 : 
     197               0 :   return NS_OK;
     198                 : }
     199                 : 
     200                 : 
     201                 : NS_IMETHODIMP
     202               0 : nsTextEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
     203                 : {
     204               0 :   if (mLockRulesSniffing) return NS_OK;
     205                 :   
     206               0 :   nsAutoLockRulesSniffing lockIt(this);
     207                 :   
     208               0 :   NS_PRECONDITION(mActionNesting>0, "bad action nesting!");
     209               0 :   nsresult res = NS_OK;
     210               0 :   if (!--mActionNesting)
     211                 :   {
     212               0 :     nsCOMPtr<nsISelection>selection;
     213               0 :     res = mEditor->GetSelection(getter_AddRefs(selection));
     214               0 :     NS_ENSURE_SUCCESS(res, res);
     215                 :   
     216                 :     res = mEditor->HandleInlineSpellCheck(action, selection,
     217                 :                                           mCachedSelectionNode, mCachedSelectionOffset,
     218               0 :                                           nsnull, 0, nsnull, 0);
     219               0 :     NS_ENSURE_SUCCESS(res, res);
     220                 : 
     221                 :     // if only trailing <br> remaining remove it
     222               0 :     res = RemoveRedundantTrailingBR();
     223               0 :     if (NS_FAILED(res))
     224               0 :       return res;
     225                 : 
     226                 :     // detect empty doc
     227               0 :     res = CreateBogusNodeIfNeeded(selection);
     228               0 :     NS_ENSURE_SUCCESS(res, res);
     229                 :     
     230                 :     // ensure trailing br node
     231               0 :     res = CreateTrailingBRIfNeeded();
     232               0 :     NS_ENSURE_SUCCESS(res, res);
     233                 : 
     234                 :     // collapse the selection to the trailing BR if it's at the end of our text node
     235               0 :     CollapseSelectionToTrailingBRIfNeeded(selection);
     236                 :   }
     237               0 :   return res;
     238                 : }
     239                 : 
     240                 : 
     241                 : NS_IMETHODIMP 
     242               0 : nsTextEditRules::WillDoAction(nsISelection *aSelection, 
     243                 :                               nsRulesInfo *aInfo, 
     244                 :                               bool *aCancel, 
     245                 :                               bool *aHandled)
     246                 : {
     247                 :   // null selection is legal
     248               0 :   if (!aInfo || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
     249                 : #if defined(DEBUG_ftang)
     250                 :   printf("nsTextEditRules::WillDoAction action= %d", aInfo->action);
     251                 : #endif
     252                 : 
     253               0 :   *aCancel = false;
     254               0 :   *aHandled = false;
     255                 : 
     256                 :   // my kingdom for dynamic cast
     257               0 :   nsTextRulesInfo *info = static_cast<nsTextRulesInfo*>(aInfo);
     258                 :     
     259               0 :   switch (info->action)
     260                 :   {
     261                 :     case kInsertBreak:
     262               0 :       return WillInsertBreak(aSelection, aCancel, aHandled, info->maxLength);
     263                 :     case kInsertText:
     264                 :     case kInsertTextIME:
     265                 :       return WillInsertText(info->action,
     266                 :                             aSelection, 
     267                 :                             aCancel,
     268                 :                             aHandled, 
     269                 :                             info->inString,
     270                 :                             info->outString,
     271               0 :                             info->maxLength);
     272                 :     case kDeleteSelection:
     273               0 :       return WillDeleteSelection(aSelection, info->collapsedAction, aCancel, aHandled);
     274                 :     case kUndo:
     275               0 :       return WillUndo(aSelection, aCancel, aHandled);
     276                 :     case kRedo:
     277               0 :       return WillRedo(aSelection, aCancel, aHandled);
     278                 :     case kSetTextProperty:
     279               0 :       return WillSetTextProperty(aSelection, aCancel, aHandled);
     280                 :     case kRemoveTextProperty:
     281               0 :       return WillRemoveTextProperty(aSelection, aCancel, aHandled);
     282                 :     case kOutputText:
     283                 :       return WillOutputText(aSelection, 
     284                 :                             info->outputFormat,
     285                 :                             info->outString,                            
     286                 :                             aCancel,
     287               0 :                             aHandled);
     288                 :     case kInsertElement:  // i had thought this would be html rules only.  but we put pre elements
     289                 :                           // into plaintext mail when doing quoting for reply!  doh!
     290               0 :       return WillInsert(aSelection, aCancel);
     291                 :   }
     292               0 :   return NS_ERROR_FAILURE;
     293                 : }
     294                 :   
     295                 : NS_IMETHODIMP 
     296               0 : nsTextEditRules::DidDoAction(nsISelection *aSelection,
     297                 :                              nsRulesInfo *aInfo, nsresult aResult)
     298                 : {
     299                 :   // don't let any txns in here move the selection around behind our back.
     300                 :   // Note that this won't prevent explicit selection setting from working.
     301               0 :   nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
     302                 : 
     303               0 :   NS_ENSURE_TRUE(aSelection && aInfo, NS_ERROR_NULL_POINTER);
     304                 :     
     305                 :   // my kingdom for dynamic cast
     306               0 :   nsTextRulesInfo *info = static_cast<nsTextRulesInfo*>(aInfo);
     307                 : 
     308               0 :   switch (info->action)
     309                 :   {
     310                 :    case kInsertBreak:
     311               0 :      return DidInsertBreak(aSelection, aResult);
     312                 :     case kInsertText:
     313                 :     case kInsertTextIME:
     314               0 :       return DidInsertText(aSelection, aResult);
     315                 :     case kDeleteSelection:
     316               0 :       return DidDeleteSelection(aSelection, info->collapsedAction, aResult);
     317                 :     case kUndo:
     318               0 :       return DidUndo(aSelection, aResult);
     319                 :     case kRedo:
     320               0 :       return DidRedo(aSelection, aResult);
     321                 :     case kSetTextProperty:
     322               0 :       return DidSetTextProperty(aSelection, aResult);
     323                 :     case kRemoveTextProperty:
     324               0 :       return DidRemoveTextProperty(aSelection, aResult);
     325                 :     case kOutputText:
     326               0 :       return DidOutputText(aSelection, aResult);
     327                 :   }
     328                 :   // Don't fail on transactions we don't handle here!
     329               0 :   return NS_OK;
     330                 : }
     331                 : 
     332                 : 
     333                 : NS_IMETHODIMP
     334               0 : nsTextEditRules::DocumentIsEmpty(bool *aDocumentIsEmpty)
     335                 : {
     336               0 :   NS_ENSURE_TRUE(aDocumentIsEmpty, NS_ERROR_NULL_POINTER);
     337                 :   
     338               0 :   *aDocumentIsEmpty = (mBogusNode != nsnull);
     339               0 :   return NS_OK;
     340                 : }
     341                 : 
     342                 : /********************************************************
     343                 :  *  Protected methods 
     344                 :  ********************************************************/
     345                 : 
     346                 : 
     347                 : nsresult
     348               0 : nsTextEditRules::WillInsert(nsISelection *aSelection, bool *aCancel)
     349                 : {
     350               0 :   NS_ENSURE_TRUE(aSelection && aCancel, NS_ERROR_NULL_POINTER);
     351                 :   
     352               0 :   CANCEL_OPERATION_IF_READONLY_OR_DISABLED
     353                 : 
     354                 :   // initialize out param
     355               0 :   *aCancel = false;
     356                 :   
     357                 :   // check for the magic content node and delete it if it exists
     358               0 :   if (mBogusNode)
     359                 :   {
     360               0 :     mEditor->DeleteNode(mBogusNode);
     361               0 :     mBogusNode = nsnull;
     362                 :   }
     363                 : 
     364               0 :   return NS_OK;
     365                 : }
     366                 : 
     367                 : nsresult
     368               0 : nsTextEditRules::DidInsert(nsISelection *aSelection, nsresult aResult)
     369                 : {
     370               0 :   return NS_OK;
     371                 : }
     372                 : 
     373                 : nsresult
     374               0 : nsTextEditRules::WillInsertBreak(nsISelection *aSelection,
     375                 :                                  bool *aCancel,
     376                 :                                  bool *aHandled,
     377                 :                                  PRInt32 aMaxLength)
     378                 : {
     379               0 :   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
     380               0 :   CANCEL_OPERATION_IF_READONLY_OR_DISABLED
     381               0 :   *aHandled = false;
     382               0 :   if (IsSingleLineEditor()) {
     383               0 :     *aCancel = true;
     384                 :   }
     385                 :   else 
     386                 :   {
     387                 :     // handle docs with a max length
     388                 :     // NOTE, this function copies inString into outString for us.
     389               0 :     NS_NAMED_LITERAL_STRING(inString, "\n");
     390               0 :     nsAutoString outString;
     391                 :     bool didTruncate;
     392                 :     nsresult res = TruncateInsertionIfNeeded(aSelection, &inString, &outString,
     393               0 :                                              aMaxLength, &didTruncate);
     394               0 :     NS_ENSURE_SUCCESS(res, res);
     395               0 :     if (didTruncate) {
     396               0 :       *aCancel = true;
     397               0 :       return NS_OK;
     398                 :     }
     399                 : 
     400               0 :     *aCancel = false;
     401                 : 
     402                 :     // if the selection isn't collapsed, delete it.
     403                 :     bool bCollapsed;
     404               0 :     res = aSelection->GetIsCollapsed(&bCollapsed);
     405               0 :     NS_ENSURE_SUCCESS(res, res);
     406               0 :     if (!bCollapsed)
     407                 :     {
     408               0 :       res = mEditor->DeleteSelection(nsIEditor::eNone);
     409               0 :       NS_ENSURE_SUCCESS(res, res);
     410                 :     }
     411                 : 
     412               0 :     res = WillInsert(aSelection, aCancel);
     413               0 :     NS_ENSURE_SUCCESS(res, res);
     414                 :     // initialize out param
     415                 :     // we want to ignore result of WillInsert()
     416               0 :     *aCancel = false;
     417                 :   
     418                 :   }
     419               0 :   return NS_OK;
     420                 : }
     421                 : 
     422                 : nsresult
     423               0 : nsTextEditRules::DidInsertBreak(nsISelection *aSelection, nsresult aResult)
     424                 : {
     425               0 :   return NS_OK;
     426                 : }
     427                 : 
     428                 : nsresult
     429               0 : nsTextEditRules::CollapseSelectionToTrailingBRIfNeeded(nsISelection* aSelection)
     430                 : {
     431                 :   // we only need to execute the stuff below if we are a plaintext editor.
     432                 :   // html editors have a different mechanism for putting in mozBR's
     433                 :   // (because there are a bunch more places you have to worry about it in html) 
     434               0 :   if (!IsPlaintextEditor()) {
     435               0 :     return NS_OK;
     436                 :   }
     437                 : 
     438                 :   // if we are at the end of the textarea, we need to set the
     439                 :   // selection to stick to the mozBR at the end of the textarea.
     440                 :   PRInt32 selOffset;
     441               0 :   nsCOMPtr<nsIDOMNode> selNode;
     442                 :   nsresult res;
     443               0 :   res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
     444               0 :   NS_ENSURE_SUCCESS(res, res);
     445                 : 
     446               0 :   nsCOMPtr<nsIDOMText> nodeAsText = do_QueryInterface(selNode);
     447               0 :   if (!nodeAsText) return NS_OK; // nothing to do if we're not at a text node
     448                 : 
     449                 :   PRUint32 length;
     450               0 :   res = nodeAsText->GetLength(&length);
     451               0 :   NS_ENSURE_SUCCESS(res, res);
     452                 : 
     453                 :   // nothing to do if we're not at the end of the text node
     454               0 :   if (selOffset != PRInt32(length))
     455               0 :     return NS_OK;
     456                 : 
     457               0 :   nsCOMPtr<nsIDOMNode> parentNode;
     458                 :   PRInt32 parentOffset;
     459                 :   res = nsEditor::GetNodeLocation(selNode, address_of(parentNode),
     460               0 :                                   &parentOffset);
     461               0 :   NS_ENSURE_SUCCESS(res, res);
     462                 : 
     463               0 :   nsCOMPtr<nsIDOMNode> root = do_QueryInterface(mEditor->GetRoot());
     464               0 :   NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
     465               0 :   if (parentNode != root) return NS_OK;
     466                 : 
     467                 :   nsCOMPtr<nsIDOMNode> nextNode = mEditor->GetChildAt(parentNode,
     468               0 :                                                       parentOffset + 1);
     469               0 :   if (nextNode && nsTextEditUtils::IsMozBR(nextNode))
     470                 :   {
     471               0 :     res = aSelection->Collapse(parentNode, parentOffset + 1);
     472               0 :     NS_ENSURE_SUCCESS(res, res);
     473                 :   }
     474               0 :   return res;
     475                 : }
     476                 : 
     477                 : static inline already_AddRefed<nsIDOMNode>
     478               0 : GetTextNode(nsISelection *selection, nsEditor *editor) {
     479                 :   PRInt32 selOffset;
     480               0 :   nsCOMPtr<nsIDOMNode> selNode;
     481               0 :   nsresult res = editor->GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset);
     482               0 :   NS_ENSURE_SUCCESS(res, nsnull);
     483               0 :   if (!editor->IsTextNode(selNode)) {
     484                 :     // Get an nsINode from the nsIDOMNode
     485               0 :     nsCOMPtr<nsINode> node = do_QueryInterface(selNode);
     486                 :     // if node is null, return it to indicate there's no text
     487               0 :     NS_ENSURE_TRUE(node, nsnull);
     488                 :     // This should be the root node, walk the tree looking for text nodes
     489               0 :     nsNodeIterator iter(node, nsIDOMNodeFilter::SHOW_TEXT, nsnull);
     490               0 :     while (!editor->IsTextNode(selNode)) {
     491               0 :       if (NS_FAILED(res = iter.NextNode(getter_AddRefs(selNode))) || !selNode) {
     492               0 :         return nsnull;
     493                 :       }
     494                 :     }
     495                 :   }
     496               0 :   return selNode.forget();
     497                 : }
     498                 : #ifdef DEBUG
     499                 : #define ASSERT_PASSWORD_LENGTHS_EQUAL()                                \
     500                 :   if (IsPasswordEditor()) {                                            \
     501                 :     PRInt32 txtLen;                                                    \
     502                 :     mEditor->GetTextLength(&txtLen);                                    \
     503                 :     NS_ASSERTION(mPasswordText.Length() == PRUint32(txtLen),           \
     504                 :                  "password length not equal to number of asterisks");  \
     505                 :   }
     506                 : #else
     507                 : #define ASSERT_PASSWORD_LENGTHS_EQUAL()
     508                 : #endif
     509                 : 
     510                 : // static
     511                 : void
     512               0 : nsTextEditRules::HandleNewLines(nsString &aString,
     513                 :                                 PRInt32 aNewlineHandling)
     514                 : {
     515               0 :   if (aNewlineHandling < 0) {
     516                 :     PRInt32 caretStyle;
     517               0 :     nsPlaintextEditor::GetDefaultEditorPrefs(aNewlineHandling, caretStyle);
     518                 :   }
     519                 : 
     520               0 :   switch(aNewlineHandling)
     521                 :   {
     522                 :   case nsIPlaintextEditor::eNewlinesReplaceWithSpaces:
     523                 :     // Strip trailing newlines first so we don't wind up with trailing spaces
     524               0 :     aString.Trim(CRLF, false, true);
     525               0 :     aString.ReplaceChar(CRLF, ' ');
     526               0 :     break;
     527                 :   case nsIPlaintextEditor::eNewlinesStrip:
     528               0 :     aString.StripChars(CRLF);
     529               0 :     break;
     530                 :   case nsIPlaintextEditor::eNewlinesPasteToFirst:
     531                 :   default:
     532                 :     {
     533               0 :       PRInt32 firstCRLF = aString.FindCharInSet(CRLF);
     534                 : 
     535                 :       // we get first *non-empty* line.
     536               0 :       PRInt32 offset = 0;
     537               0 :       while (firstCRLF == offset)
     538                 :       {
     539               0 :         offset++;
     540               0 :         firstCRLF = aString.FindCharInSet(CRLF, offset);
     541                 :       }
     542               0 :       if (firstCRLF > 0)
     543               0 :         aString.Truncate(firstCRLF);
     544               0 :       if (offset > 0)
     545               0 :         aString.Cut(0, offset);
     546                 :     }
     547               0 :     break;
     548                 :   case nsIPlaintextEditor::eNewlinesReplaceWithCommas:
     549               0 :     aString.Trim(CRLF, true, true);
     550               0 :     aString.ReplaceChar(CRLF, ',');
     551               0 :     break;
     552                 :   case nsIPlaintextEditor::eNewlinesStripSurroundingWhitespace:
     553                 :     {
     554                 :       // find each newline, and strip all the whitespace before
     555                 :       // and after it
     556               0 :       PRInt32 firstCRLF = aString.FindCharInSet(CRLF);
     557               0 :       while (firstCRLF >= 0)
     558                 :       {
     559               0 :         PRUint32 wsBegin = firstCRLF, wsEnd = firstCRLF + 1;
     560                 :         // look backwards for the first non-whitespace char
     561               0 :         while (wsBegin > 0 && NS_IS_SPACE(aString[wsBegin - 1]))
     562               0 :           --wsBegin;
     563               0 :         while (wsEnd < aString.Length() && NS_IS_SPACE(aString[wsEnd]))
     564               0 :           ++wsEnd;
     565                 :         // now cut this range out of the string
     566               0 :         aString.Cut(wsBegin, wsEnd - wsBegin);
     567                 :         // look for another CR or LF
     568               0 :         firstCRLF = aString.FindCharInSet(CRLF);
     569                 :       }
     570                 :     }
     571               0 :     break;
     572                 :   case nsIPlaintextEditor::eNewlinesPasteIntact:
     573                 :     // even if we're pasting newlines, don't paste leading/trailing ones
     574               0 :     aString.Trim(CRLF, true, true);
     575               0 :     break;
     576                 :   }
     577               0 : }
     578                 : 
     579                 : nsresult
     580               0 : nsTextEditRules::WillInsertText(PRInt32          aAction,
     581                 :                                 nsISelection *aSelection, 
     582                 :                                 bool            *aCancel,
     583                 :                                 bool            *aHandled,
     584                 :                                 const nsAString *inString,
     585                 :                                 nsAString *outString,
     586                 :                                 PRInt32          aMaxLength)
     587                 : {  
     588               0 :   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
     589                 : 
     590               0 :   if (inString->IsEmpty() && (aAction != kInsertTextIME))
     591                 :   {
     592                 :     // HACK: this is a fix for bug 19395
     593                 :     // I can't outlaw all empty insertions
     594                 :     // because IME transaction depend on them
     595                 :     // There is more work to do to make the 
     596                 :     // world safe for IME.
     597               0 :     *aCancel = true;
     598               0 :     *aHandled = false;
     599               0 :     return NS_OK;
     600                 :   }
     601                 :   
     602                 :   // initialize out param
     603               0 :   *aCancel = false;
     604               0 :   *aHandled = true;
     605                 : 
     606                 :   // handle docs with a max length
     607                 :   // NOTE, this function copies inString into outString for us.
     608               0 :   bool truncated = false;
     609                 :   nsresult res = TruncateInsertionIfNeeded(aSelection, inString, outString,
     610               0 :                                            aMaxLength, &truncated);
     611               0 :   NS_ENSURE_SUCCESS(res, res);
     612                 :   // If we're exceeding the maxlength when composing IME, we need to clean up
     613                 :   // the composing text, so we shouldn't return early.
     614               0 :   if (truncated && outString->IsEmpty() && aAction != kInsertTextIME) {
     615               0 :     *aCancel = true;
     616               0 :     return NS_OK;
     617                 :   }
     618                 :   
     619               0 :   PRUint32 start = 0;
     620               0 :   PRUint32 end = 0;  
     621                 : 
     622                 :   // handle password field docs
     623               0 :   if (IsPasswordEditor())
     624                 :   {
     625               0 :     res = mEditor->GetTextSelectionOffsets(aSelection, start, end);
     626               0 :     NS_ASSERTION((NS_SUCCEEDED(res)), "getTextSelectionOffsets failed!");
     627               0 :     NS_ENSURE_SUCCESS(res, res);
     628                 :   }
     629                 : 
     630                 :   // if the selection isn't collapsed, delete it.
     631                 :   bool bCollapsed;
     632               0 :   res = aSelection->GetIsCollapsed(&bCollapsed);
     633               0 :   NS_ENSURE_SUCCESS(res, res);
     634               0 :   if (!bCollapsed)
     635                 :   {
     636               0 :     res = mEditor->DeleteSelection(nsIEditor::eNone);
     637               0 :     NS_ENSURE_SUCCESS(res, res);
     638                 :   }
     639                 : 
     640               0 :   res = WillInsert(aSelection, aCancel);
     641               0 :   NS_ENSURE_SUCCESS(res, res);
     642                 :   // initialize out param
     643                 :   // we want to ignore result of WillInsert()
     644               0 :   *aCancel = false;
     645                 :   
     646                 :   // handle password field data
     647                 :   // this has the side effect of changing all the characters in aOutString
     648                 :   // to the replacement character
     649               0 :   if (IsPasswordEditor())
     650                 :   {
     651               0 :     if (aAction == kInsertTextIME)  {
     652               0 :       res = RemoveIMETextFromPWBuf(start, outString);
     653               0 :       NS_ENSURE_SUCCESS(res, res);
     654                 :     }
     655                 :   }
     656                 : 
     657                 :   // People have lots of different ideas about what text fields
     658                 :   // should do with multiline pastes.  See bugs 21032, 23485, 23485, 50935.
     659                 :   // The six possible options are:
     660                 :   // 0. paste newlines intact
     661                 :   // 1. paste up to the first newline (default)
     662                 :   // 2. replace newlines with spaces
     663                 :   // 3. strip newlines
     664                 :   // 4. replace with commas
     665                 :   // 5. strip newlines and surrounding whitespace
     666                 :   // So find out what we're expected to do:
     667               0 :   if (IsSingleLineEditor())
     668                 :   {
     669               0 :     nsAutoString tString(*outString);
     670                 : 
     671               0 :     HandleNewLines(tString, mEditor->mNewlineHandling);
     672                 : 
     673               0 :     outString->Assign(tString);
     674                 :   }
     675                 : 
     676               0 :   if (IsPasswordEditor())
     677                 :   {
     678                 :     // manage the password buffer
     679               0 :     mPasswordText.Insert(*outString, start);
     680                 : 
     681               0 :     if (LookAndFeel::GetEchoPassword() && !DontEchoPassword()) {
     682               0 :       HideLastPWInput();
     683               0 :       mLastStart = start;
     684               0 :       mLastLength = outString->Length();
     685               0 :       if (mTimer)
     686                 :       {
     687               0 :         mTimer->Cancel();
     688                 :       }
     689                 :       else
     690                 :       {
     691               0 :         mTimer = do_CreateInstance("@mozilla.org/timer;1", &res);
     692               0 :         NS_ENSURE_SUCCESS(res, res);
     693                 :       }
     694               0 :       mTimer->InitWithCallback(this, 600, nsITimer::TYPE_ONE_SHOT);
     695                 :     } 
     696                 :     else 
     697                 :     {
     698               0 :       res = FillBufWithPWChars(outString, outString->Length());
     699               0 :       NS_ENSURE_SUCCESS(res, res);
     700                 :     }
     701                 :   }
     702                 : 
     703                 :   // get the (collapsed) selection location
     704               0 :   nsCOMPtr<nsIDOMNode> selNode;
     705                 :   PRInt32 selOffset;
     706               0 :   res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
     707               0 :   NS_ENSURE_SUCCESS(res, res);
     708                 : 
     709                 :   // don't put text in places that can't have it
     710               0 :   if (!mEditor->IsTextNode(selNode) && !mEditor->CanContainTag(selNode, NS_LITERAL_STRING("#text")))
     711               0 :     return NS_ERROR_FAILURE;
     712                 : 
     713                 :   // we need to get the doc
     714               0 :   nsCOMPtr<nsIDOMDocument>doc;
     715               0 :   res = mEditor->GetDocument(getter_AddRefs(doc));
     716               0 :   NS_ENSURE_SUCCESS(res, res);
     717               0 :   NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);
     718                 :     
     719               0 :   if (aAction == kInsertTextIME) 
     720                 :   { 
     721               0 :     res = mEditor->InsertTextImpl(*outString, address_of(selNode), &selOffset, doc);
     722               0 :     NS_ENSURE_SUCCESS(res, res);
     723                 :   }
     724                 :   else // aAction == kInsertText
     725                 :   {
     726                 :     // find where we are
     727               0 :     nsCOMPtr<nsIDOMNode> curNode = selNode;
     728               0 :     PRInt32 curOffset = selOffset;
     729                 : 
     730                 :     // don't spaz my selection in subtransactions
     731               0 :     nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
     732                 : 
     733                 :     res = mEditor->InsertTextImpl(*outString, address_of(curNode),
     734               0 :                                   &curOffset, doc);
     735               0 :     NS_ENSURE_SUCCESS(res, res);
     736                 : 
     737               0 :     if (curNode) 
     738                 :     {
     739                 :       // Make the caret attach to the inserted text, unless this text ends with a LF, 
     740                 :       // in which case make the caret attach to the next line.
     741                 :       bool endsWithLF =
     742               0 :         !outString->IsEmpty() && outString->Last() == nsCRT::LF;
     743               0 :       nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(aSelection));
     744               0 :       selPrivate->SetInterlinePosition(endsWithLF);
     745                 : 
     746               0 :       aSelection->Collapse(curNode, curOffset);
     747                 :     }
     748                 :   }
     749               0 :   ASSERT_PASSWORD_LENGTHS_EQUAL()
     750               0 :   return res;
     751                 : }
     752                 : 
     753                 : nsresult
     754               0 : nsTextEditRules::DidInsertText(nsISelection *aSelection, 
     755                 :                                nsresult aResult)
     756                 : {
     757               0 :   return DidInsert(aSelection, aResult);
     758                 : }
     759                 : 
     760                 : 
     761                 : 
     762                 : nsresult
     763               0 : nsTextEditRules::WillSetTextProperty(nsISelection *aSelection, bool *aCancel, bool *aHandled)
     764                 : {
     765               0 :   if (!aSelection || !aCancel || !aHandled) 
     766               0 :     { return NS_ERROR_NULL_POINTER; }
     767                 : 
     768                 :   // XXX: should probably return a success value other than NS_OK that means "not allowed"
     769               0 :   if (IsPlaintextEditor()) {
     770               0 :     *aCancel = true;
     771                 :   }
     772               0 :   return NS_OK;
     773                 : }
     774                 : 
     775                 : nsresult
     776               0 : nsTextEditRules::DidSetTextProperty(nsISelection *aSelection, nsresult aResult)
     777                 : {
     778               0 :   return NS_OK;
     779                 : }
     780                 : 
     781                 : nsresult
     782               0 : nsTextEditRules::WillRemoveTextProperty(nsISelection *aSelection, bool *aCancel, bool *aHandled)
     783                 : {
     784               0 :   if (!aSelection || !aCancel || !aHandled) 
     785               0 :     { return NS_ERROR_NULL_POINTER; }
     786                 : 
     787                 :   // XXX: should probably return a success value other than NS_OK that means "not allowed"
     788               0 :   if (IsPlaintextEditor()) {
     789               0 :     *aCancel = true;
     790                 :   }
     791               0 :   return NS_OK;
     792                 : }
     793                 : 
     794                 : nsresult
     795               0 : nsTextEditRules::DidRemoveTextProperty(nsISelection *aSelection, nsresult aResult)
     796                 : {
     797               0 :   return NS_OK;
     798                 : }
     799                 : 
     800                 : nsresult
     801               0 : nsTextEditRules::WillDeleteSelection(nsISelection *aSelection, 
     802                 :                                      nsIEditor::EDirection aCollapsedAction, 
     803                 :                                      bool *aCancel,
     804                 :                                      bool *aHandled)
     805                 : {
     806               0 :   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
     807               0 :   CANCEL_OPERATION_IF_READONLY_OR_DISABLED
     808                 : 
     809                 :   // initialize out param
     810               0 :   *aCancel = false;
     811               0 :   *aHandled = false;
     812                 :   
     813                 :   // if there is only bogus content, cancel the operation
     814               0 :   if (mBogusNode) {
     815               0 :     *aCancel = true;
     816               0 :     return NS_OK;
     817                 :   }
     818                 : 
     819               0 :   nsresult res = NS_OK;
     820                 : 
     821               0 :   if (IsPasswordEditor())
     822                 :   {
     823               0 :     res = mEditor->ExtendSelectionForDelete(aSelection, &aCollapsedAction);
     824               0 :     NS_ENSURE_SUCCESS(res, res);
     825                 : 
     826                 :     // manage the password buffer
     827                 :     PRUint32 start, end;
     828               0 :     mEditor->GetTextSelectionOffsets(aSelection, start, end);
     829               0 :     NS_ENSURE_SUCCESS(res, res);
     830                 : 
     831               0 :     if (LookAndFeel::GetEchoPassword()) {
     832               0 :       HideLastPWInput();
     833               0 :       mLastStart = start;
     834               0 :       mLastLength = 0;
     835               0 :       if (mTimer)
     836                 :       {
     837               0 :         mTimer->Cancel();
     838                 :       }
     839                 :     }
     840                 : 
     841               0 :     if (end == start)
     842                 :     { // collapsed selection
     843               0 :       if (nsIEditor::ePrevious==aCollapsedAction && 0<start) { // del back
     844               0 :         mPasswordText.Cut(start-1, 1);
     845                 :       }
     846               0 :       else if (nsIEditor::eNext==aCollapsedAction) {      // del forward
     847               0 :         mPasswordText.Cut(start, 1);
     848                 :       }
     849                 :       // otherwise nothing to do for this collapsed selection
     850                 :     }
     851                 :     else {  // extended selection
     852               0 :       mPasswordText.Cut(start, end-start);
     853                 :     }
     854                 :   }
     855                 :   else
     856                 :   {
     857               0 :     nsCOMPtr<nsIDOMNode> startNode;
     858                 :     PRInt32 startOffset;
     859               0 :     res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset);
     860               0 :     NS_ENSURE_SUCCESS(res, res);
     861               0 :     NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
     862                 :     
     863                 :     bool bCollapsed;
     864               0 :     res = aSelection->GetIsCollapsed(&bCollapsed);
     865               0 :     NS_ENSURE_SUCCESS(res, res);
     866                 :   
     867               0 :     if (!bCollapsed)
     868               0 :       return NS_OK;
     869                 : 
     870                 :     // Test for distance between caret and text that will be deleted
     871               0 :     res = CheckBidiLevelForDeletion(aSelection, startNode, startOffset, aCollapsedAction, aCancel);
     872               0 :     NS_ENSURE_SUCCESS(res, res);
     873               0 :     if (*aCancel) return NS_OK;
     874                 : 
     875               0 :     res = mEditor->ExtendSelectionForDelete(aSelection, &aCollapsedAction);
     876               0 :     NS_ENSURE_SUCCESS(res, res);
     877                 :   }
     878                 : 
     879               0 :   res = mEditor->DeleteSelectionImpl(aCollapsedAction);
     880               0 :   NS_ENSURE_SUCCESS(res, res);
     881                 : 
     882               0 :   *aHandled = true;
     883               0 :   ASSERT_PASSWORD_LENGTHS_EQUAL()
     884               0 :   return NS_OK;
     885                 : }
     886                 : 
     887                 : nsresult
     888               0 : nsTextEditRules::DidDeleteSelection(nsISelection *aSelection, 
     889                 :                                     nsIEditor::EDirection aCollapsedAction, 
     890                 :                                     nsresult aResult)
     891                 : {
     892               0 :   nsCOMPtr<nsIDOMNode> startNode;
     893                 :   PRInt32 startOffset;
     894               0 :   nsresult res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset);
     895               0 :   NS_ENSURE_SUCCESS(res, res);
     896               0 :   NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
     897                 :   
     898                 :   // delete empty text nodes at selection
     899               0 :   if (mEditor->IsTextNode(startNode))
     900                 :   {
     901               0 :     nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(startNode);
     902                 :     PRUint32 strLength;
     903               0 :     res = textNode->GetLength(&strLength);
     904               0 :     NS_ENSURE_SUCCESS(res, res);
     905                 :     
     906                 :     // are we in an empty text node?
     907               0 :     if (!strLength)
     908                 :     {
     909               0 :       res = mEditor->DeleteNode(startNode);
     910               0 :       NS_ENSURE_SUCCESS(res, res);
     911                 :     }
     912                 :   }
     913               0 :   if (!mDidExplicitlySetInterline)
     914                 :   {
     915                 :     // We prevent the caret from sticking on the left of prior BR
     916                 :     // (i.e. the end of previous line) after this deletion.  Bug 92124
     917               0 :     nsCOMPtr<nsISelectionPrivate> selPriv = do_QueryInterface(aSelection);
     918               0 :     if (selPriv) res = selPriv->SetInterlinePosition(true);
     919                 :   }
     920               0 :   return res;
     921                 : }
     922                 : 
     923                 : nsresult
     924               0 : nsTextEditRules::WillUndo(nsISelection *aSelection, bool *aCancel, bool *aHandled)
     925                 : {
     926               0 :   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
     927               0 :   CANCEL_OPERATION_IF_READONLY_OR_DISABLED
     928                 :   // initialize out param
     929               0 :   *aCancel = false;
     930               0 :   *aHandled = false;
     931               0 :   return NS_OK;
     932                 : }
     933                 : 
     934                 : /* the idea here is to see if the magic empty node has suddenly reappeared as the result of the undo.
     935                 :  * if it has, set our state so we remember it.
     936                 :  * There is a tradeoff between doing here and at redo, or doing it everywhere else that might care.
     937                 :  * Since undo and redo are relatively rare, it makes sense to take the (small) performance hit here.
     938                 :  */
     939                 : nsresult
     940               0 : nsTextEditRules::DidUndo(nsISelection *aSelection, nsresult aResult)
     941                 : {
     942               0 :   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
     943                 :   // If aResult is an error, we return it.
     944               0 :   NS_ENSURE_SUCCESS(aResult, aResult);
     945                 : 
     946               0 :   dom::Element* theRoot = mEditor->GetRoot();
     947               0 :   NS_ENSURE_TRUE(theRoot, NS_ERROR_FAILURE);
     948               0 :   nsIContent* node = mEditor->GetLeftmostChild(theRoot);
     949               0 :   if (node && mEditor->IsMozEditorBogusNode(node)) {
     950               0 :     mBogusNode = do_QueryInterface(node);
     951                 :   } else {
     952               0 :     mBogusNode = nsnull;
     953                 :   }
     954               0 :   return aResult;
     955                 : }
     956                 : 
     957                 : nsresult
     958               0 : nsTextEditRules::WillRedo(nsISelection *aSelection, bool *aCancel, bool *aHandled)
     959                 : {
     960               0 :   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
     961               0 :   CANCEL_OPERATION_IF_READONLY_OR_DISABLED
     962                 :   // initialize out param
     963               0 :   *aCancel = false;
     964               0 :   *aHandled = false;
     965               0 :   return NS_OK;
     966                 : }
     967                 : 
     968                 : nsresult
     969               0 : nsTextEditRules::DidRedo(nsISelection *aSelection, nsresult aResult)
     970                 : {
     971               0 :   nsresult res = aResult;  // if aResult is an error, we return it.
     972               0 :   if (!aSelection) { return NS_ERROR_NULL_POINTER; }
     973               0 :   if (NS_SUCCEEDED(res)) 
     974                 :   {
     975               0 :     nsCOMPtr<nsIDOMElement> theRoot = do_QueryInterface(mEditor->GetRoot());
     976               0 :     NS_ENSURE_TRUE(theRoot, NS_ERROR_FAILURE);
     977                 :     
     978               0 :     nsCOMPtr<nsIDOMNodeList> nodeList;
     979               0 :     res = theRoot->GetElementsByTagName(NS_LITERAL_STRING("br"),
     980               0 :                                         getter_AddRefs(nodeList));
     981               0 :     NS_ENSURE_SUCCESS(res, res);
     982               0 :     if (nodeList)
     983                 :     {
     984                 :       PRUint32 len;
     985               0 :       nodeList->GetLength(&len);
     986                 :       
     987               0 :       if (len != 1) {
     988                 :         // only in the case of one br could there be the bogus node
     989               0 :         mBogusNode = nsnull;
     990               0 :         return NS_OK;  
     991                 :       }
     992                 : 
     993               0 :       nsCOMPtr<nsIContent> content = nodeList->GetNodeAt(0);
     994               0 :       MOZ_ASSERT(content);
     995               0 :       if (mEditor->IsMozEditorBogusNode(content)) {
     996               0 :         mBogusNode = do_QueryInterface(content);
     997                 :       } else {
     998               0 :         mBogusNode = nsnull;
     999                 :       }
    1000                 :     }
    1001                 :   }
    1002               0 :   return res;
    1003                 : }
    1004                 : 
    1005                 : nsresult
    1006               0 : nsTextEditRules::WillOutputText(nsISelection *aSelection, 
    1007                 :                                 const nsAString  *aOutputFormat,
    1008                 :                                 nsAString *aOutString,                                
    1009                 :                                 bool     *aCancel,
    1010                 :                                 bool     *aHandled)
    1011                 : {
    1012                 :   // null selection ok
    1013               0 :   if (!aOutString || !aOutputFormat || !aCancel || !aHandled) 
    1014               0 :     { return NS_ERROR_NULL_POINTER; }
    1015                 : 
    1016                 :   // initialize out param
    1017               0 :   *aCancel = false;
    1018               0 :   *aHandled = false;
    1019                 : 
    1020               0 :   nsAutoString outputFormat(*aOutputFormat);
    1021               0 :   ToLowerCase(outputFormat);
    1022               0 :   if (outputFormat.EqualsLiteral("text/plain"))
    1023                 :   { // only use these rules for plain text output
    1024               0 :     if (IsPasswordEditor())
    1025                 :     {
    1026               0 :       *aOutString = mPasswordText;
    1027               0 :       *aHandled = true;
    1028                 :     }
    1029               0 :     else if (mBogusNode)
    1030                 :     { // this means there's no content, so output null string
    1031               0 :       aOutString->Truncate();
    1032               0 :       *aHandled = true;
    1033                 :     }
    1034                 :   }
    1035               0 :   return NS_OK;
    1036                 : }
    1037                 : 
    1038                 : nsresult
    1039               0 : nsTextEditRules::DidOutputText(nsISelection *aSelection, nsresult aResult)
    1040                 : {
    1041               0 :   return NS_OK;
    1042                 : }
    1043                 : 
    1044                 : nsresult
    1045               0 : nsTextEditRules::RemoveRedundantTrailingBR()
    1046                 : {
    1047                 :   // If the bogus node exists, we have no work to do
    1048               0 :   if (mBogusNode)
    1049               0 :     return NS_OK;
    1050                 : 
    1051                 :   // Likewise, nothing to be done if we could never have inserted a trailing br
    1052               0 :   if (IsSingleLineEditor())
    1053               0 :     return NS_OK;
    1054                 : 
    1055               0 :   nsRefPtr<dom::Element> body = mEditor->GetRoot();
    1056               0 :   if (!body)
    1057               0 :     return NS_ERROR_NULL_POINTER;
    1058                 : 
    1059               0 :   PRUint32 childCount = body->GetChildCount();
    1060               0 :   if (childCount > 1) {
    1061                 :     // The trailing br is redundant if it is the only remaining child node
    1062               0 :     return NS_OK;
    1063                 :   }
    1064                 : 
    1065               0 :   nsRefPtr<nsIContent> child = body->GetFirstChild();
    1066               0 :   if (!child || !child->IsElement()) {
    1067               0 :     return NS_OK;
    1068                 :   }
    1069                 : 
    1070               0 :   dom::Element* elem = child->AsElement();
    1071               0 :   if (!nsTextEditUtils::IsMozBR(elem)) {
    1072               0 :     return NS_OK;
    1073                 :   }
    1074                 : 
    1075                 :   // Rather than deleting this node from the DOM tree we should instead
    1076                 :   // morph this br into the bogus node
    1077               0 :   elem->UnsetAttr(kNameSpaceID_None, nsGkAtoms::type, true);
    1078                 : 
    1079                 :   // set mBogusNode to be this <br>
    1080               0 :   mBogusNode = do_QueryInterface(elem);
    1081                 : 
    1082                 :   // give it the bogus node attribute
    1083                 :   elem->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
    1084               0 :                 kMOZEditorBogusNodeValue, false);
    1085               0 :   return NS_OK;
    1086                 : }
    1087                 : 
    1088                 : nsresult
    1089               0 : nsTextEditRules::CreateTrailingBRIfNeeded()
    1090                 : {
    1091                 :   // but only if we aren't a single line edit field
    1092               0 :   if (IsSingleLineEditor())
    1093               0 :     return NS_OK;
    1094               0 :   nsCOMPtr<nsIDOMNode> body = do_QueryInterface(mEditor->GetRoot());
    1095               0 :   NS_ENSURE_TRUE(body, NS_ERROR_NULL_POINTER);
    1096               0 :   nsCOMPtr<nsIDOMNode> lastChild;
    1097               0 :   nsresult res = body->GetLastChild(getter_AddRefs(lastChild));
    1098                 :   // assuming CreateBogusNodeIfNeeded() has been called first
    1099               0 :   NS_ENSURE_SUCCESS(res, res);  
    1100               0 :   NS_ENSURE_TRUE(lastChild, NS_ERROR_NULL_POINTER);
    1101                 : 
    1102               0 :   if (!nsTextEditUtils::IsBreak(lastChild))
    1103                 :   {
    1104               0 :     nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
    1105                 :     PRUint32 rootLen;
    1106               0 :     res = mEditor->GetLengthOfDOMNode(body, rootLen);
    1107               0 :     NS_ENSURE_SUCCESS(res, res); 
    1108               0 :     nsCOMPtr<nsIDOMNode> unused;
    1109               0 :     res = CreateMozBR(body, rootLen, address_of(unused));
    1110                 :   }
    1111               0 :   return res;
    1112                 : }
    1113                 : 
    1114                 : nsresult
    1115               0 : nsTextEditRules::CreateBogusNodeIfNeeded(nsISelection *aSelection)
    1116                 : {
    1117               0 :   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
    1118               0 :   NS_ENSURE_TRUE(mEditor, NS_ERROR_NULL_POINTER);
    1119                 : 
    1120               0 :   if (mBogusNode) {
    1121                 :     // Let's not create more than one, ok?
    1122               0 :     return NS_OK;
    1123                 :   }
    1124                 : 
    1125                 :   // tell rules system to not do any post-processing
    1126               0 :   nsAutoRules beginRulesSniffing(mEditor, nsEditor::kOpIgnore, nsIEditor::eNone);
    1127                 : 
    1128               0 :   nsCOMPtr<dom::Element> body = mEditor->GetRoot();
    1129               0 :   if (!body) {
    1130                 :     // We don't even have a body yet, don't insert any bogus nodes at
    1131                 :     // this point.
    1132               0 :     return NS_OK;
    1133                 :   }
    1134                 : 
    1135                 :   // Now we've got the body element. Iterate over the body element's children,
    1136                 :   // looking for editable content. If no editable content is found, insert the
    1137                 :   // bogus node.
    1138               0 :   for (nsCOMPtr<nsIContent> bodyChild = body->GetFirstChild();
    1139               0 :        bodyChild;
    1140               0 :        bodyChild = bodyChild->GetNextSibling()) {
    1141               0 :     if (mEditor->IsMozEditorBogusNode(bodyChild) ||
    1142               0 :         !mEditor->IsEditable(body) || // XXX hoist out of the loop?
    1143               0 :         mEditor->IsEditable(bodyChild)) {
    1144               0 :       return NS_OK;
    1145                 :     }
    1146                 :   }
    1147                 : 
    1148                 :   // Skip adding the bogus node if body is read-only.
    1149               0 :   if (!mEditor->IsModifiableNode(body)) {
    1150               0 :     return NS_OK;
    1151                 :   }
    1152                 : 
    1153                 :   // Create a br.
    1154               0 :   nsCOMPtr<nsIContent> newContent;
    1155               0 :   nsresult rv = mEditor->CreateHTMLContent(NS_LITERAL_STRING("br"), getter_AddRefs(newContent));
    1156               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1157                 : 
    1158                 :   // set mBogusNode to be the newly created <br>
    1159               0 :   mBogusNode = do_QueryInterface(newContent);
    1160               0 :   NS_ENSURE_TRUE(mBogusNode, NS_ERROR_NULL_POINTER);
    1161                 : 
    1162                 :   // Give it a special attribute.
    1163                 :   newContent->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
    1164               0 :                       kMOZEditorBogusNodeValue, false);
    1165                 : 
    1166                 :   // Put the node in the document.
    1167               0 :   nsCOMPtr<nsIDOMNode> bodyNode = do_QueryInterface(body);
    1168               0 :   rv = mEditor->InsertNode(mBogusNode, bodyNode, 0);
    1169               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1170                 : 
    1171                 :   // Set selection.
    1172               0 :   aSelection->CollapseNative(body, 0);
    1173               0 :   return NS_OK;
    1174                 : }
    1175                 : 
    1176                 : 
    1177                 : nsresult
    1178               0 : nsTextEditRules::TruncateInsertionIfNeeded(nsISelection *aSelection, 
    1179                 :                                            const nsAString  *aInString,
    1180                 :                                            nsAString  *aOutString,
    1181                 :                                            PRInt32          aMaxLength,
    1182                 :                                            bool *aTruncated)
    1183                 : {
    1184               0 :   if (!aSelection || !aInString || !aOutString) {return NS_ERROR_NULL_POINTER;}
    1185                 :   
    1186               0 :   nsresult res = NS_OK;
    1187               0 :   *aOutString = *aInString;
    1188               0 :   if (aTruncated) {
    1189               0 :     *aTruncated = false;
    1190                 :   }
    1191                 :   
    1192               0 :   if ((-1 != aMaxLength) && IsPlaintextEditor() && !mEditor->IsIMEComposing() )
    1193                 :   {
    1194                 :     // Get the current text length.
    1195                 :     // Get the length of inString.
    1196                 :     // Get the length of the selection.
    1197                 :     //   If selection is collapsed, it is length 0.
    1198                 :     //   Subtract the length of the selection from the len(doc) 
    1199                 :     //   since we'll delete the selection on insert.
    1200                 :     //   This is resultingDocLength.
    1201                 :     // Get old length of IME composing string
    1202                 :     //   which will be replaced by new one.
    1203                 :     // If (resultingDocLength) is at or over max, cancel the insert
    1204                 :     // If (resultingDocLength) + (length of input) > max, 
    1205                 :     //    set aOutString to subset of inString so length = max
    1206                 :     PRInt32 docLength;
    1207               0 :     res = mEditor->GetTextLength(&docLength);
    1208               0 :     if (NS_FAILED(res)) { return res; }
    1209                 : 
    1210                 :     PRUint32 start, end;
    1211               0 :     res = mEditor->GetTextSelectionOffsets(aSelection, start, end);
    1212               0 :     if (NS_FAILED(res)) { return res; }
    1213                 : 
    1214                 :     PRInt32 oldCompStrLength;
    1215               0 :     res = mEditor->GetIMEBufferLength(&oldCompStrLength);
    1216               0 :     if (NS_FAILED(res)) { return res; }
    1217                 : 
    1218               0 :     const PRInt32 selectionLength = end - start;
    1219               0 :     const PRInt32 resultingDocLength = docLength - selectionLength - oldCompStrLength;
    1220               0 :     if (resultingDocLength >= aMaxLength)
    1221                 :     {
    1222               0 :       aOutString->Truncate();
    1223               0 :       if (aTruncated) {
    1224               0 :         *aTruncated = true;
    1225                 :       }
    1226                 :     }
    1227                 :     else
    1228                 :     {
    1229               0 :       PRInt32 inCount = aOutString->Length();
    1230               0 :       if (inCount + resultingDocLength > aMaxLength)
    1231                 :       {
    1232               0 :         aOutString->Truncate(aMaxLength - resultingDocLength);
    1233               0 :         if (aTruncated) {
    1234               0 :           *aTruncated = true;
    1235                 :         }
    1236                 :       }
    1237                 :     }
    1238                 :   }
    1239               0 :   return res;
    1240                 : }
    1241                 : 
    1242                 : nsresult
    1243               0 : nsTextEditRules::ResetIMETextPWBuf()
    1244                 : {
    1245               0 :   mPasswordIMEText.Truncate();
    1246               0 :   return NS_OK;
    1247                 : }
    1248                 : 
    1249                 : nsresult
    1250               0 : nsTextEditRules::RemoveIMETextFromPWBuf(PRUint32 &aStart, nsAString *aIMEString)
    1251                 : {
    1252               0 :   if (!aIMEString) {
    1253               0 :     return NS_ERROR_NULL_POINTER;
    1254                 :   }
    1255                 : 
    1256                 :   // initialize PasswordIME
    1257               0 :   if (mPasswordIMEText.IsEmpty()) {
    1258               0 :     mPasswordIMEIndex = aStart;
    1259                 :   }
    1260                 :   else {
    1261                 :     // manage the password buffer
    1262               0 :     mPasswordText.Cut(mPasswordIMEIndex, mPasswordIMEText.Length());
    1263               0 :     aStart = mPasswordIMEIndex;
    1264                 :   }
    1265                 : 
    1266               0 :   mPasswordIMEText.Assign(*aIMEString);
    1267               0 :   return NS_OK;
    1268                 : }
    1269                 : 
    1270               0 : NS_IMETHODIMP nsTextEditRules::Notify(class nsITimer *) {
    1271               0 :   nsresult res = HideLastPWInput();
    1272               0 :   ASSERT_PASSWORD_LENGTHS_EQUAL();
    1273               0 :   mLastLength = 0;
    1274               0 :   return res;
    1275                 : }
    1276                 : 
    1277               0 : nsresult nsTextEditRules::HideLastPWInput() {
    1278               0 :   if (!mLastLength) {
    1279                 :     // Special case, we're trying to replace a range that no longer exists
    1280               0 :     return NS_OK;
    1281                 :   }
    1282                 : 
    1283               0 :   nsAutoString hiddenText;
    1284               0 :   FillBufWithPWChars(&hiddenText, mLastLength);
    1285                 : 
    1286               0 :   nsCOMPtr<nsISelection> selection;
    1287                 :   PRUint32 start, end;
    1288               0 :   nsresult res = mEditor->GetSelection(getter_AddRefs(selection));
    1289               0 :   NS_ENSURE_SUCCESS(res, res);
    1290               0 :   res = mEditor->GetTextSelectionOffsets(selection, start, end);
    1291               0 :   NS_ENSURE_SUCCESS(res, res);
    1292                 : 
    1293               0 :   nsCOMPtr<nsIDOMNode> selNode = GetTextNode(selection, mEditor);
    1294               0 :   NS_ENSURE_TRUE(selNode, NS_OK);
    1295                 :   
    1296               0 :   nsCOMPtr<nsIDOMCharacterData> nodeAsText(do_QueryInterface(selNode));
    1297               0 :   NS_ENSURE_TRUE(nodeAsText, NS_OK);
    1298                 :   
    1299               0 :   nodeAsText->ReplaceData(mLastStart, mLastLength, hiddenText);
    1300               0 :   selection->Collapse(selNode, start);
    1301               0 :   if (start != end)
    1302               0 :     selection->Extend(selNode, end);
    1303               0 :   return NS_OK;
    1304                 : }
    1305                 : 
    1306                 : // static
    1307                 : nsresult
    1308               0 : nsTextEditRules::FillBufWithPWChars(nsAString *aOutString, PRInt32 aLength)
    1309                 : {
    1310               0 :   if (!aOutString) {return NS_ERROR_NULL_POINTER;}
    1311                 : 
    1312                 :   // change the output to the platform password character
    1313               0 :   PRUnichar passwordChar = LookAndFeel::GetPasswordCharacter();
    1314                 : 
    1315                 :   PRInt32 i;
    1316               0 :   aOutString->Truncate();
    1317               0 :   for (i=0; i < aLength; i++)
    1318               0 :     aOutString->Append(passwordChar);
    1319                 : 
    1320               0 :   return NS_OK;
    1321                 : }
    1322                 : 
    1323                 : 
    1324                 : ///////////////////////////////////////////////////////////////////////////
    1325                 : // CreateMozBR: put a BR node with moz attribute at {aNode, aOffset}
    1326                 : //                       
    1327                 : nsresult 
    1328               0 : nsTextEditRules::CreateMozBR(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outBRNode)
    1329                 : {
    1330               0 :   NS_ENSURE_TRUE(inParent && outBRNode, NS_ERROR_NULL_POINTER);
    1331                 : 
    1332               0 :   nsresult res = mEditor->CreateBR(inParent, inOffset, outBRNode);
    1333               0 :   NS_ENSURE_SUCCESS(res, res);
    1334                 : 
    1335                 :   // give it special moz attr
    1336               0 :   nsCOMPtr<nsIDOMElement> brElem = do_QueryInterface(*outBRNode);
    1337               0 :   if (brElem)
    1338                 :   {
    1339               0 :     res = mEditor->SetAttribute(brElem, NS_LITERAL_STRING("type"), NS_LITERAL_STRING("_moz"));
    1340               0 :     NS_ENSURE_SUCCESS(res, res);
    1341                 :   }
    1342               0 :   return res;
    1343                 : }
    1344                 : 
    1345                 : NS_IMETHODIMP
    1346               0 : nsTextEditRules::DocumentModified()
    1347                 : {
    1348               0 :   return NS_ERROR_NOT_IMPLEMENTED;
    1349            4392 : }

Generated by: LCOV version 1.7