editor/libeditor/html/nsHTMLEditRules.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 /* vim: set ts=2 sw=2 et tw=79: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include <stdlib.h>
     9 #include "mozilla/Assertions.h"
    10 #include "mozilla/MathAlgorithms.h"
    11 #include "mozilla/Preferences.h"
    12 #include "mozilla/dom/Selection.h"
    13 #include "mozilla/dom/Element.h"
    14 #include "mozilla/mozalloc.h"
    15 #include "nsAString.h"
    16 #include "nsAlgorithm.h"
    17 #include "nsCOMArray.h"
    18 #include "nsCRT.h"
    19 #include "nsCRTGlue.h"
    20 #include "nsComponentManagerUtils.h"
    21 #include "nsContentUtils.h"
    22 #include "nsDebug.h"
    23 #include "nsEditProperty.h"
    24 #include "nsEditor.h"
    25 #include "nsEditorUtils.h"
    26 #include "nsError.h"
    27 #include "nsGkAtoms.h"
    28 #include "nsHTMLCSSUtils.h"
    29 #include "nsHTMLEditRules.h"
    30 #include "nsHTMLEditUtils.h"
    31 #include "nsHTMLEditor.h"
    32 #include "nsIAtom.h"
    33 #include "nsIContent.h"
    34 #include "nsIContentIterator.h"
    35 #include "nsID.h"
    36 #include "nsIDOMCharacterData.h"
    37 #include "nsIDOMDocument.h"
    38 #include "nsIDOMElement.h"
    39 #include "nsIDOMNode.h"
    40 #include "nsIDOMRange.h"
    41 #include "nsIDOMText.h"
    42 #include "nsIHTMLAbsPosEditor.h"
    43 #include "nsIHTMLDocument.h"
    44 #include "nsINode.h"
    45 #include "nsISelection.h"
    46 #include "nsISelectionPrivate.h"
    47 #include "nsLiteralString.h"
    48 #include "nsPlaintextEditor.h"
    49 #include "nsRange.h"
    50 #include "nsReadableUtils.h"
    51 #include "nsString.h"
    52 #include "nsStringFwd.h"
    53 #include "nsTArray.h"
    54 #include "nsTextEditUtils.h"
    55 #include "nsThreadUtils.h"
    56 #include "nsUnicharUtils.h"
    57 #include "nsWSRunObject.h"
    58 #include <algorithm>
    60 class nsISupports;
    61 class nsRulesInfo;
    63 using namespace mozilla;
    64 using namespace mozilla::dom;
    66 //const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE";
    67 //const static char* kMOZEditorBogusNodeValue="TRUE";
    69 enum
    70 {
    71   kLonely = 0,
    72   kPrevSib = 1,
    73   kNextSib = 2,
    74   kBothSibs = 3
    75 };
    77 /********************************************************
    78  *  first some helpful functors we will use
    79  ********************************************************/
    81 static bool IsBlockNode(nsIDOMNode* node)
    82 {
    83   bool isBlock (false);
    84   nsHTMLEditor::NodeIsBlockStatic(node, &isBlock);
    85   return isBlock;
    86 }
    88 static bool IsInlineNode(nsIDOMNode* node)
    89 {
    90   return !IsBlockNode(node);
    91 }
    93 static bool
    94 IsStyleCachePreservingAction(EditAction action)
    95 {
    96   return action == EditAction::deleteSelection ||
    97          action == EditAction::insertBreak ||
    98          action == EditAction::makeList ||
    99          action == EditAction::indent ||
   100          action == EditAction::outdent ||
   101          action == EditAction::align ||
   102          action == EditAction::makeBasicBlock ||
   103          action == EditAction::removeList ||
   104          action == EditAction::makeDefListItem ||
   105          action == EditAction::insertElement ||
   106          action == EditAction::insertQuotation;
   107 }
   109 class nsTableCellAndListItemFunctor : public nsBoolDomIterFunctor
   110 {
   111   public:
   112     virtual bool operator()(nsIDOMNode* aNode)  // used to build list of all li's, td's & th's iterator covers
   113     {
   114       if (nsHTMLEditUtils::IsTableCell(aNode)) return true;
   115       if (nsHTMLEditUtils::IsListItem(aNode)) return true;
   116       return false;
   117     }
   118 };
   120 class nsBRNodeFunctor : public nsBoolDomIterFunctor
   121 {
   122   public:
   123     virtual bool operator()(nsIDOMNode* aNode)  
   124     {
   125       if (nsTextEditUtils::IsBreak(aNode)) return true;
   126       return false;
   127     }
   128 };
   130 class nsEmptyEditableFunctor : public nsBoolDomIterFunctor
   131 {
   132   public:
   133     nsEmptyEditableFunctor(nsHTMLEditor* editor) : mHTMLEditor(editor) {}
   134     virtual bool operator()(nsIDOMNode* aNode)  
   135     {
   136       if (mHTMLEditor->IsEditable(aNode) &&
   137         (nsHTMLEditUtils::IsListItem(aNode) ||
   138         nsHTMLEditUtils::IsTableCellOrCaption(aNode)))
   139       {
   140         bool bIsEmptyNode;
   141         nsresult res = mHTMLEditor->IsEmptyNode(aNode, &bIsEmptyNode, false, false);
   142         NS_ENSURE_SUCCESS(res, false);
   143         if (bIsEmptyNode)
   144           return true;
   145       }
   146       return false;
   147     }
   148   protected:
   149     nsHTMLEditor* mHTMLEditor;
   150 };
   152 class nsEditableTextFunctor : public nsBoolDomIterFunctor
   153 {
   154   public:
   155     nsEditableTextFunctor(nsHTMLEditor* editor) : mHTMLEditor(editor) {}
   156     virtual bool operator()(nsIDOMNode* aNode)  
   157     {
   158       if (nsEditor::IsTextNode(aNode) && mHTMLEditor->IsEditable(aNode)) 
   159       {
   160         return true;
   161       }
   162       return false;
   163     }
   164   protected:
   165     nsHTMLEditor* mHTMLEditor;
   166 };
   169 /********************************************************
   170  *  Constructor/Destructor 
   171  ********************************************************/
   173 nsHTMLEditRules::nsHTMLEditRules()
   174 {
   175   InitFields();
   176 }
   178 void
   179 nsHTMLEditRules::InitFields()
   180 {
   181   mHTMLEditor = nullptr;
   182   mDocChangeRange = nullptr;
   183   mListenerEnabled = true;
   184   mReturnInEmptyLIKillsList = true;
   185   mDidDeleteSelection = false;
   186   mDidRangedDelete = false;
   187   mRestoreContentEditableCount = false;
   188   mUtilRange = nullptr;
   189   mJoinOffset = 0;
   190   mNewBlock = nullptr;
   191   mRangeItem = new nsRangeStore();
   192   // populate mCachedStyles
   193   mCachedStyles[0] = StyleCache(nsEditProperty::b, EmptyString(), EmptyString());
   194   mCachedStyles[1] = StyleCache(nsEditProperty::i, EmptyString(), EmptyString());
   195   mCachedStyles[2] = StyleCache(nsEditProperty::u, EmptyString(), EmptyString());
   196   mCachedStyles[3] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("face"), EmptyString());
   197   mCachedStyles[4] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("size"), EmptyString());
   198   mCachedStyles[5] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("color"), EmptyString());
   199   mCachedStyles[6] = StyleCache(nsEditProperty::tt, EmptyString(), EmptyString());
   200   mCachedStyles[7] = StyleCache(nsEditProperty::em, EmptyString(), EmptyString());
   201   mCachedStyles[8] = StyleCache(nsEditProperty::strong, EmptyString(), EmptyString());
   202   mCachedStyles[9] = StyleCache(nsEditProperty::dfn, EmptyString(), EmptyString());
   203   mCachedStyles[10] = StyleCache(nsEditProperty::code, EmptyString(), EmptyString());
   204   mCachedStyles[11] = StyleCache(nsEditProperty::samp, EmptyString(), EmptyString());
   205   mCachedStyles[12] = StyleCache(nsEditProperty::var, EmptyString(), EmptyString());
   206   mCachedStyles[13] = StyleCache(nsEditProperty::cite, EmptyString(), EmptyString());
   207   mCachedStyles[14] = StyleCache(nsEditProperty::abbr, EmptyString(), EmptyString());
   208   mCachedStyles[15] = StyleCache(nsEditProperty::acronym, EmptyString(), EmptyString());
   209   mCachedStyles[16] = StyleCache(nsEditProperty::cssBackgroundColor, EmptyString(), EmptyString());
   210   mCachedStyles[17] = StyleCache(nsEditProperty::sub, EmptyString(), EmptyString());
   211   mCachedStyles[18] = StyleCache(nsEditProperty::sup, EmptyString(), EmptyString());
   212 }
   214 nsHTMLEditRules::~nsHTMLEditRules()
   215 {
   216   // remove ourselves as a listener to edit actions
   217   // In some cases, we have already been removed by 
   218   // ~nsHTMLEditor, in which case we will get a null pointer here
   219   // which we ignore.  But this allows us to add the ability to
   220   // switch rule sets on the fly if we want.
   221   if (mHTMLEditor)
   222     mHTMLEditor->RemoveEditActionListener(this);
   223 }
   225 /********************************************************
   226  *  XPCOM Cruft
   227  ********************************************************/
   229 NS_IMPL_ADDREF_INHERITED(nsHTMLEditRules, nsTextEditRules)
   230 NS_IMPL_RELEASE_INHERITED(nsHTMLEditRules, nsTextEditRules)
   231 NS_IMPL_QUERY_INTERFACE_INHERITED(nsHTMLEditRules, nsTextEditRules, nsIEditActionListener)
   234 /********************************************************
   235  *  Public methods 
   236  ********************************************************/
   238 NS_IMETHODIMP
   239 nsHTMLEditRules::Init(nsPlaintextEditor *aEditor)
   240 {
   241   InitFields();
   243   mHTMLEditor = static_cast<nsHTMLEditor*>(aEditor);
   244   nsresult res;
   246   // call through to base class Init 
   247   res = nsTextEditRules::Init(aEditor);
   248   NS_ENSURE_SUCCESS(res, res);
   250   // cache any prefs we care about
   251   static const char kPrefName[] =
   252     "editor.html.typing.returnInEmptyListItemClosesList";
   253   nsAdoptingCString returnInEmptyLIKillsList =
   254     Preferences::GetCString(kPrefName);
   256   // only when "false", becomes FALSE.  Otherwise (including empty), TRUE.
   257   // XXX Why was this pref designed as a string and not bool?
   258   mReturnInEmptyLIKillsList = !returnInEmptyLIKillsList.EqualsLiteral("false");
   260   // make a utility range for use by the listenter
   261   nsCOMPtr<nsINode> node = mHTMLEditor->GetRoot();
   262   if (!node) {
   263     node = mHTMLEditor->GetDocument();
   264   }
   266   NS_ENSURE_STATE(node);
   268   mUtilRange = new nsRange(node);
   270   // set up mDocChangeRange to be whole doc
   271   // temporarily turn off rules sniffing
   272   nsAutoLockRulesSniffing lockIt((nsTextEditRules*)this);
   273   if (!mDocChangeRange) {
   274     mDocChangeRange = new nsRange(node);
   275   }
   277   if (node->IsElement()) {
   278     ErrorResult rv;
   279     mDocChangeRange->SelectNode(*node, rv);
   280     res = AdjustSpecialBreaks(node);
   281     NS_ENSURE_SUCCESS(res, res);
   282   }
   284   // add ourselves as a listener to edit actions
   285   res = mHTMLEditor->AddEditActionListener(this);
   287   return res;
   288 }
   290 NS_IMETHODIMP
   291 nsHTMLEditRules::DetachEditor()
   292 {
   293   if (mHTMLEditor) {
   294     mHTMLEditor->RemoveEditActionListener(this);
   295   }
   296   mHTMLEditor = nullptr;
   297   return nsTextEditRules::DetachEditor();
   298 }
   300 NS_IMETHODIMP
   301 nsHTMLEditRules::BeforeEdit(EditAction action,
   302                             nsIEditor::EDirection aDirection)
   303 {
   304   if (mLockRulesSniffing) return NS_OK;
   306   nsAutoLockRulesSniffing lockIt((nsTextEditRules*)this);
   307   mDidExplicitlySetInterline = false;
   309   if (!mActionNesting++)
   310   {
   311     // clear our flag about if just deleted a range
   312     mDidRangedDelete = false;
   314     // remember where our selection was before edit action took place:
   316     // get selection
   317     nsCOMPtr<nsISelection> selection;
   318     NS_ENSURE_STATE(mHTMLEditor);
   319     nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
   320     NS_ENSURE_SUCCESS(res, res);
   322     // get the selection start location
   323     nsCOMPtr<nsIDOMNode> selStartNode, selEndNode;
   324     int32_t selOffset;
   325     NS_ENSURE_STATE(mHTMLEditor);
   326     res = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(selStartNode), &selOffset);
   327     NS_ENSURE_SUCCESS(res, res);
   328     mRangeItem->startNode = selStartNode;
   329     mRangeItem->startOffset = selOffset;
   331     // get the selection end location
   332     NS_ENSURE_STATE(mHTMLEditor);
   333     res = mHTMLEditor->GetEndNodeAndOffset(selection, getter_AddRefs(selEndNode), &selOffset);
   334     NS_ENSURE_SUCCESS(res, res);
   335     mRangeItem->endNode = selEndNode;
   336     mRangeItem->endOffset = selOffset;
   338     // register this range with range updater to track this as we perturb the doc
   339     NS_ENSURE_STATE(mHTMLEditor);
   340     (mHTMLEditor->mRangeUpdater).RegisterRangeItem(mRangeItem);
   342     // clear deletion state bool
   343     mDidDeleteSelection = false;
   345     // clear out mDocChangeRange and mUtilRange
   346     if(mDocChangeRange)
   347     {
   348       // clear out our accounting of what changed
   349       mDocChangeRange->Reset(); 
   350     }
   351     if(mUtilRange)
   352     {
   353       // ditto for mUtilRange.
   354       mUtilRange->Reset(); 
   355     }
   357     // remember current inline styles for deletion and normal insertion operations
   358     if (action == EditAction::insertText ||
   359         action == EditAction::insertIMEText ||
   360         action == EditAction::deleteSelection ||
   361         IsStyleCachePreservingAction(action)) {
   362       nsCOMPtr<nsIDOMNode> selNode = selStartNode;
   363       if (aDirection == nsIEditor::eNext)
   364         selNode = selEndNode;
   365       res = CacheInlineStyles(selNode);
   366       NS_ENSURE_SUCCESS(res, res);
   367     }
   369     // Stabilize the document against contenteditable count changes
   370     NS_ENSURE_STATE(mHTMLEditor);
   371     nsCOMPtr<nsIDOMDocument> doc = mHTMLEditor->GetDOMDocument();
   372     NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
   373     nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
   374     NS_ENSURE_TRUE(htmlDoc, NS_ERROR_FAILURE);
   375     if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
   376       htmlDoc->ChangeContentEditableCount(nullptr, +1);
   377       mRestoreContentEditableCount = true;
   378     }
   380     // check that selection is in subtree defined by body node
   381     ConfirmSelectionInBody();
   382     // let rules remember the top level action
   383     mTheAction = action;
   384   }
   385   return NS_OK;
   386 }
   389 NS_IMETHODIMP
   390 nsHTMLEditRules::AfterEdit(EditAction action,
   391                            nsIEditor::EDirection aDirection)
   392 {
   393   if (mLockRulesSniffing) return NS_OK;
   395   nsAutoLockRulesSniffing lockIt(this);
   397   NS_PRECONDITION(mActionNesting>0, "bad action nesting!");
   398   nsresult res = NS_OK;
   399   if (!--mActionNesting)
   400   {
   401     // do all the tricky stuff
   402     res = AfterEditInner(action, aDirection);
   404     // free up selectionState range item
   405     NS_ENSURE_STATE(mHTMLEditor);
   406     (mHTMLEditor->mRangeUpdater).DropRangeItem(mRangeItem);
   408     // Reset the contenteditable count to its previous value
   409     if (mRestoreContentEditableCount) {
   410       NS_ENSURE_STATE(mHTMLEditor);
   411       nsCOMPtr<nsIDOMDocument> doc = mHTMLEditor->GetDOMDocument();
   412       NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
   413       nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
   414       NS_ENSURE_TRUE(htmlDoc, NS_ERROR_FAILURE);
   415       if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
   416         htmlDoc->ChangeContentEditableCount(nullptr, -1);
   417       }
   418       mRestoreContentEditableCount = false;
   419     }
   420   }
   422   return res;
   423 }
   426 nsresult
   427 nsHTMLEditRules::AfterEditInner(EditAction action,
   428                                 nsIEditor::EDirection aDirection)
   429 {
   430   ConfirmSelectionInBody();
   431   if (action == EditAction::ignore) return NS_OK;
   433   nsCOMPtr<nsISelection>selection;
   434   NS_ENSURE_STATE(mHTMLEditor);
   435   nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
   436   NS_ENSURE_SUCCESS(res, res);
   438   nsCOMPtr<nsIDOMNode> rangeStartParent, rangeEndParent;
   439   int32_t rangeStartOffset = 0, rangeEndOffset = 0;
   440   // do we have a real range to act on?
   441   bool bDamagedRange = false;  
   442   if (mDocChangeRange)
   443   {  
   444     mDocChangeRange->GetStartContainer(getter_AddRefs(rangeStartParent));
   445     mDocChangeRange->GetEndContainer(getter_AddRefs(rangeEndParent));
   446     mDocChangeRange->GetStartOffset(&rangeStartOffset);
   447     mDocChangeRange->GetEndOffset(&rangeEndOffset);
   448     if (rangeStartParent && rangeEndParent) 
   449       bDamagedRange = true; 
   450   }
   452   if (bDamagedRange && !((action == EditAction::undo) || (action == EditAction::redo)))
   453   {
   454     // don't let any txns in here move the selection around behind our back.
   455     // Note that this won't prevent explicit selection setting from working.
   456     NS_ENSURE_STATE(mHTMLEditor);
   457     nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
   459     // expand the "changed doc range" as needed
   460     res = PromoteRange(mDocChangeRange, action);
   461     NS_ENSURE_SUCCESS(res, res);
   463     // if we did a ranged deletion, make sure we have a place to put caret.
   464     // Note we only want to do this if the overall operation was deletion,
   465     // not if deletion was done along the way for EditAction::loadHTML, EditAction::insertText, etc.
   466     // That's why this is here rather than DidDeleteSelection().
   467     if ((action == EditAction::deleteSelection) && mDidRangedDelete)
   468     {
   469       res = InsertBRIfNeeded(selection);
   470       NS_ENSURE_SUCCESS(res, res);
   471     }  
   473     // add in any needed <br>s, and remove any unneeded ones.
   474     res = AdjustSpecialBreaks();
   475     NS_ENSURE_SUCCESS(res, res);
   477     // merge any adjacent text nodes
   478     if ( (action != EditAction::insertText &&
   479          action != EditAction::insertIMEText) )
   480     {
   481       NS_ENSURE_STATE(mHTMLEditor);
   482       res = mHTMLEditor->CollapseAdjacentTextNodes(mDocChangeRange);
   483       NS_ENSURE_SUCCESS(res, res);
   484     }
   486     // clean up any empty nodes in the selection
   487     res = RemoveEmptyNodes();
   488     NS_ENSURE_SUCCESS(res, res);
   490     // attempt to transform any unneeded nbsp's into spaces after doing various operations
   491     if ((action == EditAction::insertText) || 
   492         (action == EditAction::insertIMEText) ||
   493         (action == EditAction::deleteSelection) ||
   494         (action == EditAction::insertBreak) || 
   495         (action == EditAction::htmlPaste ||
   496         (action == EditAction::loadHTML)))
   497     {
   498       res = AdjustWhitespace(selection);
   499       NS_ENSURE_SUCCESS(res, res);
   501       // also do this for original selection endpoints. 
   502       NS_ENSURE_STATE(mHTMLEditor);
   503       nsWSRunObject(mHTMLEditor, mRangeItem->startNode,
   504                     mRangeItem->startOffset).AdjustWhitespace();
   505       // we only need to handle old selection endpoint if it was different from start
   506       if (mRangeItem->startNode != mRangeItem->endNode ||
   507           mRangeItem->startOffset != mRangeItem->endOffset) {
   508         NS_ENSURE_STATE(mHTMLEditor);
   509         nsWSRunObject(mHTMLEditor, mRangeItem->endNode,
   510                       mRangeItem->endOffset).AdjustWhitespace();
   511       }
   512     }
   514     // if we created a new block, make sure selection lands in it
   515     if (mNewBlock)
   516     {
   517       res = PinSelectionToNewBlock(selection);
   518       mNewBlock = 0;
   519     }
   521     // adjust selection for insert text, html paste, and delete actions
   522     if ((action == EditAction::insertText) || 
   523         (action == EditAction::insertIMEText) ||
   524         (action == EditAction::deleteSelection) ||
   525         (action == EditAction::insertBreak) || 
   526         (action == EditAction::htmlPaste ||
   527         (action == EditAction::loadHTML)))
   528     {
   529       res = AdjustSelection(selection, aDirection);
   530       NS_ENSURE_SUCCESS(res, res);
   531     }
   533     // check for any styles which were removed inappropriately
   534     if (action == EditAction::insertText ||
   535         action == EditAction::insertIMEText ||
   536         action == EditAction::deleteSelection ||
   537         IsStyleCachePreservingAction(action)) {
   538       NS_ENSURE_STATE(mHTMLEditor);
   539       mHTMLEditor->mTypeInState->UpdateSelState(selection);
   540       res = ReapplyCachedStyles();
   541       NS_ENSURE_SUCCESS(res, res);
   542       ClearCachedStyles();
   543     }    
   544   }
   546   NS_ENSURE_STATE(mHTMLEditor);
   548   res = mHTMLEditor->HandleInlineSpellCheck(action, selection, 
   549                                             mRangeItem->startNode,
   550                                             mRangeItem->startOffset,
   551                                             rangeStartParent, rangeStartOffset,
   552                                             rangeEndParent, rangeEndOffset);
   553   NS_ENSURE_SUCCESS(res, res);
   555   // detect empty doc
   556   res = CreateBogusNodeIfNeeded(selection);
   558   // adjust selection HINT if needed
   559   NS_ENSURE_SUCCESS(res, res);
   561   if (!mDidExplicitlySetInterline)
   562   {
   563     res = CheckInterlinePosition(selection);
   564   }
   566   return res;
   567 }
   570 NS_IMETHODIMP
   571 nsHTMLEditRules::WillDoAction(Selection* aSelection,
   572                               nsRulesInfo* aInfo,
   573                               bool* aCancel,
   574                               bool* aHandled)
   575 {
   576   MOZ_ASSERT(aInfo && aCancel && aHandled);
   578   *aCancel = false;
   579   *aHandled = false;
   581   // my kingdom for dynamic cast
   582   nsTextRulesInfo *info = static_cast<nsTextRulesInfo*>(aInfo);
   584   // Deal with actions for which we don't need to check whether the selection is
   585   // editable.
   586   if (info->action == EditAction::outputText ||
   587       info->action == EditAction::undo ||
   588       info->action == EditAction::redo) {
   589     return nsTextEditRules::WillDoAction(aSelection, aInfo, aCancel, aHandled);
   590   }
   592   // Nothing to do if there's no selection to act on
   593   if (!aSelection) {
   594     return NS_OK;
   595   }
   596   NS_ENSURE_TRUE(aSelection->GetRangeCount(), NS_OK);
   598   nsRefPtr<nsRange> range = aSelection->GetRangeAt(0);
   599   nsCOMPtr<nsINode> selStartNode = range->GetStartParent();
   601   NS_ENSURE_STATE(mHTMLEditor);
   602   if (!mHTMLEditor->IsModifiableNode(selStartNode)) {
   603     *aCancel = true;
   604     return NS_OK;
   605   }
   607   nsCOMPtr<nsINode> selEndNode = range->GetEndParent();
   609   if (selStartNode != selEndNode) {
   610     NS_ENSURE_STATE(mHTMLEditor);
   611     if (!mHTMLEditor->IsModifiableNode(selEndNode)) {
   612       *aCancel = true;
   613       return NS_OK;
   614     }
   616     NS_ENSURE_STATE(mHTMLEditor);
   617     if (!mHTMLEditor->IsModifiableNode(range->GetCommonAncestor())) {
   618       *aCancel = true;
   619       return NS_OK;
   620     }
   621   }
   623   switch (info->action) {
   624     case EditAction::insertText:
   625     case EditAction::insertIMEText:
   626       return WillInsertText(info->action, aSelection, aCancel, aHandled,
   627                             info->inString, info->outString, info->maxLength);
   628     case EditAction::loadHTML:
   629       return WillLoadHTML(aSelection, aCancel);
   630     case EditAction::insertBreak:
   631       return WillInsertBreak(aSelection, aCancel, aHandled);
   632     case EditAction::deleteSelection:
   633       return WillDeleteSelection(aSelection, info->collapsedAction,
   634                                  info->stripWrappers, aCancel, aHandled);
   635     case EditAction::makeList:
   636       return WillMakeList(aSelection, info->blockType, info->entireList,
   637                           info->bulletType, aCancel, aHandled);
   638     case EditAction::indent:
   639       return WillIndent(aSelection, aCancel, aHandled);
   640     case EditAction::outdent:
   641       return WillOutdent(aSelection, aCancel, aHandled);
   642     case EditAction::setAbsolutePosition:
   643       return WillAbsolutePosition(aSelection, aCancel, aHandled);
   644     case EditAction::removeAbsolutePosition:
   645       return WillRemoveAbsolutePosition(aSelection, aCancel, aHandled);
   646     case EditAction::align:
   647       return WillAlign(aSelection, info->alignType, aCancel, aHandled);
   648     case EditAction::makeBasicBlock:
   649       return WillMakeBasicBlock(aSelection, info->blockType, aCancel, aHandled);
   650     case EditAction::removeList:
   651       return WillRemoveList(aSelection, info->bOrdered, aCancel, aHandled);
   652     case EditAction::makeDefListItem:
   653       return WillMakeDefListItem(aSelection, info->blockType, info->entireList,
   654                                  aCancel, aHandled);
   655     case EditAction::insertElement:
   656       return WillInsert(aSelection, aCancel);
   657     case EditAction::decreaseZIndex:
   658       return WillRelativeChangeZIndex(aSelection, -1, aCancel, aHandled);
   659     case EditAction::increaseZIndex:
   660       return WillRelativeChangeZIndex(aSelection, 1, aCancel, aHandled);
   661     default:
   662       return nsTextEditRules::WillDoAction(aSelection, aInfo,
   663                                            aCancel, aHandled);
   664   }
   665 }
   668 NS_IMETHODIMP 
   669 nsHTMLEditRules::DidDoAction(nsISelection *aSelection,
   670                              nsRulesInfo *aInfo, nsresult aResult)
   671 {
   672   nsTextRulesInfo *info = static_cast<nsTextRulesInfo*>(aInfo);
   673   switch (info->action)
   674   {
   675     case EditAction::insertBreak:
   676       return DidInsertBreak(aSelection, aResult);
   677     case EditAction::deleteSelection:
   678       return DidDeleteSelection(aSelection, info->collapsedAction, aResult);
   679     case EditAction::makeBasicBlock:
   680     case EditAction::indent:
   681     case EditAction::outdent:
   682     case EditAction::align:
   683       return DidMakeBasicBlock(aSelection, aInfo, aResult);
   684     case EditAction::setAbsolutePosition: {
   685       nsresult rv = DidMakeBasicBlock(aSelection, aInfo, aResult);
   686       NS_ENSURE_SUCCESS(rv, rv);
   687       return DidAbsolutePosition();
   688     }
   689     default:
   690       // pass thru to nsTextEditRules
   691       return nsTextEditRules::DidDoAction(aSelection, aInfo, aResult);
   692   }
   693 }
   695 nsresult
   696 nsHTMLEditRules::GetListState(bool *aMixed, bool *aOL, bool *aUL, bool *aDL)
   697 {
   698   NS_ENSURE_TRUE(aMixed && aOL && aUL && aDL, NS_ERROR_NULL_POINTER);
   699   *aMixed = false;
   700   *aOL = false;
   701   *aUL = false;
   702   *aDL = false;
   703   bool bNonList = false;
   705   nsCOMArray<nsIDOMNode> arrayOfNodes;
   706   nsresult res = GetListActionNodes(arrayOfNodes, false, true);
   707   NS_ENSURE_SUCCESS(res, res);
   709   // Examine list type for nodes in selection.
   710   int32_t listCount = arrayOfNodes.Count();
   711   for (int32_t i = listCount - 1; i >= 0; --i) {
   712     nsIDOMNode* curDOMNode = arrayOfNodes[i];
   713     nsCOMPtr<dom::Element> curElement = do_QueryInterface(curDOMNode);
   715     if (!curElement) {
   716       bNonList = true;
   717     } else if (curElement->IsHTML(nsGkAtoms::ul)) {
   718       *aUL = true;
   719     } else if (curElement->IsHTML(nsGkAtoms::ol)) {
   720       *aOL = true;
   721     } else if (curElement->IsHTML(nsGkAtoms::li)) {
   722       if (dom::Element* parent = curElement->GetParentElement()) {
   723         if (parent->IsHTML(nsGkAtoms::ul)) {
   724           *aUL = true;
   725         } else if (parent->IsHTML(nsGkAtoms::ol)) {
   726           *aOL = true;
   727         }
   728       }
   729     } else if (curElement->IsHTML(nsGkAtoms::dl) ||
   730                curElement->IsHTML(nsGkAtoms::dt) ||
   731                curElement->IsHTML(nsGkAtoms::dd)) {
   732       *aDL = true;
   733     } else {
   734       bNonList = true;
   735     }
   736   }  
   738   // hokey arithmetic with booleans
   739   if ((*aUL + *aOL + *aDL + bNonList) > 1) {
   740     *aMixed = true;
   741   }
   743   return NS_OK;
   744 }
   746 nsresult 
   747 nsHTMLEditRules::GetListItemState(bool *aMixed, bool *aLI, bool *aDT, bool *aDD)
   748 {
   749   NS_ENSURE_TRUE(aMixed && aLI && aDT && aDD, NS_ERROR_NULL_POINTER);
   750   *aMixed = false;
   751   *aLI = false;
   752   *aDT = false;
   753   *aDD = false;
   754   bool bNonList = false;
   756   nsCOMArray<nsIDOMNode> arrayOfNodes;
   757   nsresult res = GetListActionNodes(arrayOfNodes, false, true);
   758   NS_ENSURE_SUCCESS(res, res);
   760   // examine list type for nodes in selection
   761   int32_t listCount = arrayOfNodes.Count();
   762   for (int32_t i = listCount - 1; i >= 0; --i) {
   763     nsIDOMNode* curNode = arrayOfNodes[i];
   764     nsCOMPtr<dom::Element> element = do_QueryInterface(curNode);
   765     if (!element) {
   766       bNonList = true;
   767     } else if (element->IsHTML(nsGkAtoms::ul) ||
   768                element->IsHTML(nsGkAtoms::ol) ||
   769                element->IsHTML(nsGkAtoms::li)) {
   770       *aLI = true;
   771     } else if (element->IsHTML(nsGkAtoms::dt)) {
   772       *aDT = true;
   773     } else if (element->IsHTML(nsGkAtoms::dd)) {
   774       *aDD = true;
   775     } else if (element->IsHTML(nsGkAtoms::dl)) {
   776       // need to look inside dl and see which types of items it has
   777       bool bDT, bDD;
   778       GetDefinitionListItemTypes(element, &bDT, &bDD);
   779       *aDT |= bDT;
   780       *aDD |= bDD;
   781     } else {
   782       bNonList = true;
   783     }
   784   }  
   786   // hokey arithmetic with booleans
   787   if ( (*aDT + *aDD + bNonList) > 1) *aMixed = true;
   789   return NS_OK;
   790 }
   792 nsresult 
   793 nsHTMLEditRules::GetAlignment(bool *aMixed, nsIHTMLEditor::EAlignment *aAlign)
   794 {
   795   // for now, just return first alignment.  we'll lie about
   796   // if it's mixed.  This is for efficiency
   797   // given that our current ui doesn't care if it's mixed.
   798   // cmanske: NOT TRUE! We would like to pay attention to mixed state
   799   //  in Format | Align submenu!
   801   // this routine assumes that alignment is done ONLY via divs
   803   // default alignment is left
   804   NS_ENSURE_TRUE(aMixed && aAlign, NS_ERROR_NULL_POINTER);
   805   *aMixed = false;
   806   *aAlign = nsIHTMLEditor::eLeft;
   808   // get selection
   809   nsCOMPtr<nsISelection>selection;
   810   NS_ENSURE_STATE(mHTMLEditor);  
   811   nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
   812   NS_ENSURE_SUCCESS(res, res);
   814   // get selection location
   815   NS_ENSURE_STATE(mHTMLEditor);  
   816   nsCOMPtr<nsIDOMElement> rootElem = do_QueryInterface(mHTMLEditor->GetRoot());
   817   NS_ENSURE_TRUE(rootElem, NS_ERROR_FAILURE);
   819   int32_t offset, rootOffset;
   820   nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(rootElem, &rootOffset);
   821   NS_ENSURE_STATE(mHTMLEditor);
   822   res = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(parent), &offset);
   823   NS_ENSURE_SUCCESS(res, res);
   825   // is the selection collapsed?
   826   nsCOMPtr<nsIDOMNode> nodeToExamine;
   827   if (selection->Collapsed()) {
   828     // if it is, we want to look at 'parent' and its ancestors
   829     // for divs with alignment on them
   830     nodeToExamine = parent;
   831   }
   832   else if (!mHTMLEditor) {
   833     return NS_ERROR_UNEXPECTED;
   834   }
   835   else if (mHTMLEditor->IsTextNode(parent)) 
   836   {
   837     // if we are in a text node, then that is the node of interest
   838     nodeToExamine = parent;
   839   }
   840   else if (nsEditor::NodeIsType(parent, nsEditProperty::html) &&
   841            offset == rootOffset)
   842   {
   843     // if we have selected the body, let's look at the first editable node
   844     NS_ENSURE_STATE(mHTMLEditor);
   845     mHTMLEditor->GetNextNode(parent, offset, true, address_of(nodeToExamine));
   846   }
   847   else
   848   {
   849     nsCOMArray<nsIDOMRange> arrayOfRanges;
   850     res = GetPromotedRanges(selection, arrayOfRanges, EditAction::align);
   851     NS_ENSURE_SUCCESS(res, res);
   853     // use these ranges to construct a list of nodes to act on.
   854     nsCOMArray<nsIDOMNode> arrayOfNodes;
   855     res = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
   856                                EditAction::align, true);
   857     NS_ENSURE_SUCCESS(res, res);                                 
   858     nodeToExamine = arrayOfNodes.SafeObjectAt(0);
   859   }
   861   NS_ENSURE_TRUE(nodeToExamine, NS_ERROR_NULL_POINTER);
   863   NS_NAMED_LITERAL_STRING(typeAttrName, "align");
   864   nsIAtom  *dummyProperty = nullptr;
   865   nsCOMPtr<nsIDOMNode> blockParent;
   866   NS_ENSURE_STATE(mHTMLEditor);
   867   if (mHTMLEditor->IsBlockNode(nodeToExamine))
   868     blockParent = nodeToExamine;
   869   else {
   870     NS_ENSURE_STATE(mHTMLEditor);
   871     blockParent = mHTMLEditor->GetBlockNodeParent(nodeToExamine);
   872   }
   874   NS_ENSURE_TRUE(blockParent, NS_ERROR_FAILURE);
   876   NS_ENSURE_STATE(mHTMLEditor);
   877   if (mHTMLEditor->IsCSSEnabled())
   878   {
   879     nsCOMPtr<nsIContent> blockParentContent = do_QueryInterface(blockParent);
   880     NS_ENSURE_STATE(mHTMLEditor);
   881     if (blockParentContent && 
   882         mHTMLEditor->mHTMLCSSUtils->IsCSSEditableProperty(blockParentContent, dummyProperty, &typeAttrName))
   883     {
   884       // we are in CSS mode and we know how to align this element with CSS
   885       nsAutoString value;
   886       // let's get the value(s) of text-align or margin-left/margin-right
   887       NS_ENSURE_STATE(mHTMLEditor);
   888       mHTMLEditor->mHTMLCSSUtils->GetCSSEquivalentToHTMLInlineStyleSet(
   889         blockParentContent, dummyProperty, &typeAttrName, value,
   890         nsHTMLCSSUtils::eComputed);
   891       if (value.EqualsLiteral("center") ||
   892           value.EqualsLiteral("-moz-center") ||
   893           value.EqualsLiteral("auto auto"))
   894       {
   895         *aAlign = nsIHTMLEditor::eCenter;
   896         return NS_OK;
   897       }
   898       if (value.EqualsLiteral("right") ||
   899           value.EqualsLiteral("-moz-right") ||
   900           value.EqualsLiteral("auto 0px"))
   901       {
   902         *aAlign = nsIHTMLEditor::eRight;
   903         return NS_OK;
   904       }
   905       if (value.EqualsLiteral("justify"))
   906       {
   907         *aAlign = nsIHTMLEditor::eJustify;
   908         return NS_OK;
   909       }
   910       *aAlign = nsIHTMLEditor::eLeft;
   911       return NS_OK;
   912     }
   913   }
   915   // check up the ladder for divs with alignment
   916   nsCOMPtr<nsIDOMNode> temp = nodeToExamine;
   917   bool isFirstNodeToExamine = true;
   918   while (nodeToExamine)
   919   {
   920     if (!isFirstNodeToExamine && nsHTMLEditUtils::IsTable(nodeToExamine))
   921     {
   922       // the node to examine is a table and this is not the first node
   923       // we examine; let's break here to materialize the 'inline-block'
   924       // behaviour of html tables regarding to text alignment
   925       return NS_OK;
   926     }
   927     if (nsHTMLEditUtils::SupportsAlignAttr(nodeToExamine))
   928     {
   929       // check for alignment
   930       nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(nodeToExamine);
   931       if (elem)
   932       {
   933         nsAutoString typeAttrVal;
   934         res = elem->GetAttribute(NS_LITERAL_STRING("align"), typeAttrVal);
   935         ToLowerCase(typeAttrVal);
   936         if (NS_SUCCEEDED(res) && typeAttrVal.Length())
   937         {
   938           if (typeAttrVal.EqualsLiteral("center"))
   939             *aAlign = nsIHTMLEditor::eCenter;
   940           else if (typeAttrVal.EqualsLiteral("right"))
   941             *aAlign = nsIHTMLEditor::eRight;
   942           else if (typeAttrVal.EqualsLiteral("justify"))
   943             *aAlign = nsIHTMLEditor::eJustify;
   944           else
   945             *aAlign = nsIHTMLEditor::eLeft;
   946           return res;
   947         }
   948       }
   949     }
   950     isFirstNodeToExamine = false;
   951     res = nodeToExamine->GetParentNode(getter_AddRefs(temp));
   952     if (NS_FAILED(res)) temp = nullptr;
   953     nodeToExamine = temp; 
   954   }
   955   return NS_OK;
   956 }
   958 nsIAtom* MarginPropertyAtomForIndent(nsHTMLCSSUtils* aHTMLCSSUtils, nsIDOMNode* aNode) {
   959   nsAutoString direction;
   960   aHTMLCSSUtils->GetComputedProperty(aNode, nsEditProperty::cssDirection, direction);
   961   return direction.EqualsLiteral("rtl") ?
   962     nsEditProperty::cssMarginRight : nsEditProperty::cssMarginLeft;
   963 }
   965 nsresult 
   966 nsHTMLEditRules::GetIndentState(bool *aCanIndent, bool *aCanOutdent)
   967 {
   968   NS_ENSURE_TRUE(aCanIndent && aCanOutdent, NS_ERROR_FAILURE);
   969   *aCanIndent = true;    
   970   *aCanOutdent = false;
   972   // get selection
   973   nsCOMPtr<nsISelection>selection;
   974   NS_ENSURE_STATE(mHTMLEditor);
   975   nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
   976   NS_ENSURE_SUCCESS(res, res);
   977   nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
   978   NS_ENSURE_TRUE(selPriv, NS_ERROR_FAILURE);
   980   // contruct a list of nodes to act on.
   981   nsCOMArray<nsIDOMNode> arrayOfNodes;
   982   res = GetNodesFromSelection(selection, EditAction::indent,
   983                               arrayOfNodes, true);
   984   NS_ENSURE_SUCCESS(res, res);
   986   // examine nodes in selection for blockquotes or list elements;
   987   // these we can outdent.  Note that we return true for canOutdent
   988   // if *any* of the selection is outdentable, rather than all of it.
   989   int32_t listCount = arrayOfNodes.Count();
   990   int32_t i;
   991   NS_ENSURE_STATE(mHTMLEditor);
   992   bool useCSS = mHTMLEditor->IsCSSEnabled();
   993   for (i=listCount-1; i>=0; i--)
   994   {
   995     nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
   997     if (nsHTMLEditUtils::IsNodeThatCanOutdent(curNode))
   998     {
   999       *aCanOutdent = true;
  1000       break;
  1002     else if (useCSS) {
  1003       // we are in CSS mode, indentation is done using the margin-left (or margin-right) property
  1004       NS_ENSURE_STATE(mHTMLEditor);
  1005       nsIAtom* marginProperty = MarginPropertyAtomForIndent(mHTMLEditor->mHTMLCSSUtils, curNode);
  1006       nsAutoString value;
  1007       // retrieve its specified value
  1008       NS_ENSURE_STATE(mHTMLEditor);
  1009       mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(curNode, marginProperty, value);
  1010       float f;
  1011       nsCOMPtr<nsIAtom> unit;
  1012       // get its number part and its unit
  1013       NS_ENSURE_STATE(mHTMLEditor);
  1014       mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit));
  1015       // if the number part is strictly positive, outdent is possible
  1016       if (0 < f) {
  1017         *aCanOutdent = true;
  1018         break;
  1023   if (!*aCanOutdent)
  1025     // if we haven't found something to outdent yet, also check the parents
  1026     // of selection endpoints.  We might have a blockquote or list item 
  1027     // in the parent hierarchy.
  1029     // gather up info we need for test
  1030     NS_ENSURE_STATE(mHTMLEditor);
  1031     nsCOMPtr<nsIDOMNode> parent, tmp, root = do_QueryInterface(mHTMLEditor->GetRoot());
  1032     NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
  1033     nsCOMPtr<nsISelection> selection;
  1034     int32_t selOffset;
  1035     NS_ENSURE_STATE(mHTMLEditor);
  1036     res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
  1037     NS_ENSURE_SUCCESS(res, res);
  1038     NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  1040     // test start parent hierarchy
  1041     NS_ENSURE_STATE(mHTMLEditor);
  1042     res = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(parent), &selOffset);
  1043     NS_ENSURE_SUCCESS(res, res);
  1044     while (parent && (parent!=root))
  1046       if (nsHTMLEditUtils::IsNodeThatCanOutdent(parent))
  1048         *aCanOutdent = true;
  1049         break;
  1051       tmp=parent;
  1052       tmp->GetParentNode(getter_AddRefs(parent));
  1055     // test end parent hierarchy
  1056     NS_ENSURE_STATE(mHTMLEditor);
  1057     res = mHTMLEditor->GetEndNodeAndOffset(selection, getter_AddRefs(parent), &selOffset);
  1058     NS_ENSURE_SUCCESS(res, res);
  1059     while (parent && (parent!=root))
  1061       if (nsHTMLEditUtils::IsNodeThatCanOutdent(parent))
  1063         *aCanOutdent = true;
  1064         break;
  1066       tmp=parent;
  1067       tmp->GetParentNode(getter_AddRefs(parent));
  1070   return res;
  1074 nsresult 
  1075 nsHTMLEditRules::GetParagraphState(bool *aMixed, nsAString &outFormat)
  1077   // This routine is *heavily* tied to our ui choices in the paragraph
  1078   // style popup.  I can't see a way around that.
  1079   NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER);
  1080   *aMixed = true;
  1081   outFormat.Truncate(0);
  1083   bool bMixed = false;
  1084   // using "x" as an uninitialized value, since "" is meaningful
  1085   nsAutoString formatStr(NS_LITERAL_STRING("x")); 
  1087   nsCOMArray<nsIDOMNode> arrayOfNodes;
  1088   nsresult res = GetParagraphFormatNodes(arrayOfNodes, true);
  1089   NS_ENSURE_SUCCESS(res, res);
  1091   // post process list.  We need to replace any block nodes that are not format
  1092   // nodes with their content.  This is so we only have to look "up" the hierarchy
  1093   // to find format nodes, instead of both up and down.
  1094   int32_t listCount = arrayOfNodes.Count();
  1095   int32_t i;
  1096   for (i=listCount-1; i>=0; i--)
  1098     nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
  1099     nsAutoString format;
  1100     // if it is a known format node we have it easy
  1101     if (IsBlockNode(curNode) && !nsHTMLEditUtils::IsFormatNode(curNode))
  1103       // arrayOfNodes.RemoveObject(curNode);
  1104       res = AppendInnerFormatNodes(arrayOfNodes, curNode);
  1105       NS_ENSURE_SUCCESS(res, res);
  1109   // we might have an empty node list.  if so, find selection parent
  1110   // and put that on the list
  1111   listCount = arrayOfNodes.Count();
  1112   if (!listCount)
  1114     nsCOMPtr<nsIDOMNode> selNode;
  1115     int32_t selOffset;
  1116     nsCOMPtr<nsISelection>selection;
  1117     NS_ENSURE_STATE(mHTMLEditor);
  1118     res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
  1119     NS_ENSURE_SUCCESS(res, res);
  1120     NS_ENSURE_STATE(mHTMLEditor);
  1121     res = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset);
  1122     NS_ENSURE_SUCCESS(res, res);
  1123     NS_ENSURE_TRUE(selNode, NS_ERROR_NULL_POINTER);
  1124     arrayOfNodes.AppendObject(selNode);
  1125     listCount = 1;
  1128   // remember root node
  1129   NS_ENSURE_STATE(mHTMLEditor);
  1130   nsCOMPtr<nsIDOMElement> rootElem = do_QueryInterface(mHTMLEditor->GetRoot());
  1131   NS_ENSURE_TRUE(rootElem, NS_ERROR_NULL_POINTER);
  1133   // loop through the nodes in selection and examine their paragraph format
  1134   for (i=listCount-1; i>=0; i--)
  1136     nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
  1137     nsAutoString format;
  1138     // if it is a known format node we have it easy
  1139     if (nsHTMLEditUtils::IsFormatNode(curNode))
  1140       GetFormatString(curNode, format);
  1141     else if (IsBlockNode(curNode))
  1143       // this is a div or some other non-format block.
  1144       // we should ignore it.  Its children were appended to this list
  1145       // by AppendInnerFormatNodes() call above.  We will get needed
  1146       // info when we examine them instead.
  1147       continue;
  1149     else
  1151       nsCOMPtr<nsIDOMNode> node, tmp = curNode;
  1152       tmp->GetParentNode(getter_AddRefs(node));
  1153       while (node)
  1155         if (node == rootElem)
  1157           format.Truncate(0);
  1158           break;
  1160         else if (nsHTMLEditUtils::IsFormatNode(node))
  1162           GetFormatString(node, format);
  1163           break;
  1165         // else keep looking up
  1166         tmp = node;
  1167         tmp->GetParentNode(getter_AddRefs(node));
  1171     // if this is the first node, we've found, remember it as the format
  1172     if (formatStr.EqualsLiteral("x"))
  1173       formatStr = format;
  1174     // else make sure it matches previously found format
  1175     else if (format != formatStr) 
  1177       bMixed = true;
  1178       break; 
  1182   *aMixed = bMixed;
  1183   outFormat = formatStr;
  1184   return res;
  1187 nsresult
  1188 nsHTMLEditRules::AppendInnerFormatNodes(nsCOMArray<nsIDOMNode>& aArray,
  1189                                         nsIDOMNode *aNode)
  1191   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  1192   NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
  1194   return AppendInnerFormatNodes(aArray, node);
  1197 nsresult
  1198 nsHTMLEditRules::AppendInnerFormatNodes(nsCOMArray<nsIDOMNode>& aArray,
  1199                                         nsINode* aNode)
  1201   MOZ_ASSERT(aNode);
  1203   // we only need to place any one inline inside this node onto 
  1204   // the list.  They are all the same for purposes of determining
  1205   // paragraph style.  We use foundInline to track this as we are 
  1206   // going through the children in the loop below.
  1207   bool foundInline = false;
  1208   for (nsIContent* child = aNode->GetFirstChild();
  1209        child;
  1210        child = child->GetNextSibling()) {
  1211     bool isBlock = IsBlockNode(child->AsDOMNode());
  1212     bool isFormat = nsHTMLEditUtils::IsFormatNode(child);
  1213     if (isBlock && !isFormat) {
  1214       // if it's a div, etc, recurse
  1215       AppendInnerFormatNodes(aArray, child);
  1216     } else if (isFormat) {
  1217       aArray.AppendObject(child->AsDOMNode());
  1218     } else if (!foundInline) {
  1219       // if this is the first inline we've found, use it
  1220       foundInline = true;      
  1221       aArray.AppendObject(child->AsDOMNode());
  1224   return NS_OK;
  1227 nsresult 
  1228 nsHTMLEditRules::GetFormatString(nsIDOMNode *aNode, nsAString &outFormat)
  1230   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
  1232   if (nsHTMLEditUtils::IsFormatNode(aNode))
  1234     nsCOMPtr<nsIAtom> atom = nsEditor::GetTag(aNode);
  1235     atom->ToString(outFormat);
  1237   else
  1238     outFormat.Truncate();
  1240   return NS_OK;
  1243 /********************************************************
  1244  *  Protected rules methods 
  1245  ********************************************************/
  1247 nsresult
  1248 nsHTMLEditRules::WillInsert(nsISelection *aSelection, bool *aCancel)
  1250   nsresult res = nsTextEditRules::WillInsert(aSelection, aCancel);
  1251   NS_ENSURE_SUCCESS(res, res); 
  1253   // Adjust selection to prevent insertion after a moz-BR.
  1254   // this next only works for collapsed selections right now,
  1255   // because selection is a pain to work with when not collapsed.
  1256   // (no good way to extend start or end of selection), so we ignore
  1257   // those types of selections.
  1258   if (!aSelection->Collapsed()) {
  1259     return NS_OK;
  1262   // if we are after a mozBR in the same block, then move selection
  1263   // to be before it
  1264   nsCOMPtr<nsIDOMNode> selNode, priorNode;
  1265   int32_t selOffset;
  1266   // get the (collapsed) selection location
  1267   NS_ENSURE_STATE(mHTMLEditor);
  1268   res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode),
  1269                                            &selOffset);
  1270   NS_ENSURE_SUCCESS(res, res);
  1271   // get prior node
  1272   NS_ENSURE_STATE(mHTMLEditor);
  1273   res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset,
  1274                                       address_of(priorNode));
  1275   if (NS_SUCCEEDED(res) && priorNode && nsTextEditUtils::IsMozBR(priorNode))
  1277     nsCOMPtr<nsIDOMNode> block1, block2;
  1278     if (IsBlockNode(selNode)) {
  1279       block1 = selNode;
  1281     else {
  1282       NS_ENSURE_STATE(mHTMLEditor);
  1283       block1 = mHTMLEditor->GetBlockNodeParent(selNode);
  1285     NS_ENSURE_STATE(mHTMLEditor);
  1286     block2 = mHTMLEditor->GetBlockNodeParent(priorNode);
  1288     if (block1 == block2)
  1290       // if we are here then the selection is right after a mozBR
  1291       // that is in the same block as the selection.  We need to move
  1292       // the selection start to be before the mozBR.
  1293       selNode = nsEditor::GetNodeLocation(priorNode, &selOffset);
  1294       res = aSelection->Collapse(selNode,selOffset);
  1295       NS_ENSURE_SUCCESS(res, res);
  1299   if (mDidDeleteSelection &&
  1300       (mTheAction == EditAction::insertText ||
  1301        mTheAction == EditAction::insertIMEText ||
  1302        mTheAction == EditAction::deleteSelection)) {
  1303     res = ReapplyCachedStyles();
  1304     NS_ENSURE_SUCCESS(res, res);
  1306   // For most actions we want to clear the cached styles, but there are
  1307   // exceptions
  1308   if (!IsStyleCachePreservingAction(mTheAction)) {
  1309     ClearCachedStyles();
  1312   return NS_OK;
  1315 nsresult
  1316 nsHTMLEditRules::WillInsertText(EditAction aAction,
  1317                                 Selection*       aSelection,
  1318                                 bool            *aCancel,
  1319                                 bool            *aHandled,
  1320                                 const nsAString *inString,
  1321                                 nsAString       *outString,
  1322                                 int32_t          aMaxLength)
  1324   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
  1326   if (inString->IsEmpty() && aAction != EditAction::insertIMEText) {
  1327     // HACK: this is a fix for bug 19395
  1328     // I can't outlaw all empty insertions
  1329     // because IME transaction depend on them
  1330     // There is more work to do to make the 
  1331     // world safe for IME.
  1332     *aCancel = true;
  1333     *aHandled = false;
  1334     return NS_OK;
  1337   // initialize out param
  1338   *aCancel = false;
  1339   *aHandled = true;
  1340   nsresult res;
  1341   nsCOMPtr<nsIDOMNode> selNode;
  1342   int32_t selOffset;
  1344   // If the selection isn't collapsed, delete it.  Don't delete existing inline
  1345   // tags, because we're hopefully going to insert text (bug 787432).
  1346   if (!aSelection->Collapsed()) {
  1347     NS_ENSURE_STATE(mHTMLEditor);
  1348     res = mHTMLEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eNoStrip);
  1349     NS_ENSURE_SUCCESS(res, res);
  1352   res = WillInsert(aSelection, aCancel);
  1353   NS_ENSURE_SUCCESS(res, res);
  1354   // initialize out param
  1355   // we want to ignore result of WillInsert()
  1356   *aCancel = false;
  1358   // we need to get the doc
  1359   NS_ENSURE_STATE(mHTMLEditor);
  1360   nsCOMPtr<nsIDOMDocument> doc = mHTMLEditor->GetDOMDocument();
  1361   NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
  1363   // for every property that is set, insert a new inline style node
  1364   res = CreateStyleForInsertText(aSelection, doc);
  1365   NS_ENSURE_SUCCESS(res, res);
  1367   // get the (collapsed) selection location
  1368   NS_ENSURE_STATE(mHTMLEditor);
  1369   res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
  1370   NS_ENSURE_SUCCESS(res, res);
  1372   // dont put text in places that can't have it
  1373   NS_ENSURE_STATE(mHTMLEditor);
  1374   if (!mHTMLEditor->IsTextNode(selNode) &&
  1375       (!mHTMLEditor ||
  1376        !mHTMLEditor->CanContainTag(selNode, nsGkAtoms::textTagName))) {
  1377     return NS_ERROR_FAILURE;
  1380   if (aAction == EditAction::insertIMEText) {
  1381     // Right now the nsWSRunObject code bails on empty strings, but IME needs 
  1382     // the InsertTextImpl() call to still happen since empty strings are meaningful there.
  1383     if (inString->IsEmpty())
  1385       NS_ENSURE_STATE(mHTMLEditor);
  1386       res = mHTMLEditor->InsertTextImpl(*inString, address_of(selNode), &selOffset, doc);
  1388     else
  1390       NS_ENSURE_STATE(mHTMLEditor);
  1391       nsWSRunObject wsObj(mHTMLEditor, selNode, selOffset);
  1392       res = wsObj.InsertText(*inString, address_of(selNode), &selOffset, doc);
  1394     NS_ENSURE_SUCCESS(res, res);
  1396   else // aAction == kInsertText
  1398     // find where we are
  1399     nsCOMPtr<nsIDOMNode> curNode = selNode;
  1400     int32_t curOffset = selOffset;
  1402     // is our text going to be PREformatted?  
  1403     // We remember this so that we know how to handle tabs.
  1404     bool isPRE;
  1405     NS_ENSURE_STATE(mHTMLEditor);
  1406     res = mHTMLEditor->IsPreformatted(selNode, &isPRE);
  1407     NS_ENSURE_SUCCESS(res, res);    
  1409     // turn off the edit listener: we know how to
  1410     // build the "doc changed range" ourselves, and it's
  1411     // must faster to do it once here than to track all
  1412     // the changes one at a time.
  1413     nsAutoLockListener lockit(&mListenerEnabled); 
  1415     // don't spaz my selection in subtransactions
  1416     NS_ENSURE_STATE(mHTMLEditor);
  1417     nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
  1418     nsAutoString tString(*inString);
  1419     const char16_t *unicodeBuf = tString.get();
  1420     nsCOMPtr<nsIDOMNode> unused;
  1421     int32_t pos = 0;
  1422     NS_NAMED_LITERAL_STRING(newlineStr, LFSTR);
  1424     // for efficiency, break out the pre case separately.  This is because
  1425     // its a lot cheaper to search the input string for only newlines than
  1426     // it is to search for both tabs and newlines.
  1427     if (isPRE || IsPlaintextEditor())
  1429       while (unicodeBuf && (pos != -1) && (pos < (int32_t)(*inString).Length()))
  1431         int32_t oldPos = pos;
  1432         int32_t subStrLen;
  1433         pos = tString.FindChar(nsCRT::LF, oldPos);
  1435         if (pos != -1) 
  1437           subStrLen = pos - oldPos;
  1438           // if first char is newline, then use just it
  1439           if (subStrLen == 0)
  1440             subStrLen = 1;
  1442         else
  1444           subStrLen = tString.Length() - oldPos;
  1445           pos = tString.Length();
  1448         nsDependentSubstring subStr(tString, oldPos, subStrLen);
  1450         // is it a return?
  1451         if (subStr.Equals(newlineStr))
  1453           NS_ENSURE_STATE(mHTMLEditor);
  1454           res = mHTMLEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
  1455           pos++;
  1457         else
  1459           NS_ENSURE_STATE(mHTMLEditor);
  1460           res = mHTMLEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
  1462         NS_ENSURE_SUCCESS(res, res);
  1465     else
  1467       NS_NAMED_LITERAL_STRING(tabStr, "\t");
  1468       NS_NAMED_LITERAL_STRING(spacesStr, "    ");
  1469       char specialChars[] = {TAB, nsCRT::LF, 0};
  1470       while (unicodeBuf && (pos != -1) && (pos < (int32_t)inString->Length()))
  1472         int32_t oldPos = pos;
  1473         int32_t subStrLen;
  1474         pos = tString.FindCharInSet(specialChars, oldPos);
  1476         if (pos != -1) 
  1478           subStrLen = pos - oldPos;
  1479           // if first char is newline, then use just it
  1480           if (subStrLen == 0)
  1481             subStrLen = 1;
  1483         else
  1485           subStrLen = tString.Length() - oldPos;
  1486           pos = tString.Length();
  1489         nsDependentSubstring subStr(tString, oldPos, subStrLen);
  1490         NS_ENSURE_STATE(mHTMLEditor);
  1491         nsWSRunObject wsObj(mHTMLEditor, curNode, curOffset);
  1493         // is it a tab?
  1494         if (subStr.Equals(tabStr))
  1496           res = wsObj.InsertText(spacesStr, address_of(curNode), &curOffset, doc);
  1497           NS_ENSURE_SUCCESS(res, res);
  1498           pos++;
  1500         // is it a return?
  1501         else if (subStr.Equals(newlineStr))
  1503           res = wsObj.InsertBreak(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
  1504           NS_ENSURE_SUCCESS(res, res);
  1505           pos++;
  1507         else
  1509           res = wsObj.InsertText(subStr, address_of(curNode), &curOffset, doc);
  1510           NS_ENSURE_SUCCESS(res, res);
  1512         NS_ENSURE_SUCCESS(res, res);
  1515     nsCOMPtr<nsISelection> selection(aSelection);
  1516     nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
  1517     selPriv->SetInterlinePosition(false);
  1518     if (curNode) aSelection->Collapse(curNode, curOffset);
  1519     // manually update the doc changed range so that AfterEdit will clean up
  1520     // the correct portion of the document.
  1521     if (!mDocChangeRange)
  1523       nsCOMPtr<nsINode> node = do_QueryInterface(selNode);
  1524       NS_ENSURE_STATE(node);
  1525       mDocChangeRange = new nsRange(node);
  1527     res = mDocChangeRange->SetStart(selNode, selOffset);
  1528     NS_ENSURE_SUCCESS(res, res);
  1529     if (curNode)
  1530       res = mDocChangeRange->SetEnd(curNode, curOffset);
  1531     else
  1532       res = mDocChangeRange->SetEnd(selNode, selOffset);
  1533     NS_ENSURE_SUCCESS(res, res);
  1535   return res;
  1538 nsresult
  1539 nsHTMLEditRules::WillLoadHTML(nsISelection *aSelection, bool *aCancel)
  1541   NS_ENSURE_TRUE(aSelection && aCancel, NS_ERROR_NULL_POINTER);
  1543   *aCancel = false;
  1545   // Delete mBogusNode if it exists. If we really need one,
  1546   // it will be added during post-processing in AfterEditInner().
  1548   if (mBogusNode)
  1550     mEditor->DeleteNode(mBogusNode);
  1551     mBogusNode = nullptr;
  1554   return NS_OK;
  1557 nsresult
  1558 nsHTMLEditRules::WillInsertBreak(Selection* aSelection,
  1559                                  bool* aCancel, bool* aHandled)
  1561   if (!aSelection || !aCancel || !aHandled) {
  1562     return NS_ERROR_NULL_POINTER;
  1564   // initialize out params
  1565   *aCancel = false;
  1566   *aHandled = false;
  1568   // if the selection isn't collapsed, delete it.
  1569   nsresult res = NS_OK;
  1570   if (!aSelection->Collapsed()) {
  1571     NS_ENSURE_STATE(mHTMLEditor);
  1572     res = mHTMLEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
  1573     NS_ENSURE_SUCCESS(res, res);
  1576   res = WillInsert(aSelection, aCancel);
  1577   NS_ENSURE_SUCCESS(res, res);
  1579   // initialize out param
  1580   // we want to ignore result of WillInsert()
  1581   *aCancel = false;
  1583   // split any mailcites in the way.
  1584   // should we abort this if we encounter table cell boundaries?
  1585   if (IsMailEditor()) {
  1586     res = SplitMailCites(aSelection, IsPlaintextEditor(), aHandled);
  1587     NS_ENSURE_SUCCESS(res, res);
  1588     if (*aHandled) {
  1589       return NS_OK;
  1593   // smart splitting rules
  1594   nsCOMPtr<nsIDOMNode> node;
  1595   int32_t offset;
  1597   NS_ENSURE_STATE(mHTMLEditor);
  1598   res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(node),
  1599                                            &offset);
  1600   NS_ENSURE_SUCCESS(res, res);
  1601   NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
  1603   // do nothing if the node is read-only
  1604   NS_ENSURE_STATE(mHTMLEditor);
  1605   if (!mHTMLEditor->IsModifiableNode(node)) {
  1606     *aCancel = true;
  1607     return NS_OK;
  1610   // identify the block
  1611   nsCOMPtr<nsIDOMNode> blockParent;
  1612   if (IsBlockNode(node)) {
  1613     blockParent = node;
  1614   } else {
  1615     NS_ENSURE_STATE(mHTMLEditor);
  1616     blockParent = mHTMLEditor->GetBlockNodeParent(node);
  1618   NS_ENSURE_TRUE(blockParent, NS_ERROR_FAILURE);
  1620   // if the active editing host is an inline element, or if the active editing
  1621   // host is the block parent itself, just append a br.
  1622   NS_ENSURE_STATE(mHTMLEditor);
  1623   nsCOMPtr<nsIContent> hostContent = mHTMLEditor->GetActiveEditingHost();
  1624   nsCOMPtr<nsIDOMNode> hostNode = do_QueryInterface(hostContent);
  1625   if (!nsEditorUtils::IsDescendantOf(blockParent, hostNode)) {
  1626     res = StandardBreakImpl(node, offset, aSelection);
  1627     NS_ENSURE_SUCCESS(res, res);
  1628     *aHandled = true;
  1629     return NS_OK;
  1632   // if block is empty, populate with br.  (for example, imagine a div that
  1633   // contains the word "text".  the user selects "text" and types return.
  1634   // "text" is deleted leaving an empty block.  we want to put in one br to
  1635   // make block have a line.  then code further below will put in a second br.)
  1636   bool isEmpty;
  1637   IsEmptyBlock(blockParent, &isEmpty);
  1638   if (isEmpty) {
  1639     uint32_t blockLen;
  1640     NS_ENSURE_STATE(mHTMLEditor);
  1641     res = mHTMLEditor->GetLengthOfDOMNode(blockParent, blockLen);
  1642     NS_ENSURE_SUCCESS(res, res);
  1643     nsCOMPtr<nsIDOMNode> brNode;
  1644     NS_ENSURE_STATE(mHTMLEditor);
  1645     res = mHTMLEditor->CreateBR(blockParent, blockLen, address_of(brNode));
  1646     NS_ENSURE_SUCCESS(res, res);
  1649   nsCOMPtr<nsIDOMNode> listItem = IsInListItem(blockParent);
  1650   if (listItem && listItem != hostNode) {
  1651     ReturnInListItem(aSelection, listItem, node, offset);
  1652     *aHandled = true;
  1653     return NS_OK;
  1654   } else if (nsHTMLEditUtils::IsHeader(blockParent)) {
  1655     // headers: close (or split) header
  1656     ReturnInHeader(aSelection, blockParent, node, offset);
  1657     *aHandled = true;
  1658     return NS_OK;
  1659   } else if (nsHTMLEditUtils::IsParagraph(blockParent)) {
  1660     // paragraphs: special rules to look for <br>s
  1661     res = ReturnInParagraph(aSelection, blockParent, node, offset,
  1662                             aCancel, aHandled);
  1663     NS_ENSURE_SUCCESS(res, res);
  1664     // fall through, we may not have handled it in ReturnInParagraph()
  1667   // if not already handled then do the standard thing
  1668   if (!(*aHandled)) {
  1669     *aHandled = true;
  1670     return StandardBreakImpl(node, offset, aSelection);
  1672   return NS_OK;
  1675 nsresult
  1676 nsHTMLEditRules::StandardBreakImpl(nsIDOMNode* aNode, int32_t aOffset,
  1677                                    nsISelection* aSelection)
  1679   nsCOMPtr<nsIDOMNode> brNode;
  1680   bool bAfterBlock = false;
  1681   bool bBeforeBlock = false;
  1682   nsresult res = NS_OK;
  1683   nsCOMPtr<nsIDOMNode> node(aNode);
  1684   nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(aSelection));
  1686   if (IsPlaintextEditor()) {
  1687     NS_ENSURE_STATE(mHTMLEditor);
  1688     res = mHTMLEditor->CreateBR(node, aOffset, address_of(brNode));
  1689   } else {
  1690     NS_ENSURE_STATE(mHTMLEditor);
  1691     nsWSRunObject wsObj(mHTMLEditor, node, aOffset);
  1692     nsCOMPtr<nsIDOMNode> visNode, linkNode;
  1693     int32_t visOffset = 0, newOffset;
  1694     WSType wsType;
  1695     wsObj.PriorVisibleNode(node, aOffset, address_of(visNode),
  1696                            &visOffset, &wsType);
  1697     if (wsType & WSType::block) {
  1698       bAfterBlock = true;
  1700     wsObj.NextVisibleNode(node, aOffset, address_of(visNode),
  1701                           &visOffset, &wsType);
  1702     if (wsType & WSType::block) {
  1703       bBeforeBlock = true;
  1705     NS_ENSURE_STATE(mHTMLEditor);
  1706     if (mHTMLEditor->IsInLink(node, address_of(linkNode))) {
  1707       // split the link
  1708       nsCOMPtr<nsIDOMNode> linkParent;
  1709       res = linkNode->GetParentNode(getter_AddRefs(linkParent));
  1710       NS_ENSURE_SUCCESS(res, res);
  1711       NS_ENSURE_STATE(mHTMLEditor);
  1712       res = mHTMLEditor->SplitNodeDeep(linkNode, node, aOffset,
  1713                                        &newOffset, true);
  1714       NS_ENSURE_SUCCESS(res, res);
  1715       // reset {node,aOffset} to the point where link was split
  1716       node = linkParent;
  1717       aOffset = newOffset;
  1719     res = wsObj.InsertBreak(address_of(node), &aOffset,
  1720                             address_of(brNode), nsIEditor::eNone);
  1722   NS_ENSURE_SUCCESS(res, res);
  1723   node = nsEditor::GetNodeLocation(brNode, &aOffset);
  1724   NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
  1725   if (bAfterBlock && bBeforeBlock) {
  1726     // we just placed a br between block boundaries.  This is the one case
  1727     // where we want the selection to be before the br we just placed, as the
  1728     // br will be on a new line, rather than at end of prior line.
  1729     selPriv->SetInterlinePosition(true);
  1730     res = aSelection->Collapse(node, aOffset);
  1731   } else {
  1732     NS_ENSURE_STATE(mHTMLEditor);
  1733     nsWSRunObject wsObj(mHTMLEditor, node, aOffset+1);
  1734     nsCOMPtr<nsIDOMNode> secondBR;
  1735     int32_t visOffset = 0;
  1736     WSType wsType;
  1737     wsObj.NextVisibleNode(node, aOffset+1, address_of(secondBR),
  1738                           &visOffset, &wsType);
  1739     if (wsType == WSType::br) {
  1740       // the next thing after the break we inserted is another break.  Move
  1741       // the 2nd break to be the first breaks sibling.  This will prevent them
  1742       // from being in different inline nodes, which would break
  1743       // SetInterlinePosition().  It will also assure that if the user clicks
  1744       // away and then clicks back on their new blank line, they will still
  1745       // get the style from the line above.
  1746       int32_t brOffset;
  1747       nsCOMPtr<nsIDOMNode> brParent = nsEditor::GetNodeLocation(secondBR, &brOffset);
  1748       if (brParent != node || brOffset != aOffset + 1) {
  1749         NS_ENSURE_STATE(mHTMLEditor);
  1750         res = mHTMLEditor->MoveNode(secondBR, node, aOffset+1);
  1751         NS_ENSURE_SUCCESS(res, res);
  1754     // SetInterlinePosition(true) means we want the caret to stick to the
  1755     // content on the "right".  We want the caret to stick to whatever is past
  1756     // the break.  This is because the break is on the same line we were on,
  1757     // but the next content will be on the following line.
  1759     // An exception to this is if the break has a next sibling that is a block
  1760     // node.  Then we stick to the left to avoid an uber caret.
  1761     nsCOMPtr<nsIDOMNode> siblingNode;
  1762     brNode->GetNextSibling(getter_AddRefs(siblingNode));
  1763     if (siblingNode && IsBlockNode(siblingNode)) {
  1764       selPriv->SetInterlinePosition(false);
  1765     } else {
  1766       selPriv->SetInterlinePosition(true);
  1768     res = aSelection->Collapse(node, aOffset+1);
  1770   return res;
  1773 nsresult
  1774 nsHTMLEditRules::DidInsertBreak(nsISelection *aSelection, nsresult aResult)
  1776   return NS_OK;
  1780 nsresult
  1781 nsHTMLEditRules::SplitMailCites(nsISelection *aSelection, bool aPlaintext, bool *aHandled)
  1783   NS_ENSURE_TRUE(aSelection && aHandled, NS_ERROR_NULL_POINTER);
  1784   nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(aSelection));
  1785   nsCOMPtr<nsIDOMNode> citeNode, selNode, leftCite, rightCite;
  1786   int32_t selOffset, newOffset;
  1787   NS_ENSURE_STATE(mHTMLEditor);
  1788   nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
  1789   NS_ENSURE_SUCCESS(res, res);
  1790   res = GetTopEnclosingMailCite(selNode, address_of(citeNode), aPlaintext);
  1791   NS_ENSURE_SUCCESS(res, res);
  1792   if (citeNode)
  1794     // If our selection is just before a break, nudge it to be
  1795     // just after it.  This does two things for us.  It saves us the trouble of having to add
  1796     // a break here ourselves to preserve the "blockness" of the inline span mailquote
  1797     // (in the inline case), and :
  1798     // it means the break won't end up making an empty line that happens to be inside a
  1799     // mailquote (in either inline or block case).  
  1800     // The latter can confuse a user if they click there and start typing,
  1801     // because being in the mailquote may affect wrapping behavior, or font color, etc.
  1802     NS_ENSURE_STATE(mHTMLEditor);
  1803     nsWSRunObject wsObj(mHTMLEditor, selNode, selOffset);
  1804     nsCOMPtr<nsIDOMNode> visNode;
  1805     int32_t visOffset=0;
  1806     WSType wsType;
  1807     wsObj.NextVisibleNode(selNode, selOffset, address_of(visNode),
  1808                           &visOffset, &wsType);
  1809     if (wsType == WSType::br) {
  1810       // ok, we are just before a break.  is it inside the mailquote?
  1811       int32_t unused;
  1812       if (nsEditorUtils::IsDescendantOf(visNode, citeNode, &unused))
  1814         // it is.  so lets reset our selection to be just after it.
  1815         NS_ENSURE_STATE(mHTMLEditor);
  1816         selNode = mHTMLEditor->GetNodeLocation(visNode, &selOffset);
  1817         ++selOffset;
  1821     nsCOMPtr<nsIDOMNode> brNode;
  1822     NS_ENSURE_STATE(mHTMLEditor);
  1823     res = mHTMLEditor->SplitNodeDeep(citeNode, selNode, selOffset, &newOffset, 
  1824                        true, address_of(leftCite), address_of(rightCite));
  1825     NS_ENSURE_SUCCESS(res, res);
  1826     res = citeNode->GetParentNode(getter_AddRefs(selNode));
  1827     NS_ENSURE_SUCCESS(res, res);
  1828     NS_ENSURE_STATE(mHTMLEditor);
  1829     res = mHTMLEditor->CreateBR(selNode, newOffset, address_of(brNode));
  1830     NS_ENSURE_SUCCESS(res, res);
  1831     // want selection before the break, and on same line
  1832     selPriv->SetInterlinePosition(true);
  1833     res = aSelection->Collapse(selNode, newOffset);
  1834     NS_ENSURE_SUCCESS(res, res);
  1835     // if citeNode wasn't a block, we might also want another break before it.
  1836     // We need to examine the content both before the br we just added and also
  1837     // just after it.  If we don't have another br or block boundary adjacent,
  1838     // then we will need a 2nd br added to achieve blank line that user expects.
  1839     if (IsInlineNode(citeNode))
  1841       NS_ENSURE_STATE(mHTMLEditor);
  1842       nsWSRunObject wsObj(mHTMLEditor, selNode, newOffset);
  1843       nsCOMPtr<nsIDOMNode> visNode;
  1844       int32_t visOffset=0;
  1845       WSType wsType;
  1846       wsObj.PriorVisibleNode(selNode, newOffset, address_of(visNode),
  1847                              &visOffset, &wsType);
  1848       if (wsType == WSType::normalWS || wsType == WSType::text ||
  1849           wsType == WSType::special) {
  1850         NS_ENSURE_STATE(mHTMLEditor);
  1851         nsWSRunObject wsObjAfterBR(mHTMLEditor, selNode, newOffset+1);
  1852         wsObjAfterBR.NextVisibleNode(selNode, newOffset+1, address_of(visNode),
  1853                                      &visOffset, &wsType);
  1854         if (wsType == WSType::normalWS || wsType == WSType::text ||
  1855             wsType == WSType::special) {
  1856           NS_ENSURE_STATE(mHTMLEditor);
  1857           res = mHTMLEditor->CreateBR(selNode, newOffset, address_of(brNode));
  1858           NS_ENSURE_SUCCESS(res, res);
  1862     // delete any empty cites
  1863     bool bEmptyCite = false;
  1864     if (leftCite)
  1866       NS_ENSURE_STATE(mHTMLEditor);
  1867       res = mHTMLEditor->IsEmptyNode(leftCite, &bEmptyCite, true, false);
  1868       if (NS_SUCCEEDED(res) && bEmptyCite) {
  1869         NS_ENSURE_STATE(mHTMLEditor);
  1870         res = mHTMLEditor->DeleteNode(leftCite);
  1872       NS_ENSURE_SUCCESS(res, res);
  1874     if (rightCite)
  1876       NS_ENSURE_STATE(mHTMLEditor);
  1877       res = mHTMLEditor->IsEmptyNode(rightCite, &bEmptyCite, true, false);
  1878       if (NS_SUCCEEDED(res) && bEmptyCite) {
  1879         NS_ENSURE_STATE(mHTMLEditor);
  1880         res = mHTMLEditor->DeleteNode(rightCite);
  1882       NS_ENSURE_SUCCESS(res, res);
  1884     *aHandled = true;
  1886   return NS_OK;
  1890 nsresult
  1891 nsHTMLEditRules::WillDeleteSelection(Selection* aSelection,
  1892                                      nsIEditor::EDirection aAction,
  1893                                      nsIEditor::EStripWrappers aStripWrappers,
  1894                                      bool* aCancel,
  1895                                      bool* aHandled)
  1897   MOZ_ASSERT(aStripWrappers == nsIEditor::eStrip ||
  1898              aStripWrappers == nsIEditor::eNoStrip);
  1900   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
  1901   // initialize out param
  1902   *aCancel = false;
  1903   *aHandled = false;
  1905   // remember that we did a selection deletion.  Used by CreateStyleForInsertText()
  1906   mDidDeleteSelection = true;
  1908   // if there is only bogus content, cancel the operation
  1909   if (mBogusNode) 
  1911     *aCancel = true;
  1912     return NS_OK;
  1915   bool bCollapsed = aSelection->Collapsed(), join = false;
  1917   // origCollapsed is used later to determine whether we should join 
  1918   // blocks. We don't really care about bCollapsed because it will be 
  1919   // modified by ExtendSelectionForDelete later. JoinBlocks should 
  1920   // happen if the original selection is collapsed and the cursor is 
  1921   // at the end of a block element, in which case ExtendSelectionForDelete 
  1922   // would always make the selection not collapsed.
  1923   bool origCollapsed = bCollapsed;
  1924   nsCOMPtr<nsIDOMNode> startNode, selNode;
  1925   int32_t startOffset, selOffset;
  1927   // first check for table selection mode.  If so,
  1928   // hand off to table editor.
  1929   nsCOMPtr<nsIDOMElement> cell;
  1930   NS_ENSURE_STATE(mHTMLEditor);
  1931   nsresult res = mHTMLEditor->GetFirstSelectedCell(nullptr, getter_AddRefs(cell));
  1932   if (NS_SUCCEEDED(res) && cell) {
  1933     NS_ENSURE_STATE(mHTMLEditor);
  1934     res = mHTMLEditor->DeleteTableCellContents();
  1935     *aHandled = true;
  1936     return res;
  1938   cell = nullptr;
  1940   NS_ENSURE_STATE(mHTMLEditor);
  1941   res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset);
  1942   NS_ENSURE_SUCCESS(res, res);
  1943   NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
  1945   if (bCollapsed)
  1947     // if we are inside an empty block, delete it.
  1948     NS_ENSURE_STATE(mHTMLEditor);
  1949     nsCOMPtr<nsIContent> hostContent = mHTMLEditor->GetActiveEditingHost();
  1950     nsCOMPtr<nsIDOMNode> hostNode = do_QueryInterface(hostContent);
  1951     NS_ENSURE_TRUE(hostNode, NS_ERROR_FAILURE);
  1952     res = CheckForEmptyBlock(startNode, hostNode, aSelection, aHandled);
  1953     NS_ENSURE_SUCCESS(res, res);
  1954     if (*aHandled) return NS_OK;
  1956     // Test for distance between caret and text that will be deleted
  1957     res = CheckBidiLevelForDeletion(aSelection, startNode, startOffset, aAction, aCancel);
  1958     NS_ENSURE_SUCCESS(res, res);
  1959     if (*aCancel) return NS_OK;
  1961     NS_ENSURE_STATE(mHTMLEditor);
  1962     res = mHTMLEditor->ExtendSelectionForDelete(aSelection, &aAction);
  1963     NS_ENSURE_SUCCESS(res, res);
  1965     // We should delete nothing.
  1966     if (aAction == nsIEditor::eNone)
  1967       return NS_OK;
  1969     // ExtendSelectionForDelete() may have changed the selection, update it
  1970     NS_ENSURE_STATE(mHTMLEditor);
  1971     res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset);
  1972     NS_ENSURE_SUCCESS(res, res);
  1973     NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
  1975     bCollapsed = aSelection->Collapsed();
  1978   if (bCollapsed)
  1980     // what's in the direction we are deleting?
  1981     NS_ENSURE_STATE(mHTMLEditor);
  1982     nsWSRunObject wsObj(mHTMLEditor, startNode, startOffset);
  1983     nsCOMPtr<nsIDOMNode> visNode;
  1984     int32_t visOffset;
  1985     WSType wsType;
  1987     // find next visible node
  1988     if (aAction == nsIEditor::eNext)
  1989       wsObj.NextVisibleNode(startNode, startOffset, address_of(visNode),
  1990                             &visOffset, &wsType);
  1991     else
  1992       wsObj.PriorVisibleNode(startNode, startOffset, address_of(visNode),
  1993                              &visOffset, &wsType);
  1995     if (!visNode) // can't find anything to delete!
  1997       *aCancel = true;
  1998       return res;
  2001     if (wsType == WSType::normalWS) {
  2002       // we found some visible ws to delete.  Let ws code handle it.
  2003       if (aAction == nsIEditor::eNext)
  2004         res = wsObj.DeleteWSForward();
  2005       else
  2006         res = wsObj.DeleteWSBackward();
  2007       *aHandled = true;
  2008       NS_ENSURE_SUCCESS(res, res);
  2009       res = InsertBRIfNeeded(aSelection);
  2010       return res;
  2011     } else if (wsType == WSType::text) {
  2012       // found normal text to delete.  
  2013       int32_t so = visOffset;
  2014       int32_t eo = visOffset+1;
  2015       if (aAction == nsIEditor::ePrevious) 
  2017         if (so == 0) return NS_ERROR_UNEXPECTED;
  2018         so--; 
  2019         eo--; 
  2021       else
  2023         nsCOMPtr<nsIDOMRange> range;
  2024         res = aSelection->GetRangeAt(0, getter_AddRefs(range));
  2025         NS_ENSURE_SUCCESS(res, res);
  2027 #ifdef DEBUG
  2028         nsIDOMNode *container;
  2030         res = range->GetStartContainer(&container);
  2031         NS_ENSURE_SUCCESS(res, res);
  2032         NS_ASSERTION(container == visNode, "selection start not in visNode");
  2034         res = range->GetEndContainer(&container);
  2035         NS_ENSURE_SUCCESS(res, res);
  2036         NS_ASSERTION(container == visNode, "selection end not in visNode");
  2037 #endif
  2039         res = range->GetStartOffset(&so);
  2040         NS_ENSURE_SUCCESS(res, res);
  2041         res = range->GetEndOffset(&eo);
  2042         NS_ENSURE_SUCCESS(res, res);
  2044       NS_ENSURE_STATE(mHTMLEditor);
  2045       res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(visNode), &so, address_of(visNode), &eo);
  2046       NS_ENSURE_SUCCESS(res, res);
  2047       nsCOMPtr<nsIDOMCharacterData> nodeAsText(do_QueryInterface(visNode));
  2048       NS_ENSURE_STATE(mHTMLEditor);
  2049       res = mHTMLEditor->DeleteText(nodeAsText, std::min(so, eo), DeprecatedAbs(eo - so));
  2050       *aHandled = true;
  2051       NS_ENSURE_SUCCESS(res, res);    
  2052       res = InsertBRIfNeeded(aSelection);
  2053       return res;
  2054     } else if (wsType == WSType::special || wsType == WSType::br ||
  2055                nsHTMLEditUtils::IsHR(visNode)) {
  2056       // short circuit for invisible breaks.  delete them and recurse.
  2057       if (nsTextEditUtils::IsBreak(visNode) &&
  2058           (!mHTMLEditor || !mHTMLEditor->IsVisBreak(visNode)))
  2060         NS_ENSURE_STATE(mHTMLEditor);
  2061         res = mHTMLEditor->DeleteNode(visNode);
  2062         NS_ENSURE_SUCCESS(res, res);
  2063         return WillDeleteSelection(aSelection, aAction, aStripWrappers,
  2064                                    aCancel, aHandled);
  2067       // special handling for backspace when positioned after <hr>
  2068       if (aAction == nsIEditor::ePrevious && nsHTMLEditUtils::IsHR(visNode))
  2070         /*
  2071           Only if the caret is positioned at the end-of-hr-line position,
  2072           we want to delete the <hr>.
  2074           In other words, we only want to delete, if
  2075           our selection position (indicated by startNode and startOffset)
  2076           is the position directly after the <hr>,
  2077           on the same line as the <hr>.
  2079           To detect this case we check:
  2080           startNode == parentOfVisNode
  2081           and
  2082           startOffset -1 == visNodeOffsetToVisNodeParent
  2083           and
  2084           interline position is false (left)
  2086           In any other case we set the position to 
  2087           startnode -1 and interlineposition to false,
  2088           only moving the caret to the end-of-hr-line position.
  2089         */
  2091         bool moveOnly = true;
  2093         selNode = nsEditor::GetNodeLocation(visNode, &selOffset);
  2095         bool interLineIsRight;
  2096         res = aSelection->GetInterlinePosition(&interLineIsRight);
  2097         NS_ENSURE_SUCCESS(res, res);
  2099         if (startNode == selNode &&
  2100             startOffset -1 == selOffset &&
  2101             !interLineIsRight)
  2103           moveOnly = false;
  2106         if (moveOnly)
  2108           // Go to the position after the <hr>, but to the end of the <hr> line
  2109           // by setting the interline position to left.
  2110           ++selOffset;
  2111           res = aSelection->Collapse(selNode, selOffset);
  2112           aSelection->SetInterlinePosition(false);
  2113           mDidExplicitlySetInterline = true;
  2114           *aHandled = true;
  2116           // There is one exception to the move only case.
  2117           // If the <hr> is followed by a <br> we want to delete the <br>.
  2119           WSType otherWSType;
  2120           nsCOMPtr<nsIDOMNode> otherNode;
  2121           int32_t otherOffset;
  2123           wsObj.NextVisibleNode(startNode, startOffset, address_of(otherNode),
  2124                                 &otherOffset, &otherWSType);
  2126           if (otherWSType == WSType::br) {
  2127             // Delete the <br>
  2129             NS_ENSURE_STATE(mHTMLEditor);
  2130             res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, otherNode);
  2131             NS_ENSURE_SUCCESS(res, res);
  2132             NS_ENSURE_STATE(mHTMLEditor);
  2133             res = mHTMLEditor->DeleteNode(otherNode);
  2134             NS_ENSURE_SUCCESS(res, res);
  2137           return NS_OK;
  2139         // else continue with normal delete code
  2142       // found break or image, or hr.  
  2143       NS_ENSURE_STATE(mHTMLEditor);
  2144       res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, visNode);
  2145       NS_ENSURE_SUCCESS(res, res);
  2146       // remember sibling to visnode, if any
  2147       nsCOMPtr<nsIDOMNode> sibling, stepbrother;
  2148       NS_ENSURE_STATE(mHTMLEditor);
  2149       mHTMLEditor->GetPriorHTMLSibling(visNode, address_of(sibling));
  2150       // delete the node, and join like nodes if appropriate
  2151       NS_ENSURE_STATE(mHTMLEditor);
  2152       res = mHTMLEditor->DeleteNode(visNode);
  2153       NS_ENSURE_SUCCESS(res, res);
  2154       // we did something, so lets say so.
  2155       *aHandled = true;
  2156       // is there a prior node and are they siblings?
  2157       if (sibling) {
  2158         NS_ENSURE_STATE(mHTMLEditor);
  2159         mHTMLEditor->GetNextHTMLSibling(sibling, address_of(stepbrother));
  2161       if (startNode == stepbrother) 
  2163         // are they both text nodes?
  2164         NS_ENSURE_STATE(mHTMLEditor);
  2165         if (mHTMLEditor->IsTextNode(startNode) &&
  2166             (!mHTMLEditor || mHTMLEditor->IsTextNode(sibling)))
  2168           NS_ENSURE_STATE(mHTMLEditor);
  2169           // if so, join them!
  2170           res = JoinNodesSmart(sibling, startNode, address_of(selNode), &selOffset);
  2171           NS_ENSURE_SUCCESS(res, res);
  2172           // fix up selection
  2173           res = aSelection->Collapse(selNode, selOffset);
  2176       NS_ENSURE_SUCCESS(res, res);    
  2177       res = InsertBRIfNeeded(aSelection);
  2178       return res;
  2179     } else if (wsType == WSType::otherBlock) {
  2180       // make sure it's not a table element.  If so, cancel the operation 
  2181       // (translation: users cannot backspace or delete across table cells)
  2182       if (nsHTMLEditUtils::IsTableElement(visNode))
  2184         *aCancel = true;
  2185         return NS_OK;
  2188       // next to a block.  See if we are between a block and a br.  If so, we really
  2189       // want to delete the br.  Else join content at selection to the block.
  2191       bool bDeletedBR = false;
  2192       WSType otherWSType;
  2193       nsCOMPtr<nsIDOMNode> otherNode;
  2194       int32_t otherOffset;
  2196       // find node in other direction
  2197       if (aAction == nsIEditor::eNext)
  2198         wsObj.PriorVisibleNode(startNode, startOffset, address_of(otherNode),
  2199                                &otherOffset, &otherWSType);
  2200       else
  2201         wsObj.NextVisibleNode(startNode, startOffset, address_of(otherNode),
  2202                               &otherOffset, &otherWSType);
  2204       // first find the adjacent node in the block
  2205       nsCOMPtr<nsIDOMNode> leafNode, leftNode, rightNode;
  2206       if (aAction == nsIEditor::ePrevious) 
  2208         NS_ENSURE_STATE(mHTMLEditor);
  2209         res = mHTMLEditor->GetLastEditableLeaf( visNode, address_of(leafNode));
  2210         NS_ENSURE_SUCCESS(res, res);
  2211         leftNode = leafNode;
  2212         rightNode = startNode;
  2214       else
  2216         NS_ENSURE_STATE(mHTMLEditor);
  2217         res = mHTMLEditor->GetFirstEditableLeaf( visNode, address_of(leafNode));
  2218         NS_ENSURE_SUCCESS(res, res);
  2219         leftNode = startNode;
  2220         rightNode = leafNode;
  2223       if (nsTextEditUtils::IsBreak(otherNode))
  2225         NS_ENSURE_STATE(mHTMLEditor);
  2226         res = mHTMLEditor->DeleteNode(otherNode);
  2227         NS_ENSURE_SUCCESS(res, res);
  2228         *aHandled = true;
  2229         bDeletedBR = true;
  2232       // don't cross table boundaries
  2233       if (leftNode && rightNode && InDifferentTableElements(leftNode, rightNode)) {
  2234         return NS_OK;
  2237       if (bDeletedBR)
  2239         // put selection at edge of block and we are done.
  2240         nsCOMPtr<nsIDOMNode> newSelNode;
  2241         int32_t newSelOffset;
  2242         res = GetGoodSelPointForNode(leafNode, aAction, address_of(newSelNode), &newSelOffset);
  2243         NS_ENSURE_SUCCESS(res, res);
  2244         aSelection->Collapse(newSelNode, newSelOffset);
  2245         return res;
  2248       // else we are joining content to block
  2250       nsCOMPtr<nsIDOMNode> selPointNode = startNode;
  2251       int32_t selPointOffset = startOffset;
  2253         NS_ENSURE_STATE(mHTMLEditor);
  2254         nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(selPointNode), &selPointOffset);
  2255         res = JoinBlocks(leftNode, rightNode, aCancel);
  2256         *aHandled = true;
  2257         NS_ENSURE_SUCCESS(res, res);
  2259       aSelection->Collapse(selPointNode, selPointOffset);
  2260       return res;
  2261     } else if (wsType == WSType::thisBlock) {
  2262       // at edge of our block.  Look beside it and see if we can join to an adjacent block
  2264       // make sure it's not a table element.  If so, cancel the operation 
  2265       // (translation: users cannot backspace or delete across table cells)
  2266       if (nsHTMLEditUtils::IsTableElement(visNode))
  2268         *aCancel = true;
  2269         return NS_OK;
  2272       // first find the relavent nodes
  2273       nsCOMPtr<nsIDOMNode> leftNode, rightNode;
  2274       if (aAction == nsIEditor::ePrevious) 
  2276         NS_ENSURE_STATE(mHTMLEditor);
  2277         res = mHTMLEditor->GetPriorHTMLNode(visNode, address_of(leftNode));
  2278         NS_ENSURE_SUCCESS(res, res);
  2279         rightNode = startNode;
  2281       else
  2283         NS_ENSURE_STATE(mHTMLEditor);
  2284         res = mHTMLEditor->GetNextHTMLNode( visNode, address_of(rightNode));
  2285         NS_ENSURE_SUCCESS(res, res);
  2286         leftNode = startNode;
  2289       // nothing to join
  2290       if (!leftNode || !rightNode)
  2292         *aCancel = true;
  2293         return NS_OK;
  2296       // don't cross table boundaries -- cancel it
  2297       if (InDifferentTableElements(leftNode, rightNode)) {
  2298         *aCancel = true;
  2299         return NS_OK;
  2302       nsCOMPtr<nsIDOMNode> selPointNode = startNode;
  2303       int32_t selPointOffset = startOffset;
  2305         NS_ENSURE_STATE(mHTMLEditor);
  2306         nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(selPointNode), &selPointOffset);
  2307         res = JoinBlocks(leftNode, rightNode, aCancel);
  2308         *aHandled = true;
  2309         NS_ENSURE_SUCCESS(res, res);
  2311       aSelection->Collapse(selPointNode, selPointOffset);
  2312       return res;
  2317   // else we have a non collapsed selection
  2318   // first adjust the selection
  2319   res = ExpandSelectionForDeletion(aSelection);
  2320   NS_ENSURE_SUCCESS(res, res);
  2322   // remember that we did a ranged delete for the benefit of AfterEditInner().
  2323   mDidRangedDelete = true;
  2325   // refresh start and end points
  2326   NS_ENSURE_STATE(mHTMLEditor);
  2327   res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset);
  2328   NS_ENSURE_SUCCESS(res, res);
  2329   NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
  2330   nsCOMPtr<nsIDOMNode> endNode;
  2331   int32_t endOffset;
  2332   NS_ENSURE_STATE(mHTMLEditor);
  2333   res = mHTMLEditor->GetEndNodeAndOffset(aSelection, getter_AddRefs(endNode), &endOffset);
  2334   NS_ENSURE_SUCCESS(res, res); 
  2335   NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE);
  2337   // figure out if the endpoints are in nodes that can be merged  
  2338   // adjust surrounding whitespace in preperation to delete selection
  2339   if (!IsPlaintextEditor())
  2341     NS_ENSURE_STATE(mHTMLEditor);
  2342     nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
  2343     res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor,
  2344                                             address_of(startNode), &startOffset, 
  2345                                             address_of(endNode), &endOffset);
  2346     NS_ENSURE_SUCCESS(res, res); 
  2350     // track location of where we are deleting
  2351     NS_ENSURE_STATE(mHTMLEditor);
  2352     nsAutoTrackDOMPoint startTracker(mHTMLEditor->mRangeUpdater,
  2353                                      address_of(startNode), &startOffset);
  2354     nsAutoTrackDOMPoint endTracker(mHTMLEditor->mRangeUpdater,
  2355                                    address_of(endNode), &endOffset);
  2356     // we are handling all ranged deletions directly now.
  2357     *aHandled = true;
  2359     if (endNode == startNode)
  2361       NS_ENSURE_STATE(mHTMLEditor);
  2362       res = mHTMLEditor->DeleteSelectionImpl(aAction, aStripWrappers);
  2363       NS_ENSURE_SUCCESS(res, res); 
  2365     else
  2367       // figure out mailcite ancestors
  2368       nsCOMPtr<nsIDOMNode> endCiteNode, startCiteNode;
  2369       res = GetTopEnclosingMailCite(startNode, address_of(startCiteNode), 
  2370                                     IsPlaintextEditor());
  2371       NS_ENSURE_SUCCESS(res, res); 
  2372       res = GetTopEnclosingMailCite(endNode, address_of(endCiteNode), 
  2373                                     IsPlaintextEditor());
  2374       NS_ENSURE_SUCCESS(res, res); 
  2376       // if we only have a mailcite at one of the two endpoints, set the directionality
  2377       // of the deletion so that the selection will end up outside the mailcite.
  2378       if (startCiteNode && !endCiteNode)
  2380         aAction = nsIEditor::eNext;
  2382       else if (!startCiteNode && endCiteNode)
  2384         aAction = nsIEditor::ePrevious;
  2387       // figure out block parents
  2388       nsCOMPtr<nsIDOMNode> leftParent;
  2389       nsCOMPtr<nsIDOMNode> rightParent;
  2390       if (IsBlockNode(startNode))
  2391         leftParent = startNode;
  2392       else {
  2393         NS_ENSURE_STATE(mHTMLEditor);
  2394         leftParent = mHTMLEditor->GetBlockNodeParent(startNode);
  2397       if (IsBlockNode(endNode))
  2398         rightParent = endNode;
  2399       else {
  2400         NS_ENSURE_STATE(mHTMLEditor);
  2401         rightParent = mHTMLEditor->GetBlockNodeParent(endNode);
  2404       // are endpoint block parents the same?  use default deletion
  2405       if (leftParent == rightParent) 
  2407         NS_ENSURE_STATE(mHTMLEditor);
  2408         res = mHTMLEditor->DeleteSelectionImpl(aAction, aStripWrappers);
  2410       else
  2412         // deleting across blocks
  2413         // are the blocks of same type?
  2414         NS_ENSURE_STATE(leftParent && rightParent);
  2416         // are the blocks siblings?
  2417         nsCOMPtr<nsIDOMNode> leftBlockParent;
  2418         nsCOMPtr<nsIDOMNode> rightBlockParent;
  2419         leftParent->GetParentNode(getter_AddRefs(leftBlockParent));
  2420         rightParent->GetParentNode(getter_AddRefs(rightBlockParent));
  2422         // MOOSE: this could conceivably screw up a table.. fix me.
  2423         if (   (leftBlockParent == rightBlockParent)
  2424             && (!mHTMLEditor || mHTMLEditor->NodesSameType(leftParent, rightParent))  )
  2426           NS_ENSURE_STATE(mHTMLEditor);
  2427           if (nsHTMLEditUtils::IsParagraph(leftParent))
  2429             // first delete the selection
  2430             NS_ENSURE_STATE(mHTMLEditor);
  2431             res = mHTMLEditor->DeleteSelectionImpl(aAction, aStripWrappers);
  2432             NS_ENSURE_SUCCESS(res, res);
  2433             // then join para's, insert break
  2434             NS_ENSURE_STATE(mHTMLEditor);
  2435             res = mHTMLEditor->JoinNodeDeep(leftParent,rightParent,address_of(selNode),&selOffset);
  2436             NS_ENSURE_SUCCESS(res, res);
  2437             // fix up selection
  2438             res = aSelection->Collapse(selNode,selOffset);
  2439             return res;
  2441           if (nsHTMLEditUtils::IsListItem(leftParent)
  2442               || nsHTMLEditUtils::IsHeader(leftParent))
  2444             // first delete the selection
  2445             NS_ENSURE_STATE(mHTMLEditor);
  2446             res = mHTMLEditor->DeleteSelectionImpl(aAction, aStripWrappers);
  2447             NS_ENSURE_SUCCESS(res, res);
  2448             // join blocks
  2449             NS_ENSURE_STATE(mHTMLEditor);
  2450             res = mHTMLEditor->JoinNodeDeep(leftParent,rightParent,address_of(selNode),&selOffset);
  2451             NS_ENSURE_SUCCESS(res, res);
  2452             // fix up selection
  2453             res = aSelection->Collapse(selNode,selOffset);
  2454             return res;
  2458         // else blocks not same type, or not siblings.  Delete everything except
  2459         // table elements.
  2460         join = true;
  2462         uint32_t rangeCount = aSelection->GetRangeCount();
  2463         for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
  2464           nsRefPtr<nsRange> range = aSelection->GetRangeAt(rangeIdx);
  2466           // build a list of nodes in the range
  2467           nsCOMArray<nsIDOMNode> arrayOfNodes;
  2468           nsTrivialFunctor functor;
  2469           nsDOMSubtreeIterator iter;
  2470           res = iter.Init(range);
  2471           NS_ENSURE_SUCCESS(res, res);
  2472           res = iter.AppendList(functor, arrayOfNodes);
  2473           NS_ENSURE_SUCCESS(res, res);
  2475           // now that we have the list, delete non table elements
  2476           int32_t listCount = arrayOfNodes.Count();
  2477           for (int32_t j = 0; j < listCount; j++) {
  2478             nsCOMPtr<nsINode> somenode = do_QueryInterface(arrayOfNodes[0]);
  2479             NS_ENSURE_STATE(somenode);
  2480             DeleteNonTableElements(somenode);
  2481             arrayOfNodes.RemoveObjectAt(0);
  2482             // If something visible is deleted, no need to join.
  2483             // Visible means all nodes except non-visible textnodes and breaks.
  2484             if (join && origCollapsed) {
  2485               if (!somenode->IsContent()) {
  2486                 join = false;
  2487                 continue;
  2489               nsCOMPtr<nsIContent> content = somenode->AsContent();
  2490               if (content->NodeType() == nsIDOMNode::TEXT_NODE) {
  2491                 NS_ENSURE_STATE(mHTMLEditor);
  2492                 mHTMLEditor->IsVisTextNode(content, &join, true);
  2493               } else {
  2494                 NS_ENSURE_STATE(mHTMLEditor);
  2495                 join = content->IsHTML(nsGkAtoms::br) &&
  2496                        !mHTMLEditor->IsVisBreak(somenode->AsDOMNode());
  2502         // check endopints for possible text deletion.
  2503         // we can assume that if text node is found, we can
  2504         // delete to end or to begining as appropriate,
  2505         // since the case where both sel endpoints in same
  2506         // text node was already handled (we wouldn't be here)
  2507         NS_ENSURE_STATE(mHTMLEditor);
  2508         if ( mHTMLEditor->IsTextNode(startNode) )
  2510           // delete to last character
  2511           nsCOMPtr<nsIDOMCharacterData>nodeAsText;
  2512           uint32_t len;
  2513           nodeAsText = do_QueryInterface(startNode);
  2514           nodeAsText->GetLength(&len);
  2515           if (len > (uint32_t)startOffset)
  2517             NS_ENSURE_STATE(mHTMLEditor);
  2518             res = mHTMLEditor->DeleteText(nodeAsText,startOffset,len-startOffset);
  2519             NS_ENSURE_SUCCESS(res, res);
  2522         NS_ENSURE_STATE(mHTMLEditor);
  2523         if ( mHTMLEditor->IsTextNode(endNode) )
  2525           // delete to first character
  2526           nsCOMPtr<nsIDOMCharacterData>nodeAsText;
  2527           nodeAsText = do_QueryInterface(endNode);
  2528           if (endOffset)
  2530             NS_ENSURE_STATE(mHTMLEditor);
  2531             res = mHTMLEditor->DeleteText(nodeAsText,0,endOffset);
  2532             NS_ENSURE_SUCCESS(res, res);
  2536         if (join) {
  2537           res = JoinBlocks(leftParent, rightParent, aCancel);
  2538           NS_ENSURE_SUCCESS(res, res);
  2543   //If we're joining blocks: if deleting forward the selection should be 
  2544   //collapsed to the end of the selection, if deleting backward the selection 
  2545   //should be collapsed to the beginning of the selection. But if we're not 
  2546   //joining then the selection should collapse to the beginning of the 
  2547   //selection if we'redeleting forward, because the end of the selection will 
  2548   //still be in the next block. And same thing for deleting backwards 
  2549   //(selection should collapse to the end, because the beginning will still 
  2550   //be in the first block). See Bug 507936
  2551   if (join ? aAction == nsIEditor::eNext : aAction == nsIEditor::ePrevious)
  2553     res = aSelection->Collapse(endNode,endOffset);
  2555   else
  2557     res = aSelection->Collapse(startNode,startOffset);
  2559   return res;
  2563 /*****************************************************************************************************
  2564 *    InsertBRIfNeeded: determines if a br is needed for current selection to not be spastic.
  2565 *    If so, it inserts one.  Callers responsibility to only call with collapsed selection.
  2566 *         nsISelection *aSelection      the collapsed selection 
  2567 */
  2568 nsresult
  2569 nsHTMLEditRules::InsertBRIfNeeded(nsISelection *aSelection)
  2571   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
  2573   // get selection  
  2574   nsCOMPtr<nsIDOMNode> node;
  2575   int32_t offset;
  2576   nsresult res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(node), &offset);
  2577   NS_ENSURE_SUCCESS(res, res);
  2578   NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
  2580   // inline elements don't need any br
  2581   if (!IsBlockNode(node))
  2582     return res;
  2584   // examine selection
  2585   NS_ENSURE_STATE(mHTMLEditor);
  2586   nsWSRunObject wsObj(mHTMLEditor, node, offset);
  2587   if (((wsObj.mStartReason & WSType::block) ||
  2588        (wsObj.mStartReason & WSType::br)) &&
  2589       (wsObj.mEndReason & WSType::block)) {
  2590     // if we are tucked between block boundaries then insert a br
  2591     // first check that we are allowed to
  2592     NS_ENSURE_STATE(mHTMLEditor);
  2593     if (mHTMLEditor->CanContainTag(node, nsGkAtoms::br)) {
  2594       nsCOMPtr<nsIDOMNode> brNode;
  2595       NS_ENSURE_STATE(mHTMLEditor);
  2596       res = mHTMLEditor->CreateBR(node, offset, address_of(brNode), nsIEditor::ePrevious);
  2599   return res;
  2602 /*****************************************************************************************************
  2603 *    GetGoodSelPointForNode: Finds where at a node you would want to set the selection if you were
  2604 *    trying to have a caret next to it.
  2605 *         nsIDOMNode *aNode                  the node 
  2606 *         nsIEditor::EDirection aAction      which edge to find: eNext indicates beginning, ePrevious ending
  2607 *         nsCOMPtr<nsIDOMNode> *outSelNode   desired sel node
  2608 *         int32_t *outSelOffset              desired sel offset
  2609 */
  2610 nsresult
  2611 nsHTMLEditRules::GetGoodSelPointForNode(nsIDOMNode *aNode, nsIEditor::EDirection aAction, 
  2612                                         nsCOMPtr<nsIDOMNode> *outSelNode, int32_t *outSelOffset)
  2614   NS_ENSURE_TRUE(aNode && outSelNode && outSelOffset, NS_ERROR_NULL_POINTER);
  2616   nsresult res = NS_OK;
  2618   // default values
  2619   *outSelNode = aNode;
  2620   *outSelOffset = 0;
  2622   NS_ENSURE_STATE(mHTMLEditor);
  2623   if (mHTMLEditor->IsTextNode(aNode) ||
  2624       !mHTMLEditor || mHTMLEditor->IsContainer(aNode))
  2626     NS_ENSURE_STATE(mHTMLEditor);
  2627     if (aAction == nsIEditor::ePrevious)
  2629       uint32_t len;
  2630       res = mHTMLEditor->GetLengthOfDOMNode(aNode, len);
  2631       *outSelOffset = int32_t(len);
  2632       NS_ENSURE_SUCCESS(res, res);
  2635   else 
  2637     *outSelNode = nsEditor::GetNodeLocation(aNode, outSelOffset);
  2638     if (!nsTextEditUtils::IsBreak(aNode) ||
  2639         !mHTMLEditor || mHTMLEditor->IsVisBreak(aNode))
  2641       NS_ENSURE_STATE(mHTMLEditor);
  2642       if (aAction == nsIEditor::ePrevious)
  2643         (*outSelOffset)++;
  2646   return res;
  2650 /*****************************************************************************************************
  2651 *    JoinBlocks: this method is used to join two block elements.  The right element is always joined
  2652 *    to the left element.  If the elements are the same type and not nested within each other, 
  2653 *    JoinNodesSmart is called (example, joining two list items together into one).  If the elements
  2654 *    are not the same type, or one is a descendant of the other, we instead destroy the right block
  2655 *    placing its children into leftblock.  DTD containment rules are followed throughout.
  2656 *         nsCOMPtr<nsIDOMNode> *aLeftBlock         pointer to the left block
  2657 *         nsCOMPtr<nsIDOMNode> *aRightBlock        pointer to the right block; will have contents moved to left block
  2658 *         bool *aCanceled                        return TRUE if we had to cancel operation
  2659 */
  2660 nsresult
  2661 nsHTMLEditRules::JoinBlocks(nsIDOMNode *aLeftNode,
  2662                             nsIDOMNode *aRightNode,
  2663                             bool *aCanceled)
  2665   NS_ENSURE_ARG_POINTER(aLeftNode && aRightNode);
  2667   nsCOMPtr<nsIDOMNode> aLeftBlock, aRightBlock;
  2669   if (IsBlockNode(aLeftNode)) {
  2670     aLeftBlock = aLeftNode;
  2671   } else if (aLeftNode) {
  2672     NS_ENSURE_STATE(mHTMLEditor);
  2673     aLeftBlock = mHTMLEditor->GetBlockNodeParent(aLeftNode);
  2676   if (IsBlockNode(aRightNode)) {
  2677     aRightBlock = aRightNode;
  2678   } else if (aRightNode) {
  2679     NS_ENSURE_STATE(mHTMLEditor);
  2680     aRightBlock = mHTMLEditor->GetBlockNodeParent(aRightNode);
  2683   // sanity checks
  2684   NS_ENSURE_TRUE(aLeftBlock && aRightBlock, NS_ERROR_NULL_POINTER);
  2685   NS_ENSURE_STATE(aLeftBlock != aRightBlock);
  2687   if (nsHTMLEditUtils::IsTableElement(aLeftBlock) ||
  2688       nsHTMLEditUtils::IsTableElement(aRightBlock)) {
  2689     // do not try to merge table elements
  2690     *aCanceled = true;
  2691     return NS_OK;
  2694   // make sure we don't try to move thing's into HR's, which look like blocks but aren't containers
  2695   if (nsHTMLEditUtils::IsHR(aLeftBlock)) {
  2696     NS_ENSURE_STATE(mHTMLEditor);
  2697     nsCOMPtr<nsIDOMNode> realLeft = mHTMLEditor->GetBlockNodeParent(aLeftBlock);
  2698     aLeftBlock = realLeft;
  2700   if (nsHTMLEditUtils::IsHR(aRightBlock)) {
  2701     NS_ENSURE_STATE(mHTMLEditor);
  2702     nsCOMPtr<nsIDOMNode> realRight = mHTMLEditor->GetBlockNodeParent(aRightBlock);
  2703     aRightBlock = realRight;
  2706   // bail if both blocks the same
  2707   if (aLeftBlock == aRightBlock) {
  2708     *aCanceled = true;
  2709     return NS_OK;
  2712   // Joining a list item to its parent is a NOP.
  2713   if (nsHTMLEditUtils::IsList(aLeftBlock) &&
  2714       nsHTMLEditUtils::IsListItem(aRightBlock)) {
  2715     nsCOMPtr<nsIDOMNode> rightParent;
  2716     aRightBlock->GetParentNode(getter_AddRefs(rightParent));
  2717     if (rightParent == aLeftBlock) {
  2718       return NS_OK;
  2722   // special rule here: if we are trying to join list items, and they are in different lists,
  2723   // join the lists instead.
  2724   bool bMergeLists = false;
  2725   nsIAtom* existingList = nsGkAtoms::_empty;
  2726   int32_t theOffset;
  2727   nsCOMPtr<nsIDOMNode> leftList, rightList;
  2728   if (nsHTMLEditUtils::IsListItem(aLeftBlock) &&
  2729       nsHTMLEditUtils::IsListItem(aRightBlock)) {
  2730     aLeftBlock->GetParentNode(getter_AddRefs(leftList));
  2731     aRightBlock->GetParentNode(getter_AddRefs(rightList));
  2732     if (leftList && rightList && (leftList!=rightList))
  2734       // there are some special complications if the lists are descendants of
  2735       // the other lists' items.  Note that it is ok for them to be descendants
  2736       // of the other lists themselves, which is the usual case for sublists
  2737       // in our impllementation.
  2738       if (!nsEditorUtils::IsDescendantOf(leftList, aRightBlock, &theOffset) &&
  2739           !nsEditorUtils::IsDescendantOf(rightList, aLeftBlock, &theOffset))
  2741         aLeftBlock = leftList;
  2742         aRightBlock = rightList;
  2743         bMergeLists = true;
  2744         NS_ENSURE_STATE(mHTMLEditor);
  2745         existingList = mHTMLEditor->GetTag(leftList);
  2750   NS_ENSURE_STATE(mHTMLEditor);
  2751   nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
  2753   nsresult res = NS_OK;
  2754   int32_t  rightOffset = 0;
  2755   int32_t  leftOffset  = -1;
  2757   // theOffset below is where you find yourself in aRightBlock when you traverse upwards
  2758   // from aLeftBlock
  2759   if (nsEditorUtils::IsDescendantOf(aLeftBlock, aRightBlock, &rightOffset)) {
  2760     // tricky case.  left block is inside right block.
  2761     // Do ws adjustment.  This just destroys non-visible ws at boundaries we will be joining.
  2762     rightOffset++;
  2763     NS_ENSURE_STATE(mHTMLEditor);
  2764     res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
  2765                                             address_of(aLeftBlock),
  2766                                             nsWSRunObject::kBlockEnd);
  2767     NS_ENSURE_SUCCESS(res, res);
  2768     NS_ENSURE_STATE(mHTMLEditor);
  2769     res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
  2770                                             address_of(aRightBlock),
  2771                                             nsWSRunObject::kAfterBlock,
  2772                                             &rightOffset);
  2773     NS_ENSURE_SUCCESS(res, res);
  2774     // Do br adjustment.
  2775     nsCOMPtr<nsIDOMNode> brNode;
  2776     res = CheckForInvisibleBR(aLeftBlock, kBlockEnd, address_of(brNode));
  2777     NS_ENSURE_SUCCESS(res, res);
  2778     if (bMergeLists)
  2780       // idea here is to take all children in  rightList that are past
  2781       // theOffset, and pull them into leftlist.
  2782       nsCOMPtr<nsIDOMNode> childToMove;
  2783       nsCOMPtr<nsIContent> parent(do_QueryInterface(rightList));
  2784       NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
  2786       nsIContent *child = parent->GetChildAt(theOffset);
  2787       while (child)
  2789         childToMove = do_QueryInterface(child);
  2790         NS_ENSURE_STATE(mHTMLEditor);
  2791         res = mHTMLEditor->MoveNode(childToMove, leftList, -1);
  2792         NS_ENSURE_SUCCESS(res, res);
  2794         child = parent->GetChildAt(rightOffset);
  2797     else
  2799       res = MoveBlock(aLeftBlock, aRightBlock, leftOffset, rightOffset);
  2801     NS_ENSURE_STATE(mHTMLEditor);
  2802     if (brNode) mHTMLEditor->DeleteNode(brNode);
  2803   // theOffset below is where you find yourself in aLeftBlock when you traverse upwards
  2804   // from aRightBlock
  2805   } else if (nsEditorUtils::IsDescendantOf(aRightBlock, aLeftBlock, &leftOffset)) {
  2806     // tricky case.  right block is inside left block.
  2807     // Do ws adjustment.  This just destroys non-visible ws at boundaries we will be joining.
  2808     NS_ENSURE_STATE(mHTMLEditor);
  2809     res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
  2810                                             address_of(aRightBlock),
  2811                                             nsWSRunObject::kBlockStart);
  2812     NS_ENSURE_SUCCESS(res, res);
  2813     NS_ENSURE_STATE(mHTMLEditor);
  2814     res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
  2815                                             address_of(aLeftBlock),
  2816                                             nsWSRunObject::kBeforeBlock,
  2817                                             &leftOffset);
  2818     NS_ENSURE_SUCCESS(res, res);
  2819     // Do br adjustment.
  2820     nsCOMPtr<nsIDOMNode> brNode;
  2821     res = CheckForInvisibleBR(aLeftBlock, kBeforeBlock, address_of(brNode),
  2822                               leftOffset);
  2823     NS_ENSURE_SUCCESS(res, res);
  2824     if (bMergeLists)
  2826       res = MoveContents(rightList, leftList, &leftOffset);
  2828     else
  2830       // Left block is a parent of right block, and the parent of the previous
  2831       // visible content.  Right block is a child and contains the contents we
  2832       // want to move.
  2834       int32_t previousContentOffset;
  2835       nsCOMPtr<nsIDOMNode> previousContentParent;
  2837       if (aLeftNode == aLeftBlock) {
  2838         // We are working with valid HTML, aLeftNode is a block node, and is
  2839         // therefore allowed to contain aRightBlock.  This is the simple case,
  2840         // we will simply move the content in aRightBlock out of its block.
  2841         previousContentParent = aLeftBlock;
  2842         previousContentOffset = leftOffset;
  2843       } else {
  2844         // We try to work as well as possible with HTML that's already invalid.
  2845         // Although "right block" is a block, and a block must not be contained
  2846         // in inline elements, reality is that broken documents do exist.  The
  2847         // DIRECT parent of "left NODE" might be an inline element.  Previous
  2848         // versions of this code skipped inline parents until the first block
  2849         // parent was found (and used "left block" as the destination).
  2850         // However, in some situations this strategy moves the content to an
  2851         // unexpected position.  (see bug 200416) The new idea is to make the
  2852         // moving content a sibling, next to the previous visible content.
  2854         previousContentParent =
  2855           nsEditor::GetNodeLocation(aLeftNode, &previousContentOffset);
  2857         // We want to move our content just after the previous visible node.
  2858         previousContentOffset++;
  2861       // Because we don't want the moving content to receive the style of the
  2862       // previous content, we split the previous content's style.
  2864       NS_ENSURE_STATE(mHTMLEditor);
  2865       nsCOMPtr<nsINode> editorRoot = mHTMLEditor->GetEditorRoot();
  2866       if (!editorRoot || aLeftNode != editorRoot->AsDOMNode()) {
  2867         nsCOMPtr<nsIDOMNode> splittedPreviousContent;
  2868         NS_ENSURE_STATE(mHTMLEditor);
  2869         res = mHTMLEditor->SplitStyleAbovePoint(address_of(previousContentParent),
  2870                                                 &previousContentOffset,
  2871                                                 nullptr, nullptr, nullptr,
  2872                                                 address_of(splittedPreviousContent));
  2873         NS_ENSURE_SUCCESS(res, res);
  2875         if (splittedPreviousContent) {
  2876           previousContentParent =
  2877             nsEditor::GetNodeLocation(splittedPreviousContent,
  2878                                       &previousContentOffset);
  2882       res = MoveBlock(previousContentParent, aRightBlock,
  2883                       previousContentOffset, rightOffset);
  2885     NS_ENSURE_STATE(mHTMLEditor);
  2886     if (brNode) mHTMLEditor->DeleteNode(brNode);
  2888   else
  2890     // normal case.  blocks are siblings, or at least close enough to siblings.  An example
  2891     // of the latter is a <p>paragraph</p><ul><li>one<li>two<li>three</ul>.  The first
  2892     // li and the p are not true siblings, but we still want to join them if you backspace
  2893     // from li into p.
  2895     // adjust whitespace at block boundaries
  2896     NS_ENSURE_STATE(mHTMLEditor);
  2897     res = nsWSRunObject::PrepareToJoinBlocks(mHTMLEditor, aLeftBlock, aRightBlock);
  2898     NS_ENSURE_SUCCESS(res, res);
  2899     // Do br adjustment.
  2900     nsCOMPtr<nsIDOMNode> brNode;
  2901     res = CheckForInvisibleBR(aLeftBlock, kBlockEnd, address_of(brNode));
  2902     NS_ENSURE_SUCCESS(res, res);
  2903     NS_ENSURE_STATE(mHTMLEditor);
  2904     if (bMergeLists || mHTMLEditor->NodesSameType(aLeftBlock, aRightBlock)) {
  2905       // nodes are same type.  merge them.
  2906       nsCOMPtr<nsIDOMNode> parent;
  2907       int32_t offset;
  2908       res = JoinNodesSmart(aLeftBlock, aRightBlock, address_of(parent), &offset);
  2909       if (NS_SUCCEEDED(res) && bMergeLists)
  2911         nsCOMPtr<nsIDOMNode> newBlock;
  2912         res = ConvertListType(aRightBlock, address_of(newBlock),
  2913                               existingList, nsGkAtoms::li);
  2916     else
  2918       // nodes are disimilar types. 
  2919       res = MoveBlock(aLeftBlock, aRightBlock, leftOffset, rightOffset);
  2921     if (NS_SUCCEEDED(res) && brNode)
  2923       NS_ENSURE_STATE(mHTMLEditor);
  2924       res = mHTMLEditor->DeleteNode(brNode);
  2927   return res;
  2931 /*****************************************************************************************************
  2932 *    MoveBlock: this method is used to move the content from rightBlock into leftBlock
  2933 *    Note that the "block" might merely be inline nodes between <br>s, or between blocks, etc.
  2934 *    DTD containment rules are followed throughout.
  2935 *         nsIDOMNode *aLeftBlock         parent to receive moved content
  2936 *         nsIDOMNode *aRightBlock        parent to provide moved content
  2937 *         int32_t aLeftOffset            offset in aLeftBlock to move content to
  2938 *         int32_t aRightOffset           offset in aRightBlock to move content from
  2939 */
  2940 nsresult
  2941 nsHTMLEditRules::MoveBlock(nsIDOMNode *aLeftBlock, nsIDOMNode *aRightBlock, int32_t aLeftOffset, int32_t aRightOffset)
  2943   nsCOMArray<nsIDOMNode> arrayOfNodes;
  2944   nsCOMPtr<nsISupports> isupports;
  2945   // GetNodesFromPoint is the workhorse that figures out what we wnat to move.
  2946   nsresult res = GetNodesFromPoint(::DOMPoint(aRightBlock,aRightOffset),
  2947                                    EditAction::makeList, arrayOfNodes, true);
  2948   NS_ENSURE_SUCCESS(res, res);
  2949   int32_t listCount = arrayOfNodes.Count();
  2950   int32_t i;
  2951   for (i=0; i<listCount; i++)
  2953     // get the node to act on
  2954     nsIDOMNode* curNode = arrayOfNodes[i];
  2955     if (IsBlockNode(curNode))
  2957       // For block nodes, move their contents only, then delete block.
  2958       res = MoveContents(curNode, aLeftBlock, &aLeftOffset); 
  2959       NS_ENSURE_SUCCESS(res, res);
  2960       NS_ENSURE_STATE(mHTMLEditor);
  2961       res = mHTMLEditor->DeleteNode(curNode);
  2963     else
  2965       // otherwise move the content as is, checking against the dtd.
  2966       res = MoveNodeSmart(curNode, aLeftBlock, &aLeftOffset);
  2969   return res;
  2972 /*****************************************************************************************************
  2973 *    MoveNodeSmart: this method is used to move node aSource to (aDest,aOffset).
  2974 *    DTD containment rules are followed throughout.  aOffset is updated to point _after_
  2975 *    inserted content.
  2976 *         nsIDOMNode *aSource       the selection.  
  2977 *         nsIDOMNode *aDest         parent to receive moved content
  2978 *         int32_t *aOffset          offset in aDest to move content to
  2979 */
  2980 nsresult
  2981 nsHTMLEditRules::MoveNodeSmart(nsIDOMNode *aSource, nsIDOMNode *aDest, int32_t *aOffset)
  2983   NS_ENSURE_TRUE(aSource && aDest && aOffset, NS_ERROR_NULL_POINTER);
  2985   nsresult res;
  2986   // check if this node can go into the destination node
  2987   NS_ENSURE_STATE(mHTMLEditor);
  2988   if (mHTMLEditor->CanContain(aDest, aSource)) {
  2989     // if it can, move it there
  2990     NS_ENSURE_STATE(mHTMLEditor);
  2991     res = mHTMLEditor->MoveNode(aSource, aDest, *aOffset);
  2992     NS_ENSURE_SUCCESS(res, res);
  2993     if (*aOffset != -1) ++(*aOffset);
  2995   else
  2997     // if it can't, move its children, and then delete it.
  2998     res = MoveContents(aSource, aDest, aOffset);
  2999     NS_ENSURE_SUCCESS(res, res);
  3000     NS_ENSURE_STATE(mHTMLEditor);
  3001     res = mHTMLEditor->DeleteNode(aSource);
  3002     NS_ENSURE_SUCCESS(res, res);
  3004   return NS_OK;
  3007 /*****************************************************************************************************
  3008 *    MoveContents: this method is used to move node the _contents_ of aSource to (aDest,aOffset).
  3009 *    DTD containment rules are followed throughout.  aOffset is updated to point _after_
  3010 *    inserted content.  aSource is deleted.
  3011 *         nsIDOMNode *aSource       the selection.  
  3012 *         nsIDOMNode *aDest         parent to receive moved content
  3013 *         int32_t *aOffset          offset in aDest to move content to
  3014 */
  3015 nsresult
  3016 nsHTMLEditRules::MoveContents(nsIDOMNode *aSource, nsIDOMNode *aDest, int32_t *aOffset)
  3018   NS_ENSURE_TRUE(aSource && aDest && aOffset, NS_ERROR_NULL_POINTER);
  3019   if (aSource == aDest) return NS_ERROR_ILLEGAL_VALUE;
  3020   NS_ENSURE_STATE(mHTMLEditor);
  3021   NS_ASSERTION(!mHTMLEditor->IsTextNode(aSource), "#text does not have contents");
  3023   nsCOMPtr<nsIDOMNode> child;
  3024   nsAutoString tag;
  3025   nsresult res;
  3026   aSource->GetFirstChild(getter_AddRefs(child));
  3027   while (child)
  3029     res = MoveNodeSmart(child, aDest, aOffset);
  3030     NS_ENSURE_SUCCESS(res, res);
  3031     aSource->GetFirstChild(getter_AddRefs(child));
  3033   return NS_OK;
  3037 nsresult
  3038 nsHTMLEditRules::DeleteNonTableElements(nsINode* aNode)
  3040   MOZ_ASSERT(aNode);
  3041   if (!nsHTMLEditUtils::IsTableElementButNotTable(aNode)) {
  3042     NS_ENSURE_STATE(mHTMLEditor);
  3043     return mHTMLEditor->DeleteNode(aNode->AsDOMNode());
  3046   for (int32_t i = aNode->GetChildCount() - 1; i >= 0; --i) {
  3047     nsresult rv = DeleteNonTableElements(aNode->GetChildAt(i));
  3048     NS_ENSURE_SUCCESS(rv, rv);
  3050   return NS_OK;
  3053 nsresult
  3054 nsHTMLEditRules::DidDeleteSelection(nsISelection *aSelection, 
  3055                                     nsIEditor::EDirection aDir, 
  3056                                     nsresult aResult)
  3058   if (!aSelection) { return NS_ERROR_NULL_POINTER; }
  3060   // find where we are
  3061   nsCOMPtr<nsIDOMNode> startNode;
  3062   int32_t startOffset;
  3063   nsresult res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset);
  3064   NS_ENSURE_SUCCESS(res, res);
  3065   NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
  3067   // find any enclosing mailcite
  3068   nsCOMPtr<nsIDOMNode> citeNode;
  3069   res = GetTopEnclosingMailCite(startNode, address_of(citeNode), 
  3070                                 IsPlaintextEditor());
  3071   NS_ENSURE_SUCCESS(res, res);
  3072   if (citeNode) {
  3073     nsCOMPtr<nsINode> cite = do_QueryInterface(citeNode);
  3074     bool isEmpty = true, seenBR = false;
  3075     NS_ENSURE_STATE(mHTMLEditor);
  3076     mHTMLEditor->IsEmptyNodeImpl(cite, &isEmpty, true, true, false, &seenBR);
  3077     if (isEmpty)
  3079       nsCOMPtr<nsIDOMNode> brNode;
  3080       int32_t offset;
  3081       nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(citeNode, &offset);
  3082       NS_ENSURE_STATE(mHTMLEditor);
  3083       res = mHTMLEditor->DeleteNode(citeNode);
  3084       NS_ENSURE_SUCCESS(res, res);
  3085       if (parent && seenBR)
  3087         NS_ENSURE_STATE(mHTMLEditor);
  3088         res = mHTMLEditor->CreateBR(parent, offset, address_of(brNode));
  3089         NS_ENSURE_SUCCESS(res, res);
  3090         aSelection->Collapse(parent, offset);
  3095   // call through to base class
  3096   return nsTextEditRules::DidDeleteSelection(aSelection, aDir, aResult);
  3099 nsresult
  3100 nsHTMLEditRules::WillMakeList(Selection* aSelection,
  3101                               const nsAString* aListType,
  3102                               bool aEntireList,
  3103                               const nsAString* aBulletType,
  3104                               bool* aCancel,
  3105                               bool* aHandled,
  3106                               const nsAString* aItemType)
  3108   if (!aSelection || !aListType || !aCancel || !aHandled) {
  3109     return NS_ERROR_NULL_POINTER;
  3111   nsCOMPtr<nsIAtom> listTypeAtom = do_GetAtom(*aListType);
  3112   NS_ENSURE_TRUE(listTypeAtom, NS_ERROR_OUT_OF_MEMORY);
  3114   nsresult res = WillInsert(aSelection, aCancel);
  3115   NS_ENSURE_SUCCESS(res, res);
  3117   // initialize out param
  3118   // we want to ignore result of WillInsert()
  3119   *aCancel = false;
  3120   *aHandled = false;
  3122   // deduce what tag to use for list items
  3123   nsCOMPtr<nsIAtom> itemType;
  3124   if (aItemType) { 
  3125     itemType = do_GetAtom(*aItemType);
  3126     NS_ENSURE_TRUE(itemType, NS_ERROR_OUT_OF_MEMORY);
  3127   } else if (listTypeAtom == nsGkAtoms::dl) {
  3128     itemType = nsGkAtoms::dd;
  3129   } else {
  3130     itemType = nsGkAtoms::li;
  3133   // convert the selection ranges into "promoted" selection ranges:
  3134   // this basically just expands the range to include the immediate
  3135   // block parent, and then further expands to include any ancestors
  3136   // whose children are all in the range
  3138   *aHandled = true;
  3140   res = NormalizeSelection(aSelection);
  3141   NS_ENSURE_SUCCESS(res, res);
  3142   NS_ENSURE_STATE(mHTMLEditor);
  3143   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
  3145   nsCOMArray<nsIDOMNode> arrayOfNodes;
  3146   res = GetListActionNodes(arrayOfNodes, aEntireList);
  3147   NS_ENSURE_SUCCESS(res, res);
  3149   int32_t listCount = arrayOfNodes.Count();
  3151   // check if all our nodes are <br>s, or empty inlines
  3152   bool bOnlyBreaks = true;
  3153   for (int32_t j = 0; j < listCount; j++) {
  3154     nsIDOMNode* curNode = arrayOfNodes[j];
  3155     // if curNode is not a Break or empty inline, we're done
  3156     if (!nsTextEditUtils::IsBreak(curNode) && !IsEmptyInline(curNode)) {
  3157       bOnlyBreaks = false;
  3158       break;
  3162   // if no nodes, we make empty list.  Ditto if the user tried to make a list
  3163   // of some # of breaks.
  3164   if (!listCount || bOnlyBreaks) {
  3165     nsCOMPtr<nsIDOMNode> parent, theList, theListItem;
  3166     int32_t offset;
  3168     // if only breaks, delete them
  3169     if (bOnlyBreaks) {
  3170       for (int32_t j = 0; j < (int32_t)listCount; j++) {
  3171         NS_ENSURE_STATE(mHTMLEditor);
  3172         res = mHTMLEditor->DeleteNode(arrayOfNodes[j]);
  3173         NS_ENSURE_SUCCESS(res, res);
  3177     // get selection location
  3178     NS_ENSURE_STATE(mHTMLEditor);
  3179     res = mHTMLEditor->GetStartNodeAndOffset(aSelection,
  3180                                              getter_AddRefs(parent), &offset);
  3181     NS_ENSURE_SUCCESS(res, res);
  3183     // make sure we can put a list here
  3184     NS_ENSURE_STATE(mHTMLEditor);
  3185     if (!mHTMLEditor->CanContainTag(parent, listTypeAtom)) {
  3186       *aCancel = true;
  3187       return NS_OK;
  3189     res = SplitAsNeeded(aListType, address_of(parent), &offset);
  3190     NS_ENSURE_SUCCESS(res, res);
  3191     NS_ENSURE_STATE(mHTMLEditor);
  3192     res = mHTMLEditor->CreateNode(*aListType, parent, offset,
  3193                                   getter_AddRefs(theList));
  3194     NS_ENSURE_SUCCESS(res, res);
  3195     NS_ENSURE_STATE(mHTMLEditor);
  3196     res = mHTMLEditor->CreateNode(nsDependentAtomString(itemType), theList, 0,
  3197                                   getter_AddRefs(theListItem));
  3198     NS_ENSURE_SUCCESS(res, res);
  3199     // remember our new block for postprocessing
  3200     mNewBlock = theListItem;
  3201     // put selection in new list item
  3202     res = aSelection->Collapse(theListItem, 0);
  3203     // to prevent selection resetter from overriding us
  3204     selectionResetter.Abort();
  3205     *aHandled = true;
  3206     return res;
  3209   // if there is only one node in the array, and it is a list, div, or
  3210   // blockquote, then look inside of it until we find inner list or content.
  3212   res = LookInsideDivBQandList(arrayOfNodes);
  3213   NS_ENSURE_SUCCESS(res, res);
  3215   // Ok, now go through all the nodes and put then in the list,
  3216   // or whatever is approriate.  Wohoo!
  3218   listCount = arrayOfNodes.Count();
  3219   nsCOMPtr<nsIDOMNode> curParent;
  3220   nsCOMPtr<nsIDOMNode> curList;
  3221   nsCOMPtr<nsIDOMNode> prevListItem;
  3223   for (int32_t i = 0; i < listCount; i++) {
  3224     // here's where we actually figure out what to do
  3225     nsCOMPtr<nsIDOMNode> newBlock;
  3226     nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
  3227     int32_t offset;
  3228     curParent = nsEditor::GetNodeLocation(curNode, &offset);
  3230     // make sure we don't assemble content that is in different table cells
  3231     // into the same list.  respect table cell boundaries when listifying.
  3232     if (curList && InDifferentTableElements(curList, curNode)) {
  3233       curList = nullptr;
  3236     // if curNode is a Break, delete it, and quit remembering prev list item
  3237     if (nsTextEditUtils::IsBreak(curNode)) {
  3238       NS_ENSURE_STATE(mHTMLEditor);
  3239       res = mHTMLEditor->DeleteNode(curNode);
  3240       NS_ENSURE_SUCCESS(res, res);
  3241       prevListItem = 0;
  3242       continue;
  3243     } else if (IsEmptyInline(curNode)) {
  3244       // if curNode is an empty inline container, delete it
  3245       NS_ENSURE_STATE(mHTMLEditor);
  3246       res = mHTMLEditor->DeleteNode(curNode);
  3247       NS_ENSURE_SUCCESS(res, res);
  3248       continue;
  3251     if (nsHTMLEditUtils::IsList(curNode)) {
  3252       // do we have a curList already?
  3253       if (curList && !nsEditorUtils::IsDescendantOf(curNode, curList)) {
  3254         // move all of our children into curList.  cheezy way to do it: move
  3255         // whole list and then RemoveContainer() on the list.  ConvertListType
  3256         // first: that routine handles converting the list item types, if
  3257         // needed
  3258         NS_ENSURE_STATE(mHTMLEditor);
  3259         res = mHTMLEditor->MoveNode(curNode, curList, -1);
  3260         NS_ENSURE_SUCCESS(res, res);
  3261         res = ConvertListType(curNode, address_of(newBlock), listTypeAtom,
  3262                               itemType);
  3263         NS_ENSURE_SUCCESS(res, res);
  3264         NS_ENSURE_STATE(mHTMLEditor);
  3265         res = mHTMLEditor->RemoveBlockContainer(newBlock);
  3266         NS_ENSURE_SUCCESS(res, res);
  3267       } else {
  3268         // replace list with new list type
  3269         res = ConvertListType(curNode, address_of(newBlock), listTypeAtom,
  3270                               itemType);
  3271         NS_ENSURE_SUCCESS(res, res);
  3272         curList = newBlock;
  3274       prevListItem = 0;
  3275       continue;
  3278     if (nsHTMLEditUtils::IsListItem(curNode)) {
  3279       NS_ENSURE_STATE(mHTMLEditor);
  3280       if (mHTMLEditor->GetTag(curParent) != listTypeAtom) {
  3281         // list item is in wrong type of list. if we don't have a curList,
  3282         // split the old list and make a new list of correct type.
  3283         if (!curList || nsEditorUtils::IsDescendantOf(curNode, curList)) {
  3284           NS_ENSURE_STATE(mHTMLEditor);
  3285           res = mHTMLEditor->SplitNode(curParent, offset,
  3286                                        getter_AddRefs(newBlock));
  3287           NS_ENSURE_SUCCESS(res, res);
  3288           int32_t offset;
  3289           nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(curParent, &offset);
  3290           NS_ENSURE_STATE(mHTMLEditor);
  3291           res = mHTMLEditor->CreateNode(*aListType, parent, offset,
  3292                                         getter_AddRefs(curList));
  3293           NS_ENSURE_SUCCESS(res, res);
  3295         // move list item to new list
  3296         NS_ENSURE_STATE(mHTMLEditor);
  3297         res = mHTMLEditor->MoveNode(curNode, curList, -1);
  3298         NS_ENSURE_SUCCESS(res, res);
  3299         // convert list item type if needed
  3300         NS_ENSURE_STATE(mHTMLEditor);
  3301         if (!mHTMLEditor->NodeIsType(curNode, itemType)) {
  3302           NS_ENSURE_STATE(mHTMLEditor);
  3303           res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock),
  3304                                               nsDependentAtomString(itemType));
  3305           NS_ENSURE_SUCCESS(res, res);
  3307       } else {
  3308         // item is in right type of list.  But we might still have to move it.
  3309         // and we might need to convert list item types.
  3310         if (!curList) {
  3311           curList = curParent;
  3312         } else if (curParent != curList) {
  3313           // move list item to new list
  3314           NS_ENSURE_STATE(mHTMLEditor);
  3315           res = mHTMLEditor->MoveNode(curNode, curList, -1);
  3316           NS_ENSURE_SUCCESS(res, res);
  3318         NS_ENSURE_STATE(mHTMLEditor);
  3319         if (!mHTMLEditor->NodeIsType(curNode, itemType)) {
  3320           NS_ENSURE_STATE(mHTMLEditor);
  3321           res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock),
  3322                                               nsDependentAtomString(itemType));
  3323           NS_ENSURE_SUCCESS(res, res);
  3326       nsCOMPtr<nsIDOMElement> curElement = do_QueryInterface(curNode);
  3327       NS_NAMED_LITERAL_STRING(typestr, "type");
  3328       if (aBulletType && !aBulletType->IsEmpty()) {
  3329         NS_ENSURE_STATE(mHTMLEditor);
  3330         res = mHTMLEditor->SetAttribute(curElement, typestr, *aBulletType);
  3331       } else {
  3332         NS_ENSURE_STATE(mHTMLEditor);
  3333         res = mHTMLEditor->RemoveAttribute(curElement, typestr);
  3335       NS_ENSURE_SUCCESS(res, res);
  3336       continue;
  3339     // if we hit a div clear our prevListItem, insert divs contents
  3340     // into our node array, and remove the div
  3341     if (nsHTMLEditUtils::IsDiv(curNode)) {
  3342       prevListItem = nullptr;
  3343       int32_t j = i + 1;
  3344       res = GetInnerContent(curNode, arrayOfNodes, &j);
  3345       NS_ENSURE_SUCCESS(res, res);
  3346       NS_ENSURE_STATE(mHTMLEditor);
  3347       res = mHTMLEditor->RemoveContainer(curNode);
  3348       NS_ENSURE_SUCCESS(res, res);
  3349       listCount = arrayOfNodes.Count();
  3350       continue;
  3353     // need to make a list to put things in if we haven't already,
  3354     if (!curList) {
  3355       res = SplitAsNeeded(aListType, address_of(curParent), &offset);
  3356       NS_ENSURE_SUCCESS(res, res);
  3357       NS_ENSURE_STATE(mHTMLEditor);
  3358       res = mHTMLEditor->CreateNode(*aListType, curParent, offset,
  3359                                     getter_AddRefs(curList));
  3360       NS_ENSURE_SUCCESS(res, res);
  3361       // remember our new block for postprocessing
  3362       mNewBlock = curList;
  3363       // curList is now the correct thing to put curNode in
  3364       prevListItem = 0;
  3367     // if curNode isn't a list item, we must wrap it in one
  3368     nsCOMPtr<nsIDOMNode> listItem;
  3369     if (!nsHTMLEditUtils::IsListItem(curNode)) {
  3370       if (IsInlineNode(curNode) && prevListItem) {
  3371         // this is a continuation of some inline nodes that belong together in
  3372         // the same list item.  use prevListItem
  3373         NS_ENSURE_STATE(mHTMLEditor);
  3374         res = mHTMLEditor->MoveNode(curNode, prevListItem, -1);
  3375         NS_ENSURE_SUCCESS(res, res);
  3376       } else {
  3377         // don't wrap li around a paragraph.  instead replace paragraph with li
  3378         if (nsHTMLEditUtils::IsParagraph(curNode)) {
  3379           NS_ENSURE_STATE(mHTMLEditor);
  3380           res = mHTMLEditor->ReplaceContainer(curNode, address_of(listItem),
  3381                                               nsDependentAtomString(itemType));
  3382         } else {
  3383           NS_ENSURE_STATE(mHTMLEditor);
  3384           res = mHTMLEditor->InsertContainerAbove(curNode, address_of(listItem),
  3385                                                   nsDependentAtomString(itemType));
  3387         NS_ENSURE_SUCCESS(res, res);
  3388         if (IsInlineNode(curNode)) {
  3389           prevListItem = listItem;
  3390         } else {
  3391           prevListItem = nullptr;
  3394     } else {
  3395       listItem = curNode;
  3398     if (listItem) {
  3399       // if we made a new list item, deal with it: tuck the listItem into the
  3400       // end of the active list
  3401       NS_ENSURE_STATE(mHTMLEditor);
  3402       res = mHTMLEditor->MoveNode(listItem, curList, -1);
  3403       NS_ENSURE_SUCCESS(res, res);
  3407   return res;
  3411 nsresult
  3412 nsHTMLEditRules::WillRemoveList(Selection* aSelection,
  3413                                 bool aOrdered, 
  3414                                 bool *aCancel,
  3415                                 bool *aHandled)
  3417   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
  3418   // initialize out param
  3419   *aCancel = false;
  3420   *aHandled = true;
  3422   nsresult res = NormalizeSelection(aSelection);
  3423   NS_ENSURE_SUCCESS(res, res);
  3424   NS_ENSURE_STATE(mHTMLEditor);
  3425   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
  3427   nsCOMArray<nsIDOMRange> arrayOfRanges;
  3428   res = GetPromotedRanges(aSelection, arrayOfRanges, EditAction::makeList);
  3429   NS_ENSURE_SUCCESS(res, res);
  3431   // use these ranges to contruct a list of nodes to act on.
  3432   nsCOMArray<nsIDOMNode> arrayOfNodes;
  3433   res = GetListActionNodes(arrayOfNodes, false);
  3434   NS_ENSURE_SUCCESS(res, res);                                 
  3436   // Remove all non-editable nodes.  Leave them be.
  3437   int32_t listCount = arrayOfNodes.Count();
  3438   int32_t i;
  3439   for (i=listCount-1; i>=0; i--)
  3441     nsIDOMNode* testNode = arrayOfNodes[i];
  3442     NS_ENSURE_STATE(mHTMLEditor);
  3443     if (!mHTMLEditor->IsEditable(testNode))
  3445       arrayOfNodes.RemoveObjectAt(i);
  3449   // reset list count
  3450   listCount = arrayOfNodes.Count();
  3452   // Only act on lists or list items in the array
  3453   nsCOMPtr<nsIDOMNode> curParent;
  3454   for (i=0; i<listCount; i++)
  3456     // here's where we actually figure out what to do
  3457     nsIDOMNode* curNode = arrayOfNodes[i];
  3458     int32_t offset;
  3459     curParent = nsEditor::GetNodeLocation(curNode, &offset);
  3461     if (nsHTMLEditUtils::IsListItem(curNode))  // unlist this listitem
  3463       bool bOutOfList;
  3464       do
  3466         res = PopListItem(curNode, &bOutOfList);
  3467         NS_ENSURE_SUCCESS(res, res);
  3468       } while (!bOutOfList); // keep popping it out until it's not in a list anymore
  3470     else if (nsHTMLEditUtils::IsList(curNode)) // node is a list, move list items out
  3472       res = RemoveListStructure(curNode);
  3473       NS_ENSURE_SUCCESS(res, res);
  3476   return res;
  3480 nsresult
  3481 nsHTMLEditRules::WillMakeDefListItem(Selection* aSelection,
  3482                                      const nsAString *aItemType, 
  3483                                      bool aEntireList, 
  3484                                      bool *aCancel,
  3485                                      bool *aHandled)
  3487   // for now we let WillMakeList handle this
  3488   NS_NAMED_LITERAL_STRING(listType, "dl");
  3489   return WillMakeList(aSelection, &listType, aEntireList, nullptr, aCancel, aHandled, aItemType);
  3492 nsresult
  3493 nsHTMLEditRules::WillMakeBasicBlock(Selection* aSelection,
  3494                                     const nsAString *aBlockType, 
  3495                                     bool *aCancel,
  3496                                     bool *aHandled)
  3498   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
  3499   // initialize out param
  3500   *aCancel = false;
  3501   *aHandled = false;
  3503   nsresult res = WillInsert(aSelection, aCancel);
  3504   NS_ENSURE_SUCCESS(res, res);
  3505   // initialize out param
  3506   // we want to ignore result of WillInsert()
  3507   *aCancel = false;
  3508   res = NormalizeSelection(aSelection);
  3509   NS_ENSURE_SUCCESS(res, res);
  3510   NS_ENSURE_STATE(mHTMLEditor);
  3511   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
  3512   NS_ENSURE_STATE(mHTMLEditor);
  3513   nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
  3514   *aHandled = true;
  3515   nsString tString(*aBlockType);
  3517   // contruct a list of nodes to act on.
  3518   nsCOMArray<nsIDOMNode> arrayOfNodes;
  3519   res = GetNodesFromSelection(aSelection, EditAction::makeBasicBlock,
  3520                               arrayOfNodes);
  3521   NS_ENSURE_SUCCESS(res, res);
  3523   // Remove all non-editable nodes.  Leave them be.
  3524   int32_t listCount = arrayOfNodes.Count();
  3525   int32_t i;
  3526   for (i=listCount-1; i>=0; i--)
  3528     NS_ENSURE_STATE(mHTMLEditor);
  3529     if (!mHTMLEditor->IsEditable(arrayOfNodes[i]))
  3531       arrayOfNodes.RemoveObjectAt(i);
  3535   // reset list count
  3536   listCount = arrayOfNodes.Count();
  3538   // if nothing visible in list, make an empty block
  3539   if (ListIsEmptyLine(arrayOfNodes))
  3541     nsCOMPtr<nsIDOMNode> parent, theBlock;
  3542     int32_t offset;
  3544     // get selection location
  3545     NS_ENSURE_STATE(mHTMLEditor);
  3546     res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset);
  3547     NS_ENSURE_SUCCESS(res, res);
  3548     if (tString.EqualsLiteral("normal") ||
  3549         tString.IsEmpty() ) // we are removing blocks (going to "body text")
  3551       nsCOMPtr<nsIDOMNode> curBlock = parent;
  3552       if (!IsBlockNode(curBlock)) {
  3553         NS_ENSURE_STATE(mHTMLEditor);
  3554         curBlock = mHTMLEditor->GetBlockNodeParent(parent);
  3556       nsCOMPtr<nsIDOMNode> curBlockPar;
  3557       NS_ENSURE_TRUE(curBlock, NS_ERROR_NULL_POINTER);
  3558       curBlock->GetParentNode(getter_AddRefs(curBlockPar));
  3559       if (nsHTMLEditUtils::IsFormatNode(curBlock))
  3561         // if the first editable node after selection is a br, consume it.  Otherwise
  3562         // it gets pushed into a following block after the split, which is visually bad.
  3563         nsCOMPtr<nsIDOMNode> brNode;
  3564         NS_ENSURE_STATE(mHTMLEditor);
  3565         res = mHTMLEditor->GetNextHTMLNode(parent, offset, address_of(brNode));
  3566         NS_ENSURE_SUCCESS(res, res);        
  3567         if (brNode && nsTextEditUtils::IsBreak(brNode))
  3569           NS_ENSURE_STATE(mHTMLEditor);
  3570           res = mHTMLEditor->DeleteNode(brNode);
  3571           NS_ENSURE_SUCCESS(res, res); 
  3573         // do the splits!
  3574         NS_ENSURE_STATE(mHTMLEditor);
  3575         res = mHTMLEditor->SplitNodeDeep(curBlock, parent, offset, &offset, true);
  3576         NS_ENSURE_SUCCESS(res, res);
  3577         // put a br at the split point
  3578         NS_ENSURE_STATE(mHTMLEditor);
  3579         res = mHTMLEditor->CreateBR(curBlockPar, offset, address_of(brNode));
  3580         NS_ENSURE_SUCCESS(res, res);
  3581         // put selection at the split point
  3582         res = aSelection->Collapse(curBlockPar, offset);
  3583         selectionResetter.Abort();  // to prevent selection reseter from overriding us.
  3584         *aHandled = true;
  3586       // else nothing to do!
  3588     else  // we are making a block
  3590       // consume a br, if needed
  3591       nsCOMPtr<nsIDOMNode> brNode;
  3592       NS_ENSURE_STATE(mHTMLEditor);
  3593       res = mHTMLEditor->GetNextHTMLNode(parent, offset, address_of(brNode), true);
  3594       NS_ENSURE_SUCCESS(res, res);
  3595       if (brNode && nsTextEditUtils::IsBreak(brNode))
  3597         NS_ENSURE_STATE(mHTMLEditor);
  3598         res = mHTMLEditor->DeleteNode(brNode);
  3599         NS_ENSURE_SUCCESS(res, res);
  3600         // we don't need to act on this node any more
  3601         arrayOfNodes.RemoveObject(brNode);
  3603       // make sure we can put a block here
  3604       res = SplitAsNeeded(aBlockType, address_of(parent), &offset);
  3605       NS_ENSURE_SUCCESS(res, res);
  3606       NS_ENSURE_STATE(mHTMLEditor);
  3607       res = mHTMLEditor->CreateNode(*aBlockType, parent, offset, getter_AddRefs(theBlock));
  3608       NS_ENSURE_SUCCESS(res, res);
  3609       // remember our new block for postprocessing
  3610       mNewBlock = theBlock;
  3611       // delete anything that was in the list of nodes
  3612       for (int32_t j = arrayOfNodes.Count() - 1; j >= 0; --j) 
  3614         nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[0];
  3615         NS_ENSURE_STATE(mHTMLEditor);
  3616         res = mHTMLEditor->DeleteNode(curNode);
  3617         NS_ENSURE_SUCCESS(res, res);
  3618         arrayOfNodes.RemoveObjectAt(0);
  3620       // put selection in new block
  3621       res = aSelection->Collapse(theBlock,0);
  3622       selectionResetter.Abort();  // to prevent selection reseter from overriding us.
  3623       *aHandled = true;
  3625     return res;    
  3627   else
  3629     // Ok, now go through all the nodes and make the right kind of blocks, 
  3630     // or whatever is approriate.  Wohoo! 
  3631     // Note: blockquote is handled a little differently
  3632     if (tString.EqualsLiteral("blockquote"))
  3633       res = MakeBlockquote(arrayOfNodes);
  3634     else if (tString.EqualsLiteral("normal") ||
  3635              tString.IsEmpty() )
  3636       res = RemoveBlockStyle(arrayOfNodes);
  3637     else
  3638       res = ApplyBlockStyle(arrayOfNodes, aBlockType);
  3639     return res;
  3641   return res;
  3644 nsresult 
  3645 nsHTMLEditRules::DidMakeBasicBlock(nsISelection *aSelection,
  3646                                    nsRulesInfo *aInfo, nsresult aResult)
  3648   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
  3649   // check for empty block.  if so, put a moz br in it.
  3650   if (!aSelection->Collapsed()) {
  3651     return NS_OK;
  3654   nsCOMPtr<nsIDOMNode> parent;
  3655   int32_t offset;
  3656   nsresult res = nsEditor::GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset);
  3657   NS_ENSURE_SUCCESS(res, res);
  3658   res = InsertMozBRIfNeeded(parent);
  3659   return res;
  3662 nsresult
  3663 nsHTMLEditRules::WillIndent(Selection* aSelection,
  3664                             bool* aCancel, bool* aHandled)
  3666   nsresult res;
  3667   NS_ENSURE_STATE(mHTMLEditor);
  3668   if (mHTMLEditor->IsCSSEnabled()) {
  3669     res = WillCSSIndent(aSelection, aCancel, aHandled);
  3671   else {
  3672     res = WillHTMLIndent(aSelection, aCancel, aHandled);
  3674   return res;
  3677 nsresult
  3678 nsHTMLEditRules::WillCSSIndent(Selection* aSelection,
  3679                                bool* aCancel, bool* aHandled)
  3681   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
  3683   nsresult res = WillInsert(aSelection, aCancel);
  3684   NS_ENSURE_SUCCESS(res, res);
  3686   // initialize out param
  3687   // we want to ignore result of WillInsert()
  3688   *aCancel = false;
  3689   *aHandled = true;
  3691   res = NormalizeSelection(aSelection);
  3692   NS_ENSURE_SUCCESS(res, res);
  3693   NS_ENSURE_STATE(mHTMLEditor);
  3694   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
  3695   nsCOMArray<nsIDOMRange>  arrayOfRanges;
  3696   nsCOMArray<nsIDOMNode> arrayOfNodes;
  3698   // short circuit: detect case of collapsed selection inside an <li>.
  3699   // just sublist that <li>.  This prevents bug 97797.
  3701   nsCOMPtr<nsIDOMNode> liNode;
  3702   if (aSelection->Collapsed()) {
  3703     nsCOMPtr<nsIDOMNode> node, block;
  3704     int32_t offset;
  3705     NS_ENSURE_STATE(mHTMLEditor);
  3706     nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(node), &offset);
  3707     NS_ENSURE_SUCCESS(res, res);
  3708     if (IsBlockNode(node)) {
  3709       block = node;
  3710     } else {
  3711       NS_ENSURE_STATE(mHTMLEditor);
  3712       block = mHTMLEditor->GetBlockNodeParent(node);
  3714     if (block && nsHTMLEditUtils::IsListItem(block))
  3715       liNode = block;
  3718   if (liNode)
  3720     arrayOfNodes.AppendObject(liNode);
  3722   else
  3724     // convert the selection ranges into "promoted" selection ranges:
  3725     // this basically just expands the range to include the immediate
  3726     // block parent, and then further expands to include any ancestors
  3727     // whose children are all in the range
  3728     res = GetNodesFromSelection(aSelection, EditAction::indent, arrayOfNodes);
  3729     NS_ENSURE_SUCCESS(res, res);
  3732   NS_NAMED_LITERAL_STRING(quoteType, "blockquote");
  3733   // if nothing visible in list, make an empty block
  3734   if (ListIsEmptyLine(arrayOfNodes))
  3736     nsCOMPtr<nsIDOMNode> parent, theBlock;
  3737     int32_t offset;
  3738     nsAutoString quoteType(NS_LITERAL_STRING("div"));
  3739     // get selection location
  3740     NS_ENSURE_STATE(mHTMLEditor);
  3741     res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset);
  3742     NS_ENSURE_SUCCESS(res, res);
  3743     // make sure we can put a block here
  3744     res = SplitAsNeeded(&quoteType, address_of(parent), &offset);
  3745     NS_ENSURE_SUCCESS(res, res);
  3746     NS_ENSURE_STATE(mHTMLEditor);
  3747     res = mHTMLEditor->CreateNode(quoteType, parent, offset, getter_AddRefs(theBlock));
  3748     NS_ENSURE_SUCCESS(res, res);
  3749     // remember our new block for postprocessing
  3750     mNewBlock = theBlock;
  3751     RelativeChangeIndentationOfElementNode(theBlock, +1);
  3752     // delete anything that was in the list of nodes
  3753     for (int32_t j = arrayOfNodes.Count() - 1; j >= 0; --j) 
  3755       nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[0];
  3756       NS_ENSURE_STATE(mHTMLEditor);
  3757       res = mHTMLEditor->DeleteNode(curNode);
  3758       NS_ENSURE_SUCCESS(res, res);
  3759       arrayOfNodes.RemoveObjectAt(0);
  3761     // put selection in new block
  3762     res = aSelection->Collapse(theBlock,0);
  3763     selectionResetter.Abort();  // to prevent selection reseter from overriding us.
  3764     *aHandled = true;
  3765     return res;
  3768   // Ok, now go through all the nodes and put them in a blockquote, 
  3769   // or whatever is appropriate.  Wohoo!
  3770   int32_t i;
  3771   nsCOMPtr<nsIDOMNode> curParent;
  3772   nsCOMPtr<nsIDOMNode> curQuote;
  3773   nsCOMPtr<nsIDOMNode> curList;
  3774   nsCOMPtr<nsIDOMNode> sibling;
  3775   int32_t listCount = arrayOfNodes.Count();
  3776   for (i=0; i<listCount; i++)
  3778     // here's where we actually figure out what to do
  3779     nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
  3781     // Ignore all non-editable nodes.  Leave them be.
  3782     NS_ENSURE_STATE(mHTMLEditor);
  3783     if (!mHTMLEditor->IsEditable(curNode)) continue;
  3785     int32_t offset;
  3786     curParent = nsEditor::GetNodeLocation(curNode, &offset);
  3788     // some logic for putting list items into nested lists...
  3789     if (nsHTMLEditUtils::IsList(curParent))
  3791       sibling = nullptr;
  3793       // Check for whether we should join a list that follows curNode.
  3794       // We do this if the next element is a list, and the list is of the
  3795       // same type (li/ol) as curNode was a part it.
  3796       NS_ENSURE_STATE(mHTMLEditor);
  3797       mHTMLEditor->GetNextHTMLSibling(curNode, address_of(sibling));
  3798       if (sibling && nsHTMLEditUtils::IsList(sibling))
  3800         nsAutoString curListTag, siblingListTag;
  3801         nsEditor::GetTagString(curParent, curListTag);
  3802         nsEditor::GetTagString(sibling, siblingListTag);
  3803         if (curListTag == siblingListTag)
  3805           NS_ENSURE_STATE(mHTMLEditor);
  3806           res = mHTMLEditor->MoveNode(curNode, sibling, 0);
  3807           NS_ENSURE_SUCCESS(res, res);
  3808           continue;
  3811       // Check for whether we should join a list that preceeds curNode.
  3812       // We do this if the previous element is a list, and the list is of
  3813       // the same type (li/ol) as curNode was a part of.
  3814       NS_ENSURE_STATE(mHTMLEditor);
  3815       mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
  3816       if (sibling && nsHTMLEditUtils::IsList(sibling))
  3818         nsAutoString curListTag, siblingListTag;
  3819         nsEditor::GetTagString(curParent, curListTag);
  3820         nsEditor::GetTagString(sibling, siblingListTag);
  3821         if (curListTag == siblingListTag)
  3823           NS_ENSURE_STATE(mHTMLEditor);
  3824           res = mHTMLEditor->MoveNode(curNode, sibling, -1);
  3825           NS_ENSURE_SUCCESS(res, res);
  3826           continue;
  3829       sibling = nullptr;
  3831       // check to see if curList is still appropriate.  Which it is if
  3832       // curNode is still right after it in the same list.
  3833       if (curList) {
  3834         NS_ENSURE_STATE(mHTMLEditor);
  3835         mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
  3838       if (!curList || (sibling && sibling != curList))
  3840         nsAutoString listTag;
  3841         nsEditor::GetTagString(curParent,listTag);
  3842         ToLowerCase(listTag);
  3843         // create a new nested list of correct type
  3844         res = SplitAsNeeded(&listTag, address_of(curParent), &offset);
  3845         NS_ENSURE_SUCCESS(res, res);
  3846         NS_ENSURE_STATE(mHTMLEditor);
  3847         res = mHTMLEditor->CreateNode(listTag, curParent, offset, getter_AddRefs(curList));
  3848         NS_ENSURE_SUCCESS(res, res);
  3849         // curList is now the correct thing to put curNode in
  3850         // remember our new block for postprocessing
  3851         mNewBlock = curList;
  3853       // tuck the node into the end of the active list
  3854       uint32_t listLen;
  3855       NS_ENSURE_STATE(mHTMLEditor);
  3856       res = mHTMLEditor->GetLengthOfDOMNode(curList, listLen);
  3857       NS_ENSURE_SUCCESS(res, res);
  3858       NS_ENSURE_STATE(mHTMLEditor);
  3859       res = mHTMLEditor->MoveNode(curNode, curList, listLen);
  3860       NS_ENSURE_SUCCESS(res, res);
  3863     else // not a list item
  3865       if (IsBlockNode(curNode)) {
  3866         RelativeChangeIndentationOfElementNode(curNode, +1);
  3867         curQuote = nullptr;
  3869       else {
  3870         if (!curQuote)
  3872           // First, check that our element can contain a div.
  3873           if (!mEditor->CanContainTag(curParent, nsGkAtoms::div)) {
  3874             return NS_OK; // cancelled
  3877           NS_NAMED_LITERAL_STRING(divquoteType, "div");
  3878           res = SplitAsNeeded(&divquoteType, address_of(curParent), &offset);
  3879           NS_ENSURE_SUCCESS(res, res);
  3880           NS_ENSURE_STATE(mHTMLEditor);
  3881           res = mHTMLEditor->CreateNode(divquoteType, curParent, offset, getter_AddRefs(curQuote));
  3882           NS_ENSURE_SUCCESS(res, res);
  3883           RelativeChangeIndentationOfElementNode(curQuote, +1);
  3884           // remember our new block for postprocessing
  3885           mNewBlock = curQuote;
  3886           // curQuote is now the correct thing to put curNode in
  3889         // tuck the node into the end of the active blockquote
  3890         uint32_t quoteLen;
  3891         NS_ENSURE_STATE(mHTMLEditor);
  3892         res = mHTMLEditor->GetLengthOfDOMNode(curQuote, quoteLen);
  3893         NS_ENSURE_SUCCESS(res, res);
  3894         NS_ENSURE_STATE(mHTMLEditor);
  3895         res = mHTMLEditor->MoveNode(curNode, curQuote, quoteLen);
  3896         NS_ENSURE_SUCCESS(res, res);
  3900   return res;
  3903 nsresult
  3904 nsHTMLEditRules::WillHTMLIndent(Selection* aSelection,
  3905                                 bool* aCancel, bool* aHandled)
  3907   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
  3908   nsresult res = WillInsert(aSelection, aCancel);
  3909   NS_ENSURE_SUCCESS(res, res);
  3911   // initialize out param
  3912   // we want to ignore result of WillInsert()
  3913   *aCancel = false;
  3914   *aHandled = true;
  3916   res = NormalizeSelection(aSelection);
  3917   NS_ENSURE_SUCCESS(res, res);
  3918   NS_ENSURE_STATE(mHTMLEditor);
  3919   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
  3921   // convert the selection ranges into "promoted" selection ranges:
  3922   // this basically just expands the range to include the immediate
  3923   // block parent, and then further expands to include any ancestors
  3924   // whose children are all in the range
  3926   nsCOMArray<nsIDOMRange> arrayOfRanges;
  3927   res = GetPromotedRanges(aSelection, arrayOfRanges, EditAction::indent);
  3928   NS_ENSURE_SUCCESS(res, res);
  3930   // use these ranges to contruct a list of nodes to act on.
  3931   nsCOMArray<nsIDOMNode> arrayOfNodes;
  3932   res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, EditAction::indent);
  3933   NS_ENSURE_SUCCESS(res, res);                                 
  3935   NS_NAMED_LITERAL_STRING(quoteType, "blockquote");
  3937   // if nothing visible in list, make an empty block
  3938   if (ListIsEmptyLine(arrayOfNodes))
  3940     nsCOMPtr<nsIDOMNode> parent, theBlock;
  3941     int32_t offset;
  3943     // get selection location
  3944     NS_ENSURE_STATE(mHTMLEditor);
  3945     res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset);
  3946     NS_ENSURE_SUCCESS(res, res);
  3947     // make sure we can put a block here
  3948     res = SplitAsNeeded(&quoteType, address_of(parent), &offset);
  3949     NS_ENSURE_SUCCESS(res, res);
  3950     NS_ENSURE_STATE(mHTMLEditor);
  3951     res = mHTMLEditor->CreateNode(quoteType, parent, offset, getter_AddRefs(theBlock));
  3952     NS_ENSURE_SUCCESS(res, res);
  3953     // remember our new block for postprocessing
  3954     mNewBlock = theBlock;
  3955     // delete anything that was in the list of nodes
  3956     for (int32_t j = arrayOfNodes.Count() - 1; j >= 0; --j) 
  3958       nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[0];
  3959       NS_ENSURE_STATE(mHTMLEditor);
  3960       res = mHTMLEditor->DeleteNode(curNode);
  3961       NS_ENSURE_SUCCESS(res, res);
  3962       arrayOfNodes.RemoveObjectAt(0);
  3964     // put selection in new block
  3965     res = aSelection->Collapse(theBlock,0);
  3966     selectionResetter.Abort();  // to prevent selection reseter from overriding us.
  3967     *aHandled = true;
  3968     return res;
  3971   // Ok, now go through all the nodes and put them in a blockquote, 
  3972   // or whatever is appropriate.  Wohoo!
  3973   int32_t i;
  3974   nsCOMPtr<nsIDOMNode> curParent, curQuote, curList, indentedLI, sibling;
  3975   int32_t listCount = arrayOfNodes.Count();
  3976   for (i=0; i<listCount; i++)
  3978     // here's where we actually figure out what to do
  3979     nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
  3981     // Ignore all non-editable nodes.  Leave them be.
  3982     NS_ENSURE_STATE(mHTMLEditor);
  3983     if (!mHTMLEditor->IsEditable(curNode)) continue;
  3985     int32_t offset;
  3986     curParent = nsEditor::GetNodeLocation(curNode, &offset);
  3988     // some logic for putting list items into nested lists...
  3989     if (nsHTMLEditUtils::IsList(curParent))
  3991       sibling = nullptr;
  3993       // Check for whether we should join a list that follows curNode.
  3994       // We do this if the next element is a list, and the list is of the
  3995       // same type (li/ol) as curNode was a part it.
  3996       NS_ENSURE_STATE(mHTMLEditor);
  3997       mHTMLEditor->GetNextHTMLSibling(curNode, address_of(sibling));
  3998       if (sibling && nsHTMLEditUtils::IsList(sibling))
  4000         nsAutoString curListTag, siblingListTag;
  4001         nsEditor::GetTagString(curParent, curListTag);
  4002         nsEditor::GetTagString(sibling, siblingListTag);
  4003         if (curListTag == siblingListTag)
  4005           NS_ENSURE_STATE(mHTMLEditor);
  4006           res = mHTMLEditor->MoveNode(curNode, sibling, 0);
  4007           NS_ENSURE_SUCCESS(res, res);
  4008           continue;
  4012       // Check for whether we should join a list that preceeds curNode.
  4013       // We do this if the previous element is a list, and the list is of
  4014       // the same type (li/ol) as curNode was a part of.
  4015       NS_ENSURE_STATE(mHTMLEditor);
  4016       mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
  4017       if (sibling && nsHTMLEditUtils::IsList(sibling))
  4019         nsAutoString curListTag, siblingListTag;
  4020         nsEditor::GetTagString(curParent, curListTag);
  4021         nsEditor::GetTagString(sibling, siblingListTag);
  4022         if (curListTag == siblingListTag)
  4024           NS_ENSURE_STATE(mHTMLEditor);
  4025           res = mHTMLEditor->MoveNode(curNode, sibling, -1);
  4026           NS_ENSURE_SUCCESS(res, res);
  4027           continue;
  4031       sibling = nullptr;
  4033       // check to see if curList is still appropriate.  Which it is if
  4034       // curNode is still right after it in the same list.
  4035       if (curList) {
  4036         NS_ENSURE_STATE(mHTMLEditor);
  4037         mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
  4040       if (!curList || (sibling && sibling != curList) )
  4042         nsAutoString listTag;
  4043         nsEditor::GetTagString(curParent,listTag);
  4044         ToLowerCase(listTag);
  4045         // create a new nested list of correct type
  4046         res = SplitAsNeeded(&listTag, address_of(curParent), &offset);
  4047         NS_ENSURE_SUCCESS(res, res);
  4048         NS_ENSURE_STATE(mHTMLEditor);
  4049         res = mHTMLEditor->CreateNode(listTag, curParent, offset, getter_AddRefs(curList));
  4050         NS_ENSURE_SUCCESS(res, res);
  4051         // curList is now the correct thing to put curNode in
  4052         // remember our new block for postprocessing
  4053         mNewBlock = curList;
  4055       // tuck the node into the end of the active list
  4056       NS_ENSURE_STATE(mHTMLEditor);
  4057       res = mHTMLEditor->MoveNode(curNode, curList, -1);
  4058       NS_ENSURE_SUCCESS(res, res);
  4059       // forget curQuote, if any
  4060       curQuote = nullptr;
  4063     else // not a list item, use blockquote?
  4065       // if we are inside a list item, we don't want to blockquote, we want
  4066       // to sublist the list item.  We may have several nodes listed in the
  4067       // array of nodes to act on, that are in the same list item.  Since
  4068       // we only want to indent that li once, we must keep track of the most
  4069       // recent indented list item, and not indent it if we find another node
  4070       // to act on that is still inside the same li.
  4071       nsCOMPtr<nsIDOMNode> listitem=IsInListItem(curNode);
  4072       if (listitem)
  4074         if (indentedLI == listitem) continue;  // already indented this list item
  4075         curParent = nsEditor::GetNodeLocation(listitem, &offset);
  4076         // check to see if curList is still appropriate.  Which it is if
  4077         // curNode is still right after it in the same list.
  4078         if (curList)
  4080           sibling = nullptr;
  4081           NS_ENSURE_STATE(mHTMLEditor);
  4082           mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
  4085         if (!curList || (sibling && sibling != curList) )
  4087           nsAutoString listTag;
  4088           nsEditor::GetTagString(curParent,listTag);
  4089           ToLowerCase(listTag);
  4090           // create a new nested list of correct type
  4091           res = SplitAsNeeded(&listTag, address_of(curParent), &offset);
  4092           NS_ENSURE_SUCCESS(res, res);
  4093           NS_ENSURE_STATE(mHTMLEditor);
  4094           res = mHTMLEditor->CreateNode(listTag, curParent, offset, getter_AddRefs(curList));
  4095           NS_ENSURE_SUCCESS(res, res);
  4097         NS_ENSURE_STATE(mHTMLEditor);
  4098         res = mHTMLEditor->MoveNode(listitem, curList, -1);
  4099         NS_ENSURE_SUCCESS(res, res);
  4100         // remember we indented this li
  4101         indentedLI = listitem;
  4104       else
  4106         // need to make a blockquote to put things in if we haven't already,
  4107         // or if this node doesn't go in blockquote we used earlier.
  4108         // One reason it might not go in prio blockquote is if we are now
  4109         // in a different table cell. 
  4110         if (curQuote && InDifferentTableElements(curQuote, curNode)) {
  4111           curQuote = nullptr;
  4114         if (!curQuote) 
  4116           // First, check that our element can contain a blockquote.
  4117           if (!mEditor->CanContainTag(curParent, nsGkAtoms::blockquote)) {
  4118             return NS_OK; // cancelled
  4121           res = SplitAsNeeded(&quoteType, address_of(curParent), &offset);
  4122           NS_ENSURE_SUCCESS(res, res);
  4123           NS_ENSURE_STATE(mHTMLEditor);
  4124           res = mHTMLEditor->CreateNode(quoteType, curParent, offset, getter_AddRefs(curQuote));
  4125           NS_ENSURE_SUCCESS(res, res);
  4126           // remember our new block for postprocessing
  4127           mNewBlock = curQuote;
  4128           // curQuote is now the correct thing to put curNode in
  4131         // tuck the node into the end of the active blockquote
  4132         NS_ENSURE_STATE(mHTMLEditor);
  4133         res = mHTMLEditor->MoveNode(curNode, curQuote, -1);
  4134         NS_ENSURE_SUCCESS(res, res);
  4135         // forget curList, if any
  4136         curList = nullptr;
  4140   return res;
  4144 nsresult
  4145 nsHTMLEditRules::WillOutdent(Selection* aSelection,
  4146                              bool* aCancel, bool* aHandled)
  4148   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
  4149   // initialize out param
  4150   *aCancel = false;
  4151   *aHandled = true;
  4152   nsresult res = NS_OK;
  4153   nsCOMPtr<nsIDOMNode> rememberedLeftBQ, rememberedRightBQ;
  4154   NS_ENSURE_STATE(mHTMLEditor);
  4155   bool useCSS = mHTMLEditor->IsCSSEnabled();
  4157   res = NormalizeSelection(aSelection);
  4158   NS_ENSURE_SUCCESS(res, res);
  4159   // some scoping for selection resetting - we may need to tweak it
  4161     NS_ENSURE_STATE(mHTMLEditor);
  4162     nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
  4164     // convert the selection ranges into "promoted" selection ranges:
  4165     // this basically just expands the range to include the immediate
  4166     // block parent, and then further expands to include any ancestors
  4167     // whose children are all in the range
  4168     nsCOMArray<nsIDOMNode> arrayOfNodes;
  4169     res = GetNodesFromSelection(aSelection, EditAction::outdent,
  4170                                 arrayOfNodes);
  4171     NS_ENSURE_SUCCESS(res, res);
  4173     // Ok, now go through all the nodes and remove a level of blockquoting, 
  4174     // or whatever is appropriate.  Wohoo!
  4176     nsCOMPtr<nsIDOMNode> curBlockQuote, firstBQChild, lastBQChild;
  4177     bool curBlockQuoteIsIndentedWithCSS = false;
  4178     int32_t listCount = arrayOfNodes.Count();
  4179     int32_t i;
  4180     nsCOMPtr<nsIDOMNode> curParent;
  4181     for (i=0; i<listCount; i++)
  4183       // here's where we actually figure out what to do
  4184       nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
  4185       int32_t offset;
  4186       curParent = nsEditor::GetNodeLocation(curNode, &offset);
  4188       // is it a blockquote?
  4189       if (nsHTMLEditUtils::IsBlockquote(curNode)) 
  4191         // if it is a blockquote, remove it.
  4192         // So we need to finish up dealng with any curBlockQuote first.
  4193         if (curBlockQuote)
  4195           res = OutdentPartOfBlock(curBlockQuote, firstBQChild, lastBQChild,
  4196                                    curBlockQuoteIsIndentedWithCSS,
  4197                                    address_of(rememberedLeftBQ),
  4198                                    address_of(rememberedRightBQ));
  4199           NS_ENSURE_SUCCESS(res, res);
  4200           curBlockQuote = 0;  firstBQChild = 0;  lastBQChild = 0;
  4201           curBlockQuoteIsIndentedWithCSS = false;
  4203         NS_ENSURE_STATE(mHTMLEditor);
  4204         res = mHTMLEditor->RemoveBlockContainer(curNode);
  4205         NS_ENSURE_SUCCESS(res, res);
  4206         continue;
  4208       // is it a block with a 'margin' property?
  4209       if (useCSS && IsBlockNode(curNode))
  4211         NS_ENSURE_STATE(mHTMLEditor);
  4212         nsIAtom* marginProperty = MarginPropertyAtomForIndent(mHTMLEditor->mHTMLCSSUtils, curNode);
  4213         nsAutoString value;
  4214         NS_ENSURE_STATE(mHTMLEditor);
  4215         mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(curNode, marginProperty, value);
  4216         float f;
  4217         nsCOMPtr<nsIAtom> unit;
  4218         NS_ENSURE_STATE(mHTMLEditor);
  4219         mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit));
  4220         if (f > 0)
  4222           RelativeChangeIndentationOfElementNode(curNode, -1);
  4223           continue;
  4226       // is it a list item?
  4227       if (nsHTMLEditUtils::IsListItem(curNode)) 
  4229         // if it is a list item, that means we are not outdenting whole list.
  4230         // So we need to finish up dealing with any curBlockQuote, and then
  4231         // pop this list item.
  4232         if (curBlockQuote)
  4234           res = OutdentPartOfBlock(curBlockQuote, firstBQChild, lastBQChild,
  4235                                    curBlockQuoteIsIndentedWithCSS,
  4236                                    address_of(rememberedLeftBQ),
  4237                                    address_of(rememberedRightBQ));
  4238           NS_ENSURE_SUCCESS(res, res);
  4239           curBlockQuote = 0;  firstBQChild = 0;  lastBQChild = 0;
  4240           curBlockQuoteIsIndentedWithCSS = false;
  4242         bool bOutOfList;
  4243         res = PopListItem(curNode, &bOutOfList);
  4244         NS_ENSURE_SUCCESS(res, res);
  4245         continue;
  4247       // do we have a blockquote that we are already committed to removing?
  4248       if (curBlockQuote)
  4250         // if so, is this node a descendant?
  4251         if (nsEditorUtils::IsDescendantOf(curNode, curBlockQuote))
  4253           lastBQChild = curNode;
  4254           continue;  // then we don't need to do anything different for this node
  4256         else
  4258           // otherwise, we have progressed beyond end of curBlockQuote,
  4259           // so lets handle it now.  We need to remove the portion of 
  4260           // curBlockQuote that contains [firstBQChild - lastBQChild].
  4261           res = OutdentPartOfBlock(curBlockQuote, firstBQChild, lastBQChild,
  4262                                    curBlockQuoteIsIndentedWithCSS,
  4263                                    address_of(rememberedLeftBQ),
  4264                                    address_of(rememberedRightBQ));
  4265           NS_ENSURE_SUCCESS(res, res);
  4266           curBlockQuote = 0;  firstBQChild = 0;  lastBQChild = 0;
  4267           curBlockQuoteIsIndentedWithCSS = false;
  4268           // fall out and handle curNode
  4272       // are we inside a blockquote?
  4273       nsCOMPtr<nsIDOMNode> n = curNode;
  4274       nsCOMPtr<nsIDOMNode> tmp;
  4275       curBlockQuoteIsIndentedWithCSS = false;
  4276       // keep looking up the hierarchy as long as we don't hit the body or the
  4277       // active editing host or a table element (other than an entire table)
  4278       while (!nsTextEditUtils::IsBody(n) && mHTMLEditor &&
  4279              mHTMLEditor->IsDescendantOfEditorRoot(n)
  4280           && (nsHTMLEditUtils::IsTable(n) || !nsHTMLEditUtils::IsTableElement(n)))
  4282         n->GetParentNode(getter_AddRefs(tmp));
  4283         if (!tmp) {
  4284           break;
  4286         n = tmp;
  4287         if (nsHTMLEditUtils::IsBlockquote(n))
  4289           // if so, remember it, and remember first node we are taking out of it.
  4290           curBlockQuote = n;
  4291           firstBQChild  = curNode;
  4292           lastBQChild   = curNode;
  4293           break;
  4295         else if (useCSS)
  4297           NS_ENSURE_STATE(mHTMLEditor);
  4298           nsIAtom* marginProperty = MarginPropertyAtomForIndent(mHTMLEditor->mHTMLCSSUtils, curNode);
  4299           nsAutoString value;
  4300           NS_ENSURE_STATE(mHTMLEditor);
  4301           mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(n, marginProperty, value);
  4302           float f;
  4303           nsCOMPtr<nsIAtom> unit;
  4304           NS_ENSURE_STATE(mHTMLEditor);
  4305           mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit));
  4306           if (f > 0 && !(nsHTMLEditUtils::IsList(curParent) && nsHTMLEditUtils::IsList(curNode)))
  4308             curBlockQuote = n;
  4309             firstBQChild  = curNode;
  4310             lastBQChild   = curNode;
  4311             curBlockQuoteIsIndentedWithCSS = true;
  4312             break;
  4317       if (!curBlockQuote)
  4319         // could not find an enclosing blockquote for this node.  handle list cases.
  4320         if (nsHTMLEditUtils::IsList(curParent))  // move node out of list
  4322           if (nsHTMLEditUtils::IsList(curNode))  // just unwrap this sublist
  4324             NS_ENSURE_STATE(mHTMLEditor);
  4325             res = mHTMLEditor->RemoveBlockContainer(curNode);
  4326             NS_ENSURE_SUCCESS(res, res);
  4328           // handled list item case above
  4330         else if (nsHTMLEditUtils::IsList(curNode)) // node is a list, but parent is non-list: move list items out
  4332           nsCOMPtr<nsIDOMNode> child;
  4333           curNode->GetLastChild(getter_AddRefs(child));
  4334           while (child)
  4336             if (nsHTMLEditUtils::IsListItem(child))
  4338               bool bOutOfList;
  4339               res = PopListItem(child, &bOutOfList);
  4340               NS_ENSURE_SUCCESS(res, res);
  4342             else if (nsHTMLEditUtils::IsList(child))
  4344               // We have an embedded list, so move it out from under the
  4345               // parent list. Be sure to put it after the parent list
  4346               // because this loop iterates backwards through the parent's
  4347               // list of children.
  4349               NS_ENSURE_STATE(mHTMLEditor);
  4350               res = mHTMLEditor->MoveNode(child, curParent, offset + 1);
  4351               NS_ENSURE_SUCCESS(res, res);
  4353             else
  4355               // delete any non- list items for now
  4356               NS_ENSURE_STATE(mHTMLEditor);
  4357               res = mHTMLEditor->DeleteNode(child);
  4358               NS_ENSURE_SUCCESS(res, res);
  4360             curNode->GetLastChild(getter_AddRefs(child));
  4362           // delete the now-empty list
  4363           NS_ENSURE_STATE(mHTMLEditor);
  4364           res = mHTMLEditor->RemoveBlockContainer(curNode);
  4365           NS_ENSURE_SUCCESS(res, res);
  4367         else if (useCSS) {
  4368           nsCOMPtr<nsIDOMElement> element;
  4369           nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(curNode);
  4370           if (textNode) {
  4371             // We want to outdent the parent of text nodes
  4372             nsCOMPtr<nsIDOMNode> parent;
  4373             textNode->GetParentNode(getter_AddRefs(parent));
  4374             element = do_QueryInterface(parent);
  4375           } else {
  4376             element = do_QueryInterface(curNode);
  4378           if (element) {
  4379             RelativeChangeIndentationOfElementNode(element, -1);
  4384     if (curBlockQuote)
  4386       // we have a blockquote we haven't finished handling
  4387       res = OutdentPartOfBlock(curBlockQuote, firstBQChild, lastBQChild,
  4388                                curBlockQuoteIsIndentedWithCSS,
  4389                                address_of(rememberedLeftBQ),
  4390                                address_of(rememberedRightBQ));
  4391       NS_ENSURE_SUCCESS(res, res);
  4394   // make sure selection didn't stick to last piece of content in old bq
  4395   // (only a problem for collapsed selections)
  4396   if (rememberedLeftBQ || rememberedRightBQ) {
  4397     if (aSelection->Collapsed()) {
  4398       // push selection past end of rememberedLeftBQ
  4399       nsCOMPtr<nsIDOMNode> sNode;
  4400       int32_t sOffset;
  4401       NS_ENSURE_STATE(mHTMLEditor);
  4402       mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(sNode), &sOffset);
  4403       if (rememberedLeftBQ &&
  4404           ((sNode == rememberedLeftBQ) || nsEditorUtils::IsDescendantOf(sNode, rememberedLeftBQ)))
  4406         // selection is inside rememberedLeftBQ - push it past it.
  4407         sNode = nsEditor::GetNodeLocation(rememberedLeftBQ, &sOffset);
  4408         sOffset++;
  4409         aSelection->Collapse(sNode, sOffset);
  4411       // and pull selection before beginning of rememberedRightBQ
  4412       NS_ENSURE_STATE(mHTMLEditor);
  4413       mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(sNode), &sOffset);
  4414       if (rememberedRightBQ &&
  4415           ((sNode == rememberedRightBQ) || nsEditorUtils::IsDescendantOf(sNode, rememberedRightBQ)))
  4417         // selection is inside rememberedRightBQ - push it before it.
  4418         sNode = nsEditor::GetNodeLocation(rememberedRightBQ, &sOffset);
  4419         aSelection->Collapse(sNode, sOffset);
  4422     return NS_OK;
  4424   return res;
  4428 ///////////////////////////////////////////////////////////////////////////
  4429 // RemovePartOfBlock:  split aBlock and move aStartChild to aEndChild out
  4430 //                     of aBlock.  return left side of block (if any) in
  4431 //                     aLeftNode.  return right side of block (if any) in
  4432 //                     aRightNode.  
  4433 //                  
  4434 nsresult 
  4435 nsHTMLEditRules::RemovePartOfBlock(nsIDOMNode *aBlock, 
  4436                                    nsIDOMNode *aStartChild, 
  4437                                    nsIDOMNode *aEndChild,
  4438                                    nsCOMPtr<nsIDOMNode> *aLeftNode,
  4439                                    nsCOMPtr<nsIDOMNode> *aRightNode)
  4441   nsCOMPtr<nsIDOMNode> middleNode;
  4442   nsresult res = SplitBlock(aBlock, aStartChild, aEndChild,
  4443                             aLeftNode, aRightNode,
  4444                             address_of(middleNode));
  4445   NS_ENSURE_SUCCESS(res, res);
  4446   // get rid of part of blockquote we are outdenting
  4448   NS_ENSURE_STATE(mHTMLEditor);
  4449   return mHTMLEditor->RemoveBlockContainer(aBlock);
  4452 nsresult 
  4453 nsHTMLEditRules::SplitBlock(nsIDOMNode *aBlock, 
  4454                             nsIDOMNode *aStartChild, 
  4455                             nsIDOMNode *aEndChild,
  4456                             nsCOMPtr<nsIDOMNode> *aLeftNode,
  4457                             nsCOMPtr<nsIDOMNode> *aRightNode,
  4458                             nsCOMPtr<nsIDOMNode> *aMiddleNode)
  4460   NS_ENSURE_TRUE(aBlock && aStartChild && aEndChild, NS_ERROR_NULL_POINTER);
  4462   nsCOMPtr<nsIDOMNode> leftNode, rightNode;
  4463   int32_t startOffset, endOffset, offset;
  4464   nsresult res;
  4466   // get split point location
  4467   nsCOMPtr<nsIDOMNode> startParent = nsEditor::GetNodeLocation(aStartChild, &startOffset);
  4469   // do the splits!
  4470   NS_ENSURE_STATE(mHTMLEditor);
  4471   res = mHTMLEditor->SplitNodeDeep(aBlock, startParent, startOffset, &offset, 
  4472                                    true, address_of(leftNode), address_of(rightNode));
  4473   NS_ENSURE_SUCCESS(res, res);
  4474   if (rightNode)  aBlock = rightNode;
  4476   // remember left portion of block if caller requested
  4477   if (aLeftNode) 
  4478     *aLeftNode = leftNode;
  4480   // get split point location
  4481   nsCOMPtr<nsIDOMNode> endParent = nsEditor::GetNodeLocation(aEndChild, &endOffset);
  4482   endOffset++;  // want to be after lastBQChild
  4484   // do the splits!
  4485   NS_ENSURE_STATE(mHTMLEditor);
  4486   res = mHTMLEditor->SplitNodeDeep(aBlock, endParent, endOffset, &offset, 
  4487                                    true, address_of(leftNode), address_of(rightNode));
  4488   NS_ENSURE_SUCCESS(res, res);
  4489   if (leftNode)  aBlock = leftNode;
  4491   // remember right portion of block if caller requested
  4492   if (aRightNode) 
  4493     *aRightNode = rightNode;
  4495   if (aMiddleNode)
  4496     *aMiddleNode = aBlock;
  4498   return NS_OK;
  4501 nsresult
  4502 nsHTMLEditRules::OutdentPartOfBlock(nsIDOMNode *aBlock, 
  4503                                     nsIDOMNode *aStartChild, 
  4504                                     nsIDOMNode *aEndChild,
  4505                                     bool aIsBlockIndentedWithCSS,
  4506                                     nsCOMPtr<nsIDOMNode> *aLeftNode,
  4507                                     nsCOMPtr<nsIDOMNode> *aRightNode)
  4509   nsCOMPtr<nsIDOMNode> middleNode;
  4510   nsresult res = SplitBlock(aBlock, aStartChild, aEndChild, 
  4511                             aLeftNode,
  4512                             aRightNode,
  4513                             address_of(middleNode));
  4514   NS_ENSURE_SUCCESS(res, res);
  4515   if (aIsBlockIndentedWithCSS) {
  4516     res = RelativeChangeIndentationOfElementNode(middleNode, -1);
  4517   } else {
  4518     NS_ENSURE_STATE(mHTMLEditor);
  4519     res = mHTMLEditor->RemoveBlockContainer(middleNode);
  4521   return res;
  4524 ///////////////////////////////////////////////////////////////////////////
  4525 // ConvertListType:  convert list type and list item type.
  4526 //                
  4527 //                  
  4528 nsresult
  4529 nsHTMLEditRules::ConvertListType(nsIDOMNode* aList,
  4530                                  nsCOMPtr<nsIDOMNode>* outList,
  4531                                  nsIAtom* aListType,
  4532                                  nsIAtom* aItemType)
  4534   MOZ_ASSERT(aListType);
  4535   MOZ_ASSERT(aItemType);
  4537   NS_ENSURE_TRUE(aList && outList, NS_ERROR_NULL_POINTER);
  4538   nsCOMPtr<nsINode> list = do_QueryInterface(aList);
  4539   NS_ENSURE_STATE(list);
  4541   nsCOMPtr<dom::Element> outNode;
  4542   nsresult rv = ConvertListType(list, getter_AddRefs(outNode), aListType, aItemType);
  4543   *outList = outNode ? outNode->AsDOMNode() : nullptr;
  4544   return rv;
  4547 nsresult
  4548 nsHTMLEditRules::ConvertListType(nsINode* aList,
  4549                                  dom::Element** aOutList,
  4550                                  nsIAtom* aListType,
  4551                                  nsIAtom* aItemType)
  4553   MOZ_ASSERT(aList);
  4554   MOZ_ASSERT(aOutList);
  4555   MOZ_ASSERT(aListType);
  4556   MOZ_ASSERT(aItemType);
  4558   nsCOMPtr<nsINode> child = aList->GetFirstChild();
  4559   while (child)
  4561     if (child->IsElement()) {
  4562       dom::Element* element = child->AsElement();
  4563       if (nsHTMLEditUtils::IsListItem(element) && !element->IsHTML(aItemType)) {
  4564         nsCOMPtr<dom::Element> temp;
  4565         nsresult rv =
  4566           mHTMLEditor->ReplaceContainer(child, getter_AddRefs(temp),
  4567                                         nsDependentAtomString(aItemType));
  4568         NS_ENSURE_SUCCESS(rv, rv);
  4569         child = temp.forget();
  4570       } else if (nsHTMLEditUtils::IsList(element) &&
  4571                  !element->IsHTML(aListType)) {
  4572         nsCOMPtr<dom::Element> temp;
  4573         nsresult rv =
  4574           ConvertListType(child, getter_AddRefs(temp), aListType, aItemType);
  4575         NS_ENSURE_SUCCESS(rv, rv);
  4576         child = temp.forget();
  4579     child = child->GetNextSibling();
  4582   if (aList->IsElement() && aList->AsElement()->IsHTML(aListType)) {
  4583     nsCOMPtr<dom::Element> list = aList->AsElement();
  4584     list.forget(aOutList);
  4585     return NS_OK;
  4588   return mHTMLEditor->ReplaceContainer(aList, aOutList,
  4589                                        nsDependentAtomString(aListType));
  4593 ///////////////////////////////////////////////////////////////////////////
  4594 // CreateStyleForInsertText:  take care of clearing and setting appropriate
  4595 //                            style nodes for text insertion.
  4596 //
  4597 //
  4598 nsresult
  4599 nsHTMLEditRules::CreateStyleForInsertText(nsISelection *aSelection,
  4600                                           nsIDOMDocument *aDoc)
  4602   MOZ_ASSERT(aSelection && aDoc && mHTMLEditor->mTypeInState);
  4604   bool weDidSomething = false;
  4605   nsCOMPtr<nsIDOMNode> node, tmp;
  4606   int32_t offset;
  4607   NS_ENSURE_STATE(mHTMLEditor);
  4608   nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection,
  4609                                                     getter_AddRefs(node),
  4610                                                     &offset);
  4611   NS_ENSURE_SUCCESS(res, res);
  4613   // next examine our present style and make sure default styles are either
  4614   // present or explicitly overridden.  If neither, add the default style to
  4615   // the TypeInState
  4616   int32_t length = mHTMLEditor->mDefaultStyles.Length();
  4617   for (int32_t j = 0; j < length; j++) {
  4618     PropItem* propItem = mHTMLEditor->mDefaultStyles[j];
  4619     MOZ_ASSERT(propItem);
  4620     bool bFirst, bAny, bAll;
  4622     // GetInlineProperty also examine TypeInState.  The only gotcha here is
  4623     // that a cleared property looks like an unset property.  For now I'm
  4624     // assuming that's not a problem: that default styles will always be
  4625     // multivalue styles (like font face or size) where clearing the style
  4626     // means we want to go back to the default.  If we ever wanted a "toggle"
  4627     // style like bold for a default, though, I'll have to add code to detect
  4628     // the difference between unset and explicitly cleared, else user would
  4629     // never be able to unbold, for instance.
  4630     nsAutoString curValue;
  4631     NS_ENSURE_STATE(mHTMLEditor);
  4632     res = mHTMLEditor->GetInlinePropertyBase(propItem->tag, &propItem->attr,
  4633                                              nullptr, &bFirst, &bAny, &bAll,
  4634                                              &curValue, false);
  4635     NS_ENSURE_SUCCESS(res, res);
  4637     if (!bAny) {
  4638       // no style set for this prop/attr
  4639       mHTMLEditor->mTypeInState->SetProp(propItem->tag, propItem->attr,
  4640                                          propItem->value);
  4644   nsCOMPtr<nsIDOMElement> rootElement;
  4645   res = aDoc->GetDocumentElement(getter_AddRefs(rootElement));
  4646   NS_ENSURE_SUCCESS(res, res);
  4648   // process clearing any styles first
  4649   nsAutoPtr<PropItem> item(mHTMLEditor->mTypeInState->TakeClearProperty());
  4650   while (item && node != rootElement) {
  4651     NS_ENSURE_STATE(mHTMLEditor);
  4652     res = mHTMLEditor->ClearStyle(address_of(node), &offset,
  4653                                   item->tag, &item->attr);
  4654     NS_ENSURE_SUCCESS(res, res);
  4655     item = mHTMLEditor->mTypeInState->TakeClearProperty();
  4656     weDidSomething = true;
  4659   // then process setting any styles
  4660   int32_t relFontSize = mHTMLEditor->mTypeInState->TakeRelativeFontSize();
  4661   item = mHTMLEditor->mTypeInState->TakeSetProperty();
  4663   if (item || relFontSize) {
  4664     // we have at least one style to add; make a new text node to insert style
  4665     // nodes above.
  4666     if (mHTMLEditor->IsTextNode(node)) {
  4667       // if we are in a text node, split it
  4668       NS_ENSURE_STATE(mHTMLEditor);
  4669       res = mHTMLEditor->SplitNodeDeep(node, node, offset, &offset);
  4670       NS_ENSURE_SUCCESS(res, res);
  4671       node->GetParentNode(getter_AddRefs(tmp));
  4672       node = tmp;
  4674     if (!mHTMLEditor->IsContainer(node)) {
  4675       return NS_OK;
  4677     nsCOMPtr<nsIDOMNode> newNode;
  4678     nsCOMPtr<nsIDOMText> nodeAsText;
  4679     res = aDoc->CreateTextNode(EmptyString(), getter_AddRefs(nodeAsText));
  4680     NS_ENSURE_SUCCESS(res, res);
  4681     NS_ENSURE_TRUE(nodeAsText, NS_ERROR_NULL_POINTER);
  4682     newNode = do_QueryInterface(nodeAsText);
  4683     NS_ENSURE_STATE(mHTMLEditor);
  4684     res = mHTMLEditor->InsertNode(newNode, node, offset);
  4685     NS_ENSURE_SUCCESS(res, res);
  4686     node = newNode;
  4687     offset = 0;
  4688     weDidSomething = true;
  4690     if (relFontSize) {
  4691       // dir indicated bigger versus smaller.  1 = bigger, -1 = smaller
  4692       int32_t dir = relFontSize > 0 ? 1 : -1;
  4693       for (int32_t j = 0; j < DeprecatedAbs(relFontSize); j++) {
  4694         NS_ENSURE_STATE(mHTMLEditor);
  4695         res = mHTMLEditor->RelativeFontChangeOnTextNode(dir, nodeAsText,
  4696                                                         0, -1);
  4697         NS_ENSURE_SUCCESS(res, res);
  4701     while (item) {
  4702       NS_ENSURE_STATE(mHTMLEditor);
  4703       res = mHTMLEditor->SetInlinePropertyOnNode(node, item->tag, &item->attr,
  4704                                                  &item->value);
  4705       NS_ENSURE_SUCCESS(res, res);
  4706       item = mHTMLEditor->mTypeInState->TakeSetProperty();
  4709   if (weDidSomething) {
  4710     return aSelection->Collapse(node, offset);
  4713   return NS_OK;
  4717 ///////////////////////////////////////////////////////////////////////////
  4718 // IsEmptyBlock: figure out if aNode is (or is inside) an empty block.
  4719 //               A block can have children and still be considered empty,
  4720 //               if the children are empty or non-editable.
  4721 //                  
  4722 nsresult 
  4723 nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode, 
  4724                               bool *outIsEmptyBlock, 
  4725                               bool aMozBRDoesntCount,
  4726                               bool aListItemsNotEmpty) 
  4728   NS_ENSURE_TRUE(aNode && outIsEmptyBlock, NS_ERROR_NULL_POINTER);
  4729   *outIsEmptyBlock = true;
  4731 //  nsresult res = NS_OK;
  4732   nsCOMPtr<nsIDOMNode> nodeToTest;
  4733   if (IsBlockNode(aNode)) nodeToTest = do_QueryInterface(aNode);
  4734 //  else nsCOMPtr<nsIDOMElement> block;
  4735 //  looks like I forgot to finish this.  Wonder what I was going to do?
  4737   NS_ENSURE_TRUE(nodeToTest, NS_ERROR_NULL_POINTER);
  4738   return mHTMLEditor->IsEmptyNode(nodeToTest, outIsEmptyBlock,
  4739                      aMozBRDoesntCount, aListItemsNotEmpty);
  4743 nsresult
  4744 nsHTMLEditRules::WillAlign(Selection* aSelection,
  4745                            const nsAString *alignType, 
  4746                            bool *aCancel,
  4747                            bool *aHandled)
  4749   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
  4751   nsresult res = WillInsert(aSelection, aCancel);
  4752   NS_ENSURE_SUCCESS(res, res);
  4754   // initialize out param
  4755   // we want to ignore result of WillInsert()
  4756   *aCancel = false;
  4757   *aHandled = false;
  4759   res = NormalizeSelection(aSelection);
  4760   NS_ENSURE_SUCCESS(res, res);
  4761   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
  4763   // convert the selection ranges into "promoted" selection ranges:
  4764   // this basically just expands the range to include the immediate
  4765   // block parent, and then further expands to include any ancestors
  4766   // whose children are all in the range
  4767   *aHandled = true;
  4768   nsCOMArray<nsIDOMNode> arrayOfNodes;
  4769   res = GetNodesFromSelection(aSelection, EditAction::align, arrayOfNodes);
  4770   NS_ENSURE_SUCCESS(res, res);
  4772   // if we don't have any nodes, or we have only a single br, then we are
  4773   // creating an empty alignment div.  We have to do some different things for these.
  4774   bool emptyDiv = false;
  4775   int32_t listCount = arrayOfNodes.Count();
  4776   if (!listCount) emptyDiv = true;
  4777   if (listCount == 1)
  4779     nsCOMPtr<nsIDOMNode> theNode = arrayOfNodes[0];
  4781     if (nsHTMLEditUtils::SupportsAlignAttr(theNode))
  4783       // the node is a table element, an horiz rule, a paragraph, a div
  4784       // or a section header; in HTML 4, it can directly carry the ALIGN
  4785       // attribute and we don't need to make a div! If we are in CSS mode,
  4786       // all the work is done in AlignBlock
  4787       nsCOMPtr<nsIDOMElement> theElem = do_QueryInterface(theNode);
  4788       res = AlignBlock(theElem, alignType, true);
  4789       NS_ENSURE_SUCCESS(res, res);
  4790       return NS_OK;
  4793     if (nsTextEditUtils::IsBreak(theNode))
  4795       // The special case emptyDiv code (below) that consumes BRs can
  4796       // cause tables to split if the start node of the selection is
  4797       // not in a table cell or caption, for example parent is a <tr>.
  4798       // Avoid this unnecessary splitting if possible by leaving emptyDiv
  4799       // FALSE so that we fall through to the normal case alignment code.
  4800       //
  4801       // XXX: It seems a little error prone for the emptyDiv special
  4802       //      case code to assume that the start node of the selection
  4803       //      is the parent of the single node in the arrayOfNodes, as
  4804       //      the paragraph above points out. Do we rely on the selection
  4805       //      start node because of the fact that arrayOfNodes can be empty?
  4806       //      We should probably revisit this issue. - kin
  4808       nsCOMPtr<nsIDOMNode> parent;
  4809       int32_t offset;
  4810       NS_ENSURE_STATE(mHTMLEditor);
  4811       res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset);
  4813       if (!nsHTMLEditUtils::IsTableElement(parent) || nsHTMLEditUtils::IsTableCellOrCaption(parent))
  4814         emptyDiv = true;
  4817   if (emptyDiv)
  4819     int32_t offset;
  4820     nsCOMPtr<nsIDOMNode> brNode, parent, theDiv, sib;
  4821     NS_NAMED_LITERAL_STRING(divType, "div");
  4822     NS_ENSURE_STATE(mHTMLEditor);
  4823     res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset);
  4824     NS_ENSURE_SUCCESS(res, res);
  4825     res = SplitAsNeeded(&divType, address_of(parent), &offset);
  4826     NS_ENSURE_SUCCESS(res, res);
  4827     // consume a trailing br, if any.  This is to keep an alignment from
  4828     // creating extra lines, if possible.
  4829     NS_ENSURE_STATE(mHTMLEditor);
  4830     res = mHTMLEditor->GetNextHTMLNode(parent, offset, address_of(brNode));
  4831     NS_ENSURE_SUCCESS(res, res);
  4832     if (brNode && nsTextEditUtils::IsBreak(brNode))
  4834       // making use of html structure... if next node after where
  4835       // we are putting our div is not a block, then the br we 
  4836       // found is in same block we are, so its safe to consume it.
  4837       NS_ENSURE_STATE(mHTMLEditor);
  4838       res = mHTMLEditor->GetNextHTMLSibling(parent, offset, address_of(sib));
  4839       NS_ENSURE_SUCCESS(res, res);
  4840       if (!IsBlockNode(sib))
  4842         NS_ENSURE_STATE(mHTMLEditor);
  4843         res = mHTMLEditor->DeleteNode(brNode);
  4844         NS_ENSURE_SUCCESS(res, res);
  4847     NS_ENSURE_STATE(mHTMLEditor);
  4848     res = mHTMLEditor->CreateNode(divType, parent, offset, getter_AddRefs(theDiv));
  4849     NS_ENSURE_SUCCESS(res, res);
  4850     // remember our new block for postprocessing
  4851     mNewBlock = theDiv;
  4852     // set up the alignment on the div, using HTML or CSS
  4853     nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(theDiv);
  4854     res = AlignBlock(divElem, alignType, true);
  4855     NS_ENSURE_SUCCESS(res, res);
  4856     *aHandled = true;
  4857     // put in a moz-br so that it won't get deleted
  4858     res = CreateMozBR(theDiv, 0);
  4859     NS_ENSURE_SUCCESS(res, res);
  4860     res = aSelection->Collapse(theDiv, 0);
  4861     selectionResetter.Abort();  // don't reset our selection in this case.
  4862     return res;
  4865   // Next we detect all the transitions in the array, where a transition
  4866   // means that adjacent nodes in the array don't have the same parent.
  4868   nsTArray<bool> transitionList;
  4869   res = MakeTransitionList(arrayOfNodes, transitionList);
  4870   NS_ENSURE_SUCCESS(res, res);                                 
  4872   // Ok, now go through all the nodes and give them an align attrib or put them in a div, 
  4873   // or whatever is appropriate.  Wohoo!
  4875   nsCOMPtr<nsIDOMNode> curParent;
  4876   nsCOMPtr<nsIDOMNode> curDiv;
  4877   bool useCSS = mHTMLEditor->IsCSSEnabled();
  4878   for (int32_t i = 0; i < listCount; ++i) {
  4879     // here's where we actually figure out what to do
  4880     nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
  4882     // Ignore all non-editable nodes.  Leave them be.
  4883     if (!mHTMLEditor->IsEditable(curNode)) continue;
  4885     int32_t offset;
  4886     curParent = nsEditor::GetNodeLocation(curNode, &offset);
  4888     // the node is a table element, an horiz rule, a paragraph, a div
  4889     // or a section header; in HTML 4, it can directly carry the ALIGN
  4890     // attribute and we don't need to nest it, just set the alignment.
  4891     // In CSS, assign the corresponding CSS styles in AlignBlock
  4892     if (nsHTMLEditUtils::SupportsAlignAttr(curNode))
  4894       nsCOMPtr<nsIDOMElement> curElem = do_QueryInterface(curNode);
  4895       res = AlignBlock(curElem, alignType, false);
  4896       NS_ENSURE_SUCCESS(res, res);
  4897       // clear out curDiv so that we don't put nodes after this one into it
  4898       curDiv = 0;
  4899       continue;
  4902     // Skip insignificant formatting text nodes to prevent
  4903     // unnecessary structure splitting!
  4904     bool isEmptyTextNode = false;
  4905     if (nsEditor::IsTextNode(curNode) &&
  4906        ((nsHTMLEditUtils::IsTableElement(curParent) && !nsHTMLEditUtils::IsTableCellOrCaption(curParent)) ||
  4907         nsHTMLEditUtils::IsList(curParent) ||
  4908         (NS_SUCCEEDED(mHTMLEditor->IsEmptyNode(curNode, &isEmptyTextNode)) && isEmptyTextNode)))
  4909       continue;
  4911     // if it's a list item, or a list
  4912     // inside a list, forget any "current" div, and instead put divs inside
  4913     // the appropriate block (td, li, etc)
  4914     if ( nsHTMLEditUtils::IsListItem(curNode)
  4915          || nsHTMLEditUtils::IsList(curNode))
  4917       res = RemoveAlignment(curNode, *alignType, true);
  4918       NS_ENSURE_SUCCESS(res, res);
  4919       if (useCSS) {
  4920         nsCOMPtr<nsIDOMElement> curElem = do_QueryInterface(curNode);
  4921         NS_NAMED_LITERAL_STRING(attrName, "align");
  4922         int32_t count;
  4923         mHTMLEditor->mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(curNode, nullptr,
  4924                                                                 &attrName, alignType,
  4925                                                                 &count, false);
  4926         curDiv = 0;
  4927         continue;
  4929       else if (nsHTMLEditUtils::IsList(curParent)) {
  4930         // if we don't use CSS, add a contraint to list element : they have
  4931         // to be inside another list, ie >= second level of nesting
  4932         res = AlignInnerBlocks(curNode, alignType);
  4933         NS_ENSURE_SUCCESS(res, res);
  4934         curDiv = 0;
  4935         continue;
  4937       // clear out curDiv so that we don't put nodes after this one into it
  4940     // need to make a div to put things in if we haven't already,
  4941     // or if this node doesn't go in div we used earlier.
  4942     if (!curDiv || transitionList[i])
  4944       // First, check that our element can contain a div.
  4945       NS_NAMED_LITERAL_STRING(divType, "div");
  4946       if (!mEditor->CanContainTag(curParent, nsGkAtoms::div)) {
  4947         return NS_OK; // cancelled
  4950       res = SplitAsNeeded(&divType, address_of(curParent), &offset);
  4951       NS_ENSURE_SUCCESS(res, res);
  4952       NS_ENSURE_STATE(mHTMLEditor);
  4953       res = mHTMLEditor->CreateNode(divType, curParent, offset, getter_AddRefs(curDiv));
  4954       NS_ENSURE_SUCCESS(res, res);
  4955       // remember our new block for postprocessing
  4956       mNewBlock = curDiv;
  4957       // set up the alignment on the div
  4958       nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(curDiv);
  4959       res = AlignBlock(divElem, alignType, true);
  4960       //nsAutoString attr(NS_LITERAL_STRING("align"));
  4961       //res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
  4962       //NS_ENSURE_SUCCESS(res, res);
  4963       // curDiv is now the correct thing to put curNode in
  4966     // tuck the node into the end of the active div
  4967     NS_ENSURE_STATE(mHTMLEditor);
  4968     res = mHTMLEditor->MoveNode(curNode, curDiv, -1);
  4969     NS_ENSURE_SUCCESS(res, res);
  4972   return res;
  4976 ///////////////////////////////////////////////////////////////////////////
  4977 // AlignInnerBlocks: align inside table cells or list items
  4978 //       
  4979 nsresult
  4980 nsHTMLEditRules::AlignInnerBlocks(nsIDOMNode *aNode, const nsAString *alignType)
  4982   NS_ENSURE_TRUE(aNode && alignType, NS_ERROR_NULL_POINTER);
  4983   nsresult res;
  4985   // gather list of table cells or list items
  4986   nsCOMArray<nsIDOMNode> arrayOfNodes;
  4987   nsTableCellAndListItemFunctor functor;
  4988   nsDOMIterator iter;
  4989   res = iter.Init(aNode);
  4990   NS_ENSURE_SUCCESS(res, res);
  4991   res = iter.AppendList(functor, arrayOfNodes);
  4992   NS_ENSURE_SUCCESS(res, res);
  4994   // now that we have the list, align their contents as requested
  4995   int32_t listCount = arrayOfNodes.Count();
  4996   int32_t j;
  4998   for (j = 0; j < listCount; j++)
  5000     nsIDOMNode* node = arrayOfNodes[0];
  5001     res = AlignBlockContents(node, alignType);
  5002     NS_ENSURE_SUCCESS(res, res);
  5003     arrayOfNodes.RemoveObjectAt(0);
  5006   return res;  
  5010 ///////////////////////////////////////////////////////////////////////////
  5011 // AlignBlockContents: align contents of a block element
  5012 //                  
  5013 nsresult
  5014 nsHTMLEditRules::AlignBlockContents(nsIDOMNode *aNode, const nsAString *alignType)
  5016   NS_ENSURE_TRUE(aNode && alignType, NS_ERROR_NULL_POINTER);
  5017   nsresult res;
  5018   nsCOMPtr <nsIDOMNode> firstChild, lastChild, divNode;
  5020   bool useCSS = mHTMLEditor->IsCSSEnabled();
  5022   NS_ENSURE_STATE(mHTMLEditor);
  5023   res = mHTMLEditor->GetFirstEditableChild(aNode, address_of(firstChild));
  5024   NS_ENSURE_SUCCESS(res, res);
  5025   NS_ENSURE_STATE(mHTMLEditor);
  5026   res = mHTMLEditor->GetLastEditableChild(aNode, address_of(lastChild));
  5027   NS_ENSURE_SUCCESS(res, res);
  5028   NS_NAMED_LITERAL_STRING(attr, "align");
  5029   if (!firstChild)
  5031     // this cell has no content, nothing to align
  5033   else if ((firstChild==lastChild) && nsHTMLEditUtils::IsDiv(firstChild))
  5035     // the cell already has a div containing all of its content: just
  5036     // act on this div.
  5037     nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(firstChild);
  5038     if (useCSS) {
  5039       NS_ENSURE_STATE(mHTMLEditor);
  5040       res = mHTMLEditor->SetAttributeOrEquivalent(divElem, attr, *alignType, false); 
  5042     else {
  5043       NS_ENSURE_STATE(mHTMLEditor);
  5044       res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
  5046     NS_ENSURE_SUCCESS(res, res);
  5048   else
  5050     // else we need to put in a div, set the alignment, and toss in all the children
  5051     NS_ENSURE_STATE(mHTMLEditor);
  5052     res = mHTMLEditor->CreateNode(NS_LITERAL_STRING("div"), aNode, 0, getter_AddRefs(divNode));
  5053     NS_ENSURE_SUCCESS(res, res);
  5054     // set up the alignment on the div
  5055     nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(divNode);
  5056     if (useCSS) {
  5057       NS_ENSURE_STATE(mHTMLEditor);
  5058       res = mHTMLEditor->SetAttributeOrEquivalent(divElem, attr, *alignType, false); 
  5060     else {
  5061       NS_ENSURE_STATE(mHTMLEditor);
  5062       res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
  5064     NS_ENSURE_SUCCESS(res, res);
  5065     // tuck the children into the end of the active div
  5066     while (lastChild && (lastChild != divNode))
  5068       NS_ENSURE_STATE(mHTMLEditor);
  5069       res = mHTMLEditor->MoveNode(lastChild, divNode, 0);
  5070       NS_ENSURE_SUCCESS(res, res);
  5071       NS_ENSURE_STATE(mHTMLEditor);
  5072       res = mHTMLEditor->GetLastEditableChild(aNode, address_of(lastChild));
  5073       NS_ENSURE_SUCCESS(res, res);
  5076   return res;
  5079 ///////////////////////////////////////////////////////////////////////////
  5080 // CheckForEmptyBlock: Called by WillDeleteSelection to detect and handle
  5081 //                     case of deleting from inside an empty block.
  5082 //                  
  5083 nsresult
  5084 nsHTMLEditRules::CheckForEmptyBlock(nsIDOMNode *aStartNode, 
  5085                                     nsIDOMNode *aBodyNode,
  5086                                     nsISelection *aSelection,
  5087                                     bool *aHandled)
  5089   // If the editing host is an inline element, bail out early.
  5090   if (IsInlineNode(aBodyNode)) {
  5091     return NS_OK;
  5093   // if we are inside an empty block, delete it.
  5094   // Note: do NOT delete table elements this way.
  5095   nsresult res = NS_OK;
  5096   nsCOMPtr<nsIDOMNode> block, emptyBlock;
  5097   if (IsBlockNode(aStartNode)) 
  5098     block = aStartNode;
  5099   else
  5100     block = mHTMLEditor->GetBlockNodeParent(aStartNode);
  5101   bool bIsEmptyNode;
  5102   if (block != aBodyNode)  // efficiency hack. avoiding IsEmptyNode() call when in body
  5104     NS_ENSURE_STATE(mHTMLEditor);
  5105     res = mHTMLEditor->IsEmptyNode(block, &bIsEmptyNode, true, false);
  5106     NS_ENSURE_SUCCESS(res, res);
  5107     while (bIsEmptyNode && !nsHTMLEditUtils::IsTableElement(block) && (block != aBodyNode))
  5109       emptyBlock = block;
  5110       block = mHTMLEditor->GetBlockNodeParent(emptyBlock);
  5111       NS_ENSURE_STATE(mHTMLEditor);
  5112       res = mHTMLEditor->IsEmptyNode(block, &bIsEmptyNode, true, false);
  5113       NS_ENSURE_SUCCESS(res, res);
  5117   nsCOMPtr<nsIContent> emptyContent = do_QueryInterface(emptyBlock);
  5118   if (emptyBlock && emptyContent->IsEditable())
  5120     int32_t offset;
  5121     nsCOMPtr<nsIDOMNode> blockParent = nsEditor::GetNodeLocation(emptyBlock, &offset);
  5122     NS_ENSURE_TRUE(blockParent && offset >= 0, NS_ERROR_FAILURE);
  5124     if (nsHTMLEditUtils::IsListItem(emptyBlock))
  5126       // are we the first list item in the list?
  5127       bool bIsFirst;
  5128       NS_ENSURE_STATE(mHTMLEditor);
  5129       res = mHTMLEditor->IsFirstEditableChild(emptyBlock, &bIsFirst);
  5130       NS_ENSURE_SUCCESS(res, res);
  5131       if (bIsFirst)
  5133         int32_t listOffset;
  5134         nsCOMPtr<nsIDOMNode> listParent = nsEditor::GetNodeLocation(blockParent,
  5135                                                                     &listOffset);
  5136         NS_ENSURE_TRUE(listParent && listOffset >= 0, NS_ERROR_FAILURE);
  5137         // if we are a sublist, skip the br creation
  5138         if (!nsHTMLEditUtils::IsList(listParent))
  5140           // create a br before list
  5141           nsCOMPtr<nsIDOMNode> brNode;
  5142           NS_ENSURE_STATE(mHTMLEditor);
  5143           res = mHTMLEditor->CreateBR(listParent, listOffset, address_of(brNode));
  5144           NS_ENSURE_SUCCESS(res, res);
  5145           // adjust selection to be right before it
  5146           res = aSelection->Collapse(listParent, listOffset);
  5147           NS_ENSURE_SUCCESS(res, res);
  5149         // else just let selection perculate up.  We'll adjust it in AfterEdit()
  5152     else
  5154       // adjust selection to be right after it
  5155       res = aSelection->Collapse(blockParent, offset+1);
  5156       NS_ENSURE_SUCCESS(res, res);
  5158     NS_ENSURE_STATE(mHTMLEditor);
  5159     res = mHTMLEditor->DeleteNode(emptyBlock);
  5160     *aHandled = true;
  5162   return res;
  5165 nsresult
  5166 nsHTMLEditRules::CheckForInvisibleBR(nsIDOMNode *aBlock, 
  5167                                      BRLocation aWhere, 
  5168                                      nsCOMPtr<nsIDOMNode> *outBRNode,
  5169                                      int32_t aOffset)
  5171   NS_ENSURE_TRUE(aBlock && outBRNode, NS_ERROR_NULL_POINTER);
  5172   *outBRNode = nullptr;
  5174   nsCOMPtr<nsIDOMNode> testNode;
  5175   int32_t testOffset = 0;
  5176   bool runTest = false;
  5178   if (aWhere == kBlockEnd)
  5180     nsCOMPtr<nsIDOMNode> rightmostNode =
  5181       mHTMLEditor->GetRightmostChild(aBlock, true); // no block crossing
  5183     if (rightmostNode)
  5185       int32_t nodeOffset;
  5186       nsCOMPtr<nsIDOMNode> nodeParent = nsEditor::GetNodeLocation(rightmostNode,
  5187                                                                   &nodeOffset);
  5188       runTest = true;
  5189       testNode = nodeParent;
  5190       // use offset + 1, because we want the last node included in our
  5191       // evaluation
  5192       testOffset = nodeOffset + 1;
  5195   else if (aOffset)
  5197     runTest = true;
  5198     testNode = aBlock;
  5199     // we'll check everything to the left of the input position
  5200     testOffset = aOffset;
  5203   if (runTest)
  5205     nsWSRunObject wsTester(mHTMLEditor, testNode, testOffset);
  5206     if (WSType::br == wsTester.mStartReason) {
  5207       *outBRNode = wsTester.mStartReasonNode;
  5211   return NS_OK;
  5215 ///////////////////////////////////////////////////////////////////////////
  5216 // GetInnerContent: aList and aTbl allow the caller to specify what kind 
  5217 //                  of content to "look inside".  If aTbl is true, look inside
  5218 //                  any table content, and insert the inner content into the
  5219 //                  supplied issupportsarray at offset aIndex.  
  5220 //                  Similarly with aList and list content.
  5221 //                  aIndex is updated to point past inserted elements.
  5222 //                  
  5223 nsresult
  5224 nsHTMLEditRules::GetInnerContent(nsIDOMNode *aNode, nsCOMArray<nsIDOMNode> &outArrayOfNodes, 
  5225                                  int32_t *aIndex, bool aList, bool aTbl)
  5227   NS_ENSURE_TRUE(aNode && aIndex, NS_ERROR_NULL_POINTER);
  5229   nsCOMPtr<nsIDOMNode> node;
  5231   nsresult res = mHTMLEditor->GetFirstEditableChild(aNode, address_of(node));
  5232   while (NS_SUCCEEDED(res) && node)
  5234     if (  ( aList && (nsHTMLEditUtils::IsList(node)     || 
  5235                       nsHTMLEditUtils::IsListItem(node) ) )
  5236        || ( aTbl && nsHTMLEditUtils::IsTableElement(node) )  )
  5238       res = GetInnerContent(node, outArrayOfNodes, aIndex, aList, aTbl);
  5239       NS_ENSURE_SUCCESS(res, res);
  5241     else
  5243       outArrayOfNodes.InsertObjectAt(node, *aIndex);
  5244       (*aIndex)++;
  5246     nsCOMPtr<nsIDOMNode> tmp;
  5247     res = node->GetNextSibling(getter_AddRefs(tmp));
  5248     node = tmp;
  5251   return res;
  5254 ///////////////////////////////////////////////////////////////////////////
  5255 // ExpandSelectionForDeletion: this promotes our selection to include blocks
  5256 // that have all their children selected.
  5257 //                  
  5258 nsresult
  5259 nsHTMLEditRules::ExpandSelectionForDeletion(nsISelection *aSelection)
  5261   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
  5263   // don't need to touch collapsed selections
  5264   if (aSelection->Collapsed()) {
  5265     return NS_OK;
  5268   int32_t rangeCount;
  5269   nsresult res = aSelection->GetRangeCount(&rangeCount);
  5270   NS_ENSURE_SUCCESS(res, res);
  5272   // we don't need to mess with cell selections, and we assume multirange selections are those.
  5273   if (rangeCount != 1) return NS_OK;
  5275   // find current sel start and end
  5276   nsCOMPtr<nsIDOMRange> range;
  5277   res = aSelection->GetRangeAt(0, getter_AddRefs(range));
  5278   NS_ENSURE_SUCCESS(res, res);
  5279   NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
  5280   nsCOMPtr<nsIDOMNode> selStartNode, selEndNode, selCommon;
  5281   int32_t selStartOffset, selEndOffset;
  5283   res = range->GetStartContainer(getter_AddRefs(selStartNode));
  5284   NS_ENSURE_SUCCESS(res, res);
  5285   res = range->GetStartOffset(&selStartOffset);
  5286   NS_ENSURE_SUCCESS(res, res);
  5287   res = range->GetEndContainer(getter_AddRefs(selEndNode));
  5288   NS_ENSURE_SUCCESS(res, res);
  5289   res = range->GetEndOffset(&selEndOffset);
  5290   NS_ENSURE_SUCCESS(res, res);
  5292   // find current selection common block parent
  5293   res = range->GetCommonAncestorContainer(getter_AddRefs(selCommon));
  5294   NS_ENSURE_SUCCESS(res, res);
  5295   if (!IsBlockNode(selCommon))
  5296     selCommon = nsHTMLEditor::GetBlockNodeParent(selCommon);
  5298   // set up for loops and cache our root element
  5299   bool stillLooking = true;
  5300   nsCOMPtr<nsIDOMNode> visNode, firstBRParent;
  5301   int32_t visOffset=0, firstBROffset=0;
  5302   WSType wsType;
  5303   nsCOMPtr<nsIContent> rootContent = mHTMLEditor->GetActiveEditingHost();
  5304   nsCOMPtr<nsIDOMNode> rootElement = do_QueryInterface(rootContent);
  5305   NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE);
  5307   // find previous visible thingy before start of selection
  5308   if ((selStartNode!=selCommon) && (selStartNode!=rootElement))
  5310     while (stillLooking)
  5312       nsWSRunObject wsObj(mHTMLEditor, selStartNode, selStartOffset);
  5313       wsObj.PriorVisibleNode(selStartNode, selStartOffset, address_of(visNode),
  5314                              &visOffset, &wsType);
  5315       if (wsType == WSType::thisBlock) {
  5316         // we want to keep looking up.  But stop if we are crossing table element
  5317         // boundaries, or if we hit the root.
  5318         if ( nsHTMLEditUtils::IsTableElement(wsObj.mStartReasonNode) ||
  5319             (selCommon == wsObj.mStartReasonNode)                    ||
  5320             (rootElement == wsObj.mStartReasonNode) )
  5322           stillLooking = false;
  5324         else
  5326           selStartNode = nsEditor::GetNodeLocation(wsObj.mStartReasonNode,
  5327                                                    &selStartOffset);
  5330       else
  5332         stillLooking = false;
  5337   stillLooking = true;
  5338   // find next visible thingy after end of selection
  5339   if ((selEndNode!=selCommon) && (selEndNode!=rootElement))
  5341     while (stillLooking)
  5343       nsWSRunObject wsObj(mHTMLEditor, selEndNode, selEndOffset);
  5344       wsObj.NextVisibleNode(selEndNode, selEndOffset, address_of(visNode),
  5345                             &visOffset, &wsType);
  5346       if (wsType == WSType::br) {
  5347         if (mHTMLEditor->IsVisBreak(wsObj.mEndReasonNode))
  5349           stillLooking = false;
  5351         else
  5353           if (!firstBRParent)
  5355             firstBRParent = selEndNode;
  5356             firstBROffset = selEndOffset;
  5358           selEndNode = nsEditor::GetNodeLocation(wsObj.mEndReasonNode, &selEndOffset);
  5359           ++selEndOffset;
  5361       } else if (wsType == WSType::thisBlock) {
  5362         // we want to keep looking up.  But stop if we are crossing table element
  5363         // boundaries, or if we hit the root.
  5364         if ( nsHTMLEditUtils::IsTableElement(wsObj.mEndReasonNode) ||
  5365             (selCommon == wsObj.mEndReasonNode)                    ||
  5366             (rootElement == wsObj.mEndReasonNode) )
  5368           stillLooking = false;
  5370         else
  5372           selEndNode = nsEditor::GetNodeLocation(wsObj.mEndReasonNode, &selEndOffset);
  5373           ++selEndOffset;
  5376       else
  5378         stillLooking = false;
  5382   // now set the selection to the new range
  5383   aSelection->Collapse(selStartNode, selStartOffset);
  5385   // expand selection endpoint only if we didnt pass a br,
  5386   // or if we really needed to pass that br (ie, its block is now 
  5387   // totally selected)
  5388   bool doEndExpansion = true;
  5389   if (firstBRParent)
  5391     // find block node containing br
  5392     nsCOMPtr<nsIDOMNode> brBlock = firstBRParent;
  5393     if (!IsBlockNode(brBlock))
  5394       brBlock = nsHTMLEditor::GetBlockNodeParent(brBlock);
  5395     bool nodeBefore=false, nodeAfter=false;
  5397     // create a range that represents expanded selection
  5398     nsCOMPtr<nsINode> node = do_QueryInterface(selStartNode);
  5399     NS_ENSURE_STATE(node);
  5400     nsRefPtr<nsRange> range = new nsRange(node);
  5401     res = range->SetStart(selStartNode, selStartOffset);
  5402     NS_ENSURE_SUCCESS(res, res);
  5403     res = range->SetEnd(selEndNode, selEndOffset);
  5404     NS_ENSURE_SUCCESS(res, res);
  5406     // check if block is entirely inside range
  5407     nsCOMPtr<nsIContent> brContentBlock = do_QueryInterface(brBlock);
  5408     res = nsRange::CompareNodeToRange(brContentBlock, range, &nodeBefore, &nodeAfter);
  5410     // if block isn't contained, forgo grabbing the br in the expanded selection
  5411     if (nodeBefore || nodeAfter)
  5412       doEndExpansion = false;
  5414   if (doEndExpansion)
  5416     res = aSelection->Extend(selEndNode, selEndOffset);
  5418   else
  5420     // only expand to just before br
  5421     res = aSelection->Extend(firstBRParent, firstBROffset);
  5424   return res;
  5428 ///////////////////////////////////////////////////////////////////////////
  5429 // NormalizeSelection:  tweak non-collapsed selections to be more "natural".
  5430 //    Idea here is to adjust selection endpoint so that they do not cross
  5431 //    breaks or block boundaries unless something editable beyond that boundary
  5432 //    is also selected.  This adjustment makes it much easier for the various
  5433 //    block operations to determine what nodes to act on.
  5434 //                       
  5435 nsresult 
  5436 nsHTMLEditRules::NormalizeSelection(nsISelection *inSelection)
  5438   NS_ENSURE_TRUE(inSelection, NS_ERROR_NULL_POINTER);
  5440   // don't need to touch collapsed selections
  5441   if (inSelection->Collapsed()) {
  5442     return NS_OK;
  5445   int32_t rangeCount;
  5446   nsresult res = inSelection->GetRangeCount(&rangeCount);
  5447   NS_ENSURE_SUCCESS(res, res);
  5449   // we don't need to mess with cell selections, and we assume multirange selections are those.
  5450   if (rangeCount != 1) return NS_OK;
  5452   nsCOMPtr<nsIDOMRange> range;
  5453   res = inSelection->GetRangeAt(0, getter_AddRefs(range));
  5454   NS_ENSURE_SUCCESS(res, res);
  5455   NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
  5456   nsCOMPtr<nsIDOMNode> startNode, endNode;
  5457   int32_t startOffset, endOffset;
  5458   nsCOMPtr<nsIDOMNode> newStartNode, newEndNode;
  5459   int32_t newStartOffset, newEndOffset;
  5461   res = range->GetStartContainer(getter_AddRefs(startNode));
  5462   NS_ENSURE_SUCCESS(res, res);
  5463   res = range->GetStartOffset(&startOffset);
  5464   NS_ENSURE_SUCCESS(res, res);
  5465   res = range->GetEndContainer(getter_AddRefs(endNode));
  5466   NS_ENSURE_SUCCESS(res, res);
  5467   res = range->GetEndOffset(&endOffset);
  5468   NS_ENSURE_SUCCESS(res, res);
  5470   // adjusted values default to original values
  5471   newStartNode = startNode; 
  5472   newStartOffset = startOffset;
  5473   newEndNode = endNode; 
  5474   newEndOffset = endOffset;
  5476   // some locals we need for whitespace code
  5477   nsCOMPtr<nsIDOMNode> someNode;
  5478   int32_t offset;
  5479   WSType wsType;
  5481   // let the whitespace code do the heavy lifting
  5482   nsWSRunObject wsEndObj(mHTMLEditor, endNode, endOffset);
  5483   // is there any intervening visible whitespace?  if so we can't push selection past that,
  5484   // it would visibly change maening of users selection
  5485   wsEndObj.PriorVisibleNode(endNode, endOffset, address_of(someNode),
  5486                             &offset, &wsType);
  5487   if (wsType != WSType::text && wsType != WSType::normalWS) {
  5488     // eThisBlock and eOtherBlock conveniently distinquish cases
  5489     // of going "down" into a block and "up" out of a block.
  5490     if (wsEndObj.mStartReason == WSType::otherBlock) {
  5491       // endpoint is just after the close of a block.
  5492       nsCOMPtr<nsIDOMNode> child = mHTMLEditor->GetRightmostChild(wsEndObj.mStartReasonNode, true);
  5493       if (child)
  5495         newEndNode = nsEditor::GetNodeLocation(child, &newEndOffset);
  5496         ++newEndOffset; // offset *after* child
  5498       // else block is empty - we can leave selection alone here, i think.
  5499     } else if (wsEndObj.mStartReason == WSType::thisBlock) {
  5500       // endpoint is just after start of this block
  5501       nsCOMPtr<nsIDOMNode> child;
  5502       NS_ENSURE_STATE(mHTMLEditor);
  5503       res = mHTMLEditor->GetPriorHTMLNode(endNode, endOffset, address_of(child));
  5504       if (child)
  5506         newEndNode = nsEditor::GetNodeLocation(child, &newEndOffset);
  5507         ++newEndOffset; // offset *after* child
  5509       // else block is empty - we can leave selection alone here, i think.
  5510     } else if (wsEndObj.mStartReason == WSType::br) {
  5511       // endpoint is just after break.  lets adjust it to before it.
  5512       newEndNode = nsEditor::GetNodeLocation(wsEndObj.mStartReasonNode,
  5513                                              &newEndOffset);
  5518   // similar dealio for start of range
  5519   nsWSRunObject wsStartObj(mHTMLEditor, startNode, startOffset);
  5520   // is there any intervening visible whitespace?  if so we can't push selection past that,
  5521   // it would visibly change maening of users selection
  5522   wsStartObj.NextVisibleNode(startNode, startOffset, address_of(someNode),
  5523                              &offset, &wsType);
  5524   if (wsType != WSType::text && wsType != WSType::normalWS) {
  5525     // eThisBlock and eOtherBlock conveniently distinquish cases
  5526     // of going "down" into a block and "up" out of a block.
  5527     if (wsStartObj.mEndReason == WSType::otherBlock) {
  5528       // startpoint is just before the start of a block.
  5529       nsCOMPtr<nsIDOMNode> child = mHTMLEditor->GetLeftmostChild(wsStartObj.mEndReasonNode, true);
  5530       if (child)
  5532         newStartNode = nsEditor::GetNodeLocation(child, &newStartOffset);
  5534       // else block is empty - we can leave selection alone here, i think.
  5535     } else if (wsStartObj.mEndReason == WSType::thisBlock) {
  5536       // startpoint is just before end of this block
  5537       nsCOMPtr<nsIDOMNode> child;
  5538       NS_ENSURE_STATE(mHTMLEditor);
  5539       res = mHTMLEditor->GetNextHTMLNode(startNode, startOffset, address_of(child));
  5540       if (child)
  5542         newStartNode = nsEditor::GetNodeLocation(child, &newStartOffset);
  5544       // else block is empty - we can leave selection alone here, i think.
  5545     } else if (wsStartObj.mEndReason == WSType::br) {
  5546       // startpoint is just before a break.  lets adjust it to after it.
  5547       newStartNode = nsEditor::GetNodeLocation(wsStartObj.mEndReasonNode,
  5548                                                &newStartOffset);
  5549       ++newStartOffset; // offset *after* break
  5553   // there is a demented possiblity we have to check for.  We might have a very strange selection
  5554   // that is not collapsed and yet does not contain any editable content, and satisfies some of the
  5555   // above conditions that cause tweaking.  In this case we don't want to tweak the selection into
  5556   // a block it was never in, etc.  There are a variety of strategies one might use to try to
  5557   // detect these cases, but I think the most straightforward is to see if the adjusted locations
  5558   // "cross" the old values: ie, new end before old start, or new start after old end.  If so 
  5559   // then just leave things alone.
  5561   int16_t comp;
  5562   comp = nsContentUtils::ComparePoints(startNode, startOffset,
  5563                                        newEndNode, newEndOffset);
  5564   if (comp == 1) return NS_OK;  // new end before old start
  5565   comp = nsContentUtils::ComparePoints(newStartNode, newStartOffset,
  5566                                        endNode, endOffset);
  5567   if (comp == 1) return NS_OK;  // new start after old end
  5569   // otherwise set selection to new values.  
  5570   inSelection->Collapse(newStartNode, newStartOffset);
  5571   inSelection->Extend(newEndNode, newEndOffset);
  5572   return NS_OK;
  5576 ///////////////////////////////////////////////////////////////////////////
  5577 // GetPromotedPoint: figure out where a start or end point for a block
  5578 //                   operation really is
  5579 void
  5580 nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode* aNode,
  5581                                   int32_t aOffset,
  5582                                   EditAction actionID,
  5583                                   nsCOMPtr<nsIDOMNode>* outNode,
  5584                                   int32_t* outOffset)
  5586   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  5587   MOZ_ASSERT(node && outNode && outOffset);
  5589   // default values
  5590   *outNode = node->AsDOMNode();
  5591   *outOffset = aOffset;
  5593   // we do one thing for text actions, something else entirely for other
  5594   // actions
  5595   if (actionID == EditAction::insertText ||
  5596       actionID == EditAction::insertIMEText ||
  5597       actionID == EditAction::insertBreak ||
  5598       actionID == EditAction::deleteText) {
  5599     bool isSpace, isNBSP;
  5600     nsCOMPtr<nsIContent> content = do_QueryInterface(node), temp;
  5601     // for text actions, we want to look backwards (or forwards, as
  5602     // appropriate) for additional whitespace or nbsp's.  We may have to act on
  5603     // these later even though they are outside of the initial selection.  Even
  5604     // if they are in another node!
  5605     while (content) {
  5606       int32_t offset;
  5607       if (aWhere == kStart) {
  5608         NS_ENSURE_TRUE(mHTMLEditor, /* void */);
  5609         mHTMLEditor->IsPrevCharInNodeWhitespace(content, *outOffset,
  5610                                                 &isSpace, &isNBSP,
  5611                                                 getter_AddRefs(temp), &offset);
  5612       } else {
  5613         NS_ENSURE_TRUE(mHTMLEditor, /* void */);
  5614         mHTMLEditor->IsNextCharInNodeWhitespace(content, *outOffset,
  5615                                                 &isSpace, &isNBSP,
  5616                                                 getter_AddRefs(temp), &offset);
  5618       if (isSpace || isNBSP) {
  5619         content = temp;
  5620         *outOffset = offset;
  5621       } else {
  5622         break;
  5626     *outNode = content->AsDOMNode();
  5627     return;
  5630   int32_t offset = aOffset;
  5632   // else not a text section.  In this case we want to see if we should grab
  5633   // any adjacent inline nodes and/or parents and other ancestors
  5634   if (aWhere == kStart) {
  5635     // some special casing for text nodes
  5636     if (node->IsNodeOfType(nsINode::eTEXT)) {
  5637       if (!node->GetParentNode()) {
  5638         // Okay, can't promote any further
  5639         return;
  5641       offset = node->GetParentNode()->IndexOf(node);
  5642       node = node->GetParentNode();
  5645     // look back through any further inline nodes that aren't across a <br>
  5646     // from us, and that are enclosed in the same block.
  5647     NS_ENSURE_TRUE(mHTMLEditor, /* void */);
  5648     nsCOMPtr<nsINode> priorNode =
  5649       mHTMLEditor->GetPriorHTMLNode(node, offset, true);
  5651     while (priorNode && priorNode->GetParentNode() &&
  5652            mHTMLEditor && !mHTMLEditor->IsVisBreak(priorNode->AsDOMNode()) &&
  5653            !IsBlockNode(priorNode->AsDOMNode())) {
  5654       offset = priorNode->GetParentNode()->IndexOf(priorNode);
  5655       node = priorNode->GetParentNode();
  5656       NS_ENSURE_TRUE(mHTMLEditor, /* void */);
  5657       priorNode = mHTMLEditor->GetPriorHTMLNode(node, offset, true);
  5660     // finding the real start for this point.  look up the tree for as long as
  5661     // we are the first node in the container, and as long as we haven't hit
  5662     // the body node.
  5663     NS_ENSURE_TRUE(mHTMLEditor, /* void */);
  5664     nsCOMPtr<nsIContent> nearNode =
  5665       mHTMLEditor->GetPriorHTMLNode(node, offset, true);
  5666     while (!nearNode && node->Tag() != nsGkAtoms::body &&
  5667            node->GetParentNode()) {
  5668       // some cutoffs are here: we don't need to also include them in the
  5669       // aWhere == kEnd case.  as long as they are in one or the other it will
  5670       // work.  special case for outdent: don't keep looking up if we have
  5671       // found a blockquote element to act on
  5672       if (actionID == EditAction::outdent &&
  5673           node->Tag() == nsGkAtoms::blockquote) {
  5674         break;
  5677       int32_t parentOffset = node->GetParentNode()->IndexOf(node);
  5678       nsCOMPtr<nsINode> parent = node->GetParentNode();
  5680       // Don't walk past the editable section. Note that we need to check
  5681       // before walking up to a parent because we need to return the parent
  5682       // object, so the parent itself might not be in the editable area, but
  5683       // it's OK if we're not performing a block-level action.
  5684       bool blockLevelAction = actionID == EditAction::indent ||
  5685                               actionID == EditAction::outdent ||
  5686                               actionID == EditAction::align ||
  5687                               actionID == EditAction::makeBasicBlock;
  5688       NS_ENSURE_TRUE(mHTMLEditor, /* void */);
  5689       if (!mHTMLEditor->IsDescendantOfEditorRoot(parent) &&
  5690           (blockLevelAction || !mHTMLEditor ||
  5691            !mHTMLEditor->IsDescendantOfEditorRoot(node))) {
  5692         NS_ENSURE_TRUE(mHTMLEditor, /* void */);
  5693         break;
  5696       node = parent;
  5697       offset = parentOffset;
  5698       NS_ENSURE_TRUE(mHTMLEditor, /* void */);
  5699       nearNode = mHTMLEditor->GetPriorHTMLNode(node, offset, true);
  5701     *outNode = node->AsDOMNode();
  5702     *outOffset = offset;
  5703     return;
  5706   // aWhere == kEnd
  5707   // some special casing for text nodes
  5708   if (node->IsNodeOfType(nsINode::eTEXT)) {
  5709     if (!node->GetParentNode()) {
  5710       // Okay, can't promote any further
  5711       return;
  5713     // want to be after the text node
  5714     offset = 1 + node->GetParentNode()->IndexOf(node);
  5715     node = node->GetParentNode();
  5718   // look ahead through any further inline nodes that aren't across a <br> from
  5719   // us, and that are enclosed in the same block.
  5720   NS_ENSURE_TRUE(mHTMLEditor, /* void */);
  5721   nsCOMPtr<nsIContent> nextNode =
  5722     mHTMLEditor->GetNextHTMLNode(node, offset, true);
  5724   while (nextNode && !IsBlockNode(nextNode->AsDOMNode()) &&
  5725          nextNode->GetParentNode()) {
  5726     offset = 1 + nextNode->GetParentNode()->IndexOf(nextNode);
  5727     node = nextNode->GetParentNode();
  5728     NS_ENSURE_TRUE(mHTMLEditor, /* void */);
  5729     if (mHTMLEditor->IsVisBreak(nextNode->AsDOMNode())) {
  5730       break;
  5732     NS_ENSURE_TRUE(mHTMLEditor, /* void */);
  5733     nextNode = mHTMLEditor->GetNextHTMLNode(node, offset, true);
  5736   // finding the real end for this point.  look up the tree for as long as we
  5737   // are the last node in the container, and as long as we haven't hit the body
  5738   // node.
  5739   NS_ENSURE_TRUE(mHTMLEditor, /* void */);
  5740   nsCOMPtr<nsIContent> nearNode =
  5741     mHTMLEditor->GetNextHTMLNode(node, offset, true);
  5742   while (!nearNode && node->Tag() != nsGkAtoms::body &&
  5743          node->GetParentNode()) {
  5744     int32_t parentOffset = node->GetParentNode()->IndexOf(node);
  5745     nsCOMPtr<nsINode> parent = node->GetParentNode();
  5747     // Don't walk past the editable section. Note that we need to check before
  5748     // walking up to a parent because we need to return the parent object, so
  5749     // the parent itself might not be in the editable area, but it's OK.
  5750     if ((!mHTMLEditor || !mHTMLEditor->IsDescendantOfEditorRoot(node)) &&
  5751         (!mHTMLEditor || !mHTMLEditor->IsDescendantOfEditorRoot(parent))) {
  5752       NS_ENSURE_TRUE(mHTMLEditor, /* void */);
  5753       break;
  5756     node = parent;
  5757     // we want to be AFTER nearNode
  5758     offset = parentOffset + 1;
  5759     NS_ENSURE_TRUE(mHTMLEditor, /* void */);
  5760     nearNode = mHTMLEditor->GetNextHTMLNode(node, offset, true);
  5762   *outNode = node->AsDOMNode();
  5763   *outOffset = offset;
  5767 ///////////////////////////////////////////////////////////////////////////
  5768 // GetPromotedRanges: run all the selection range endpoint through 
  5769 //                    GetPromotedPoint()
  5770 //                       
  5771 nsresult 
  5772 nsHTMLEditRules::GetPromotedRanges(nsISelection *inSelection, 
  5773                                    nsCOMArray<nsIDOMRange> &outArrayOfRanges, 
  5774                                    EditAction inOperationType)
  5776   NS_ENSURE_TRUE(inSelection, NS_ERROR_NULL_POINTER);
  5778   int32_t rangeCount;
  5779   nsresult res = inSelection->GetRangeCount(&rangeCount);
  5780   NS_ENSURE_SUCCESS(res, res);
  5782   int32_t i;
  5783   nsCOMPtr<nsIDOMRange> selectionRange;
  5784   nsCOMPtr<nsIDOMRange> opRange;
  5786   for (i = 0; i < rangeCount; i++)
  5788     res = inSelection->GetRangeAt(i, getter_AddRefs(selectionRange));
  5789     NS_ENSURE_SUCCESS(res, res);
  5791     // clone range so we don't muck with actual selection ranges
  5792     res = selectionRange->CloneRange(getter_AddRefs(opRange));
  5793     NS_ENSURE_SUCCESS(res, res);
  5795     // make a new adjusted range to represent the appropriate block content.
  5796     // The basic idea is to push out the range endpoints
  5797     // to truly enclose the blocks that we will affect.
  5798     // This call alters opRange.
  5799     res = PromoteRange(opRange, inOperationType);
  5800     NS_ENSURE_SUCCESS(res, res);
  5802     // stuff new opRange into array
  5803     outArrayOfRanges.AppendObject(opRange);
  5805   return res;
  5809 ///////////////////////////////////////////////////////////////////////////
  5810 // PromoteRange: expand a range to include any parents for which all
  5811 //               editable children are already in range. 
  5812 //                       
  5813 nsresult 
  5814 nsHTMLEditRules::PromoteRange(nsIDOMRange *inRange, 
  5815                               EditAction inOperationType)
  5817   NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER);
  5818   nsresult res;
  5819   nsCOMPtr<nsIDOMNode> startNode, endNode;
  5820   int32_t startOffset, endOffset;
  5822   res = inRange->GetStartContainer(getter_AddRefs(startNode));
  5823   NS_ENSURE_SUCCESS(res, res);
  5824   res = inRange->GetStartOffset(&startOffset);
  5825   NS_ENSURE_SUCCESS(res, res);
  5826   res = inRange->GetEndContainer(getter_AddRefs(endNode));
  5827   NS_ENSURE_SUCCESS(res, res);
  5828   res = inRange->GetEndOffset(&endOffset);
  5829   NS_ENSURE_SUCCESS(res, res);
  5831   // MOOSE major hack:
  5832   // GetPromotedPoint doesn't really do the right thing for collapsed ranges
  5833   // inside block elements that contain nothing but a solo <br>.  It's easier
  5834   // to put a workaround here than to revamp GetPromotedPoint.  :-(
  5835   if ( (startNode == endNode) && (startOffset == endOffset))
  5837     nsCOMPtr<nsIDOMNode> block;
  5838     if (IsBlockNode(startNode)) {
  5839       block = startNode;
  5840     } else {
  5841       NS_ENSURE_STATE(mHTMLEditor);
  5842       block = mHTMLEditor->GetBlockNodeParent(startNode);
  5844     if (block)
  5846       bool bIsEmptyNode = false;
  5847       // check for the editing host
  5848       NS_ENSURE_STATE(mHTMLEditor);
  5849       nsIContent *rootContent = mHTMLEditor->GetActiveEditingHost();
  5850       nsCOMPtr<nsINode> rootNode = do_QueryInterface(rootContent);
  5851       nsCOMPtr<nsINode> blockNode = do_QueryInterface(block);
  5852       NS_ENSURE_TRUE(rootNode && blockNode, NS_ERROR_UNEXPECTED);
  5853       // Make sure we don't go higher than our root element in the content tree
  5854       if (!nsContentUtils::ContentIsDescendantOf(rootNode, blockNode))
  5856         NS_ENSURE_STATE(mHTMLEditor);
  5857         res = mHTMLEditor->IsEmptyNode(block, &bIsEmptyNode, true, false);
  5859       if (bIsEmptyNode)
  5861         uint32_t numChildren;
  5862         nsEditor::GetLengthOfDOMNode(block, numChildren); 
  5863         startNode = block;
  5864         endNode = block;
  5865         startOffset = 0;
  5866         endOffset = numChildren;
  5871   // make a new adjusted range to represent the appropriate block content.
  5872   // this is tricky.  the basic idea is to push out the range endpoints
  5873   // to truly enclose the blocks that we will affect
  5875   nsCOMPtr<nsIDOMNode> opStartNode;
  5876   nsCOMPtr<nsIDOMNode> opEndNode;
  5877   int32_t opStartOffset, opEndOffset;
  5878   nsCOMPtr<nsIDOMRange> opRange;
  5880   GetPromotedPoint(kStart, startNode, startOffset, inOperationType,
  5881                    address_of(opStartNode), &opStartOffset);
  5882   GetPromotedPoint(kEnd, endNode, endOffset, inOperationType,
  5883                    address_of(opEndNode), &opEndOffset);
  5885   // Make sure that the new range ends up to be in the editable section.
  5886   NS_ENSURE_STATE(mHTMLEditor);
  5887   if (!mHTMLEditor->IsDescendantOfEditorRoot(nsEditor::GetNodeAtRangeOffsetPoint(opStartNode, opStartOffset)) ||
  5888       !mHTMLEditor || // Check again, since it may have gone away
  5889       !mHTMLEditor->IsDescendantOfEditorRoot(nsEditor::GetNodeAtRangeOffsetPoint(opEndNode, opEndOffset - 1))) {
  5890     NS_ENSURE_STATE(mHTMLEditor);
  5891     return NS_OK;
  5894   res = inRange->SetStart(opStartNode, opStartOffset);
  5895   NS_ENSURE_SUCCESS(res, res);
  5896   res = inRange->SetEnd(opEndNode, opEndOffset);
  5897   return res;
  5900 class nsUniqueFunctor : public nsBoolDomIterFunctor
  5902 public:
  5903   nsUniqueFunctor(nsCOMArray<nsIDOMNode> &aArray) : mArray(aArray)
  5906   virtual bool operator()(nsIDOMNode* aNode)  // used to build list of all nodes iterator covers
  5908     return mArray.IndexOf(aNode) < 0;
  5911 private:
  5912   nsCOMArray<nsIDOMNode> &mArray;
  5913 };
  5915 ///////////////////////////////////////////////////////////////////////////
  5916 // GetNodesForOperation: run through the ranges in the array and construct 
  5917 //                       a new array of nodes to be acted on.
  5918 //                       
  5919 nsresult 
  5920 nsHTMLEditRules::GetNodesForOperation(nsCOMArray<nsIDOMRange>& inArrayOfRanges, 
  5921                                       nsCOMArray<nsIDOMNode>& outArrayOfNodes, 
  5922                                       EditAction inOperationType,
  5923                                       bool aDontTouchContent)
  5925   int32_t rangeCount = inArrayOfRanges.Count();
  5927   int32_t i;
  5928   nsCOMPtr<nsIDOMRange> opRange;
  5930   nsresult res = NS_OK;
  5932   // bust up any inlines that cross our range endpoints,
  5933   // but only if we are allowed to touch content.
  5935   if (!aDontTouchContent)
  5937     nsTArray<nsRefPtr<nsRangeStore> > rangeItemArray;
  5938     if (!rangeItemArray.AppendElements(rangeCount)) {
  5939       return NS_ERROR_OUT_OF_MEMORY;
  5942     NS_ASSERTION(static_cast<uint32_t>(rangeCount) == rangeItemArray.Length(),
  5943                  "How did that happen?");
  5945     // first register ranges for special editor gravity
  5946     for (i = 0; i < rangeCount; i++)
  5948       opRange = inArrayOfRanges[0];
  5949       rangeItemArray[i] = new nsRangeStore();
  5950       rangeItemArray[i]->StoreRange(opRange);
  5951       NS_ENSURE_STATE(mHTMLEditor);
  5952       mHTMLEditor->mRangeUpdater.RegisterRangeItem(rangeItemArray[i]);
  5953       inArrayOfRanges.RemoveObjectAt(0);
  5955     // now bust up inlines.  Safe to start at rangeCount-1, since we
  5956     // asserted we have enough items above.
  5957     for (i = rangeCount-1; i >= 0 && NS_SUCCEEDED(res); i--)
  5959       res = BustUpInlinesAtRangeEndpoints(*rangeItemArray[i]);
  5961     // then unregister the ranges
  5962     for (i = 0; i < rangeCount; i++)
  5964       nsRangeStore* item = rangeItemArray[i];
  5965       NS_ENSURE_STATE(mHTMLEditor);
  5966       mHTMLEditor->mRangeUpdater.DropRangeItem(item);
  5967       nsRefPtr<nsRange> range;
  5968       nsresult res2 = item->GetRange(getter_AddRefs(range));
  5969       opRange = range;
  5970       if (NS_FAILED(res2) && NS_SUCCEEDED(res)) {
  5971         // Remember the failure, but keep going so we make sure to unregister
  5972         // all our range items.
  5973         res = res2;
  5975       inArrayOfRanges.AppendObject(opRange);
  5977     NS_ENSURE_SUCCESS(res, res);
  5979   // gather up a list of all the nodes
  5980   for (i = 0; i < rangeCount; i++)
  5982     opRange = inArrayOfRanges[i];
  5984     nsDOMSubtreeIterator iter;
  5985     res = iter.Init(opRange);
  5986     NS_ENSURE_SUCCESS(res, res);
  5987     if (outArrayOfNodes.Count() == 0) {
  5988       nsTrivialFunctor functor;
  5989       res = iter.AppendList(functor, outArrayOfNodes);
  5990       NS_ENSURE_SUCCESS(res, res);    
  5992     else {
  5993       // We don't want duplicates in outArrayOfNodes, so we use an
  5994       // iterator/functor that only return nodes that are not already in
  5995       // outArrayOfNodes.
  5996       nsCOMArray<nsIDOMNode> nodes;
  5997       nsUniqueFunctor functor(outArrayOfNodes);
  5998       res = iter.AppendList(functor, nodes);
  5999       NS_ENSURE_SUCCESS(res, res);
  6000       if (!outArrayOfNodes.AppendObjects(nodes))
  6001         return NS_ERROR_OUT_OF_MEMORY;
  6005   // certain operations should not act on li's and td's, but rather inside 
  6006   // them.  alter the list as needed
  6007   if (inOperationType == EditAction::makeBasicBlock) {
  6008     int32_t listCount = outArrayOfNodes.Count();
  6009     for (i=listCount-1; i>=0; i--)
  6011       nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i];
  6012       if (nsHTMLEditUtils::IsListItem(node))
  6014         int32_t j=i;
  6015         outArrayOfNodes.RemoveObjectAt(i);
  6016         res = GetInnerContent(node, outArrayOfNodes, &j);
  6017         NS_ENSURE_SUCCESS(res, res);
  6021   // indent/outdent already do something special for list items, but
  6022   // we still need to make sure we don't act on table elements
  6023   else if (inOperationType == EditAction::outdent ||
  6024            inOperationType == EditAction::indent ||
  6025            inOperationType == EditAction::setAbsolutePosition) {
  6026     int32_t listCount = outArrayOfNodes.Count();
  6027     for (i=listCount-1; i>=0; i--)
  6029       nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i];
  6030       if (nsHTMLEditUtils::IsTableElementButNotTable(node))
  6032         int32_t j=i;
  6033         outArrayOfNodes.RemoveObjectAt(i);
  6034         res = GetInnerContent(node, outArrayOfNodes, &j);
  6035         NS_ENSURE_SUCCESS(res, res);
  6039   // outdent should look inside of divs.
  6040   if (inOperationType == EditAction::outdent &&
  6041       (!mHTMLEditor || !mHTMLEditor->IsCSSEnabled())) {
  6042     NS_ENSURE_STATE(mHTMLEditor);
  6043     int32_t listCount = outArrayOfNodes.Count();
  6044     for (i=listCount-1; i>=0; i--)
  6046       nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i];
  6047       if (nsHTMLEditUtils::IsDiv(node))
  6049         int32_t j=i;
  6050         outArrayOfNodes.RemoveObjectAt(i);
  6051         res = GetInnerContent(node, outArrayOfNodes, &j, false, false);
  6052         NS_ENSURE_SUCCESS(res, res);
  6058   // post process the list to break up inline containers that contain br's.
  6059   // but only for operations that might care, like making lists or para's...
  6060   if (inOperationType == EditAction::makeBasicBlock ||
  6061       inOperationType == EditAction::makeList ||
  6062       inOperationType == EditAction::align ||
  6063       inOperationType == EditAction::setAbsolutePosition ||
  6064       inOperationType == EditAction::indent ||
  6065       inOperationType == EditAction::outdent) {
  6066     int32_t listCount = outArrayOfNodes.Count();
  6067     for (i=listCount-1; i>=0; i--)
  6069       nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i];
  6070       if (!aDontTouchContent && IsInlineNode(node) &&
  6071           (!mHTMLEditor || mHTMLEditor->IsContainer(node)) &&
  6072           (!mHTMLEditor || !mHTMLEditor->IsTextNode(node)))
  6074         NS_ENSURE_STATE(mHTMLEditor);
  6075         nsCOMArray<nsIDOMNode> arrayOfInlines;
  6076         res = BustUpInlinesAtBRs(node, arrayOfInlines);
  6077         NS_ENSURE_SUCCESS(res, res);
  6078         // put these nodes in outArrayOfNodes, replacing the current node
  6079         outArrayOfNodes.RemoveObjectAt(i);
  6080         outArrayOfNodes.InsertObjectsAt(arrayOfInlines, i);
  6084   return res;
  6089 ///////////////////////////////////////////////////////////////////////////
  6090 // GetChildNodesForOperation: 
  6091 //                       
  6092 nsresult 
  6093 nsHTMLEditRules::GetChildNodesForOperation(nsIDOMNode *inNode, 
  6094                                            nsCOMArray<nsIDOMNode>& outArrayOfNodes)
  6096   nsCOMPtr<nsINode> node = do_QueryInterface(inNode);
  6097   NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
  6099   for (nsIContent* child = node->GetFirstChild();
  6100        child;
  6101        child = child->GetNextSibling()) {
  6102     nsIDOMNode* childNode = child->AsDOMNode();
  6103     if (!outArrayOfNodes.AppendObject(childNode)) {
  6104       return NS_ERROR_FAILURE;
  6107   return NS_OK;
  6112 ///////////////////////////////////////////////////////////////////////////
  6113 // GetListActionNodes: 
  6114 //                       
  6115 nsresult 
  6116 nsHTMLEditRules::GetListActionNodes(nsCOMArray<nsIDOMNode> &outArrayOfNodes, 
  6117                                     bool aEntireList,
  6118                                     bool aDontTouchContent)
  6120   nsresult res = NS_OK;
  6122   nsCOMPtr<nsISelection>selection;
  6123   NS_ENSURE_STATE(mHTMLEditor);
  6124   res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
  6125   NS_ENSURE_SUCCESS(res, res);
  6126   Selection* sel = static_cast<Selection*>(selection.get());
  6127   NS_ENSURE_TRUE(sel, NS_ERROR_FAILURE);
  6128   // added this in so that ui code can ask to change an entire list, even if selection
  6129   // is only in part of it.  used by list item dialog.
  6130   if (aEntireList)
  6132     uint32_t rangeCount = sel->GetRangeCount();
  6133     for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
  6134       nsRefPtr<nsRange> range = sel->GetRangeAt(rangeIdx);
  6135       nsCOMPtr<nsIDOMNode> commonParent, parent, tmp;
  6136       range->GetCommonAncestorContainer(getter_AddRefs(commonParent));
  6137       if (commonParent)
  6139         parent = commonParent;
  6140         while (parent)
  6142           if (nsHTMLEditUtils::IsList(parent))
  6144             outArrayOfNodes.AppendObject(parent);
  6145             break;
  6147           parent->GetParentNode(getter_AddRefs(tmp));
  6148           parent = tmp;
  6152     // if we didn't find any nodes this way, then try the normal way.  perhaps the
  6153     // selection spans multiple lists but with no common list parent.
  6154     if (outArrayOfNodes.Count()) return NS_OK;
  6158     // We don't like other people messing with our selection!
  6159     NS_ENSURE_STATE(mHTMLEditor);
  6160     nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
  6162     // contruct a list of nodes to act on.
  6163     res = GetNodesFromSelection(selection, EditAction::makeList,
  6164                                 outArrayOfNodes, aDontTouchContent);
  6165     NS_ENSURE_SUCCESS(res, res);
  6168   // pre process our list of nodes...                      
  6169   int32_t listCount = outArrayOfNodes.Count();
  6170   int32_t i;
  6171   for (i=listCount-1; i>=0; i--)
  6173     nsCOMPtr<nsIDOMNode> testNode = outArrayOfNodes[i];
  6175     // Remove all non-editable nodes.  Leave them be.
  6176     NS_ENSURE_STATE(mHTMLEditor);
  6177     if (!mHTMLEditor->IsEditable(testNode))
  6179       outArrayOfNodes.RemoveObjectAt(i);
  6182     // scan for table elements and divs.  If we find table elements other than table,
  6183     // replace it with a list of any editable non-table content.
  6184     if (nsHTMLEditUtils::IsTableElementButNotTable(testNode))
  6186       int32_t j=i;
  6187       outArrayOfNodes.RemoveObjectAt(i);
  6188       res = GetInnerContent(testNode, outArrayOfNodes, &j, false);
  6189       NS_ENSURE_SUCCESS(res, res);
  6193   // if there is only one node in the array, and it is a list, div, or blockquote,
  6194   // then look inside of it until we find inner list or content.
  6195   res = LookInsideDivBQandList(outArrayOfNodes);
  6196   return res;
  6200 ///////////////////////////////////////////////////////////////////////////
  6201 // LookInsideDivBQandList: 
  6202 //                       
  6203 nsresult 
  6204 nsHTMLEditRules::LookInsideDivBQandList(nsCOMArray<nsIDOMNode>& aNodeArray)
  6206   // if there is only one node in the array, and it is a list, div, or blockquote,
  6207   // then look inside of it until we find inner list or content.
  6208   int32_t listCount = aNodeArray.Count();
  6209   if (listCount != 1) {
  6210     return NS_OK;
  6213   nsCOMPtr<nsINode> curNode = do_QueryInterface(aNodeArray[0]);
  6214   NS_ENSURE_STATE(curNode);
  6216   while (curNode->IsElement() &&
  6217          (curNode->AsElement()->IsHTML(nsGkAtoms::div) ||
  6218           nsHTMLEditUtils::IsList(curNode) ||
  6219           curNode->AsElement()->IsHTML(nsGkAtoms::blockquote))) {
  6220     // dive as long as there is only one child, and it is a list, div, blockquote
  6221     NS_ENSURE_STATE(mHTMLEditor);
  6222     uint32_t numChildren = mHTMLEditor->CountEditableChildren(curNode);
  6223     if (numChildren != 1) {
  6224       break;
  6227     // keep diving
  6228     // XXX One would expect to dive into the one editable node.
  6229     nsIContent* tmp = curNode->GetFirstChild();
  6230     if (!tmp->IsElement()) {
  6231       break;
  6234     dom::Element* element = tmp->AsElement();
  6235     if (!element->IsHTML(nsGkAtoms::div) &&
  6236         !nsHTMLEditUtils::IsList(element) &&
  6237         !element->IsHTML(nsGkAtoms::blockquote)) {
  6238       break;
  6241     // check editablility XXX floppy moose
  6242     curNode = tmp;
  6245   // we've found innermost list/blockquote/div: 
  6246   // replace the one node in the array with these nodes
  6247   aNodeArray.RemoveObjectAt(0);
  6248   if (curNode->IsElement() &&
  6249       (curNode->AsElement()->IsHTML(nsGkAtoms::div) ||
  6250        curNode->AsElement()->IsHTML(nsGkAtoms::blockquote))) {
  6251     int32_t j = 0;
  6252     return GetInnerContent(curNode->AsDOMNode(), aNodeArray, &j, false, false);
  6255   aNodeArray.AppendObject(curNode->AsDOMNode());
  6256   return NS_OK;
  6260 ///////////////////////////////////////////////////////////////////////////
  6261 // GetDefinitionListItemTypes: 
  6262 //                       
  6263 void
  6264 nsHTMLEditRules::GetDefinitionListItemTypes(dom::Element* aElement, bool* aDT, bool* aDD)
  6266   MOZ_ASSERT(aElement);
  6267   MOZ_ASSERT(aElement->IsHTML(nsGkAtoms::dl));
  6268   MOZ_ASSERT(aDT);
  6269   MOZ_ASSERT(aDD);
  6271   *aDT = *aDD = false;
  6272   for (nsIContent* child = aElement->GetFirstChild();
  6273        child;
  6274        child = child->GetNextSibling()) {
  6275     if (child->IsHTML(nsGkAtoms::dt)) {
  6276       *aDT = true;
  6277     } else if (child->IsHTML(nsGkAtoms::dd)) {
  6278       *aDD = true;
  6283 ///////////////////////////////////////////////////////////////////////////
  6284 // GetParagraphFormatNodes: 
  6285 //                       
  6286 nsresult 
  6287 nsHTMLEditRules::GetParagraphFormatNodes(nsCOMArray<nsIDOMNode>& outArrayOfNodes,
  6288                                          bool aDontTouchContent)
  6290   nsCOMPtr<nsISelection>selection;
  6291   NS_ENSURE_STATE(mHTMLEditor);
  6292   nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
  6293   NS_ENSURE_SUCCESS(res, res);
  6295   // contruct a list of nodes to act on.
  6296   res = GetNodesFromSelection(selection, EditAction::makeBasicBlock,
  6297                               outArrayOfNodes, aDontTouchContent);
  6298   NS_ENSURE_SUCCESS(res, res);
  6300   // pre process our list of nodes...                      
  6301   int32_t listCount = outArrayOfNodes.Count();
  6302   int32_t i;
  6303   for (i=listCount-1; i>=0; i--)
  6305     nsCOMPtr<nsIDOMNode> testNode = outArrayOfNodes[i];
  6307     // Remove all non-editable nodes.  Leave them be.
  6308     NS_ENSURE_STATE(mHTMLEditor);
  6309     if (!mHTMLEditor->IsEditable(testNode))
  6311       outArrayOfNodes.RemoveObjectAt(i);
  6314     // scan for table elements.  If we find table elements other than table,
  6315     // replace it with a list of any editable non-table content.  Ditto for list elements.
  6316     if (nsHTMLEditUtils::IsTableElement(testNode) ||
  6317         nsHTMLEditUtils::IsList(testNode) || 
  6318         nsHTMLEditUtils::IsListItem(testNode) )
  6320       int32_t j=i;
  6321       outArrayOfNodes.RemoveObjectAt(i);
  6322       res = GetInnerContent(testNode, outArrayOfNodes, &j);
  6323       NS_ENSURE_SUCCESS(res, res);
  6326   return res;
  6330 ///////////////////////////////////////////////////////////////////////////
  6331 // BustUpInlinesAtRangeEndpoints: 
  6332 //                       
  6333 nsresult 
  6334 nsHTMLEditRules::BustUpInlinesAtRangeEndpoints(nsRangeStore &item)
  6336   nsresult res = NS_OK;
  6337   bool isCollapsed = ((item.startNode == item.endNode) && (item.startOffset == item.endOffset));
  6339   nsCOMPtr<nsIDOMNode> endInline = GetHighestInlineParent(item.endNode);
  6341   // if we have inline parents above range endpoints, split them
  6342   if (endInline && !isCollapsed)
  6344     nsCOMPtr<nsIDOMNode> resultEndNode;
  6345     int32_t resultEndOffset;
  6346     endInline->GetParentNode(getter_AddRefs(resultEndNode));
  6347     NS_ENSURE_STATE(mHTMLEditor);
  6348     res = mHTMLEditor->SplitNodeDeep(endInline, item.endNode, item.endOffset,
  6349                           &resultEndOffset, true);
  6350     NS_ENSURE_SUCCESS(res, res);
  6351     // reset range
  6352     item.endNode = resultEndNode; item.endOffset = resultEndOffset;
  6355   nsCOMPtr<nsIDOMNode> startInline = GetHighestInlineParent(item.startNode);
  6357   if (startInline)
  6359     nsCOMPtr<nsIDOMNode> resultStartNode;
  6360     int32_t resultStartOffset;
  6361     startInline->GetParentNode(getter_AddRefs(resultStartNode));
  6362     NS_ENSURE_STATE(mHTMLEditor);
  6363     res = mHTMLEditor->SplitNodeDeep(startInline, item.startNode, item.startOffset,
  6364                           &resultStartOffset, true);
  6365     NS_ENSURE_SUCCESS(res, res);
  6366     // reset range
  6367     item.startNode = resultStartNode; item.startOffset = resultStartOffset;
  6370   return res;
  6375 ///////////////////////////////////////////////////////////////////////////
  6376 // BustUpInlinesAtBRs: 
  6377 //                       
  6378 nsresult 
  6379 nsHTMLEditRules::BustUpInlinesAtBRs(nsIDOMNode *inNode, 
  6380                                     nsCOMArray<nsIDOMNode>& outArrayOfNodes)
  6382   NS_ENSURE_TRUE(inNode, NS_ERROR_NULL_POINTER);
  6384   // first step is to build up a list of all the break nodes inside 
  6385   // the inline container.
  6386   nsCOMArray<nsIDOMNode> arrayOfBreaks;
  6387   nsBRNodeFunctor functor;
  6388   nsDOMIterator iter;
  6389   nsresult res = iter.Init(inNode);
  6390   NS_ENSURE_SUCCESS(res, res);
  6391   res = iter.AppendList(functor, arrayOfBreaks);
  6392   NS_ENSURE_SUCCESS(res, res);
  6394   // if there aren't any breaks, just put inNode itself in the array
  6395   int32_t listCount = arrayOfBreaks.Count();
  6396   if (!listCount)
  6398     if (!outArrayOfNodes.AppendObject(inNode))
  6399       return NS_ERROR_FAILURE;
  6401   else
  6403     // else we need to bust up inNode along all the breaks
  6404     nsCOMPtr<nsIDOMNode> breakNode;
  6405     nsCOMPtr<nsIDOMNode> inlineParentNode;
  6406     nsCOMPtr<nsIDOMNode> leftNode;
  6407     nsCOMPtr<nsIDOMNode> rightNode;
  6408     nsCOMPtr<nsIDOMNode> splitDeepNode = inNode;
  6409     nsCOMPtr<nsIDOMNode> splitParentNode;
  6410     int32_t splitOffset, resultOffset, i;
  6411     inNode->GetParentNode(getter_AddRefs(inlineParentNode));
  6413     for (i=0; i< listCount; i++)
  6415       breakNode = arrayOfBreaks[i];
  6416       NS_ENSURE_TRUE(breakNode, NS_ERROR_NULL_POINTER);
  6417       NS_ENSURE_TRUE(splitDeepNode, NS_ERROR_NULL_POINTER);
  6418       splitParentNode = nsEditor::GetNodeLocation(breakNode, &splitOffset);
  6419       NS_ENSURE_STATE(mHTMLEditor);
  6420       res = mHTMLEditor->SplitNodeDeep(splitDeepNode, splitParentNode, splitOffset,
  6421                           &resultOffset, false, address_of(leftNode), address_of(rightNode));
  6422       NS_ENSURE_SUCCESS(res, res);
  6423       // put left node in node list
  6424       if (leftNode)
  6426         // might not be a left node.  a break might have been at the very
  6427         // beginning of inline container, in which case splitnodedeep
  6428         // would not actually split anything
  6429         if (!outArrayOfNodes.AppendObject(leftNode))
  6430           return NS_ERROR_FAILURE;
  6432       // move break outside of container and also put in node list
  6433       NS_ENSURE_STATE(mHTMLEditor);
  6434       res = mHTMLEditor->MoveNode(breakNode, inlineParentNode, resultOffset);
  6435       NS_ENSURE_SUCCESS(res, res);
  6436       if (!outArrayOfNodes.AppendObject(breakNode))
  6437         return  NS_ERROR_FAILURE;
  6438       // now rightNode becomes the new node to split
  6439       splitDeepNode = rightNode;
  6441     // now tack on remaining rightNode, if any, to the list
  6442     if (rightNode)
  6444       if (!outArrayOfNodes.AppendObject(rightNode))
  6445         return NS_ERROR_FAILURE;
  6448   return res;
  6452 nsCOMPtr<nsIDOMNode> 
  6453 nsHTMLEditRules::GetHighestInlineParent(nsIDOMNode* aNode)
  6455   NS_ENSURE_TRUE(aNode, nullptr);
  6456   if (IsBlockNode(aNode)) return nullptr;
  6457   nsCOMPtr<nsIDOMNode> inlineNode, node=aNode;
  6459   while (node && IsInlineNode(node))
  6461     inlineNode = node;
  6462     inlineNode->GetParentNode(getter_AddRefs(node));
  6464   return inlineNode;
  6468 ///////////////////////////////////////////////////////////////////////////
  6469 // GetNodesFromPoint: given a particular operation, construct a list  
  6470 //                     of nodes from a point that will be operated on. 
  6471 //                       
  6472 nsresult 
  6473 nsHTMLEditRules::GetNodesFromPoint(::DOMPoint point,
  6474                                    EditAction operation,
  6475                                    nsCOMArray<nsIDOMNode> &arrayOfNodes,
  6476                                    bool dontTouchContent)
  6478   nsresult res;
  6480   // get our point
  6481   nsCOMPtr<nsIDOMNode> node;
  6482   int32_t offset;
  6483   point.GetPoint(node, offset);
  6485   // use it to make a range
  6486   nsCOMPtr<nsINode> nativeNode = do_QueryInterface(node);
  6487   NS_ENSURE_STATE(nativeNode);
  6488   nsRefPtr<nsRange> range = new nsRange(nativeNode);
  6489   res = range->SetStart(node, offset);
  6490   NS_ENSURE_SUCCESS(res, res);
  6491   /* SetStart() will also set the end for this new range
  6492   res = range->SetEnd(node, offset);
  6493   NS_ENSURE_SUCCESS(res, res); */
  6495   // expand the range to include adjacent inlines
  6496   res = PromoteRange(range, operation);
  6497   NS_ENSURE_SUCCESS(res, res);
  6499   // make array of ranges
  6500   nsCOMArray<nsIDOMRange> arrayOfRanges;
  6502   // stuff new opRange into array
  6503   arrayOfRanges.AppendObject(range);
  6505   // use these ranges to contruct a list of nodes to act on.
  6506   res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, operation, dontTouchContent); 
  6507   return res;
  6511 ///////////////////////////////////////////////////////////////////////////
  6512 // GetNodesFromSelection: given a particular operation, construct a list  
  6513 //                     of nodes from the selection that will be operated on. 
  6514 //                       
  6515 nsresult 
  6516 nsHTMLEditRules::GetNodesFromSelection(nsISelection *selection,
  6517                                        EditAction operation,
  6518                                        nsCOMArray<nsIDOMNode>& arrayOfNodes,
  6519                                        bool dontTouchContent)
  6521   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
  6522   nsresult res;
  6524   // promote selection ranges
  6525   nsCOMArray<nsIDOMRange> arrayOfRanges;
  6526   res = GetPromotedRanges(selection, arrayOfRanges, operation);
  6527   NS_ENSURE_SUCCESS(res, res);
  6529   // use these ranges to contruct a list of nodes to act on.
  6530   res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, operation, dontTouchContent); 
  6531   return res;
  6535 ///////////////////////////////////////////////////////////////////////////
  6536 // MakeTransitionList: detect all the transitions in the array, where a 
  6537 //                     transition means that adjacent nodes in the array 
  6538 //                     don't have the same parent.
  6539 //                       
  6540 nsresult 
  6541 nsHTMLEditRules::MakeTransitionList(nsCOMArray<nsIDOMNode>& inArrayOfNodes, 
  6542                                     nsTArray<bool> &inTransitionArray)
  6544   uint32_t listCount = inArrayOfNodes.Count();
  6545   inTransitionArray.EnsureLengthAtLeast(listCount);
  6546   uint32_t i;
  6547   nsCOMPtr<nsIDOMNode> prevElementParent;
  6548   nsCOMPtr<nsIDOMNode> curElementParent;
  6550   for (i=0; i<listCount; i++)
  6552     nsIDOMNode* transNode = inArrayOfNodes[i];
  6553     transNode->GetParentNode(getter_AddRefs(curElementParent));
  6554     if (curElementParent != prevElementParent)
  6556       // different parents, or separated by <br>: transition point
  6557       inTransitionArray[i] = true;
  6559     else
  6561       // same parents: these nodes grew up together
  6562       inTransitionArray[i] = false;
  6564     prevElementParent = curElementParent;
  6566   return NS_OK;
  6571 /********************************************************
  6572  *  main implementation methods 
  6573  ********************************************************/
  6575 ///////////////////////////////////////////////////////////////////////////
  6576 // IsInListItem: if aNode is the descendant of a listitem, return that li.
  6577 //               But table element boundaries are stoppers on the search.
  6578 //               Also stops on the active editor host (contenteditable).
  6579 //               Also test if aNode is an li itself.
  6580 //                       
  6581 already_AddRefed<nsIDOMNode>
  6582 nsHTMLEditRules::IsInListItem(nsIDOMNode* aNode)
  6584   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  6585   nsCOMPtr<nsIDOMNode> retval = do_QueryInterface(IsInListItem(node));
  6586   return retval.forget();
  6589 nsINode*
  6590 nsHTMLEditRules::IsInListItem(nsINode* aNode)
  6592   NS_ENSURE_TRUE(aNode, nullptr);
  6593   if (nsHTMLEditUtils::IsListItem(aNode)) {
  6594     return aNode;
  6597   nsINode* parent = aNode->GetParentNode();
  6598   while (parent && mHTMLEditor && mHTMLEditor->IsDescendantOfEditorRoot(parent) &&
  6599          !nsHTMLEditUtils::IsTableElement(parent)) {
  6600     if (nsHTMLEditUtils::IsListItem(parent)) {
  6601       return parent;
  6603     parent = parent->GetParentNode();
  6605   return nullptr;
  6609 ///////////////////////////////////////////////////////////////////////////
  6610 // ReturnInHeader: do the right thing for returns pressed in headers
  6611 //                       
  6612 nsresult 
  6613 nsHTMLEditRules::ReturnInHeader(nsISelection *aSelection, 
  6614                                 nsIDOMNode *aHeader, 
  6615                                 nsIDOMNode *aNode, 
  6616                                 int32_t aOffset)
  6618   NS_ENSURE_TRUE(aSelection && aHeader && aNode, NS_ERROR_NULL_POINTER);  
  6620   // remeber where the header is
  6621   int32_t offset;
  6622   nsCOMPtr<nsIDOMNode> headerParent = nsEditor::GetNodeLocation(aHeader, &offset);
  6624   // get ws code to adjust any ws
  6625   nsCOMPtr<nsIDOMNode> selNode = aNode;
  6626   NS_ENSURE_STATE(mHTMLEditor);
  6627   nsresult res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor,
  6628                                                            address_of(selNode),
  6629                                                            &aOffset);
  6630   NS_ENSURE_SUCCESS(res, res);
  6632   // split the header
  6633   int32_t newOffset;
  6634   NS_ENSURE_STATE(mHTMLEditor);
  6635   res = mHTMLEditor->SplitNodeDeep( aHeader, selNode, aOffset, &newOffset);
  6636   NS_ENSURE_SUCCESS(res, res);
  6638   // if the leftand heading is empty, put a mozbr in it
  6639   nsCOMPtr<nsIDOMNode> prevItem;
  6640   NS_ENSURE_STATE(mHTMLEditor);
  6641   mHTMLEditor->GetPriorHTMLSibling(aHeader, address_of(prevItem));
  6642   if (prevItem && nsHTMLEditUtils::IsHeader(prevItem))
  6644     bool bIsEmptyNode;
  6645     NS_ENSURE_STATE(mHTMLEditor);
  6646     res = mHTMLEditor->IsEmptyNode(prevItem, &bIsEmptyNode);
  6647     NS_ENSURE_SUCCESS(res, res);
  6648     if (bIsEmptyNode) {
  6649       res = CreateMozBR(prevItem, 0);
  6650       NS_ENSURE_SUCCESS(res, res);
  6654   // if the new (righthand) header node is empty, delete it
  6655   bool isEmpty;
  6656   res = IsEmptyBlock(aHeader, &isEmpty, true);
  6657   NS_ENSURE_SUCCESS(res, res);
  6658   if (isEmpty)
  6660     NS_ENSURE_STATE(mHTMLEditor);
  6661     res = mHTMLEditor->DeleteNode(aHeader);
  6662     NS_ENSURE_SUCCESS(res, res);
  6663     // layout tells the caret to blink in a weird place
  6664     // if we don't place a break after the header.
  6665     nsCOMPtr<nsIDOMNode> sibling;
  6666     NS_ENSURE_STATE(mHTMLEditor);
  6667     res = mHTMLEditor->GetNextHTMLSibling(headerParent, offset+1, address_of(sibling));
  6668     NS_ENSURE_SUCCESS(res, res);
  6669     if (!sibling || !nsTextEditUtils::IsBreak(sibling))
  6671       ClearCachedStyles();
  6672       NS_ENSURE_STATE(mHTMLEditor);
  6673       mHTMLEditor->mTypeInState->ClearAllProps();
  6675       // create a paragraph
  6676       NS_NAMED_LITERAL_STRING(pType, "p");
  6677       nsCOMPtr<nsIDOMNode> pNode;
  6678       NS_ENSURE_STATE(mHTMLEditor);
  6679       res = mHTMLEditor->CreateNode(pType, headerParent, offset+1, getter_AddRefs(pNode));
  6680       NS_ENSURE_SUCCESS(res, res);
  6682       // append a <br> to it
  6683       nsCOMPtr<nsIDOMNode> brNode;
  6684       NS_ENSURE_STATE(mHTMLEditor);
  6685       res = mHTMLEditor->CreateBR(pNode, 0, address_of(brNode));
  6686       NS_ENSURE_SUCCESS(res, res);
  6688       // set selection to before the break
  6689       res = aSelection->Collapse(pNode, 0);
  6691     else
  6693       headerParent = nsEditor::GetNodeLocation(sibling, &offset);
  6694       // put selection after break
  6695       res = aSelection->Collapse(headerParent,offset+1);
  6698   else
  6700     // put selection at front of righthand heading
  6701     res = aSelection->Collapse(aHeader,0);
  6703   return res;
  6706 ///////////////////////////////////////////////////////////////////////////
  6707 // ReturnInParagraph: do the right thing for returns pressed in paragraphs
  6708 //
  6709 nsresult
  6710 nsHTMLEditRules::ReturnInParagraph(nsISelection* aSelection,
  6711                                    nsIDOMNode* aPara,
  6712                                    nsIDOMNode* aNode,
  6713                                    int32_t aOffset,
  6714                                    bool* aCancel,
  6715                                    bool* aHandled)
  6717   if (!aSelection || !aPara || !aNode || !aCancel || !aHandled) {
  6718     return NS_ERROR_NULL_POINTER;
  6720   *aCancel = false;
  6721   *aHandled = false;
  6722   nsresult res;
  6724   int32_t offset;
  6725   nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(aNode, &offset);
  6727   NS_ENSURE_STATE(mHTMLEditor);
  6728   bool doesCRCreateNewP = mHTMLEditor->GetReturnInParagraphCreatesNewParagraph();
  6730   bool newBRneeded = false;
  6731   nsCOMPtr<nsIDOMNode> sibling;
  6733   NS_ENSURE_STATE(mHTMLEditor);
  6734   if (aNode == aPara && doesCRCreateNewP) {
  6735     // we are at the edges of the block, newBRneeded not needed!
  6736     sibling = aNode;
  6737   } else if (mHTMLEditor->IsTextNode(aNode)) {
  6738     nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(aNode);
  6739     uint32_t strLength;
  6740     res = textNode->GetLength(&strLength);
  6741     NS_ENSURE_SUCCESS(res, res);
  6743     // at beginning of text node?
  6744     if (!aOffset) {
  6745       // is there a BR prior to it?
  6746       NS_ENSURE_STATE(mHTMLEditor);
  6747       mHTMLEditor->GetPriorHTMLSibling(aNode, address_of(sibling));
  6748       if (!sibling || !mHTMLEditor || !mHTMLEditor->IsVisBreak(sibling) ||
  6749           nsTextEditUtils::HasMozAttr(sibling)) {
  6750         NS_ENSURE_STATE(mHTMLEditor);
  6751         newBRneeded = true;
  6753     } else if (aOffset == (int32_t)strLength) {
  6754       // we're at the end of text node...
  6755       // is there a BR after to it?
  6756       NS_ENSURE_STATE(mHTMLEditor);
  6757       res = mHTMLEditor->GetNextHTMLSibling(aNode, address_of(sibling));
  6758       if (!sibling || !mHTMLEditor || !mHTMLEditor->IsVisBreak(sibling) ||
  6759           nsTextEditUtils::HasMozAttr(sibling)) {
  6760         NS_ENSURE_STATE(mHTMLEditor);
  6761         newBRneeded = true;
  6762         offset++;
  6764     } else {
  6765       if (doesCRCreateNewP) {
  6766         nsCOMPtr<nsIDOMNode> tmp;
  6767         res = mEditor->SplitNode(aNode, aOffset, getter_AddRefs(tmp));
  6768         NS_ENSURE_SUCCESS(res, res);
  6769         aNode = tmp;
  6772       newBRneeded = true;
  6773       offset++;
  6775   } else {
  6776     // not in a text node.
  6777     // is there a BR prior to it?
  6778     nsCOMPtr<nsIDOMNode> nearNode, selNode = aNode;
  6779     NS_ENSURE_STATE(mHTMLEditor);
  6780     res = mHTMLEditor->GetPriorHTMLNode(aNode, aOffset, address_of(nearNode));
  6781     NS_ENSURE_SUCCESS(res, res);
  6782     NS_ENSURE_STATE(mHTMLEditor);
  6783     if (!nearNode || !mHTMLEditor->IsVisBreak(nearNode) ||
  6784         nsTextEditUtils::HasMozAttr(nearNode)) {
  6785       // is there a BR after it?
  6786       NS_ENSURE_STATE(mHTMLEditor);
  6787       res = mHTMLEditor->GetNextHTMLNode(aNode, aOffset, address_of(nearNode));
  6788       NS_ENSURE_SUCCESS(res, res);
  6789       NS_ENSURE_STATE(mHTMLEditor);
  6790       if (!nearNode || !mHTMLEditor->IsVisBreak(nearNode) ||
  6791           nsTextEditUtils::HasMozAttr(nearNode)) {
  6792         newBRneeded = true;
  6795     if (!newBRneeded) {
  6796       sibling = nearNode;
  6799   if (newBRneeded) {
  6800     // if CR does not create a new P, default to BR creation
  6801     NS_ENSURE_TRUE(doesCRCreateNewP, NS_OK);
  6803     nsCOMPtr<nsIDOMNode> brNode;
  6804     NS_ENSURE_STATE(mHTMLEditor);
  6805     res =  mHTMLEditor->CreateBR(parent, offset, address_of(brNode));
  6806     sibling = brNode;
  6808   nsCOMPtr<nsIDOMNode> selNode = aNode;
  6809   *aHandled = true;
  6810   return SplitParagraph(aPara, sibling, aSelection, address_of(selNode), &aOffset);
  6813 ///////////////////////////////////////////////////////////////////////////
  6814 // SplitParagraph: split a paragraph at selection point, possibly deleting a br
  6815 //                       
  6816 nsresult 
  6817 nsHTMLEditRules::SplitParagraph(nsIDOMNode *aPara,
  6818                                 nsIDOMNode *aBRNode, 
  6819                                 nsISelection *aSelection,
  6820                                 nsCOMPtr<nsIDOMNode> *aSelNode, 
  6821                                 int32_t *aOffset)
  6823   NS_ENSURE_TRUE(aPara && aBRNode && aSelNode && *aSelNode && aOffset && aSelection, NS_ERROR_NULL_POINTER);
  6824   nsresult res = NS_OK;
  6826   // split para
  6827   int32_t newOffset;
  6828   // get ws code to adjust any ws
  6829   nsCOMPtr<nsIDOMNode> leftPara, rightPara;
  6830   NS_ENSURE_STATE(mHTMLEditor);
  6831   res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, aSelNode, aOffset);
  6832   NS_ENSURE_SUCCESS(res, res);
  6833   // split the paragraph
  6834   NS_ENSURE_STATE(mHTMLEditor);
  6835   res = mHTMLEditor->SplitNodeDeep(aPara, *aSelNode, *aOffset, &newOffset, false,
  6836                                    address_of(leftPara), address_of(rightPara));
  6837   NS_ENSURE_SUCCESS(res, res);
  6838   // get rid of the break, if it is visible (otherwise it may be needed to prevent an empty p)
  6839   NS_ENSURE_STATE(mHTMLEditor);
  6840   if (mHTMLEditor->IsVisBreak(aBRNode))
  6842     NS_ENSURE_STATE(mHTMLEditor);
  6843     res = mHTMLEditor->DeleteNode(aBRNode);  
  6844     NS_ENSURE_SUCCESS(res, res);
  6847   // remove ID attribute on the paragraph we just created
  6848   nsCOMPtr<nsIDOMElement> rightElt = do_QueryInterface(rightPara);
  6849   NS_ENSURE_STATE(mHTMLEditor);
  6850   res = mHTMLEditor->RemoveAttribute(rightElt, NS_LITERAL_STRING("id"));
  6851   NS_ENSURE_SUCCESS(res, res);
  6853   // check both halves of para to see if we need mozBR
  6854   res = InsertMozBRIfNeeded(leftPara);
  6855   NS_ENSURE_SUCCESS(res, res);
  6856   res = InsertMozBRIfNeeded(rightPara);
  6857   NS_ENSURE_SUCCESS(res, res);
  6859   // selection to beginning of right hand para;
  6860   // look inside any containers that are up front.
  6861   NS_ENSURE_STATE(mHTMLEditor);
  6862   nsCOMPtr<nsIDOMNode> child = mHTMLEditor->GetLeftmostChild(rightPara, true);
  6863   NS_ENSURE_STATE(mHTMLEditor);
  6864   if (mHTMLEditor->IsTextNode(child) || !mHTMLEditor ||
  6865       mHTMLEditor->IsContainer(child))
  6867     NS_ENSURE_STATE(mHTMLEditor);
  6868     aSelection->Collapse(child,0);
  6870   else
  6872     int32_t offset;
  6873     nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(child, &offset);
  6874     aSelection->Collapse(parent,offset);
  6876   return res;
  6880 ///////////////////////////////////////////////////////////////////////////
  6881 // ReturnInListItem: do the right thing for returns pressed in list items
  6882 //                       
  6883 nsresult 
  6884 nsHTMLEditRules::ReturnInListItem(nsISelection *aSelection, 
  6885                                   nsIDOMNode *aListItem, 
  6886                                   nsIDOMNode *aNode, 
  6887                                   int32_t aOffset)
  6889   NS_ENSURE_TRUE(aSelection && aListItem && aNode, NS_ERROR_NULL_POINTER);
  6890   nsCOMPtr<nsISelection> selection(aSelection);
  6891   nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
  6892   nsresult res = NS_OK;
  6894   nsCOMPtr<nsIDOMNode> listitem;
  6896   // sanity check
  6897   NS_PRECONDITION(true == nsHTMLEditUtils::IsListItem(aListItem),
  6898                   "expected a list item and didn't get one");
  6900   // get the listitem parent and the active editing host.
  6901   NS_ENSURE_STATE(mHTMLEditor);
  6902   nsIContent* rootContent = mHTMLEditor->GetActiveEditingHost();
  6903   nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(rootContent);
  6904   int32_t itemOffset;
  6905   nsCOMPtr<nsIDOMNode> list = nsEditor::GetNodeLocation(aListItem, &itemOffset);
  6907   // if we are in an empty listitem, then we want to pop up out of the list
  6908   // but only if prefs says it's ok and if the parent isn't the active editing host.
  6909   bool isEmpty;
  6910   res = IsEmptyBlock(aListItem, &isEmpty, true, false);
  6911   NS_ENSURE_SUCCESS(res, res);
  6912   if (isEmpty && (rootNode != list) && mReturnInEmptyLIKillsList)
  6914     // get the list offset now -- before we might eventually split the list
  6915     int32_t offset;
  6916     nsCOMPtr<nsIDOMNode> listparent = nsEditor::GetNodeLocation(list, &offset);
  6918     // are we the last list item in the list?
  6919     bool bIsLast;
  6920     NS_ENSURE_STATE(mHTMLEditor);
  6921     res = mHTMLEditor->IsLastEditableChild(aListItem, &bIsLast);
  6922     NS_ENSURE_SUCCESS(res, res);
  6923     if (!bIsLast)
  6925       // we need to split the list!
  6926       nsCOMPtr<nsIDOMNode> tempNode;
  6927       NS_ENSURE_STATE(mHTMLEditor);
  6928       res = mHTMLEditor->SplitNode(list, itemOffset, getter_AddRefs(tempNode));
  6929       NS_ENSURE_SUCCESS(res, res);
  6932     // are we in a sublist?
  6933     if (nsHTMLEditUtils::IsList(listparent))  //in a sublist
  6935       // if so, move this list item out of this list and into the grandparent list
  6936       NS_ENSURE_STATE(mHTMLEditor);
  6937       res = mHTMLEditor->MoveNode(aListItem,listparent,offset+1);
  6938       NS_ENSURE_SUCCESS(res, res);
  6939       res = aSelection->Collapse(aListItem,0);
  6941     else
  6943       // otherwise kill this listitem
  6944       NS_ENSURE_STATE(mHTMLEditor);
  6945       res = mHTMLEditor->DeleteNode(aListItem);
  6946       NS_ENSURE_SUCCESS(res, res);
  6948       // time to insert a paragraph
  6949       NS_NAMED_LITERAL_STRING(pType, "p");
  6950       nsCOMPtr<nsIDOMNode> pNode;
  6951       NS_ENSURE_STATE(mHTMLEditor);
  6952       res = mHTMLEditor->CreateNode(pType, listparent, offset+1, getter_AddRefs(pNode));
  6953       NS_ENSURE_SUCCESS(res, res);
  6955       // append a <br> to it
  6956       nsCOMPtr<nsIDOMNode> brNode;
  6957       NS_ENSURE_STATE(mHTMLEditor);
  6958       res = mHTMLEditor->CreateBR(pNode, 0, address_of(brNode));
  6959       NS_ENSURE_SUCCESS(res, res);
  6961       // set selection to before the break
  6962       res = aSelection->Collapse(pNode, 0);
  6964     return res;
  6967   // else we want a new list item at the same list level.
  6968   // get ws code to adjust any ws
  6969   nsCOMPtr<nsIDOMNode> selNode = aNode;
  6970   NS_ENSURE_STATE(mHTMLEditor);
  6971   res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset);
  6972   NS_ENSURE_SUCCESS(res, res);
  6973   // now split list item
  6974   int32_t newOffset;
  6975   NS_ENSURE_STATE(mHTMLEditor);
  6976   res = mHTMLEditor->SplitNodeDeep( aListItem, selNode, aOffset, &newOffset, false);
  6977   NS_ENSURE_SUCCESS(res, res);
  6978   // hack: until I can change the damaged doc range code back to being
  6979   // extra inclusive, I have to manually detect certain list items that
  6980   // may be left empty.
  6981   nsCOMPtr<nsIDOMNode> prevItem;
  6982   NS_ENSURE_STATE(mHTMLEditor);
  6983   mHTMLEditor->GetPriorHTMLSibling(aListItem, address_of(prevItem));
  6985   if (prevItem && nsHTMLEditUtils::IsListItem(prevItem))
  6987     bool bIsEmptyNode;
  6988     NS_ENSURE_STATE(mHTMLEditor);
  6989     res = mHTMLEditor->IsEmptyNode(prevItem, &bIsEmptyNode);
  6990     NS_ENSURE_SUCCESS(res, res);
  6991     if (bIsEmptyNode) {
  6992       res = CreateMozBR(prevItem, 0);
  6993       NS_ENSURE_SUCCESS(res, res);
  6994     } else {
  6995       NS_ENSURE_STATE(mHTMLEditor);
  6996       res = mHTMLEditor->IsEmptyNode(aListItem, &bIsEmptyNode, true);
  6997       NS_ENSURE_SUCCESS(res, res);
  6998       if (bIsEmptyNode) 
  7000         nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aListItem);
  7001         if (nodeAtom == nsEditProperty::dd || nodeAtom == nsEditProperty::dt)
  7003           int32_t itemOffset;
  7004           nsCOMPtr<nsIDOMNode> list = nsEditor::GetNodeLocation(aListItem, &itemOffset);
  7006           nsAutoString listTag((nodeAtom == nsEditProperty::dt) ? NS_LITERAL_STRING("dd") : NS_LITERAL_STRING("dt"));
  7007           nsCOMPtr<nsIDOMNode> newListItem;
  7008           NS_ENSURE_STATE(mHTMLEditor);
  7009           res = mHTMLEditor->CreateNode(listTag, list, itemOffset+1, getter_AddRefs(newListItem));
  7010           NS_ENSURE_SUCCESS(res, res);
  7011           res = mEditor->DeleteNode(aListItem);
  7012           NS_ENSURE_SUCCESS(res, res);
  7013           return aSelection->Collapse(newListItem, 0);
  7016         nsCOMPtr<nsIDOMNode> brNode;
  7017         NS_ENSURE_STATE(mHTMLEditor);
  7018         res = mHTMLEditor->CopyLastEditableChildStyles(prevItem, aListItem, getter_AddRefs(brNode));
  7019         NS_ENSURE_SUCCESS(res, res);
  7020         if (brNode) 
  7022           int32_t offset;
  7023           nsCOMPtr<nsIDOMNode> brParent = nsEditor::GetNodeLocation(brNode, &offset);
  7024           return aSelection->Collapse(brParent, offset);
  7027       else
  7029         NS_ENSURE_STATE(mHTMLEditor);
  7030         nsWSRunObject wsObj(mHTMLEditor, aListItem, 0);
  7031         nsCOMPtr<nsIDOMNode> visNode;
  7032         int32_t visOffset = 0;
  7033         WSType wsType;
  7034         wsObj.NextVisibleNode(aListItem, 0, address_of(visNode),
  7035                               &visOffset, &wsType);
  7036         if (wsType == WSType::special || wsType == WSType::br ||
  7037             nsHTMLEditUtils::IsHR(visNode)) {
  7038           int32_t offset;
  7039           nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(visNode, &offset);
  7040           return aSelection->Collapse(parent, offset);
  7042         else
  7044           return aSelection->Collapse(visNode, visOffset);
  7049   res = aSelection->Collapse(aListItem,0);
  7050   return res;
  7054 ///////////////////////////////////////////////////////////////////////////
  7055 // MakeBlockquote:  put the list of nodes into one or more blockquotes.  
  7056 //                       
  7057 nsresult 
  7058 nsHTMLEditRules::MakeBlockquote(nsCOMArray<nsIDOMNode>& arrayOfNodes)
  7060   // the idea here is to put the nodes into a minimal number of 
  7061   // blockquotes.  When the user blockquotes something, they expect
  7062   // one blockquote.  That may not be possible (for instance, if they
  7063   // have two table cells selected, you need two blockquotes inside the cells).
  7065   nsresult res = NS_OK;
  7067   nsCOMPtr<nsIDOMNode> curNode, curParent, curBlock, newBlock;
  7068   int32_t offset;
  7069   int32_t listCount = arrayOfNodes.Count();
  7071   nsCOMPtr<nsIDOMNode> prevParent;
  7073   int32_t i;
  7074   for (i=0; i<listCount; i++)
  7076     // get the node to act on, and its location
  7077     curNode = arrayOfNodes[i];
  7078     curParent = nsEditor::GetNodeLocation(curNode, &offset);
  7080     // if the node is a table element or list item, dive inside
  7081     if (nsHTMLEditUtils::IsTableElementButNotTable(curNode) || 
  7082         nsHTMLEditUtils::IsListItem(curNode))
  7084       curBlock = 0;  // forget any previous block
  7085       // recursion time
  7086       nsCOMArray<nsIDOMNode> childArray;
  7087       res = GetChildNodesForOperation(curNode, childArray);
  7088       NS_ENSURE_SUCCESS(res, res);
  7089       res = MakeBlockquote(childArray);
  7090       NS_ENSURE_SUCCESS(res, res);
  7093     // if the node has different parent than previous node,
  7094     // further nodes in a new parent
  7095     if (prevParent)
  7097       nsCOMPtr<nsIDOMNode> temp;
  7098       curNode->GetParentNode(getter_AddRefs(temp));
  7099       if (temp != prevParent)
  7101         curBlock = 0;  // forget any previous blockquote node we were using
  7102         prevParent = temp;
  7105     else     
  7108       curNode->GetParentNode(getter_AddRefs(prevParent));
  7111     // if no curBlock, make one
  7112     if (!curBlock)
  7114       NS_NAMED_LITERAL_STRING(quoteType, "blockquote");
  7115       res = SplitAsNeeded(&quoteType, address_of(curParent), &offset);
  7116       NS_ENSURE_SUCCESS(res, res);
  7117       NS_ENSURE_STATE(mHTMLEditor);
  7118       res = mHTMLEditor->CreateNode(quoteType, curParent, offset, getter_AddRefs(curBlock));
  7119       NS_ENSURE_SUCCESS(res, res);
  7120       // remember our new block for postprocessing
  7121       mNewBlock = curBlock;
  7122       // note: doesn't matter if we set mNewBlock multiple times.
  7125     NS_ENSURE_STATE(mHTMLEditor);
  7126     res = mHTMLEditor->MoveNode(curNode, curBlock, -1);
  7127     NS_ENSURE_SUCCESS(res, res);
  7129   return res;
  7134 ///////////////////////////////////////////////////////////////////////////
  7135 // RemoveBlockStyle:  make the nodes have no special block type.  
  7136 //                       
  7137 nsresult 
  7138 nsHTMLEditRules::RemoveBlockStyle(nsCOMArray<nsIDOMNode>& arrayOfNodes)
  7140   // intent of this routine is to be used for converting to/from
  7141   // headers, paragraphs, pre, and address.  Those blocks
  7142   // that pretty much just contain inline things...
  7144   nsresult res = NS_OK;
  7146   nsCOMPtr<nsIDOMNode> curBlock, firstNode, lastNode;
  7147   int32_t listCount = arrayOfNodes.Count();
  7148   for (int32_t i = 0; i < listCount; ++i) {
  7149     // get the node to act on, and its location
  7150     nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
  7152     nsCOMPtr<dom::Element> curElement = do_QueryInterface(curNode);
  7154     // if curNode is a address, p, header, address, or pre, remove it 
  7155     if (curElement && nsHTMLEditUtils::IsFormatNode(curElement)) {
  7156       // process any partial progress saved
  7157       if (curBlock)
  7159         res = RemovePartOfBlock(curBlock, firstNode, lastNode);
  7160         NS_ENSURE_SUCCESS(res, res);
  7161         curBlock = 0;  firstNode = 0;  lastNode = 0;
  7163       // remove curent block
  7164       NS_ENSURE_STATE(mHTMLEditor);
  7165       res = mHTMLEditor->RemoveBlockContainer(curNode); 
  7166       NS_ENSURE_SUCCESS(res, res);
  7167     } else if (curElement &&
  7168                (curElement->IsHTML(nsGkAtoms::table)      ||
  7169                 curElement->IsHTML(nsGkAtoms::tr)         ||
  7170                 curElement->IsHTML(nsGkAtoms::tbody)      ||
  7171                 curElement->IsHTML(nsGkAtoms::td)         ||
  7172                 nsHTMLEditUtils::IsList(curElement)       ||
  7173                 curElement->IsHTML(nsGkAtoms::li)         ||
  7174                 curElement->IsHTML(nsGkAtoms::blockquote) ||
  7175                 curElement->IsHTML(nsGkAtoms::div))) {
  7176       // process any partial progress saved
  7177       if (curBlock)
  7179         res = RemovePartOfBlock(curBlock, firstNode, lastNode);
  7180         NS_ENSURE_SUCCESS(res, res);
  7181         curBlock = 0;  firstNode = 0;  lastNode = 0;
  7183       // recursion time
  7184       nsCOMArray<nsIDOMNode> childArray;
  7185       res = GetChildNodesForOperation(curNode, childArray);
  7186       NS_ENSURE_SUCCESS(res, res);
  7187       res = RemoveBlockStyle(childArray);
  7188       NS_ENSURE_SUCCESS(res, res);
  7190     else if (IsInlineNode(curNode))
  7192       if (curBlock)
  7194         // if so, is this node a descendant?
  7195         if (nsEditorUtils::IsDescendantOf(curNode, curBlock))
  7197           lastNode = curNode;
  7198           continue;  // then we don't need to do anything different for this node
  7200         else
  7202           // otherwise, we have progressed beyond end of curBlock,
  7203           // so lets handle it now.  We need to remove the portion of 
  7204           // curBlock that contains [firstNode - lastNode].
  7205           res = RemovePartOfBlock(curBlock, firstNode, lastNode);
  7206           NS_ENSURE_SUCCESS(res, res);
  7207           curBlock = 0;  firstNode = 0;  lastNode = 0;
  7208           // fall out and handle curNode
  7211       NS_ENSURE_STATE(mHTMLEditor);
  7212       curBlock = mHTMLEditor->GetBlockNodeParent(curNode);
  7213       if (nsHTMLEditUtils::IsFormatNode(curBlock))
  7215         firstNode = curNode;  
  7216         lastNode = curNode;
  7218       else
  7219         curBlock = 0;  // not a block kind that we care about.
  7221     else
  7222     { // some node that is already sans block style.  skip over it and
  7223       // process any partial progress saved
  7224       if (curBlock)
  7226         res = RemovePartOfBlock(curBlock, firstNode, lastNode);
  7227         NS_ENSURE_SUCCESS(res, res);
  7228         curBlock = 0;  firstNode = 0;  lastNode = 0;
  7232   // process any partial progress saved
  7233   if (curBlock)
  7235     res = RemovePartOfBlock(curBlock, firstNode, lastNode);
  7236     NS_ENSURE_SUCCESS(res, res);
  7237     curBlock = 0;  firstNode = 0;  lastNode = 0;
  7239   return res;
  7243 ///////////////////////////////////////////////////////////////////////////
  7244 // ApplyBlockStyle:  do whatever it takes to make the list of nodes into 
  7245 //                   one or more blocks of type blockTag.  
  7246 //                       
  7247 nsresult 
  7248 nsHTMLEditRules::ApplyBlockStyle(nsCOMArray<nsIDOMNode>& arrayOfNodes, const nsAString *aBlockTag)
  7250   // intent of this routine is to be used for converting to/from
  7251   // headers, paragraphs, pre, and address.  Those blocks
  7252   // that pretty much just contain inline things...
  7254   NS_ENSURE_TRUE(aBlockTag, NS_ERROR_NULL_POINTER);
  7255   nsresult res = NS_OK;
  7257   nsCOMPtr<nsIDOMNode> curNode, curParent, curBlock, newBlock;
  7258   int32_t offset;
  7259   int32_t listCount = arrayOfNodes.Count();
  7260   nsString tString(*aBlockTag);////MJUDGE SCC NEED HELP
  7262   // Remove all non-editable nodes.  Leave them be.
  7263   int32_t j;
  7264   for (j=listCount-1; j>=0; j--)
  7266     NS_ENSURE_STATE(mHTMLEditor);
  7267     if (!mHTMLEditor->IsEditable(arrayOfNodes[j]))
  7269       arrayOfNodes.RemoveObjectAt(j);
  7273   // reset list count
  7274   listCount = arrayOfNodes.Count();
  7276   int32_t i;
  7277   for (i=0; i<listCount; i++)
  7279     // get the node to act on, and its location
  7280     curNode = arrayOfNodes[i];
  7281     curParent = nsEditor::GetNodeLocation(curNode, &offset);
  7282     nsAutoString curNodeTag;
  7283     nsEditor::GetTagString(curNode, curNodeTag);
  7284     ToLowerCase(curNodeTag);
  7286     // is it already the right kind of block?
  7287     if (curNodeTag == *aBlockTag)
  7289       curBlock = 0;  // forget any previous block used for previous inline nodes
  7290       continue;  // do nothing to this block
  7293     // if curNode is a address, p, header, address, or pre, replace 
  7294     // it with a new block of correct type.
  7295     // xxx floppy moose: pre can't hold everything the others can
  7296     if (nsHTMLEditUtils::IsMozDiv(curNode)     ||
  7297         nsHTMLEditUtils::IsFormatNode(curNode))
  7299       curBlock = 0;  // forget any previous block used for previous inline nodes
  7300       NS_ENSURE_STATE(mHTMLEditor);
  7301       res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), *aBlockTag,
  7302                                           nullptr, nullptr, true);
  7303       NS_ENSURE_SUCCESS(res, res);
  7305     else if (nsHTMLEditUtils::IsTable(curNode)                    || 
  7306              (curNodeTag.EqualsLiteral("tbody"))      ||
  7307              (curNodeTag.EqualsLiteral("tr"))         ||
  7308              (curNodeTag.EqualsLiteral("td"))         ||
  7309              nsHTMLEditUtils::IsList(curNode)                     ||
  7310              (curNodeTag.EqualsLiteral("li"))         ||
  7311              nsHTMLEditUtils::IsBlockquote(curNode)               ||
  7312              nsHTMLEditUtils::IsDiv(curNode))
  7314       curBlock = 0;  // forget any previous block used for previous inline nodes
  7315       // recursion time
  7316       nsCOMArray<nsIDOMNode> childArray;
  7317       res = GetChildNodesForOperation(curNode, childArray);
  7318       NS_ENSURE_SUCCESS(res, res);
  7319       int32_t childCount = childArray.Count();
  7320       if (childCount)
  7322         res = ApplyBlockStyle(childArray, aBlockTag);
  7323         NS_ENSURE_SUCCESS(res, res);
  7325       else
  7327         // make sure we can put a block here
  7328         res = SplitAsNeeded(aBlockTag, address_of(curParent), &offset);
  7329         NS_ENSURE_SUCCESS(res, res);
  7330         nsCOMPtr<nsIDOMNode> theBlock;
  7331         NS_ENSURE_STATE(mHTMLEditor);
  7332         res = mHTMLEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(theBlock));
  7333         NS_ENSURE_SUCCESS(res, res);
  7334         // remember our new block for postprocessing
  7335         mNewBlock = theBlock;
  7339     // if the node is a break, we honor it by putting further nodes in a new parent
  7340     else if (curNodeTag.EqualsLiteral("br"))
  7342       if (curBlock)
  7344         curBlock = 0;  // forget any previous block used for previous inline nodes
  7345         NS_ENSURE_STATE(mHTMLEditor);
  7346         res = mHTMLEditor->DeleteNode(curNode);
  7347         NS_ENSURE_SUCCESS(res, res);
  7349       else
  7351         // the break is the first (or even only) node we encountered.  Create a
  7352         // block for it.
  7353         res = SplitAsNeeded(aBlockTag, address_of(curParent), &offset);
  7354         NS_ENSURE_SUCCESS(res, res);
  7355         NS_ENSURE_STATE(mHTMLEditor);
  7356         res = mHTMLEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(curBlock));
  7357         NS_ENSURE_SUCCESS(res, res);
  7358         // remember our new block for postprocessing
  7359         mNewBlock = curBlock;
  7360         // note: doesn't matter if we set mNewBlock multiple times.
  7361         NS_ENSURE_STATE(mHTMLEditor);
  7362         res = mHTMLEditor->MoveNode(curNode, curBlock, -1);
  7363         NS_ENSURE_SUCCESS(res, res);
  7368     // if curNode is inline, pull it into curBlock
  7369     // note: it's assumed that consecutive inline nodes in the 
  7370     // arrayOfNodes are actually members of the same block parent.
  7371     // this happens to be true now as a side effect of how
  7372     // arrayOfNodes is contructed, but some additional logic should
  7373     // be added here if that should change
  7375     else if (IsInlineNode(curNode))
  7377       // if curNode is a non editable, drop it if we are going to <pre>
  7378       NS_ENSURE_STATE(mHTMLEditor);
  7379       if (tString.LowerCaseEqualsLiteral("pre") 
  7380         && (!mHTMLEditor->IsEditable(curNode)))
  7381         continue; // do nothing to this block
  7383       // if no curBlock, make one
  7384       if (!curBlock)
  7386         res = SplitAsNeeded(aBlockTag, address_of(curParent), &offset);
  7387         NS_ENSURE_SUCCESS(res, res);
  7388         NS_ENSURE_STATE(mHTMLEditor);
  7389         res = mHTMLEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(curBlock));
  7390         NS_ENSURE_SUCCESS(res, res);
  7391         // remember our new block for postprocessing
  7392         mNewBlock = curBlock;
  7393         // note: doesn't matter if we set mNewBlock multiple times.
  7396       // if curNode is a Break, replace it with a return if we are going to <pre>
  7397       // xxx floppy moose
  7399       // this is a continuation of some inline nodes that belong together in
  7400       // the same block item.  use curBlock
  7401       NS_ENSURE_STATE(mHTMLEditor);
  7402       res = mHTMLEditor->MoveNode(curNode, curBlock, -1);
  7403       NS_ENSURE_SUCCESS(res, res);
  7406   return res;
  7410 ///////////////////////////////////////////////////////////////////////////
  7411 // SplitAsNeeded:  given a tag name, split inOutParent up to the point   
  7412 //                 where we can insert the tag.  Adjust inOutParent and
  7413 //                 inOutOffset to pint to new location for tag.
  7414 nsresult 
  7415 nsHTMLEditRules::SplitAsNeeded(const nsAString *aTag, 
  7416                                nsCOMPtr<nsIDOMNode> *inOutParent,
  7417                                int32_t *inOutOffset)
  7419   NS_ENSURE_TRUE(aTag && inOutParent && inOutOffset, NS_ERROR_NULL_POINTER);
  7420   NS_ENSURE_TRUE(*inOutParent, NS_ERROR_NULL_POINTER);
  7421   nsCOMPtr<nsIDOMNode> tagParent, temp, splitNode, parent = *inOutParent;
  7422   nsresult res = NS_OK;
  7423   nsCOMPtr<nsIAtom> tagAtom = do_GetAtom(*aTag);
  7425   // check that we have a place that can legally contain the tag
  7426   while (!tagParent)
  7428     // sniffing up the parent tree until we find 
  7429     // a legal place for the block
  7430     if (!parent) break;
  7431     // Don't leave the active editing host
  7432     NS_ENSURE_STATE(mHTMLEditor);
  7433     if (!mHTMLEditor->IsDescendantOfEditorRoot(parent)) {
  7434       nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
  7435       NS_ENSURE_STATE(mHTMLEditor);
  7436       if (parentContent != mHTMLEditor->GetActiveEditingHost()) {
  7437         break;
  7440     NS_ENSURE_STATE(mHTMLEditor);
  7441     if (mHTMLEditor->CanContainTag(parent, tagAtom)) {
  7442       tagParent = parent;
  7443       break;
  7445     splitNode = parent;
  7446     parent->GetParentNode(getter_AddRefs(temp));
  7447     parent = temp;
  7449   if (!tagParent)
  7451     // could not find a place to build tag!
  7452     return NS_ERROR_FAILURE;
  7454   if (splitNode)
  7456     // we found a place for block, but above inOutParent.  We need to split nodes.
  7457     NS_ENSURE_STATE(mHTMLEditor);
  7458     res = mHTMLEditor->SplitNodeDeep(splitNode, *inOutParent, *inOutOffset, inOutOffset);
  7459     NS_ENSURE_SUCCESS(res, res);
  7460     *inOutParent = tagParent;
  7462   return res;
  7465 ///////////////////////////////////////////////////////////////////////////
  7466 // JoinNodesSmart:  join two nodes, doing whatever makes sense for their  
  7467 //                  children (which often means joining them, too).
  7468 //                  aNodeLeft & aNodeRight must be same type of node.
  7469 nsresult 
  7470 nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft, 
  7471                                  nsIDOMNode *aNodeRight, 
  7472                                  nsCOMPtr<nsIDOMNode> *aOutMergeParent, 
  7473                                  int32_t *aOutMergeOffset)
  7475   // check parms
  7476   NS_ENSURE_TRUE(aNodeLeft &&  
  7477       aNodeRight && 
  7478       aOutMergeParent &&
  7479       aOutMergeOffset, NS_ERROR_NULL_POINTER);
  7481   nsresult res = NS_OK;
  7482   // caller responsible for:
  7483   //   left & right node are same type
  7484   int32_t parOffset;
  7485   nsCOMPtr<nsIDOMNode> rightParent;
  7486   nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(aNodeLeft, &parOffset);
  7487   aNodeRight->GetParentNode(getter_AddRefs(rightParent));
  7489   // if they don't have the same parent, first move the 'right' node 
  7490   // to after the 'left' one
  7491   if (parent != rightParent)
  7493     NS_ENSURE_STATE(mHTMLEditor);
  7494     res = mHTMLEditor->MoveNode(aNodeRight, parent, parOffset);
  7495     NS_ENSURE_SUCCESS(res, res);
  7498   // defaults for outParams
  7499   *aOutMergeParent = aNodeRight;
  7500   NS_ENSURE_STATE(mHTMLEditor);
  7501   res = mHTMLEditor->GetLengthOfDOMNode(aNodeLeft, *((uint32_t*)aOutMergeOffset));
  7502   NS_ENSURE_SUCCESS(res, res);
  7504   // separate join rules for differing blocks
  7505   if (nsHTMLEditUtils::IsList(aNodeLeft) ||
  7506       !mHTMLEditor ||
  7507       mHTMLEditor->IsTextNode(aNodeLeft))
  7509     // for list's, merge shallow (wouldn't want to combine list items)
  7510     NS_ENSURE_STATE(mHTMLEditor);
  7511     res = mHTMLEditor->JoinNodes(aNodeLeft, aNodeRight, parent);
  7512     NS_ENSURE_SUCCESS(res, res);
  7513     return res;
  7515   else
  7517     // remember the last left child, and firt right child
  7518     nsCOMPtr<nsIDOMNode> lastLeft, firstRight;
  7519     NS_ENSURE_STATE(mHTMLEditor);
  7520     res = mHTMLEditor->GetLastEditableChild(aNodeLeft, address_of(lastLeft));
  7521     NS_ENSURE_SUCCESS(res, res);
  7522     NS_ENSURE_STATE(mHTMLEditor);
  7523     res = mHTMLEditor->GetFirstEditableChild(aNodeRight, address_of(firstRight));
  7524     NS_ENSURE_SUCCESS(res, res);
  7526     // for list items, divs, etc, merge smart
  7527     NS_ENSURE_STATE(mHTMLEditor);
  7528     res = mHTMLEditor->JoinNodes(aNodeLeft, aNodeRight, parent);
  7529     NS_ENSURE_SUCCESS(res, res);
  7531     if (lastLeft && firstRight && mHTMLEditor &&
  7532         mHTMLEditor->NodesSameType(lastLeft, firstRight) &&
  7533         (nsEditor::IsTextNode(lastLeft) ||
  7534          !mHTMLEditor ||
  7535          mHTMLEditor->mHTMLCSSUtils->ElementsSameStyle(lastLeft, firstRight))) {
  7536       NS_ENSURE_STATE(mHTMLEditor);      
  7537       return JoinNodesSmart(lastLeft, firstRight, aOutMergeParent, aOutMergeOffset);
  7540   return res;
  7544 nsresult 
  7545 nsHTMLEditRules::GetTopEnclosingMailCite(nsIDOMNode *aNode, 
  7546                                          nsCOMPtr<nsIDOMNode> *aOutCiteNode,
  7547                                          bool aPlainText)
  7549   // check parms
  7550   NS_ENSURE_TRUE(aNode && aOutCiteNode, NS_ERROR_NULL_POINTER);
  7552   nsresult res = NS_OK;
  7553   nsCOMPtr<nsIDOMNode> node, parentNode;
  7554   node = do_QueryInterface(aNode);
  7556   while (node)
  7558     if ( (aPlainText && nsHTMLEditUtils::IsPre(node)) ||
  7559          nsHTMLEditUtils::IsMailCite(node) )
  7560       *aOutCiteNode = node;
  7561     if (nsTextEditUtils::IsBody(node)) break;
  7563     res = node->GetParentNode(getter_AddRefs(parentNode));
  7564     NS_ENSURE_SUCCESS(res, res);
  7565     node = parentNode;
  7568   return res;
  7572 nsresult 
  7573 nsHTMLEditRules::CacheInlineStyles(nsIDOMNode *aNode)
  7575   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
  7577   NS_ENSURE_STATE(mHTMLEditor);
  7578   bool useCSS = mHTMLEditor->IsCSSEnabled();
  7580   for (int32_t j = 0; j < SIZE_STYLE_TABLE; ++j)
  7582     bool isSet = false;
  7583     nsAutoString outValue;
  7584     // Don't use CSS for <font size>, we don't support it usefully (bug 780035)
  7585     if (!useCSS || (mCachedStyles[j].tag == nsGkAtoms::font &&
  7586                     mCachedStyles[j].attr.EqualsLiteral("size"))) {
  7587       NS_ENSURE_STATE(mHTMLEditor);
  7588       mHTMLEditor->IsTextPropertySetByContent(aNode, mCachedStyles[j].tag,
  7589                                               &(mCachedStyles[j].attr), nullptr,
  7590                                               isSet, &outValue);
  7592     else
  7594       NS_ENSURE_STATE(mHTMLEditor);
  7595       mHTMLEditor->mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(aNode,
  7596         mCachedStyles[j].tag, &(mCachedStyles[j].attr), isSet, outValue,
  7597         nsHTMLCSSUtils::eComputed);
  7599     if (isSet)
  7601       mCachedStyles[j].mPresent = true;
  7602       mCachedStyles[j].value.Assign(outValue);
  7605   return NS_OK;
  7609 nsresult
  7610 nsHTMLEditRules::ReapplyCachedStyles()
  7612   // The idea here is to examine our cached list of styles and see if any have
  7613   // been removed.  If so, add typeinstate for them, so that they will be
  7614   // reinserted when new content is added.
  7616   // remember if we are in css mode
  7617   NS_ENSURE_STATE(mHTMLEditor);
  7618   bool useCSS = mHTMLEditor->IsCSSEnabled();
  7620   // get selection point; if it doesn't exist, we have nothing to do
  7621   NS_ENSURE_STATE(mHTMLEditor);
  7622   nsRefPtr<Selection> selection = mHTMLEditor->GetSelection();
  7623   MOZ_ASSERT(selection);
  7624   if (!selection->GetRangeCount()) {
  7625     // Nothing to do
  7626     return NS_OK;
  7628   nsCOMPtr<nsIContent> selNode =
  7629     do_QueryInterface(selection->GetRangeAt(0)->GetStartParent());
  7630   if (!selNode) {
  7631     // Nothing to do
  7632     return NS_OK;
  7635   for (int32_t i = 0; i < SIZE_STYLE_TABLE; ++i) {
  7636     if (mCachedStyles[i].mPresent) {
  7637       bool bFirst, bAny, bAll;
  7638       bFirst = bAny = bAll = false;
  7640       nsAutoString curValue;
  7641       if (useCSS) {
  7642         // check computed style first in css case
  7643         NS_ENSURE_STATE(mHTMLEditor);
  7644         bAny = mHTMLEditor->mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(
  7645           selNode, mCachedStyles[i].tag, &(mCachedStyles[i].attr), curValue,
  7646           nsHTMLCSSUtils::eComputed);
  7648       if (!bAny) {
  7649         // then check typeinstate and html style
  7650         NS_ENSURE_STATE(mHTMLEditor);
  7651         nsresult res = mHTMLEditor->GetInlinePropertyBase(mCachedStyles[i].tag,
  7652                                                      &(mCachedStyles[i].attr),
  7653                                                      &(mCachedStyles[i].value),
  7654                                                      &bFirst, &bAny, &bAll,
  7655                                                      &curValue, false);
  7656         NS_ENSURE_SUCCESS(res, res);
  7658       // this style has disappeared through deletion.  Add to our typeinstate:
  7659       if (!bAny || IsStyleCachePreservingAction(mTheAction)) {
  7660         NS_ENSURE_STATE(mHTMLEditor);
  7661         mHTMLEditor->mTypeInState->SetProp(mCachedStyles[i].tag,
  7662                                            mCachedStyles[i].attr,
  7663                                            mCachedStyles[i].value);
  7668   return NS_OK;
  7672 void
  7673 nsHTMLEditRules::ClearCachedStyles()
  7675   // clear the mPresent bits in mCachedStyles array
  7676   for (uint32_t j = 0; j < SIZE_STYLE_TABLE; j++) {
  7677     mCachedStyles[j].mPresent = false;
  7678     mCachedStyles[j].value.Truncate();
  7683 nsresult 
  7684 nsHTMLEditRules::AdjustSpecialBreaks(bool aSafeToAskFrames)
  7686   nsCOMArray<nsIDOMNode> arrayOfNodes;
  7687   nsCOMPtr<nsISupports> isupports;
  7688   int32_t nodeCount,j;
  7690   // gather list of empty nodes
  7691   NS_ENSURE_STATE(mHTMLEditor);
  7692   nsEmptyEditableFunctor functor(mHTMLEditor);
  7693   nsDOMIterator iter;
  7694   nsresult res = iter.Init(mDocChangeRange);
  7695   NS_ENSURE_SUCCESS(res, res);
  7696   res = iter.AppendList(functor, arrayOfNodes);
  7697   NS_ENSURE_SUCCESS(res, res);
  7699   // put moz-br's into these empty li's and td's
  7700   nodeCount = arrayOfNodes.Count();
  7701   for (j = 0; j < nodeCount; j++)
  7703     // need to put br at END of node.  It may have
  7704     // empty containers in it and still pass the "IsEmptynode" test,
  7705     // and we want the br's to be after them.  Also, we want the br
  7706     // to be after the selection if the selection is in this node.
  7707     uint32_t len;
  7708     nsCOMPtr<nsIDOMNode> theNode = arrayOfNodes[0];
  7709     arrayOfNodes.RemoveObjectAt(0);
  7710     res = nsEditor::GetLengthOfDOMNode(theNode, len);
  7711     NS_ENSURE_SUCCESS(res, res);
  7712     res = CreateMozBR(theNode, (int32_t)len);
  7713     NS_ENSURE_SUCCESS(res, res);
  7716   return res;
  7719 nsresult 
  7720 nsHTMLEditRules::AdjustWhitespace(nsISelection *aSelection)
  7722   // get selection point
  7723   nsCOMPtr<nsIDOMNode> selNode;
  7724   int32_t selOffset;
  7725   NS_ENSURE_STATE(mHTMLEditor);
  7726   nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
  7727   NS_ENSURE_SUCCESS(res, res);
  7729   // ask whitespace object to tweak nbsp's
  7730   NS_ENSURE_STATE(mHTMLEditor);
  7731   return nsWSRunObject(mHTMLEditor, selNode, selOffset).AdjustWhitespace();
  7734 nsresult 
  7735 nsHTMLEditRules::PinSelectionToNewBlock(nsISelection *aSelection)
  7737   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
  7738   if (!aSelection->Collapsed()) {
  7739     return NS_OK;
  7742   // get the (collapsed) selection location
  7743   nsCOMPtr<nsIDOMNode> selNode, temp;
  7744   int32_t selOffset;
  7745   NS_ENSURE_STATE(mHTMLEditor);
  7746   nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
  7747   NS_ENSURE_SUCCESS(res, res);
  7748   temp = selNode;
  7750   // use ranges and sRangeHelper to compare sel point to new block
  7751   nsCOMPtr<nsINode> node = do_QueryInterface(selNode);
  7752   NS_ENSURE_STATE(node);
  7753   nsRefPtr<nsRange> range = new nsRange(node);
  7754   res = range->SetStart(selNode, selOffset);
  7755   NS_ENSURE_SUCCESS(res, res);
  7756   res = range->SetEnd(selNode, selOffset);
  7757   NS_ENSURE_SUCCESS(res, res);
  7758   nsCOMPtr<nsIContent> block (do_QueryInterface(mNewBlock));
  7759   NS_ENSURE_TRUE(block, NS_ERROR_NO_INTERFACE);
  7760   bool nodeBefore, nodeAfter;
  7761   res = nsRange::CompareNodeToRange(block, range, &nodeBefore, &nodeAfter);
  7762   NS_ENSURE_SUCCESS(res, res);
  7764   if (nodeBefore && nodeAfter)
  7765     return NS_OK;  // selection is inside block
  7766   else if (nodeBefore)
  7768     // selection is after block.  put at end of block.
  7769     nsCOMPtr<nsIDOMNode> tmp = mNewBlock;
  7770     NS_ENSURE_STATE(mHTMLEditor);
  7771     mHTMLEditor->GetLastEditableChild(mNewBlock, address_of(tmp));
  7772     uint32_t endPoint;
  7773     NS_ENSURE_STATE(mHTMLEditor);
  7774     if (mHTMLEditor->IsTextNode(tmp) || !mHTMLEditor ||
  7775         mHTMLEditor->IsContainer(tmp))
  7777       NS_ENSURE_STATE(mHTMLEditor);
  7778       res = nsEditor::GetLengthOfDOMNode(tmp, endPoint);
  7779       NS_ENSURE_SUCCESS(res, res);
  7781     else
  7783       tmp = nsEditor::GetNodeLocation(tmp, (int32_t*)&endPoint);
  7784       endPoint++;  // want to be after this node
  7786     return aSelection->Collapse(tmp, (int32_t)endPoint);
  7788   else
  7790     // selection is before block.  put at start of block.
  7791     nsCOMPtr<nsIDOMNode> tmp = mNewBlock;
  7792     NS_ENSURE_STATE(mHTMLEditor);
  7793     mHTMLEditor->GetFirstEditableChild(mNewBlock, address_of(tmp));
  7794     int32_t offset;
  7795     if (!(mHTMLEditor->IsTextNode(tmp) || !mHTMLEditor ||
  7796           mHTMLEditor->IsContainer(tmp)))
  7798       tmp = nsEditor::GetNodeLocation(tmp, &offset);
  7800     NS_ENSURE_STATE(mHTMLEditor);
  7801     return aSelection->Collapse(tmp, 0);
  7805 nsresult 
  7806 nsHTMLEditRules::CheckInterlinePosition(nsISelection *aSelection)
  7808   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
  7809   nsCOMPtr<nsISelection> selection(aSelection);
  7810   nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
  7812   // if the selection isn't collapsed, do nothing.
  7813   if (!aSelection->Collapsed()) {
  7814     return NS_OK;
  7817   // get the (collapsed) selection location
  7818   nsCOMPtr<nsIDOMNode> selNode, node;
  7819   int32_t selOffset;
  7820   NS_ENSURE_STATE(mHTMLEditor);
  7821   nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
  7822   NS_ENSURE_SUCCESS(res, res);
  7824   // First, let's check to see if we are after a <br>.  We take care of this
  7825   // special-case first so that we don't accidentally fall through into one
  7826   // of the other conditionals.
  7827   NS_ENSURE_STATE(mHTMLEditor);
  7828   mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, address_of(node), true);
  7829   if (node && nsTextEditUtils::IsBreak(node))
  7831     selPriv->SetInterlinePosition(true);
  7832     return NS_OK;
  7835   // are we after a block?  If so try set caret to following content
  7836   NS_ENSURE_STATE(mHTMLEditor);
  7837   mHTMLEditor->GetPriorHTMLSibling(selNode, selOffset, address_of(node));
  7838   if (node && IsBlockNode(node))
  7840     selPriv->SetInterlinePosition(true);
  7841     return NS_OK;
  7844   // are we before a block?  If so try set caret to prior content
  7845   NS_ENSURE_STATE(mHTMLEditor);
  7846   mHTMLEditor->GetNextHTMLSibling(selNode, selOffset, address_of(node));
  7847   if (node && IsBlockNode(node))
  7848     selPriv->SetInterlinePosition(false);
  7849   return NS_OK;
  7852 nsresult 
  7853 nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aAction)
  7855   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
  7856   nsCOMPtr<nsISelection> selection(aSelection);
  7857   nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
  7859   // if the selection isn't collapsed, do nothing.
  7860   // moose: one thing to do instead is check for the case of
  7861   // only a single break selected, and collapse it.  Good thing?  Beats me.
  7862   if (!aSelection->Collapsed()) {
  7863     return NS_OK;
  7866   // get the (collapsed) selection location
  7867   nsCOMPtr<nsIDOMNode> selNode, temp;
  7868   int32_t selOffset;
  7869   NS_ENSURE_STATE(mHTMLEditor);
  7870   nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
  7871   NS_ENSURE_SUCCESS(res, res);
  7872   temp = selNode;
  7874   // are we in an editable node?
  7875   NS_ENSURE_STATE(mHTMLEditor);
  7876   while (!mHTMLEditor->IsEditable(selNode))
  7878     // scan up the tree until we find an editable place to be
  7879     selNode = nsEditor::GetNodeLocation(temp, &selOffset);
  7880     NS_ENSURE_TRUE(selNode, NS_ERROR_FAILURE);
  7881     temp = selNode;
  7882     NS_ENSURE_STATE(mHTMLEditor);
  7885   // make sure we aren't in an empty block - user will see no cursor.  If this
  7886   // is happening, put a <br> in the block if allowed.
  7887   nsCOMPtr<nsIDOMNode> theblock;
  7888   if (IsBlockNode(selNode)) {
  7889     theblock = selNode;
  7890   } else {
  7891     NS_ENSURE_STATE(mHTMLEditor);
  7892     theblock = mHTMLEditor->GetBlockNodeParent(selNode);
  7894   NS_ENSURE_STATE(mHTMLEditor);
  7895   if (theblock && mHTMLEditor->IsEditable(theblock)) {
  7896     bool bIsEmptyNode;
  7897     NS_ENSURE_STATE(mHTMLEditor);
  7898     res = mHTMLEditor->IsEmptyNode(theblock, &bIsEmptyNode, false, false);
  7899     NS_ENSURE_SUCCESS(res, res);
  7900     // check if br can go into the destination node
  7901     NS_ENSURE_STATE(mHTMLEditor);
  7902     if (bIsEmptyNode && mHTMLEditor->CanContainTag(selNode, nsGkAtoms::br)) {
  7903       NS_ENSURE_STATE(mHTMLEditor);
  7904       nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(mHTMLEditor->GetRoot());
  7905       NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
  7906       if (selNode == rootNode)
  7908         // Our root node is completely empty. Don't add a <br> here.
  7909         // AfterEditInner() will add one for us when it calls
  7910         // CreateBogusNodeIfNeeded()!
  7911         return NS_OK;
  7914       // we know we can skip the rest of this routine given the cirumstance
  7915       return CreateMozBR(selNode, selOffset);
  7919   // are we in a text node? 
  7920   nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(selNode);
  7921   if (textNode)
  7922     return NS_OK; // we LIKE it when we are in a text node.  that RULZ
  7924   // do we need to insert a special mozBR?  We do if we are:
  7925   // 1) prior node is in same block where selection is AND
  7926   // 2) prior node is a br AND
  7927   // 3) that br is not visible
  7929   nsCOMPtr<nsIDOMNode> nearNode;
  7930   NS_ENSURE_STATE(mHTMLEditor);
  7931   res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, address_of(nearNode));
  7932   NS_ENSURE_SUCCESS(res, res);
  7933   if (nearNode) 
  7935     // is nearNode also a descendant of same block?
  7936     nsCOMPtr<nsIDOMNode> block, nearBlock;
  7937     if (IsBlockNode(selNode)) {
  7938       block = selNode;
  7939     } else {
  7940       NS_ENSURE_STATE(mHTMLEditor);
  7941       block = mHTMLEditor->GetBlockNodeParent(selNode);
  7943     NS_ENSURE_STATE(mHTMLEditor);
  7944     nearBlock = mHTMLEditor->GetBlockNodeParent(nearNode);
  7945     if (block == nearBlock)
  7947       if (nearNode && nsTextEditUtils::IsBreak(nearNode) )
  7949         NS_ENSURE_STATE(mHTMLEditor);
  7950         if (!mHTMLEditor->IsVisBreak(nearNode))
  7952           // need to insert special moz BR. Why?  Because if we don't
  7953           // the user will see no new line for the break.  Also, things
  7954           // like table cells won't grow in height.
  7955           nsCOMPtr<nsIDOMNode> brNode;
  7956           res = CreateMozBR(selNode, selOffset, getter_AddRefs(brNode));
  7957           NS_ENSURE_SUCCESS(res, res);
  7958           selNode = nsEditor::GetNodeLocation(brNode, &selOffset);
  7959           // selection stays *before* moz-br, sticking to it
  7960           selPriv->SetInterlinePosition(true);
  7961           res = aSelection->Collapse(selNode,selOffset);
  7962           NS_ENSURE_SUCCESS(res, res);
  7964         else
  7966           nsCOMPtr<nsIDOMNode> nextNode;
  7967           NS_ENSURE_STATE(mHTMLEditor);
  7968           mHTMLEditor->GetNextHTMLNode(nearNode, address_of(nextNode), true);
  7969           if (nextNode && nsTextEditUtils::IsMozBR(nextNode))
  7971             // selection between br and mozbr.  make it stick to mozbr
  7972             // so that it will be on blank line.   
  7973             selPriv->SetInterlinePosition(true);
  7980   // we aren't in a textnode: are we adjacent to text or a break or an image?
  7981   NS_ENSURE_STATE(mHTMLEditor);
  7982   res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, address_of(nearNode), true);
  7983   NS_ENSURE_SUCCESS(res, res);
  7984   if (nearNode && (nsTextEditUtils::IsBreak(nearNode)
  7985                    || nsEditor::IsTextNode(nearNode)
  7986                    || nsHTMLEditUtils::IsImage(nearNode)
  7987                    || nsHTMLEditUtils::IsHR(nearNode)))
  7988     return NS_OK; // this is a good place for the caret to be
  7989   NS_ENSURE_STATE(mHTMLEditor);
  7990   res = mHTMLEditor->GetNextHTMLNode(selNode, selOffset, address_of(nearNode), true);
  7991   NS_ENSURE_SUCCESS(res, res);
  7992   if (nearNode && (nsTextEditUtils::IsBreak(nearNode)
  7993                    || nsEditor::IsTextNode(nearNode)
  7994                    || nsHTMLEditUtils::IsImage(nearNode)
  7995                    || nsHTMLEditUtils::IsHR(nearNode)))
  7996     return NS_OK; // this is a good place for the caret to be
  7998   // look for a nearby text node.
  7999   // prefer the correct direction.
  8000   res = FindNearSelectableNode(selNode, selOffset, aAction, address_of(nearNode));
  8001   NS_ENSURE_SUCCESS(res, res);
  8003   if (nearNode)
  8005     // is the nearnode a text node?
  8006     textNode = do_QueryInterface(nearNode);
  8007     if (textNode)
  8009       int32_t offset = 0;
  8010       // put selection in right place:
  8011       if (aAction == nsIEditor::ePrevious)
  8012         textNode->GetLength((uint32_t*)&offset);
  8013       res = aSelection->Collapse(nearNode,offset);
  8015     else  // must be break or image
  8017       selNode = nsEditor::GetNodeLocation(nearNode, &selOffset);
  8018       if (aAction == nsIEditor::ePrevious) selOffset++;  // want to be beyond it if we backed up to it
  8019       res = aSelection->Collapse(selNode, selOffset);
  8022   return res;
  8026 nsresult
  8027 nsHTMLEditRules::FindNearSelectableNode(nsIDOMNode *aSelNode, 
  8028                                         int32_t aSelOffset, 
  8029                                         nsIEditor::EDirection &aDirection,
  8030                                         nsCOMPtr<nsIDOMNode> *outSelectableNode)
  8032   NS_ENSURE_TRUE(aSelNode && outSelectableNode, NS_ERROR_NULL_POINTER);
  8033   *outSelectableNode = nullptr;
  8034   nsresult res = NS_OK;
  8036   nsCOMPtr<nsIDOMNode> nearNode, curNode;
  8037   if (aDirection == nsIEditor::ePrevious) {
  8038     NS_ENSURE_STATE(mHTMLEditor);
  8039     res = mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset, address_of(nearNode));
  8040   } else {
  8041     NS_ENSURE_STATE(mHTMLEditor);
  8042     res = mHTMLEditor->GetNextHTMLNode(aSelNode, aSelOffset, address_of(nearNode));
  8044   NS_ENSURE_SUCCESS(res, res);
  8046   if (!nearNode) // try the other direction then
  8048     if (aDirection == nsIEditor::ePrevious)
  8049       aDirection = nsIEditor::eNext;
  8050     else
  8051       aDirection = nsIEditor::ePrevious;
  8053     if (aDirection == nsIEditor::ePrevious) {
  8054       NS_ENSURE_STATE(mHTMLEditor);
  8055       res = mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset, address_of(nearNode));
  8056     } else {
  8057       NS_ENSURE_STATE(mHTMLEditor);
  8058       res = mHTMLEditor->GetNextHTMLNode(aSelNode, aSelOffset, address_of(nearNode));
  8060     NS_ENSURE_SUCCESS(res, res);
  8063   // scan in the right direction until we find an eligible text node,
  8064   // but don't cross any breaks, images, or table elements.
  8065   NS_ENSURE_STATE(mHTMLEditor);
  8066   while (nearNode && !(mHTMLEditor->IsTextNode(nearNode)
  8067                        || nsTextEditUtils::IsBreak(nearNode)
  8068                        || nsHTMLEditUtils::IsImage(nearNode)))
  8070     curNode = nearNode;
  8071     if (aDirection == nsIEditor::ePrevious) {
  8072       NS_ENSURE_STATE(mHTMLEditor);
  8073       res = mHTMLEditor->GetPriorHTMLNode(curNode, address_of(nearNode));
  8074     } else {
  8075       NS_ENSURE_STATE(mHTMLEditor);
  8076       res = mHTMLEditor->GetNextHTMLNode(curNode, address_of(nearNode));
  8078     NS_ENSURE_SUCCESS(res, res);
  8079     NS_ENSURE_STATE(mHTMLEditor);
  8082   if (nearNode)
  8084     // don't cross any table elements
  8085     if (InDifferentTableElements(nearNode, aSelNode)) {
  8086       return NS_OK;
  8089     // otherwise, ok, we have found a good spot to put the selection
  8090     *outSelectableNode = do_QueryInterface(nearNode);
  8092   return res;
  8096 bool nsHTMLEditRules::InDifferentTableElements(nsIDOMNode* aNode1,
  8097                                                nsIDOMNode* aNode2)
  8099   nsCOMPtr<nsINode> node1 = do_QueryInterface(aNode1);
  8100   nsCOMPtr<nsINode> node2 = do_QueryInterface(aNode2);
  8101   return InDifferentTableElements(node1, node2);
  8104 bool
  8105 nsHTMLEditRules::InDifferentTableElements(nsINode* aNode1, nsINode* aNode2)
  8107   MOZ_ASSERT(aNode1 && aNode2);
  8109   while (aNode1 && !nsHTMLEditUtils::IsTableElement(aNode1)) {
  8110     aNode1 = aNode1->GetParentNode();
  8113   while (aNode2 && !nsHTMLEditUtils::IsTableElement(aNode2)) {
  8114     aNode2 = aNode2->GetParentNode();
  8117   return aNode1 != aNode2;
  8121 nsresult 
  8122 nsHTMLEditRules::RemoveEmptyNodes()
  8124   // some general notes on the algorithm used here: the goal is to examine all the
  8125   // nodes in mDocChangeRange, and remove the empty ones.  We do this by using a
  8126   // content iterator to traverse all the nodes in the range, and placing the empty
  8127   // nodes into an array.  After finishing the iteration, we delete the empty nodes
  8128   // in the array.  (they cannot be deleted as we find them becasue that would 
  8129   // invalidate the iterator.)  
  8130   // Since checking to see if a node is empty can be costly for nodes with many
  8131   // descendants, there are some optimizations made.  I rely on the fact that the
  8132   // iterator is post-order: it will visit children of a node before visiting the 
  8133   // parent node.  So if I find that a child node is not empty, I know that its
  8134   // parent is not empty without even checking.  So I put the parent on a "skipList"
  8135   // which is just a voidArray of nodes I can skip the empty check on.  If I 
  8136   // encounter a node on the skiplist, i skip the processing for that node and replace
  8137   // its slot in the skiplist with that node's parent.
  8138   // An interseting idea is to go ahead and regard parent nodes that are NOT on the
  8139   // skiplist as being empty (without even doing the IsEmptyNode check) on the theory
  8140   // that if they weren't empty, we would have encountered a non-empty child earlier
  8141   // and thus put this parent node on the skiplist.
  8142   // Unfortunately I can't use that strategy here, because the range may include 
  8143   // some children of a node while excluding others.  Thus I could find all the 
  8144   // _examined_ children empty, but still not have an empty parent.
  8146   // need an iterator
  8147   nsCOMPtr<nsIContentIterator> iter =
  8148                   do_CreateInstance("@mozilla.org/content/post-content-iterator;1");
  8149   NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER);
  8151   nsresult res = iter->Init(mDocChangeRange);
  8152   NS_ENSURE_SUCCESS(res, res);
  8154   nsCOMArray<nsINode> arrayOfEmptyNodes, arrayOfEmptyCites;
  8155   nsTArray<nsCOMPtr<nsINode> > skipList;
  8157   // check for empty nodes
  8158   while (!iter->IsDone()) {
  8159     nsINode* node = iter->GetCurrentNode();
  8160     NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
  8162     nsINode* parent = node->GetParentNode();
  8164     uint32_t idx = skipList.IndexOf(node);
  8165     if (idx != skipList.NoIndex) {
  8166       // this node is on our skip list.  Skip processing for this node, 
  8167       // and replace its value in the skip list with the value of its parent
  8168       skipList[idx] = parent;
  8169     } else {
  8170       bool bIsCandidate = false;
  8171       bool bIsEmptyNode = false;
  8172       bool bIsMailCite = false;
  8174       if (node->IsElement()) {
  8175         dom::Element* element = node->AsElement();
  8176         if (element->IsHTML(nsGkAtoms::body)) {
  8177           // don't delete the body
  8178         } else if ((bIsMailCite = nsHTMLEditUtils::IsMailCite(element))  ||
  8179                    element->IsHTML(nsGkAtoms::a)                         ||
  8180                    nsHTMLEditUtils::IsInlineStyle(element)               ||
  8181                    nsHTMLEditUtils::IsList(element)                      ||
  8182                    element->IsHTML(nsGkAtoms::div)) {
  8183           // only consider certain nodes to be empty for purposes of removal
  8184           bIsCandidate = true;
  8185         } else if (nsHTMLEditUtils::IsFormatNode(element) ||
  8186                    nsHTMLEditUtils::IsListItem(element)   ||
  8187                    element->IsHTML(nsGkAtoms::blockquote)) {
  8188           // these node types are candidates if selection is not in them
  8189           // if it is one of these, don't delete if selection inside.
  8190           // this is so we can create empty headings, etc, for the
  8191           // user to type into.
  8192           bool bIsSelInNode;
  8193           res = SelectionEndpointInNode(node, &bIsSelInNode);
  8194           NS_ENSURE_SUCCESS(res, res);
  8195           if (!bIsSelInNode)
  8197             bIsCandidate = true;
  8202       if (bIsCandidate) {
  8203         // we delete mailcites even if they have a solo br in them
  8204         // other nodes we require to be empty
  8205         NS_ENSURE_STATE(mHTMLEditor);
  8206         res = mHTMLEditor->IsEmptyNode(node->AsDOMNode(), &bIsEmptyNode,
  8207                                        bIsMailCite, true);
  8208         NS_ENSURE_SUCCESS(res, res);
  8209         if (bIsEmptyNode) {
  8210           if (bIsMailCite) {
  8211             // mailcites go on a separate list from other empty nodes
  8212             arrayOfEmptyCites.AppendObject(node);
  8213           } else {
  8214             arrayOfEmptyNodes.AppendObject(node);
  8219       if (!bIsEmptyNode) {
  8220         // put parent on skip list
  8221         skipList.AppendElement(parent);
  8225     iter->Next();
  8228   // now delete the empty nodes
  8229   int32_t nodeCount = arrayOfEmptyNodes.Count();
  8230   for (int32_t j = 0; j < nodeCount; j++) {
  8231     nsCOMPtr<nsIDOMNode> delNode = arrayOfEmptyNodes[0]->AsDOMNode();
  8232     arrayOfEmptyNodes.RemoveObjectAt(0);
  8233     NS_ENSURE_STATE(mHTMLEditor);
  8234     if (mHTMLEditor->IsModifiableNode(delNode)) {
  8235       NS_ENSURE_STATE(mHTMLEditor);
  8236       res = mHTMLEditor->DeleteNode(delNode);
  8237       NS_ENSURE_SUCCESS(res, res);
  8241   // now delete the empty mailcites
  8242   // this is a separate step because we want to pull out any br's and preserve them.
  8243   nodeCount = arrayOfEmptyCites.Count();
  8244   for (int32_t j = 0; j < nodeCount; j++) {
  8245     nsCOMPtr<nsIDOMNode> delNode = arrayOfEmptyCites[0]->AsDOMNode();
  8246     arrayOfEmptyCites.RemoveObjectAt(0);
  8247     bool bIsEmptyNode;
  8248     NS_ENSURE_STATE(mHTMLEditor);
  8249     res = mHTMLEditor->IsEmptyNode(delNode, &bIsEmptyNode, false, true);
  8250     NS_ENSURE_SUCCESS(res, res);
  8251     if (!bIsEmptyNode)
  8253       // we are deleting a cite that has just a br.  We want to delete cite, 
  8254       // but preserve br.
  8255       nsCOMPtr<nsIDOMNode> parent, brNode;
  8256       int32_t offset;
  8257       parent = nsEditor::GetNodeLocation(delNode, &offset);
  8258       NS_ENSURE_STATE(mHTMLEditor);
  8259       res = mHTMLEditor->CreateBR(parent, offset, address_of(brNode));
  8260       NS_ENSURE_SUCCESS(res, res);
  8262     NS_ENSURE_STATE(mHTMLEditor);
  8263     res = mHTMLEditor->DeleteNode(delNode);
  8264     NS_ENSURE_SUCCESS(res, res);
  8267   return res;
  8270 nsresult
  8271 nsHTMLEditRules::SelectionEndpointInNode(nsINode* aNode, bool* aResult)
  8273   NS_ENSURE_TRUE(aNode && aResult, NS_ERROR_NULL_POINTER);
  8275   nsIDOMNode* node = aNode->AsDOMNode();
  8277   *aResult = false;
  8279   nsCOMPtr<nsISelection>selection;
  8280   NS_ENSURE_STATE(mHTMLEditor);
  8281   nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
  8282   NS_ENSURE_SUCCESS(res, res);
  8284   Selection* sel = static_cast<Selection*>(selection.get());
  8285   uint32_t rangeCount = sel->GetRangeCount();
  8286   for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
  8287     nsRefPtr<nsRange> range = sel->GetRangeAt(rangeIdx);
  8288     nsCOMPtr<nsIDOMNode> startParent, endParent;
  8289     range->GetStartContainer(getter_AddRefs(startParent));
  8290     if (startParent)
  8292       if (node == startParent) {
  8293         *aResult = true;
  8294         return NS_OK;
  8296       if (nsEditorUtils::IsDescendantOf(startParent, node)) {
  8297         *aResult = true;
  8298         return NS_OK;
  8301     range->GetEndContainer(getter_AddRefs(endParent));
  8302     if (startParent == endParent) continue;
  8303     if (endParent)
  8305       if (node == endParent) {
  8306         *aResult = true;
  8307         return NS_OK;
  8309       if (nsEditorUtils::IsDescendantOf(endParent, node)) {
  8310         *aResult = true;
  8311         return NS_OK;
  8315   return res;
  8318 ///////////////////////////////////////////////////////////////////////////
  8319 // IsEmptyInline:  return true if aNode is an empty inline container
  8320 //                
  8321 //                  
  8322 bool 
  8323 nsHTMLEditRules::IsEmptyInline(nsIDOMNode *aNode)
  8325   if (aNode && IsInlineNode(aNode) && mHTMLEditor &&
  8326       mHTMLEditor->IsContainer(aNode)) 
  8328     bool bEmpty;
  8329     NS_ENSURE_TRUE(mHTMLEditor, false);
  8330     mHTMLEditor->IsEmptyNode(aNode, &bEmpty);
  8331     return bEmpty;
  8333   return false;
  8337 bool 
  8338 nsHTMLEditRules::ListIsEmptyLine(nsCOMArray<nsIDOMNode> &arrayOfNodes)
  8340   // we have a list of nodes which we are candidates for being moved
  8341   // into a new block.  Determine if it's anything more than a blank line.
  8342   // Look for editable content above and beyond one single BR.
  8343   int32_t listCount = arrayOfNodes.Count();
  8344   NS_ENSURE_TRUE(listCount, true);
  8345   nsCOMPtr<nsIDOMNode> somenode;
  8346   int32_t j, brCount=0;
  8347   for (j = 0; j < listCount; j++)
  8349     somenode = arrayOfNodes[j];
  8350     NS_ENSURE_TRUE(mHTMLEditor, false);
  8351     if (somenode && mHTMLEditor->IsEditable(somenode))
  8353       if (nsTextEditUtils::IsBreak(somenode))
  8355         // first break doesn't count
  8356         if (brCount) return false;
  8357         brCount++;
  8359       else if (IsEmptyInline(somenode)) 
  8361         // empty inline, keep looking
  8363       else return false;
  8366   return true;
  8370 nsresult 
  8371 nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, bool *aOutOfList)
  8373   // check parms
  8374   NS_ENSURE_TRUE(aListItem && aOutOfList, NS_ERROR_NULL_POINTER);
  8376   // init out params
  8377   *aOutOfList = false;
  8379   nsCOMPtr<nsIDOMNode> curNode( do_QueryInterface(aListItem));
  8380   int32_t offset;
  8381   nsCOMPtr<nsIDOMNode> curParent = nsEditor::GetNodeLocation(curNode, &offset);
  8383   if (!nsHTMLEditUtils::IsListItem(curNode))
  8384     return NS_ERROR_FAILURE;
  8386   // if it's first or last list item, don't need to split the list
  8387   // otherwise we do.
  8388   int32_t parOffset;
  8389   nsCOMPtr<nsIDOMNode> curParPar = nsEditor::GetNodeLocation(curParent, &parOffset);
  8391   bool bIsFirstListItem;
  8392   NS_ENSURE_STATE(mHTMLEditor);
  8393   nsresult res = mHTMLEditor->IsFirstEditableChild(curNode, &bIsFirstListItem);
  8394   NS_ENSURE_SUCCESS(res, res);
  8396   bool bIsLastListItem;
  8397   NS_ENSURE_STATE(mHTMLEditor);
  8398   res = mHTMLEditor->IsLastEditableChild(curNode, &bIsLastListItem);
  8399   NS_ENSURE_SUCCESS(res, res);
  8401   if (!bIsFirstListItem && !bIsLastListItem)
  8403     // split the list
  8404     nsCOMPtr<nsIDOMNode> newBlock;
  8405     NS_ENSURE_STATE(mHTMLEditor);
  8406     res = mHTMLEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock));
  8407     NS_ENSURE_SUCCESS(res, res);
  8410   if (!bIsFirstListItem) parOffset++;
  8412   NS_ENSURE_STATE(mHTMLEditor);
  8413   res = mHTMLEditor->MoveNode(curNode, curParPar, parOffset);
  8414   NS_ENSURE_SUCCESS(res, res);
  8416   // unwrap list item contents if they are no longer in a list
  8417   if (!nsHTMLEditUtils::IsList(curParPar)
  8418       && nsHTMLEditUtils::IsListItem(curNode)) 
  8420     NS_ENSURE_STATE(mHTMLEditor);
  8421     res = mHTMLEditor->RemoveBlockContainer(curNode);
  8422     NS_ENSURE_SUCCESS(res, res);
  8423     *aOutOfList = true;
  8425   return res;
  8428 nsresult
  8429 nsHTMLEditRules::RemoveListStructure(nsIDOMNode *aList)
  8431   NS_ENSURE_ARG_POINTER(aList);
  8433   nsresult res;
  8435   nsCOMPtr<nsIDOMNode> child;
  8436   aList->GetFirstChild(getter_AddRefs(child));
  8438   while (child)
  8440     if (nsHTMLEditUtils::IsListItem(child))
  8442       bool bOutOfList;
  8443       do
  8445         res = PopListItem(child, &bOutOfList);
  8446         NS_ENSURE_SUCCESS(res, res);
  8447       } while (!bOutOfList);   // keep popping it out until it's not in a list anymore
  8449     else if (nsHTMLEditUtils::IsList(child))
  8451       res = RemoveListStructure(child);
  8452       NS_ENSURE_SUCCESS(res, res);
  8454     else
  8456       // delete any non- list items for now
  8457       NS_ENSURE_STATE(mHTMLEditor);
  8458       res = mHTMLEditor->DeleteNode(child);
  8459       NS_ENSURE_SUCCESS(res, res);
  8461     aList->GetFirstChild(getter_AddRefs(child));
  8463   // delete the now-empty list
  8464   NS_ENSURE_STATE(mHTMLEditor);
  8465   res = mHTMLEditor->RemoveBlockContainer(aList);
  8466   NS_ENSURE_SUCCESS(res, res);
  8468   return res;
  8472 nsresult 
  8473 nsHTMLEditRules::ConfirmSelectionInBody()
  8475   nsresult res = NS_OK;
  8477   // get the body  
  8478   NS_ENSURE_STATE(mHTMLEditor);
  8479   nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(mHTMLEditor->GetRoot());
  8480   NS_ENSURE_TRUE(rootElement, NS_ERROR_UNEXPECTED);
  8482   // get the selection
  8483   nsCOMPtr<nsISelection>selection;
  8484   NS_ENSURE_STATE(mHTMLEditor);
  8485   res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
  8486   NS_ENSURE_SUCCESS(res, res);
  8488   // get the selection start location
  8489   nsCOMPtr<nsIDOMNode> selNode, temp, parent;
  8490   int32_t selOffset;
  8491   NS_ENSURE_STATE(mHTMLEditor);
  8492   res = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset);
  8493   NS_ENSURE_SUCCESS(res, res);
  8494   temp = selNode;
  8496   // check that selNode is inside body
  8497   while (temp && !nsTextEditUtils::IsBody(temp))
  8499     res = temp->GetParentNode(getter_AddRefs(parent));
  8500     temp = parent;
  8503   // if we aren't in the body, force the issue
  8504   if (!temp) 
  8506 //    uncomment this to see when we get bad selections
  8507 //    NS_NOTREACHED("selection not in body");
  8508     selection->Collapse(rootElement, 0);
  8511   // get the selection end location
  8512   NS_ENSURE_STATE(mHTMLEditor);
  8513   res = mHTMLEditor->GetEndNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset);
  8514   NS_ENSURE_SUCCESS(res, res);
  8515   temp = selNode;
  8517   // check that selNode is inside body
  8518   while (temp && !nsTextEditUtils::IsBody(temp))
  8520     res = temp->GetParentNode(getter_AddRefs(parent));
  8521     temp = parent;
  8524   // if we aren't in the body, force the issue
  8525   if (!temp) 
  8527 //    uncomment this to see when we get bad selections
  8528 //    NS_NOTREACHED("selection not in body");
  8529     selection->Collapse(rootElement, 0);
  8532   return res;
  8536 nsresult 
  8537 nsHTMLEditRules::UpdateDocChangeRange(nsIDOMRange *aRange)
  8539   nsresult res = NS_OK;
  8541   // first make sure aRange is in the document.  It might not be if
  8542   // portions of our editting action involved manipulating nodes
  8543   // prior to placing them in the document (e.g., populating a list item
  8544   // before placing it in its list)
  8545   nsCOMPtr<nsIDOMNode> startNode;
  8546   res = aRange->GetStartContainer(getter_AddRefs(startNode));
  8547   NS_ENSURE_SUCCESS(res, res);
  8548   NS_ENSURE_STATE(mHTMLEditor);
  8549   if (!mHTMLEditor->IsDescendantOfRoot(startNode)) {
  8550     // just return - we don't need to adjust mDocChangeRange in this case
  8551     return NS_OK;
  8554   if (!mDocChangeRange)
  8556     // clone aRange.
  8557     nsCOMPtr<nsIDOMRange> range;
  8558     res = aRange->CloneRange(getter_AddRefs(range));
  8559     mDocChangeRange = static_cast<nsRange*>(range.get());
  8561   else
  8563     int16_t result;
  8565     // compare starts of ranges
  8566     res = mDocChangeRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START, aRange, &result);
  8567     if (res == NS_ERROR_NOT_INITIALIZED) {
  8568       // This will happen is mDocChangeRange is non-null, but the range is
  8569       // uninitialized. In this case we'll set the start to aRange start.
  8570       // The same test won't be needed further down since after we've set
  8571       // the start the range will be collapsed to that point.
  8572       result = 1;
  8573       res = NS_OK;
  8575     NS_ENSURE_SUCCESS(res, res);
  8576     if (result > 0)  // positive result means mDocChangeRange start is after aRange start
  8578       int32_t startOffset;
  8579       res = aRange->GetStartOffset(&startOffset);
  8580       NS_ENSURE_SUCCESS(res, res);
  8581       res = mDocChangeRange->SetStart(startNode, startOffset);
  8582       NS_ENSURE_SUCCESS(res, res);
  8585     // compare ends of ranges
  8586     res = mDocChangeRange->CompareBoundaryPoints(nsIDOMRange::END_TO_END, aRange, &result);
  8587     NS_ENSURE_SUCCESS(res, res);
  8588     if (result < 0)  // negative result means mDocChangeRange end is before aRange end
  8590       nsCOMPtr<nsIDOMNode> endNode;
  8591       int32_t endOffset;
  8592       res = aRange->GetEndContainer(getter_AddRefs(endNode));
  8593       NS_ENSURE_SUCCESS(res, res);
  8594       res = aRange->GetEndOffset(&endOffset);
  8595       NS_ENSURE_SUCCESS(res, res);
  8596       res = mDocChangeRange->SetEnd(endNode, endOffset);
  8597       NS_ENSURE_SUCCESS(res, res);
  8600   return res;
  8603 nsresult 
  8604 nsHTMLEditRules::InsertMozBRIfNeeded(nsIDOMNode *aNode)
  8606   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
  8607   if (!IsBlockNode(aNode)) return NS_OK;
  8609   bool isEmpty;
  8610   NS_ENSURE_STATE(mHTMLEditor);
  8611   nsresult res = mHTMLEditor->IsEmptyNode(aNode, &isEmpty);
  8612   NS_ENSURE_SUCCESS(res, res);
  8613   if (!isEmpty) {
  8614     return NS_OK;
  8617   return CreateMozBR(aNode, 0);
  8620 NS_IMETHODIMP 
  8621 nsHTMLEditRules::WillCreateNode(const nsAString& aTag, nsIDOMNode *aParent, int32_t aPosition)
  8623   return NS_OK;  
  8626 NS_IMETHODIMP 
  8627 nsHTMLEditRules::DidCreateNode(const nsAString& aTag, 
  8628                                nsIDOMNode *aNode, 
  8629                                nsIDOMNode *aParent, 
  8630                                int32_t aPosition, 
  8631                                nsresult aResult)
  8633   if (!mListenerEnabled) {
  8634     return NS_OK;
  8636   // assumption that Join keeps the righthand node
  8637   nsresult res = mUtilRange->SelectNode(aNode);
  8638   NS_ENSURE_SUCCESS(res, res);
  8639   res = UpdateDocChangeRange(mUtilRange);
  8640   return res;  
  8644 NS_IMETHODIMP 
  8645 nsHTMLEditRules::WillInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, int32_t aPosition)
  8647   return NS_OK;  
  8651 NS_IMETHODIMP 
  8652 nsHTMLEditRules::DidInsertNode(nsIDOMNode *aNode, 
  8653                                nsIDOMNode *aParent, 
  8654                                int32_t aPosition, 
  8655                                nsresult aResult)
  8657   if (!mListenerEnabled) {
  8658     return NS_OK;
  8660   nsresult res = mUtilRange->SelectNode(aNode);
  8661   NS_ENSURE_SUCCESS(res, res);
  8662   res = UpdateDocChangeRange(mUtilRange);
  8663   return res;  
  8667 NS_IMETHODIMP 
  8668 nsHTMLEditRules::WillDeleteNode(nsIDOMNode *aChild)
  8670   if (!mListenerEnabled) {
  8671     return NS_OK;
  8673   nsresult res = mUtilRange->SelectNode(aChild);
  8674   NS_ENSURE_SUCCESS(res, res);
  8675   res = UpdateDocChangeRange(mUtilRange);
  8676   return res;  
  8680 NS_IMETHODIMP 
  8681 nsHTMLEditRules::DidDeleteNode(nsIDOMNode *aChild, nsresult aResult)
  8683   return NS_OK;  
  8687 NS_IMETHODIMP 
  8688 nsHTMLEditRules::WillSplitNode(nsIDOMNode *aExistingRightNode, int32_t aOffset)
  8690   return NS_OK;  
  8694 NS_IMETHODIMP 
  8695 nsHTMLEditRules::DidSplitNode(nsIDOMNode *aExistingRightNode, 
  8696                               int32_t aOffset, 
  8697                               nsIDOMNode *aNewLeftNode, 
  8698                               nsresult aResult)
  8700   if (!mListenerEnabled) {
  8701     return NS_OK;
  8703   nsresult res = mUtilRange->SetStart(aNewLeftNode, 0);
  8704   NS_ENSURE_SUCCESS(res, res);
  8705   res = mUtilRange->SetEnd(aExistingRightNode, 0);
  8706   NS_ENSURE_SUCCESS(res, res);
  8707   res = UpdateDocChangeRange(mUtilRange);
  8708   return res;  
  8712 NS_IMETHODIMP 
  8713 nsHTMLEditRules::WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent)
  8715   if (!mListenerEnabled) {
  8716     return NS_OK;
  8718   // remember split point
  8719   nsresult res = nsEditor::GetLengthOfDOMNode(aLeftNode, mJoinOffset);
  8720   return res;  
  8724 NS_IMETHODIMP 
  8725 nsHTMLEditRules::DidJoinNodes(nsIDOMNode  *aLeftNode, 
  8726                               nsIDOMNode *aRightNode, 
  8727                               nsIDOMNode *aParent, 
  8728                               nsresult aResult)
  8730   if (!mListenerEnabled) {
  8731     return NS_OK;
  8733   // assumption that Join keeps the righthand node
  8734   nsresult res = mUtilRange->SetStart(aRightNode, mJoinOffset);
  8735   NS_ENSURE_SUCCESS(res, res);
  8736   res = mUtilRange->SetEnd(aRightNode, mJoinOffset);
  8737   NS_ENSURE_SUCCESS(res, res);
  8738   res = UpdateDocChangeRange(mUtilRange);
  8739   return res;  
  8743 NS_IMETHODIMP 
  8744 nsHTMLEditRules::WillInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString)
  8746   return NS_OK;  
  8750 NS_IMETHODIMP 
  8751 nsHTMLEditRules::DidInsertText(nsIDOMCharacterData *aTextNode, 
  8752                                   int32_t aOffset, 
  8753                                   const nsAString &aString, 
  8754                                   nsresult aResult)
  8756   if (!mListenerEnabled) {
  8757     return NS_OK;
  8759   int32_t length = aString.Length();
  8760   nsCOMPtr<nsIDOMNode> theNode = do_QueryInterface(aTextNode);
  8761   nsresult res = mUtilRange->SetStart(theNode, aOffset);
  8762   NS_ENSURE_SUCCESS(res, res);
  8763   res = mUtilRange->SetEnd(theNode, aOffset+length);
  8764   NS_ENSURE_SUCCESS(res, res);
  8765   res = UpdateDocChangeRange(mUtilRange);
  8766   return res;  
  8770 NS_IMETHODIMP 
  8771 nsHTMLEditRules::WillDeleteText(nsIDOMCharacterData *aTextNode, int32_t aOffset, int32_t aLength)
  8773   return NS_OK;  
  8777 NS_IMETHODIMP 
  8778 nsHTMLEditRules::DidDeleteText(nsIDOMCharacterData *aTextNode, 
  8779                                   int32_t aOffset, 
  8780                                   int32_t aLength, 
  8781                                   nsresult aResult)
  8783   if (!mListenerEnabled) {
  8784     return NS_OK;
  8786   nsCOMPtr<nsIDOMNode> theNode = do_QueryInterface(aTextNode);
  8787   nsresult res = mUtilRange->SetStart(theNode, aOffset);
  8788   NS_ENSURE_SUCCESS(res, res);
  8789   res = mUtilRange->SetEnd(theNode, aOffset);
  8790   NS_ENSURE_SUCCESS(res, res);
  8791   res = UpdateDocChangeRange(mUtilRange);
  8792   return res;  
  8795 NS_IMETHODIMP
  8796 nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection)
  8798   if (!mListenerEnabled) {
  8799     return NS_OK;
  8801   // get the (collapsed) selection location
  8802   nsCOMPtr<nsIDOMNode> selNode;
  8803   int32_t selOffset;
  8805   NS_ENSURE_STATE(mHTMLEditor);
  8806   nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
  8807   NS_ENSURE_SUCCESS(res, res);
  8808   res = mUtilRange->SetStart(selNode, selOffset);
  8809   NS_ENSURE_SUCCESS(res, res);
  8810   NS_ENSURE_STATE(mHTMLEditor);
  8811   res = mHTMLEditor->GetEndNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
  8812   NS_ENSURE_SUCCESS(res, res);
  8813   res = mUtilRange->SetEnd(selNode, selOffset);
  8814   NS_ENSURE_SUCCESS(res, res);
  8815   res = UpdateDocChangeRange(mUtilRange);
  8816   return res;  
  8819 NS_IMETHODIMP
  8820 nsHTMLEditRules::DidDeleteSelection(nsISelection *aSelection)
  8822   return NS_OK;
  8825 // Let's remove all alignment hints in the children of aNode; it can
  8826 // be an ALIGN attribute (in case we just remove it) or a CENTER
  8827 // element (here we have to remove the container and keep its
  8828 // children). We break on tables and don't look at their children.
  8829 nsresult
  8830 nsHTMLEditRules::RemoveAlignment(nsIDOMNode * aNode, const nsAString & aAlignType, bool aChildrenOnly)
  8832   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
  8834   NS_ENSURE_STATE(mHTMLEditor);
  8835   if (mHTMLEditor->IsTextNode(aNode) || nsHTMLEditUtils::IsTable(aNode)) return NS_OK;
  8836   nsresult res = NS_OK;
  8838   nsCOMPtr<nsIDOMNode> child = aNode,tmp;
  8839   if (aChildrenOnly)
  8841     aNode->GetFirstChild(getter_AddRefs(child));
  8843   NS_ENSURE_STATE(mHTMLEditor);
  8844   bool useCSS = mHTMLEditor->IsCSSEnabled();
  8846   while (child)
  8848     if (aChildrenOnly) {
  8849       // get the next sibling right now because we could have to remove child
  8850       child->GetNextSibling(getter_AddRefs(tmp));
  8852     else
  8854       tmp = nullptr;
  8856     bool isBlock;
  8857     NS_ENSURE_STATE(mHTMLEditor);
  8858     res = mHTMLEditor->NodeIsBlockStatic(child, &isBlock);
  8859     NS_ENSURE_SUCCESS(res, res);
  8861     if (nsEditor::NodeIsType(child, nsEditProperty::center))
  8863       // the current node is a CENTER element
  8864       // first remove children's alignment
  8865       res = RemoveAlignment(child, aAlignType, true);
  8866       NS_ENSURE_SUCCESS(res, res);
  8868       // we may have to insert BRs in first and last position of element's children
  8869       // if the nodes before/after are not blocks and not BRs
  8870       res = MakeSureElemStartsOrEndsOnCR(child);
  8871       NS_ENSURE_SUCCESS(res, res);
  8873       // now remove the CENTER container
  8874       NS_ENSURE_STATE(mHTMLEditor);
  8875       res = mHTMLEditor->RemoveContainer(child);
  8876       NS_ENSURE_SUCCESS(res, res);
  8878     else if (isBlock || nsHTMLEditUtils::IsHR(child))
  8880       // the current node is a block element
  8881       nsCOMPtr<nsIDOMElement> curElem = do_QueryInterface(child);
  8882       if (nsHTMLEditUtils::SupportsAlignAttr(child))
  8884         // remove the ALIGN attribute if this element can have it
  8885         NS_ENSURE_STATE(mHTMLEditor);
  8886         res = mHTMLEditor->RemoveAttribute(curElem, NS_LITERAL_STRING("align"));
  8887         NS_ENSURE_SUCCESS(res, res);
  8889       if (useCSS)
  8891         if (nsHTMLEditUtils::IsTable(child) || nsHTMLEditUtils::IsHR(child))
  8893           NS_ENSURE_STATE(mHTMLEditor);
  8894           res = mHTMLEditor->SetAttributeOrEquivalent(curElem, NS_LITERAL_STRING("align"), aAlignType, false); 
  8896         else
  8898           nsAutoString dummyCssValue;
  8899           NS_ENSURE_STATE(mHTMLEditor);
  8900           res = mHTMLEditor->mHTMLCSSUtils->RemoveCSSInlineStyle(child, nsEditProperty::cssTextAlign, dummyCssValue);
  8902         NS_ENSURE_SUCCESS(res, res);
  8904       if (!nsHTMLEditUtils::IsTable(child))
  8906         // unless this is a table, look at children
  8907         res = RemoveAlignment(child, aAlignType, true);
  8908         NS_ENSURE_SUCCESS(res, res);
  8911     child = tmp;
  8913   return NS_OK;
  8916 // Let's insert a BR as first (resp. last) child of aNode if its
  8917 // first (resp. last) child is not a block nor a BR, and if the
  8918 // previous (resp. next) sibling is not a block nor a BR
  8919 nsresult
  8920 nsHTMLEditRules::MakeSureElemStartsOrEndsOnCR(nsIDOMNode *aNode, bool aStarts)
  8922   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
  8924   nsCOMPtr<nsIDOMNode> child;
  8925   nsresult res;
  8926   if (aStarts)
  8928     NS_ENSURE_STATE(mHTMLEditor);
  8929     res = mHTMLEditor->GetFirstEditableChild(aNode, address_of(child));
  8931   else
  8933     NS_ENSURE_STATE(mHTMLEditor);
  8934     res = mHTMLEditor->GetLastEditableChild(aNode, address_of(child));
  8936   NS_ENSURE_SUCCESS(res, res);
  8937   NS_ENSURE_TRUE(child, NS_OK);
  8938   bool isChildBlock;
  8939   NS_ENSURE_STATE(mHTMLEditor);
  8940   res = mHTMLEditor->NodeIsBlockStatic(child, &isChildBlock);
  8941   NS_ENSURE_SUCCESS(res, res);
  8942   bool foundCR = false;
  8943   if (isChildBlock || nsTextEditUtils::IsBreak(child))
  8945     foundCR = true;
  8947   else
  8949     nsCOMPtr<nsIDOMNode> sibling;
  8950     if (aStarts)
  8952       NS_ENSURE_STATE(mHTMLEditor);
  8953       res = mHTMLEditor->GetPriorHTMLSibling(aNode, address_of(sibling));
  8955     else
  8957       NS_ENSURE_STATE(mHTMLEditor);
  8958       res = mHTMLEditor->GetNextHTMLSibling(aNode, address_of(sibling));
  8960     NS_ENSURE_SUCCESS(res, res);
  8961     if (sibling)
  8963       bool isBlock;
  8964       NS_ENSURE_STATE(mHTMLEditor);
  8965       res = mHTMLEditor->NodeIsBlockStatic(sibling, &isBlock);
  8966       NS_ENSURE_SUCCESS(res, res);
  8967       if (isBlock || nsTextEditUtils::IsBreak(sibling))
  8969         foundCR = true;
  8972     else
  8974       foundCR = true;
  8977   if (!foundCR)
  8979     int32_t offset = 0;
  8980     if (!aStarts) {
  8981       nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  8982       NS_ENSURE_STATE(node);
  8983       offset = node->GetChildCount();
  8985     nsCOMPtr<nsIDOMNode> brNode;
  8986     NS_ENSURE_STATE(mHTMLEditor);
  8987     res = mHTMLEditor->CreateBR(aNode, offset, address_of(brNode));
  8988     NS_ENSURE_SUCCESS(res, res);
  8990   return NS_OK;
  8993 nsresult
  8994 nsHTMLEditRules::MakeSureElemStartsOrEndsOnCR(nsIDOMNode *aNode)
  8996   nsresult res = MakeSureElemStartsOrEndsOnCR(aNode, false);
  8997   NS_ENSURE_SUCCESS(res, res);
  8998   res = MakeSureElemStartsOrEndsOnCR(aNode, true);
  8999   return res;
  9002 nsresult
  9003 nsHTMLEditRules::AlignBlock(nsIDOMElement * aElement, const nsAString * aAlignType, bool aContentsOnly)
  9005   NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
  9007   nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement);
  9008   bool isBlock = IsBlockNode(node);
  9009   if (!isBlock && !nsHTMLEditUtils::IsHR(node)) {
  9010     // we deal only with blocks; early way out
  9011     return NS_OK;
  9014   nsresult res = RemoveAlignment(node, *aAlignType, aContentsOnly);
  9015   NS_ENSURE_SUCCESS(res, res);
  9016   NS_NAMED_LITERAL_STRING(attr, "align");
  9017   NS_ENSURE_STATE(mHTMLEditor);
  9018   if (mHTMLEditor->IsCSSEnabled()) {
  9019     // let's use CSS alignment; we use margin-left and margin-right for tables
  9020     // and text-align for other block-level elements
  9021     NS_ENSURE_STATE(mHTMLEditor);
  9022     res = mHTMLEditor->SetAttributeOrEquivalent(aElement, attr, *aAlignType, false); 
  9023     NS_ENSURE_SUCCESS(res, res);
  9025   else {
  9026     // HTML case; this code is supposed to be called ONLY if the element
  9027     // supports the align attribute but we'll never know...
  9028     if (nsHTMLEditUtils::SupportsAlignAttr(node)) {
  9029       NS_ENSURE_STATE(mHTMLEditor);
  9030       res = mHTMLEditor->SetAttribute(aElement, attr, *aAlignType);
  9031       NS_ENSURE_SUCCESS(res, res);
  9034   return NS_OK;
  9037 nsresult
  9038 nsHTMLEditRules::RelativeChangeIndentationOfElementNode(nsIDOMNode *aNode, int8_t aRelativeChange)
  9040   NS_ENSURE_ARG_POINTER(aNode);
  9042   if (aRelativeChange != 1 && aRelativeChange != -1) {
  9043     return NS_ERROR_ILLEGAL_VALUE;
  9046   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
  9047   if (!element) {
  9048     return NS_OK;
  9051   NS_ENSURE_STATE(mHTMLEditor);
  9052   nsIAtom* marginProperty = MarginPropertyAtomForIndent(mHTMLEditor->mHTMLCSSUtils, element);    
  9053   nsAutoString value;
  9054   NS_ENSURE_STATE(mHTMLEditor);
  9055   mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(aNode, marginProperty, value);
  9056   float f;
  9057   nsCOMPtr<nsIAtom> unit;
  9058   NS_ENSURE_STATE(mHTMLEditor);
  9059   mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit));
  9060   if (0 == f) {
  9061     nsAutoString defaultLengthUnit;
  9062     NS_ENSURE_STATE(mHTMLEditor);
  9063     mHTMLEditor->mHTMLCSSUtils->GetDefaultLengthUnit(defaultLengthUnit);
  9064     unit = do_GetAtom(defaultLengthUnit);
  9066   if      (nsEditProperty::cssInUnit == unit)
  9067             f += NS_EDITOR_INDENT_INCREMENT_IN * aRelativeChange;
  9068   else if (nsEditProperty::cssCmUnit == unit)
  9069             f += NS_EDITOR_INDENT_INCREMENT_CM * aRelativeChange;
  9070   else if (nsEditProperty::cssMmUnit == unit)
  9071             f += NS_EDITOR_INDENT_INCREMENT_MM * aRelativeChange;
  9072   else if (nsEditProperty::cssPtUnit == unit)
  9073             f += NS_EDITOR_INDENT_INCREMENT_PT * aRelativeChange;
  9074   else if (nsEditProperty::cssPcUnit == unit)
  9075             f += NS_EDITOR_INDENT_INCREMENT_PC * aRelativeChange;
  9076   else if (nsEditProperty::cssEmUnit == unit)
  9077             f += NS_EDITOR_INDENT_INCREMENT_EM * aRelativeChange;
  9078   else if (nsEditProperty::cssExUnit == unit)
  9079             f += NS_EDITOR_INDENT_INCREMENT_EX * aRelativeChange;
  9080   else if (nsEditProperty::cssPxUnit == unit)
  9081             f += NS_EDITOR_INDENT_INCREMENT_PX * aRelativeChange;
  9082   else if (nsEditProperty::cssPercentUnit == unit)
  9083             f += NS_EDITOR_INDENT_INCREMENT_PERCENT * aRelativeChange;    
  9085   if (0 < f) {
  9086     nsAutoString newValue;
  9087     newValue.AppendFloat(f);
  9088     newValue.Append(nsDependentAtomString(unit));
  9089     NS_ENSURE_STATE(mHTMLEditor);
  9090     mHTMLEditor->mHTMLCSSUtils->SetCSSProperty(element, marginProperty, newValue, false);
  9091     return NS_OK;
  9094   NS_ENSURE_STATE(mHTMLEditor);
  9095   mHTMLEditor->mHTMLCSSUtils->RemoveCSSProperty(element, marginProperty, value, false);
  9097   // remove unnecessary DIV blocks:
  9098   // we could skip this section but that would cause a FAIL in
  9099   // editor/libeditor/html/tests/browserscope/richtext.html, which expects
  9100   // to unapply a CSS "indent" (<div style="margin-left: 40px;">) by
  9101   // removing the DIV container instead of just removing the CSS property.
  9102   nsCOMPtr<dom::Element> node = do_QueryInterface(aNode);
  9103   if (!node || !node->IsHTML(nsGkAtoms::div) ||
  9104       !mHTMLEditor ||
  9105       node == mHTMLEditor->GetActiveEditingHost() ||
  9106       !mHTMLEditor->IsDescendantOfEditorRoot(node) ||
  9107       nsHTMLEditor::HasAttributes(node)) {
  9108     NS_ENSURE_STATE(mHTMLEditor);
  9109     return NS_OK;
  9112   NS_ENSURE_STATE(mHTMLEditor);
  9113   return mHTMLEditor->RemoveContainer(element);
  9116 //
  9117 // Support for Absolute Positioning
  9118 //
  9120 nsresult
  9121 nsHTMLEditRules::WillAbsolutePosition(Selection* aSelection,
  9122                                       bool* aCancel, bool* aHandled)
  9124   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
  9125   nsresult res = WillInsert(aSelection, aCancel);
  9126   NS_ENSURE_SUCCESS(res, res);
  9128   // initialize out param
  9129   // we want to ignore result of WillInsert()
  9130   *aCancel = false;
  9131   *aHandled = true;
  9133   nsCOMPtr<nsIDOMElement> focusElement;
  9134   NS_ENSURE_STATE(mHTMLEditor);
  9135   res = mHTMLEditor->GetSelectionContainer(getter_AddRefs(focusElement));
  9136   if (focusElement) {
  9137     nsCOMPtr<nsIDOMNode> node = do_QueryInterface(focusElement);
  9138     if (nsHTMLEditUtils::IsImage(node)) {
  9139       mNewBlock = node;
  9140       return NS_OK;
  9144   res = NormalizeSelection(aSelection);
  9145   NS_ENSURE_SUCCESS(res, res);
  9146   NS_ENSURE_STATE(mHTMLEditor);
  9147   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
  9149   // convert the selection ranges into "promoted" selection ranges:
  9150   // this basically just expands the range to include the immediate
  9151   // block parent, and then further expands to include any ancestors
  9152   // whose children are all in the range
  9154   nsCOMArray<nsIDOMRange> arrayOfRanges;
  9155   res = GetPromotedRanges(aSelection, arrayOfRanges,
  9156                           EditAction::setAbsolutePosition);
  9157   NS_ENSURE_SUCCESS(res, res);
  9159   // use these ranges to contruct a list of nodes to act on.
  9160   nsCOMArray<nsIDOMNode> arrayOfNodes;
  9161   res = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
  9162                              EditAction::setAbsolutePosition);
  9163   NS_ENSURE_SUCCESS(res, res);                                 
  9165   NS_NAMED_LITERAL_STRING(divType, "div");
  9168   // if nothing visible in list, make an empty block
  9169   if (ListIsEmptyLine(arrayOfNodes))
  9171     nsCOMPtr<nsIDOMNode> parent, thePositionedDiv;
  9172     int32_t offset;
  9174     // get selection location
  9175     NS_ENSURE_STATE(mHTMLEditor);
  9176     res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset);
  9177     NS_ENSURE_SUCCESS(res, res);
  9178     // make sure we can put a block here
  9179     res = SplitAsNeeded(&divType, address_of(parent), &offset);
  9180     NS_ENSURE_SUCCESS(res, res);
  9181     NS_ENSURE_STATE(mHTMLEditor);
  9182     res = mHTMLEditor->CreateNode(divType, parent, offset, getter_AddRefs(thePositionedDiv));
  9183     NS_ENSURE_SUCCESS(res, res);
  9184     // remember our new block for postprocessing
  9185     mNewBlock = thePositionedDiv;
  9186     // delete anything that was in the list of nodes
  9187     for (int32_t j = arrayOfNodes.Count() - 1; j >= 0; --j) 
  9189       nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[0];
  9190       NS_ENSURE_STATE(mHTMLEditor);
  9191       res = mHTMLEditor->DeleteNode(curNode);
  9192       NS_ENSURE_SUCCESS(res, res);
  9193       arrayOfNodes.RemoveObjectAt(0);
  9195     // put selection in new block
  9196     res = aSelection->Collapse(thePositionedDiv,0);
  9197     selectionResetter.Abort();  // to prevent selection reseter from overriding us.
  9198     *aHandled = true;
  9199     return res;
  9202   // Ok, now go through all the nodes and put them in a blockquote, 
  9203   // or whatever is appropriate.  Wohoo!
  9204   int32_t i;
  9205   nsCOMPtr<nsIDOMNode> curParent, curPositionedDiv, curList, indentedLI, sibling;
  9206   int32_t listCount = arrayOfNodes.Count();
  9207   for (i=0; i<listCount; i++)
  9209     // here's where we actually figure out what to do
  9210     nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
  9212     // Ignore all non-editable nodes.  Leave them be.
  9213     NS_ENSURE_STATE(mHTMLEditor);
  9214     if (!mHTMLEditor->IsEditable(curNode)) continue;
  9216     int32_t offset;
  9217     curParent = nsEditor::GetNodeLocation(curNode, &offset);
  9219     // some logic for putting list items into nested lists...
  9220     if (nsHTMLEditUtils::IsList(curParent))
  9222       // check to see if curList is still appropriate.  Which it is if
  9223       // curNode is still right after it in the same list.
  9224       if (curList)
  9226         sibling = nullptr;
  9227         NS_ENSURE_STATE(mHTMLEditor);
  9228         mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
  9231       if (!curList || (sibling && sibling != curList) )
  9233         nsAutoString listTag;
  9234         nsEditor::GetTagString(curParent,listTag);
  9235         ToLowerCase(listTag);
  9236         // create a new nested list of correct type
  9237         res = SplitAsNeeded(&listTag, address_of(curParent), &offset);
  9238         NS_ENSURE_SUCCESS(res, res);
  9239         if (!curPositionedDiv) {
  9240           int32_t parentOffset;
  9241           nsCOMPtr<nsIDOMNode> curParentParent = nsEditor::GetNodeLocation(curParent, &parentOffset);
  9242           NS_ENSURE_STATE(mHTMLEditor);
  9243           res = mHTMLEditor->CreateNode(divType, curParentParent, parentOffset, getter_AddRefs(curPositionedDiv));
  9244           mNewBlock = curPositionedDiv;
  9246         NS_ENSURE_STATE(mHTMLEditor);
  9247         res = mHTMLEditor->CreateNode(listTag, curPositionedDiv, -1, getter_AddRefs(curList));
  9248         NS_ENSURE_SUCCESS(res, res);
  9249         // curList is now the correct thing to put curNode in
  9250         // remember our new block for postprocessing
  9251         // mNewBlock = curList;
  9253       // tuck the node into the end of the active list
  9254       NS_ENSURE_STATE(mHTMLEditor);
  9255       res = mHTMLEditor->MoveNode(curNode, curList, -1);
  9256       NS_ENSURE_SUCCESS(res, res);
  9257       // forget curPositionedDiv, if any
  9258       // curPositionedDiv = nullptr;
  9261     else // not a list item, use blockquote?
  9263       // if we are inside a list item, we don't want to blockquote, we want
  9264       // to sublist the list item.  We may have several nodes listed in the
  9265       // array of nodes to act on, that are in the same list item.  Since
  9266       // we only want to indent that li once, we must keep track of the most
  9267       // recent indented list item, and not indent it if we find another node
  9268       // to act on that is still inside the same li.
  9269       nsCOMPtr<nsIDOMNode> listitem=IsInListItem(curNode);
  9270       if (listitem)
  9272         if (indentedLI == listitem) continue;  // already indented this list item
  9273         curParent = nsEditor::GetNodeLocation(listitem, &offset);
  9274         // check to see if curList is still appropriate.  Which it is if
  9275         // curNode is still right after it in the same list.
  9276         if (curList)
  9278           sibling = nullptr;
  9279           NS_ENSURE_STATE(mHTMLEditor);
  9280           mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
  9283         if (!curList || (sibling && sibling != curList) )
  9285           nsAutoString listTag;
  9286           nsEditor::GetTagString(curParent,listTag);
  9287           ToLowerCase(listTag);
  9288           // create a new nested list of correct type
  9289           res = SplitAsNeeded(&listTag, address_of(curParent), &offset);
  9290           NS_ENSURE_SUCCESS(res, res);
  9291           if (!curPositionedDiv) {
  9292           int32_t parentOffset;
  9293           nsCOMPtr<nsIDOMNode> curParentParent = nsEditor::GetNodeLocation(curParent, &parentOffset);
  9294           NS_ENSURE_STATE(mHTMLEditor);
  9295           res = mHTMLEditor->CreateNode(divType, curParentParent, parentOffset, getter_AddRefs(curPositionedDiv));
  9296             mNewBlock = curPositionedDiv;
  9298           NS_ENSURE_STATE(mHTMLEditor);
  9299           res = mHTMLEditor->CreateNode(listTag, curPositionedDiv, -1, getter_AddRefs(curList));
  9300           NS_ENSURE_SUCCESS(res, res);
  9302         NS_ENSURE_STATE(mHTMLEditor);
  9303         res = mHTMLEditor->MoveNode(listitem, curList, -1);
  9304         NS_ENSURE_SUCCESS(res, res);
  9305         // remember we indented this li
  9306         indentedLI = listitem;
  9309       else
  9311         // need to make a div to put things in if we haven't already
  9313         if (!curPositionedDiv) 
  9315           if (nsHTMLEditUtils::IsDiv(curNode))
  9317             curPositionedDiv = curNode;
  9318             mNewBlock = curPositionedDiv;
  9319             curList = nullptr;
  9320             continue;
  9322           res = SplitAsNeeded(&divType, address_of(curParent), &offset);
  9323           NS_ENSURE_SUCCESS(res, res);
  9324           NS_ENSURE_STATE(mHTMLEditor);
  9325           res = mHTMLEditor->CreateNode(divType, curParent, offset, getter_AddRefs(curPositionedDiv));
  9326           NS_ENSURE_SUCCESS(res, res);
  9327           // remember our new block for postprocessing
  9328           mNewBlock = curPositionedDiv;
  9329           // curPositionedDiv is now the correct thing to put curNode in
  9332         // tuck the node into the end of the active blockquote
  9333         NS_ENSURE_STATE(mHTMLEditor);
  9334         res = mHTMLEditor->MoveNode(curNode, curPositionedDiv, -1);
  9335         NS_ENSURE_SUCCESS(res, res);
  9336         // forget curList, if any
  9337         curList = nullptr;
  9341   return res;
  9344 nsresult
  9345 nsHTMLEditRules::DidAbsolutePosition()
  9347   NS_ENSURE_STATE(mHTMLEditor);
  9348   nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor;
  9349   nsCOMPtr<nsIDOMElement> elt = do_QueryInterface(mNewBlock);
  9350   return absPosHTMLEditor->AbsolutelyPositionElement(elt, true);
  9353 nsresult
  9354 nsHTMLEditRules::WillRemoveAbsolutePosition(Selection* aSelection,
  9355                                             bool* aCancel, bool* aHandled) {
  9356   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
  9357   nsresult res = WillInsert(aSelection, aCancel);
  9358   NS_ENSURE_SUCCESS(res, res);
  9360   // initialize out param
  9361   // we want to ignore aCancel from WillInsert()
  9362   *aCancel = false;
  9363   *aHandled = true;
  9365   nsCOMPtr<nsIDOMElement>  elt;
  9366   NS_ENSURE_STATE(mHTMLEditor);
  9367   res = mHTMLEditor->GetAbsolutelyPositionedSelectionContainer(getter_AddRefs(elt));
  9368   NS_ENSURE_SUCCESS(res, res);
  9370   NS_ENSURE_STATE(mHTMLEditor);
  9371   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
  9373   NS_ENSURE_STATE(mHTMLEditor);
  9374   nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor;
  9375   return absPosHTMLEditor->AbsolutelyPositionElement(elt, false);
  9378 nsresult
  9379 nsHTMLEditRules::WillRelativeChangeZIndex(Selection* aSelection,
  9380                                           int32_t aChange,
  9381                                           bool *aCancel,
  9382                                           bool * aHandled)
  9384   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
  9385   nsresult res = WillInsert(aSelection, aCancel);
  9386   NS_ENSURE_SUCCESS(res, res);
  9388   // initialize out param
  9389   // we want to ignore aCancel from WillInsert()
  9390   *aCancel = false;
  9391   *aHandled = true;
  9393   nsCOMPtr<nsIDOMElement>  elt;
  9394   NS_ENSURE_STATE(mHTMLEditor);
  9395   res = mHTMLEditor->GetAbsolutelyPositionedSelectionContainer(getter_AddRefs(elt));
  9396   NS_ENSURE_SUCCESS(res, res);
  9398   NS_ENSURE_STATE(mHTMLEditor);
  9399   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
  9401   NS_ENSURE_STATE(mHTMLEditor);
  9402   nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor;
  9403   int32_t zIndex;
  9404   return absPosHTMLEditor->RelativeChangeElementZIndex(elt, aChange, &zIndex);
  9407 NS_IMETHODIMP
  9408 nsHTMLEditRules::DocumentModified()
  9410   nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, &nsHTMLEditRules::DocumentModifiedWorker));
  9411   return NS_OK;
  9414 void
  9415 nsHTMLEditRules::DocumentModifiedWorker()
  9417   if (!mHTMLEditor) {
  9418     return;
  9421   // DeleteNode below may cause a flush, which could destroy the editor
  9422   nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
  9424   nsCOMPtr<nsIHTMLEditor> kungFuDeathGrip(mHTMLEditor);
  9425   nsCOMPtr<nsISelection> selection;
  9426   nsresult rv = mHTMLEditor->GetSelection(getter_AddRefs(selection));
  9427   if (NS_FAILED(rv)) {
  9428     return;
  9431   // Delete our bogus node, if we have one, since the document might not be
  9432   // empty any more.
  9433   if (mBogusNode) {
  9434     mEditor->DeleteNode(mBogusNode);
  9435     mBogusNode = nullptr;
  9438   // Try to recreate the bogus node if needed.
  9439   CreateBogusNodeIfNeeded(selection);

mercurial